tiny.machine.instance
Defined in tiny.machine.
Two runs of one guest operating system from one starting state with one set of inputs do the same work and land in the same place.
API (54)
Actions
Public operations.
eventTranscriptDigest: A receipt binds itself to the events a turn produced, so the call hashes the filled slots as raw bytes, together with how many there are and the order they sit in.initWithStart: Boots a guest with accelerator semantics through a caller-supplied callback, so a backend harness can supply its own start callback while keeping the rest of the path unchanged.projectSemanticReceipt: A checkpoint keeps the authority-free form, so the call checks the receipt and copies out the form checkpoints work from.restoreWithAcquire: Restores a guest with accelerator semantics through a caller-supplied callback, so a backend harness can substitute the backend acquire callback during a restore.semanticReceiptDigest: Naming a stored guest state takes its digest, so the call checks the value, then hashes its canonical fields.validateRestore: Checks whether a direct restore would work and brings no backend up, so a caller verifies the operation before committing resources to it.validateStorageBytes: Accepts exactly one byte count, the value ofstorage_bytes, so a caller allocating storage knows the size is right before starting anything.verifyEventBatch: The events a turn produced have to belong to that turn, so the call decodes each filled slot and requires the handed-in fence on every encoded message in the kernel wire form, or frame.verifyEventBatchReceipt: One call ties a batch, a receipt, and a fence together, so the call checks the receipt against the fence, compares the batch's raw transcript digest with the one the receipt carries, and then checks every frame.verifyQuiescenceReceipt: A valid receipt from some other turn is worse than none, so the call checks the receipt and then requires its world, generation, and token to match the ones handed in.verifySemanticReceipt: A semantic receipt read back out of storage has to stand on its own, so the call runs every invariant the authority-free form has.
Types and contracts
Public types and contracts.
AcknowledgeErrorBackendAvailabilityBackendStageBasisErrorCaptureCheckpointErrorCaptureHotErrorDecodedEventDeliveryErrorDoorbell: Holds one yield code read off the fixed machine doorbell port, so a caller receives the value a guest yield carries.EventBatch: Room for every event one instance doorbell produces, so draining a doorbell has somewhere to put what comes out.EventBatchError: Checking a batch can go wrong in more ways than checking a receipt, so this error set reports the failures coming from decoding the bytes, matching the fence, checking the receipt, bounding the count, or comparing the transcript.EventErrorExecutionFingerprintExit: Reports one execution boundary in the form a caller sees, so everyrunanswers with this value and the answer decides whether the lifecycle goes on.Fault: Presents one terminal fault in a single shape across backends, so a caller reads the details once a run has ended in a fault.FaultKind: Records where a terminal execution fault came from, so a fault that ends a run states its source.InitErrorInput: Gathers everything one cold start of a K0 guest needs into one value, so a caller supplies all of this at once and knows how long to keep the image and the manifest around.Instance: Operates one K0 guest that is running now, borrowing its storage and whatever backs its guest memory, so every lifecycle call goes through this handle.K0State: The state K0 owns and keeps when it is reactivated, so a receipt carries the state the guest keeps across reactivation.MemoryErrorQuiescenceReceipt: The checkable evidence for one quiescent turn a caller has acknowledged, so a caller holds the evidence one settled turn leaves behind.QuiescenceReceiptErrorReactivateErrorRestoreErrorRestoreInput: Gathers everything needed to bring a checkpoint back into RAM the caller owns, so a caller supplies all of this at once for a restore.RestoreResult: Reports how a restore turned out, so the caller learns the state of the restored buffers across the three outcomes.RunErrorRunPhase: Represents the stage of one lifecycle as a caller can observe it, so the caller knows which lifecycle calls are legal at this moment.SemanticReceipt: The part of a quiescence receipt a checkpoint works from, so a checkpoint identifies guest state with this value.SettledTransport: The two ring positions at a settled guest boundary, which are equal there, so a caller reading a receipt learns where the transport stood when the guest came to rest.SharedRestoreInput: Gathers everything needed for a portable restore whose pages come from an immutable root provider, so the caller supplies a provider and branch storage.StartResult: Reports how a cold start turned out, so the caller learns the state of its buffers across the three outcomes.Storage: Holds the bytes a caller sets aside for one running instance, so the caller supplies the storage and holds it still.Unavailable
Namespaces
Public namespaces.
Values and defaults
Public values and defaults.
determinism_sourcesevent_batch_maxram_alignmentram_bytesrun_stutter_limitstorage_alignmentstorage_bytes
Source
Source: lib/machine/src/instance/owner.zig:209
pub const AcknowledgeError = input_admission.Error || os.abi.ring.Error || quiescence_receipt.Error || ProtocolError || OwnerError || core.memory.Error;Source: lib/machine/src/instance/owner.zig:214
pub const BasisError = ProtocolError || os.abi.ring.Error || core.memory.Error;Source: lib/machine/src/instance/owner.zig:218
pub const CaptureCheckpointError = QuiescenceReceiptError || OwnerError || checkpoint.Error || core.provenance.Error || layout.Error || core.memory.Error;Source: lib/machine/src/instance/owner.zig:220
pub const CaptureHotError = QuiescenceReceiptError || OwnerError || checkpoint.Error || core.provenance.Error || layout.Error || core.memory.Error;Source: lib/machine/src/instance/owner.zig:205
pub const DeliveryError = input_admission.Error || delivery.Error || os.abi.ring.Error || ProtocolError || core.memory.Error;Source: lib/machine/src/instance/owner.zig:207
pub const EventError = os.abi.message.Error || os.abi.ring.Error || ProtocolError || OwnerError || core.memory.Error;Source: lib/machine/src/instance/owner.zig:195
pub const InitError = MachineError || profile.Error || image_admission.Error || input_admission.Error || os.abi.boot.Error || os.abi.ring.Error || layout.Error;Source: lib/machine/src/instance/owner.zig:204
pub const MemoryError = core.memory.Error || error{Closed};Source: lib/machine/src/instance/owner.zig:212
pub const QuiescenceReceiptError = os.abi.ring.Error || quiescence_receipt.Error || QuiescenceProtocolError || core.memory.Error;Source: lib/machine/src/instance/owner.zig:215
pub const ReactivateError = os.abi.boot.Error || os.abi.ring.Error || backend.RestartFailure || ProtocolError || OwnerError || layout.Error;Source: lib/machine/src/instance/owner.zig:203
pub const RunError = backend.RunFailure || ProtocolError;Source: lib/machine/src/instance/owner.zig:827
/// Boots a guest with accelerator semantics through a caller-supplied callback,/// so a backend harness can supply its own start callback while keeping the/// rest of the path unchanged. The call answers with the same start outcome as/// `init`.pub fn initWithStart( storage: *Storage, ram: []align(layout.page_bytes) u8, input: types.Input, context: ?*anyopaque, start: backend.StartFunction,) StartResult { return initSelected( storage, ram, input, .accelerator, context, start, );}Source: lib/machine/src/instance/owner.zig:848
/// Restores a guest with accelerator semantics through a caller-supplied/// callback, so a backend harness can substitute the backend acquire callback/// during a restore. The checkpoint and the profile are checked first, and the/// callback runs after.pub fn restoreWithAcquire( storage: *Storage, ram: []align(layout.page_bytes) u8, input: types.RestoreInput, context: ?*anyopaque, acquire: backend.RestoreAcquireFunction,) RestoreResult { return restoreSelected( storage, ram, input, .accelerator, context, acquire, );}Source: lib/machine/src/instance/owner.zig:39
pub const run_stutter_limit: usize = 128;Source: lib/machine/src/instance/owner.zig:128
pub const storage_alignment: usize = @alignOf(OwnerState);Source: lib/machine/src/instance/owner.zig:129
pub const storage_bytes: usize = @sizeOf(OwnerState);Source: lib/machine/src/instance/owner.zig:869
/// Checks whether a direct restore would work and brings no backend up, so a/// caller verifies the operation before committing resources to it. The storage/// and the destination RAM are left as they were. When the restore would work,/// the call reports the checkpoint identity it verified.pub fn validateRestore( storage: *Storage, ram: []align(layout.page_bytes) u8, input: types.RestoreInput,) RestoreError!checkpoint.Identity { const selected: BackendKind = switch (input.profile.backend) { .linux_kvm_single_vcpu_v1 => .accelerator, .portable_x86_64_interpreter_v1 => .reference, }; return validateRestoreSelected(storage, ram, input, selected);}Source: lib/machine/src/instance/owner.zig:252
/// Accepts exactly one byte count, the value of `storage_bytes`, so a caller/// allocating storage knows the size is right before starting anything. Any/// other count comes back as `StorageBytesMismatch`.pub fn validateStorageBytes(bytes: usize) error{StorageBytesMismatch}!void { if (bytes != storage_bytes) return error.StorageBytesMismatch;}Source: lib/machine/src/instance/receipt/owner.zig:32
/// Checking a batch can go wrong in more ways than checking a receipt, so this/// error set reports the failures coming from decoding the bytes, matching the/// fence, checking the receipt, bounding the count, or comparing the/// transcript.pub const EventBatchError = os.abi.message.Error || os.abi.wire.FenceError || Error || EventBatchOwnerError;Source: lib/machine/src/instance/receipt/owner.zig:160
/// A checkpoint keeps the authority-free form, so the call checks the receipt/// and copies out the form checkpoints work from.pub fn projectSemantic( value: types.QuiescenceReceipt,) Error!types.SemanticReceipt { try verify(value); return semanticReceipt(value);}Source: lib/machine/src/instance/receipt/owner.zig:176
/// Naming a stored guest state takes its digest, so the call checks the value,/// then hashes its canonical fields.pub fn semanticReceiptDigest( value: types.SemanticReceipt,) Error!os.abi.Digest { try validateSemantic(value); return semanticReceiptHash(value);}Source: lib/machine/src/instance/receipt/owner.zig:253
/// A receipt binds itself to the events a turn produced, so the call hashes the/// filled slots as raw bytes, together with how many there are and the order/// they sit in. The count has to be at least one and no more than/// `event_batch_max`, which the call asserts.pub fn transcriptDigest(batch: *const types.EventBatch) os.abi.Digest { std.debug.assert(batch.count > 0); std.debug.assert(batch.count <= batch.storage.len); var hasher = Sha256.init(.{}); hasher.update(transcript_domain); hashInteger(&hasher, batch.count); for (batch.frames()) |*frame| hasher.update(frame); var output: os.abi.Digest = undefined; hasher.final(&output); return output;}Source: lib/machine/src/instance/receipt/owner.zig:111
/// The events a turn produced have to belong to that turn, so the call decodes/// each filled slot and requires the handed-in fence on every encoded message/// in the kernel wire form, or frame. A fence that fails its own check, a count/// past the slots, or a frame that will not decode all come back as/// `EventBatchError`.pub fn verifyEventBatch( batch: *const types.EventBatch, expected_fence: os.abi.ActivationFence,) EventBatchError!void { try os.abi.wire.validateFence(expected_fence); if (batch.count > batch.storage.len) { return error.EventBatchCountInvalid; } for (batch.storage[0..batch.count]) |*frame| { const decoded = try os.abi.decodeEvent(frame); if (!os.abi.wire.equalFence(decoded.header.fence, expected_fence)) { return error.EventBatchFenceMismatch; } }}Source: lib/machine/src/instance/receipt/owner.zig:130
/// One call ties a batch, a receipt, and a fence together, so the call checks/// the receipt against the fence, compares the batch's raw transcript digest/// with the one the receipt carries, and then checks every frame.pub fn verifyEventBatchReceipt( batch: *const types.EventBatch, value: types.QuiescenceReceipt, expected_fence: os.abi.ActivationFence,) EventBatchError!void { try verifyForFence(value, expected_fence); if (batch.count == 0 or batch.count > batch.storage.len) { return error.EventBatchCountInvalid; } if (!std.mem.eql( u8, &transcriptDigest(batch), &value.event_transcript_digest, )) { return error.EventTranscriptMismatch; } try verifyEventBatch(batch, expected_fence);}Source: lib/machine/src/instance/receipt/owner.zig:96
/// A valid receipt from some other turn is worse than none, so the call checks/// the receipt and then requires its world, generation, and token to match the/// ones handed in.pub fn verifyForFence( value: types.QuiescenceReceipt, expected_fence: os.abi.ActivationFence,) Error!void { try verify(value); if (!os.abi.wire.equalFence(value.fence, expected_fence)) { return error.InvalidQuiescenceReceipt; }}Source: lib/machine/src/instance/receipt/owner.zig:170
/// A semantic receipt read back out of storage has to stand on its own, so the/// call runs every invariant the authority-free form has. A value that/// disagrees with itself comes back as `InvalidQuiescenceReceipt`.pub fn verifySemantic(value: types.SemanticReceipt) Error!void { try validateSemantic(value);}Source: lib/machine/src/instance/root.zig
//! Two runs of one guest operating system from one starting state with one set//! of inputs do the same work and land in the same place. This code boots one//! small restricted kernel image, runs it inside the guest's RAM, and brings it//! to rest at points where a caller can read the state out and copy it. The//! guest's RAM is a fixed 67,108,864 bytes addressed from zero, which hold//! 16,384 pages of 4096 bytes, each page at a 4096-aligned address and named by//! its zero-based index. The host and the guest pass work to each other through//! two message queues at fixed addresses in that RAM, one carrying requests and//! one carrying events.//!//! A second run has to do what the first did, so nothing the host happens to be//! doing may reach the guest. A caller needs a place to stop where no work is//! in flight, because a stop like that is the only one worth copying and//! resuming. Someone reading the bytes later has to be able to tell that one//! stop happened under one stated authority with one stated input, with the//! machine long gone. The caller owns the memory: this code takes the bytes//! holding one run's whole state and the guest's RAM from the caller, and keeps//! both at fixed addresses until the run is closed.//!//! Running a guest on real hardware lets the host show through, by way of//! interrupts, timing, the processor's own identification, devices this design//! does not model, and instructions that read a clock or a hardware random//! source. An instruction whose answer comes from the host makes the second run//! differ, and noticing once the guest is already running is too late to help.//! Stopping wherever the instruction pointer happens to be leaves messages part//! drained and work outstanding, so the guest has to pick the moment. A machine//! with no virtualization at all still has to produce the same answer.//!//! Linux KVM, the Linux kernel interface for running a virtual machine on host//! hardware, faced the same execution problem. Its answer, in the [Linux KVM//! API](https://docs.kernel.org/virt/kvm/api.html), is a shape a host program//! follows: open the KVM device, make one virtual machine and one virtual CPU,//! give the kernel regions of the program's own memory to serve as the guest's//! physical memory, and enter the guest with a call that comes back at every//! boundary the host has to deal with. This code follows that shape: one//! virtual machine, one virtual CPU, one fixed region of caller memory//! registered as the guest's, a kernel entered with no firmware ahead of it,//! and a loop that runs and then deals with one boundary. This code also takes//! KVM's read-only regions, giving the guest's executable pages to the kernel//! in a form the guest cannot write.//!//! The code that executes the guest's instructions (a *backend*) is a named//! choice. One record holds that choice, carries its determinism claims, and//! pins the rules that code must follow (a *profile*), so a second//! implementation can answer to the same rules. A portable x86-64 interpreter,//! the *reference backend*, executes the admitted instruction shapes in//! software and reaches the same evidence on a machine with no KVM. Every//! instruction the guest may run is settled before the run starts, out of the//! kernel image's own record listing each admitted instruction position with//! its address and its instruction shape (an *execution manifest*), so an//! instruction whose answer would come from the host never gets to run. The//! boundaries a general host program would service, among them memory-mapped//! device access and hypercalls, end execution here as faults, because a run//! that arrived at one of them leaned on a device this design does not model.//! The guest's single way out is one byte written to one output port (a//! *doorbell*), whose value is a yield code saying that the guest is ready, has//! come to rest, or has faulted.//!//! Each outside input is checked against the position the guest has reached//! before the guest can see it (*admission*), in the `admission` namespace.//! Work proceeds one delivered input at a time, with the guest work that//! settles it, a *turn*. That turn proceeds under an authority of three values//! carried together, an *activation fence*: a world, a generation, and a token.//! A turn that takes an input first pulls out the events the guest's stop//! produced, then acknowledges the input, then offers up the evidence for that//! turn. That authenticated evidence, a *quiescence receipt*, binds the//! authority, the checked input, the evidence of which execution ran, both ring//! positions, and the stopped guest state. `reactivate` puts a newer authority//! in place for the next turn.//!//! Callers hand over `Storage` (the caller-owned bytes holding one lifecycle's//! whole state at an address the caller keeps stable) for one run at a time. A//! direct start and a direct restore also hand over page-aligned guest RAM. A//! shared restore hands over two things in place of that RAM: the//! content-addressed store that owns every stored byte of a checkpoint, and a//! fixed pool of caller-owned pages where the restore places its writes. The//! live execution (an *instance*) keeps every one of those buffers until//! `deinit`.//!//! - *K0*: the restricted kernel from the `lib/os` package that an instance//! boots.//! - *execution contract*: the execution rules one profile kind shares across//! backends, fixing RAM geometry, transport sizes, the determinism identity,//! and capacity limits.//! - *exit*: one public execution boundary an instance reports, a doorbell//! code, a halt, a shutdown, or a fault.//! - *event batch*: fixed storage for every event K0 emits at one doorbell.//! - *root store*: the authenticated, caller-supplied content-addressed store//! that owns every stored byte of a checkpoint.//! - *branch page pool*: the fixed pool of caller-owned pages where a shared//! restore places its writes.const core = @import("machine_instance_core");const owner = @import("owner.zig");const profile = @import("../profile/root.zig");const receipt = @import("receipt/root.zig");pub const types = @import("types.zig");pub const BackendAvailability = types.BackendAvailability;pub const BackendStage = types.BackendStage;pub const Doorbell = types.Doorbell;pub const DecodedEvent = types.DecodedEvent;pub const ExecutionFingerprint = types.ExecutionFingerprint;pub const Exit = types.Exit;pub const Fault = types.Fault;pub const FaultKind = types.FaultKind;pub const AcknowledgeError = owner.AcknowledgeError;pub const BasisError = owner.BasisError;pub const CaptureCheckpointError = owner.CaptureCheckpointError;pub const CaptureHotError = owner.CaptureHotError;pub const DeliveryError = owner.DeliveryError;pub const EventError = owner.EventError;pub const EventBatchError = receipt.EventBatchError;pub const EventBatch = types.EventBatch;pub const InitError = owner.InitError;pub const Input = types.Input;pub const Instance = owner.Instance;pub const K0State = types.K0State;pub const MemoryError = owner.MemoryError;pub const QuiescenceReceipt = types.QuiescenceReceipt;pub const SemanticReceipt = receipt.SemanticReceipt;pub const QuiescenceReceiptError = owner.QuiescenceReceiptError;pub const ReactivateError = owner.ReactivateError;pub const RestoreError = owner.RestoreError;pub const RestoreInput = types.RestoreInput;pub const RestoreResult = owner.RestoreResult;pub const SharedRestoreInput = types.SharedRestoreInput;pub const RunError = owner.RunError;pub const RunPhase = types.RunPhase;pub const SettledTransport = types.SettledTransport;pub const StartResult = owner.StartResult;pub const Storage = owner.Storage;pub const Unavailable = types.Unavailable;pub const storage_alignment = owner.storage_alignment;pub const storage_bytes = owner.storage_bytes;pub const run_stutter_limit = owner.run_stutter_limit;pub const event_batch_max = types.event_batch_max;pub const ram_alignment = core.layout.page_bytes;pub const ram_bytes = core.layout.ram_bytes;pub const eventTranscriptDigest = receipt.transcriptDigest;pub const initWithStart = owner.initWithStart;pub const projectSemanticReceipt = receipt.projectSemantic;pub const restoreWithAcquire = owner.restoreWithAcquire;pub const semanticReceiptDigest = receipt.semanticReceiptDigest;pub const validateStorageBytes = owner.validateStorageBytes;pub const validateRestore = owner.validateRestore;pub const verifyQuiescenceReceipt = receipt.verifyForFence;pub const verifyEventBatch = receipt.verifyEventBatch;pub const verifyEventBatchReceipt = receipt.verifyEventBatchReceipt;pub const verifySemanticReceipt = receipt.verifySemantic;pub const determinism_sources = [_]profile.DeterminismSource{ .executable_bytes, .instruction_forms, .instruction_flags, .initial_cpu_state, .backend_instruction_result, .cpuid_vendor, .cpuid_signature, .cpuid_page_size_extension, .cpuid_physical_address_extension, .cpuid_conditional_move, .cpuid_execute_disable, .cpuid_long_mode, .cpuid_address_widths, .cpuid_leaf_range, .guest_schedule, .host_run_stutter, .guest_interrupt, .host_signal_interrupt, .channel_device, .doorbell_exit, .unmodeled_device_exit, .cpu_clock_instruction, .cpu_entropy_instruction,};Source: lib/machine/src/root.zig:71
pub const instance = @import("instance/root.zig");Audit
| Definitions | 29 |
|---|---|
| Public names | 29 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |