lib/machine/src/instance/root.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 //! Two runs of one guest operating system from one starting state with one set
  2 //! of inputs do the same work and land in the same place. This code boots one
  3 //! small restricted kernel image, runs it inside the guest's RAM, and brings it
  4 //! to rest at points where a caller can read the state out and copy it. The
  5 //! guest's RAM is a fixed 67,108,864 bytes addressed from zero, which hold
  6 //! 16,384 pages of 4096 bytes, each page at a 4096-aligned address and named by
  7 //! its zero-based index. The host and the guest pass work to each other through
  8 //! two message queues at fixed addresses in that RAM, one carrying requests and
  9 //! one carrying events.
 10 //!
 11 //! A second run has to do what the first did, so nothing the host happens to be
 12 //! doing may reach the guest. A caller needs a place to stop where no work is
 13 //! in flight, because a stop like that is the only one worth copying and
 14 //! resuming. Someone reading the bytes later has to be able to tell that one
 15 //! stop happened under one stated authority with one stated input, with the
 16 //! machine long gone. The caller owns the memory: this code takes the bytes
 17 //! holding one run's whole state and the guest's RAM from the caller, and keeps
 18 //! both at fixed addresses until the run is closed.
 19 //!
 20 //! Running a guest on real hardware lets the host show through, by way of
 21 //! interrupts, timing, the processor's own identification, devices this design
 22 //! does not model, and instructions that read a clock or a hardware random
 23 //! source. An instruction whose answer comes from the host makes the second run
 24 //! differ, and noticing once the guest is already running is too late to help.
 25 //! Stopping wherever the instruction pointer happens to be leaves messages part
 26 //! drained and work outstanding, so the guest has to pick the moment. A machine
 27 //! with no virtualization at all still has to produce the same answer.
 28 //!
 29 //! Linux KVM, the Linux kernel interface for running a virtual machine on host
 30 //! hardware, faced the same execution problem. Its answer, in the [Linux KVM
 31 //! API](https://docs.kernel.org/virt/kvm/api.html), is a shape a host program
 32 //! follows: open the KVM device, make one virtual machine and one virtual CPU,
 33 //! give the kernel regions of the program's own memory to serve as the guest's
 34 //! physical memory, and enter the guest with a call that comes back at every
 35 //! boundary the host has to deal with. This code follows that shape: one
 36 //! virtual machine, one virtual CPU, one fixed region of caller memory
 37 //! registered as the guest's, a kernel entered with no firmware ahead of it,
 38 //! and a loop that runs and then deals with one boundary. This code also takes
 39 //! KVM's read-only regions, giving the guest's executable pages to the kernel
 40 //! in a form the guest cannot write.
 41 //!
 42 //! The code that executes the guest's instructions (a *backend*) is a named
 43 //! choice. One record holds that choice, carries its determinism claims, and
 44 //! pins the rules that code must follow (a *profile*), so a second
 45 //! implementation can answer to the same rules. A portable x86-64 interpreter,
 46 //! the *reference backend*, executes the admitted instruction shapes in
 47 //! software and reaches the same evidence on a machine with no KVM. Every
 48 //! instruction the guest may run is settled before the run starts, out of the
 49 //! kernel image's own record listing each admitted instruction position with
 50 //! its address and its instruction shape (an *execution manifest*), so an
 51 //! instruction whose answer would come from the host never gets to run. The
 52 //! boundaries a general host program would service, among them memory-mapped
 53 //! device access and hypercalls, end execution here as faults, because a run
 54 //! that arrived at one of them leaned on a device this design does not model.
 55 //! The guest's single way out is one byte written to one output port (a
 56 //! *doorbell*), whose value is a yield code saying that the guest is ready, has
 57 //! come to rest, or has faulted.
 58 //!
 59 //! Each outside input is checked against the position the guest has reached
 60 //! before the guest can see it (*admission*), in the `admission` namespace.
 61 //! Work proceeds one delivered input at a time, with the guest work that
 62 //! settles it, a *turn*. That turn proceeds under an authority of three values
 63 //! carried together, an *activation fence*: a world, a generation, and a token.
 64 //! A turn that takes an input first pulls out the events the guest's stop
 65 //! produced, then acknowledges the input, then offers up the evidence for that
 66 //! turn. That authenticated evidence, a *quiescence receipt*, binds the
 67 //! authority, the checked input, the evidence of which execution ran, both ring
 68 //! positions, and the stopped guest state. `reactivate` puts a newer authority
 69 //! in place for the next turn.
 70 //!
 71 //! Callers hand over `Storage` (the caller-owned bytes holding one lifecycle's
 72 //! whole state at an address the caller keeps stable) for one run at a time. A
 73 //! direct start and a direct restore also hand over page-aligned guest RAM. A
 74 //! shared restore hands over two things in place of that RAM: the
 75 //! content-addressed store that owns every stored byte of a checkpoint, and a
 76 //! fixed pool of caller-owned pages where the restore places its writes. The
 77 //! live execution (an *instance*) keeps every one of those buffers until
 78 //! `deinit`.
 79 //!
 80 //! - *K0*: the restricted kernel from the `lib/os` package that an instance
 81 //!   boots.
 82 //! - *execution contract*: the execution rules one profile kind shares across
 83 //!   backends, fixing RAM geometry, transport sizes, the determinism identity,
 84 //!   and capacity limits.
 85 //! - *exit*: one public execution boundary an instance reports, a doorbell
 86 //!   code, a halt, a shutdown, or a fault.
 87 //! - *event batch*: fixed storage for every event K0 emits at one doorbell.
 88 //! - *root store*: the authenticated, caller-supplied content-addressed store
 89 //!   that owns every stored byte of a checkpoint.
 90 //! - *branch page pool*: the fixed pool of caller-owned pages where a shared
 91 //!   restore places its writes.
 92 
 93 const core = @import("machine_instance_core");
 94 const owner = @import("owner.zig");
 95 const profile = @import("../profile/root.zig");
 96 const receipt = @import("receipt/root.zig");
 97 
 98 pub const types = @import("types.zig");
 99 
