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 };