100 pub const BackendAvailability = types.BackendAvailability;
101 pub const BackendStage = types.BackendStage;
102 pub const Doorbell = types.Doorbell;
103 pub const DecodedEvent = types.DecodedEvent;
104 pub const ExecutionFingerprint = types.ExecutionFingerprint;
105 pub const Exit = types.Exit;
106 pub const Fault = types.Fault;
107 pub const FaultKind = types.FaultKind;
108 pub const AcknowledgeError = owner.AcknowledgeError;
109 pub const BasisError = owner.BasisError;
110 pub const CaptureCheckpointError = owner.CaptureCheckpointError;
111 pub const CaptureHotError = owner.CaptureHotError;
112 pub const DeliveryError = owner.DeliveryError;
113 pub const EventError = owner.EventError;
114 pub const EventBatchError = receipt.EventBatchError;
115 pub const EventBatch = types.EventBatch;
116 pub const InitError = owner.InitError;
117 pub const Input = types.Input;
118 pub const Instance = owner.Instance;
119 pub const K0State = types.K0State;
120 pub const MemoryError = owner.MemoryError;
121 pub const QuiescenceReceipt = types.QuiescenceReceipt;
122 pub const SemanticReceipt = receipt.SemanticReceipt;
123 pub const QuiescenceReceiptError = owner.QuiescenceReceiptError;
124 pub const ReactivateError = owner.ReactivateError;
125 pub const RestoreError = owner.RestoreError;
126 pub const RestoreInput = types.RestoreInput;
127 pub const RestoreResult = owner.RestoreResult;
128 pub const SharedRestoreInput = types.SharedRestoreInput;
129 pub const RunError = owner.RunError;
130 pub const RunPhase = types.RunPhase;
131 pub const SettledTransport = types.SettledTransport;
132 pub const StartResult = owner.StartResult;
133 pub const Storage = owner.Storage;
134 pub const Unavailable = types.Unavailable;
135 pub const storage_alignment = owner.storage_alignment;
136 pub const storage_bytes = owner.storage_bytes;
137 pub const run_stutter_limit = owner.run_stutter_limit;
138 pub const event_batch_max = types.event_batch_max;
139 pub const ram_alignment = core.layout.page_bytes;
140 pub const ram_bytes = core.layout.ram_bytes;
141 pub const eventTranscriptDigest = receipt.transcriptDigest;
142 pub const initWithStart = owner.initWithStart;
143 pub const projectSemanticReceipt = receipt.projectSemantic;
144 pub const restoreWithAcquire = owner.restoreWithAcquire;
145 pub const semanticReceiptDigest = receipt.semanticReceiptDigest;
146 pub const validateStorageBytes = owner.validateStorageBytes;
147 pub const validateRestore = owner.validateRestore;
148 pub const verifyQuiescenceReceipt = receipt.verifyForFence;
149 pub const verifyEventBatch = receipt.verifyEventBatch;
150 pub const verifyEventBatchReceipt = receipt.verifyEventBatchReceipt;
151 pub const verifySemanticReceipt = receipt.verifySemantic;
152 
153 pub const determinism_sources = [_]profile.DeterminismSource{
154     .executable_bytes,
155     .instruction_forms,
156     .instruction_flags,
157     .initial_cpu_state,
158     .backend_instruction_result,
159     .cpuid_vendor,
160     .cpuid_signature,
161     .cpuid_page_size_extension,
162     .cpuid_physical_address_extension,
163     .cpuid_conditional_move,
164     .cpuid_execute_disable,
165     .cpuid_long_mode,
166     .cpuid_address_widths,
167     .cpuid_leaf_range,
168     .guest_schedule,
169     .host_run_stutter,
170     .guest_interrupt,
171     .host_signal_interrupt,
172     .channel_device,
173     .doorbell_exit,
174     .unmodeled_device_exit,
175     .cpu_clock_instruction,
176     .cpu_entropy_instruction,
177 };