lib/machine/src/instance/test.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const isa = @import("isa");
2 const core = @import("machine_instance_core");
3 const std = @import("std");
4 const os = @import("os");
5 const sys = @import("sys");
6 const instance = @import("root.zig");
7 const admission = @import("../admission/root.zig");
8 const checkpoint = @import("../checkpoint/root.zig");
9 const profile = @import("../profile/root.zig");
10 const receipt_owner = @import("receipt/root.zig");
11 const test_support = @import("fixture/root.zig");
12
13 const fake = test_support.fake;
14 const fixture = test_support.fixture;
15 const harness = test_support.harness;
16
17 const real_k0_elf = @embedFile("machine-k0-elf");
18 const real_k0_manifest = @embedFile("machine-k0-manifest");
19 const manifest = os.boot.kernel.manifest;
20 const cpu = os.boot.kernel.cpu;
21 const layout = core.layout;
22 const protection = core.protection;
23 const test_page_present: u64 = 1 << 0;
24 const test_page_writable: u64 = 1 << 1;
25 const test_page_large: u64 = 1 << 7;
26 const test_page_no_execute: u64 = 1 << 63;
27
28 const ManifestMutation = struct {
29 storage: [manifest.encoded_bytes_max]u8,
30 len: u32,
31 fingerprint: manifest.Fingerprint,
32
33 fn bytes(self: *const ManifestMutation) []const u8 {
34 return self.storage[0..self.len];
35 }
36 };
37
38 const InputCompletion = struct {
39 root: os.abi.Digest,
40 receipt: admission.Receipt,
41 basis: admission.Basis,
42 quiescence: instance.QuiescenceReceipt,
43 };
44
45 const CheckpointBoundary = struct {
46 root: checkpoint.Root,
47 receipt: instance.QuiescenceReceipt,
48 };
49
50 const CapturedBoundary = struct {
51 checkpoint: checkpoint.Checkpoint,
52 boundary: CheckpointBoundary,
53 };
54
55 const PortableBoundaries = struct {
56 first: CheckpointBoundary,
57 second: CheckpointBoundary,
58 };
59
60 const MachineSnapshot = struct {
61 owner: [instance.storage_bytes]u8,
62 requests: os.abi.RequestRing.Storage,
63 events: os.abi.EventRing.Storage,
64 phase: instance.RunPhase,
65 };
66
67 const launch_state_bytes =
68 layout.page_table_count * layout.page_bytes + fixture.code.len;
69 var launch_state_snapshot: [launch_state_bytes]u8 = undefined;
70 var accelerator_checkpoint_ram: [checkpoint.ram_bytes]u8 align(checkpoint.ram_alignment) = undefined;
71 var mutation: ManifestMutation = undefined;
72 var mutation_loads: [manifest.loads_max]manifest.Load = undefined;
73 var mutation_forms: [manifest.forms_max]u64 = undefined;
74 var mutation_sites: [manifest.sites_max]manifest.Site = undefined;
75
76 test "one fixed-RAM K0 instance boots and completes an output doorbell" {
77 var operations: fake.Operations = .{};
78 var storage = instance.Storage.init();
79 const image = fixture.elf();
80 var machine = switch (harness.init(
81 &storage,
82 &fixture.ram,
83 try input(&image),
84 sys.kvm.operationsFor(&operations),
85 )) {
86 .ready => |value| value,
87 .unavailable => return error.UnexpectedBackendUnavailable,
88 .rejected => |failure| return failure,
89 };
90 try expectBootGeometry(&machine, &operations, &image);
91 captureLaunchState();
92
93 const run_exit = try machine.run();
94 try std.testing.expectEqual(.doorbell, std.meta.activeTag(run_exit));
95 try std.testing.expectEqual(os.abi.channel.DoorbellCode.ready, run_exit.doorbell.code);
96 try std.testing.expectEqual(@as(u8, 2), operations.run_count);
97 try std.testing.expect(operations.drain_immediate_exit);
98 try std.testing.expectEqual(
99 operations.instruction_pointer_after_io,
100 operations.instruction_pointer_after_drain,
101 );
102 try std.testing.expectEqual(instance.RunPhase.draining_activation, machine.phase());
103 const run_header: *const sys.kvm.abi.RunHeader = @ptrCast(&operations.run_storage);
104 try std.testing.expectEqual(@as(u8, 0), run_header.immediate_exit);
105 try expectLaunchStateUnchanged();
106
107 try expectLaunchStateUnchanged();
108
109 machine.deinit();
110 machine.deinit();
111 try std.testing.expectEqual(@as(usize, 1), operations.count(.unmap));
112 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(30));
113 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(20));
114 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
115 }
116
117 test "KVM launch installs the exact K0 CPU before memory and register state" {
118 var operations: fake.Operations = .{};
119 var storage = instance.Storage.init();
120 const image = fixture.elf();
121 var machine = switch (harness.init(
122 &storage,
123 &fixture.ram,
124 try input(&image),
125 sys.kvm.operationsFor(&operations),
126 )) {
127 .ready => |value| value,
128 .unavailable => return error.UnexpectedBackendUnavailable,
129 .rejected => |failure| return failure,
130 };
131 defer machine.deinit();
132
133 try expectInstalledCpu(&operations);
134 try expectControlOrder(&operations, &.{
135 sys.kvm.abi.request.get_supported_cpuid,
136 sys.kvm.abi.request.create_vm,
137 sys.kvm.abi.request.create_vcpu,
138 sys.kvm.abi.request.set_cpuid,
139 sys.kvm.abi.request.set_user_memory_region,
140 sys.kvm.abi.request.set_special_registers,
141 sys.kvm.abi.request.set_registers,
142 });
143 }
144
145 test "stale copied handles cannot operate a reused storage lifecycle" {
146 const image = fixture.elf();
147 const execution = try fixture.execution(&image);
148 var first_operations: fake.Operations = .{};
149 var storage = instance.Storage.init();
150 const pristine_storage = storage;
151 var first = switch (harness.init(
152 &storage,
153 &fixture.ram,
154 fixture.input(
155 instance.Input,
156 profile.kvmContinuationTestV1(),
157 &image,
158 &execution,
159 ),
160 sys.kvm.operationsFor(&first_operations),
161 )) {
162 .ready => |value| value,
163 .unavailable => return error.UnexpectedBackendUnavailable,
164 .rejected => |failure| return failure,
165 };
166 var stale = first;
167 first.deinit();
168 storage = pristine_storage;
169
170 var second_operations: fake.Operations = .{};
171 var second = switch (harness.init(
172 &storage,
173 &fixture.ram,
174 fixture.input(
175 instance.Input,
176 profile.kvmContinuationTestV1(),
177 &image,
178 &execution,
179 ),
180 sys.kvm.operationsFor(&second_operations),
181 )) {
182 .ready => |value| value,
183 .unavailable => return error.UnexpectedBackendUnavailable,
184 .rejected => |failure| return failure,
185 };
186 defer second.deinit();
187
188 try std.testing.expect(!stale.active());
189 try std.testing.expectEqual(instance.RunPhase.closed, stale.phase());
190 try std.testing.expectError(error.Closed, stale.run());
191 try std.testing.expectError(error.Closed, stale.admissionBasis());
192 stale.deinit();
193 try std.testing.expect(second.active());
194 try std.testing.expectEqual(instance.RunPhase.booting, second.phase());
195 const ready = try second.run();
196 try std.testing.expectEqual(.doorbell, std.meta.activeTag(ready));
197 try std.testing.expectEqual(
198 instance.RunPhase.draining_activation,
199 second.phase(),
200 );
201 }
202
203 test "portable reference backend executes verified Debug K0 to ready" {
204 const execution = try manifest.parse(real_k0_manifest);
205 var storage = instance.Storage.init();
206 var selected = realK0Input(execution);
207 selected.profile = profile.interpretedContinuationTestV1();
208 var machine = switch (instance.Instance.init(
209 &storage,
210 &fixture.ram,
211 selected,
212 )) {
213 .ready => |value| value,
214 .unavailable => return error.UnexpectedBackendUnavailable,
215 .rejected => |failure| return failure,
216 };
217 defer machine.deinit();
218 try std.testing.expectError(
219 error.QuiescenceUnavailable,
220 machine.quiescenceReceipt(),
221 );
222
223 const exit = try machine.run();
224 try std.testing.expectEqual(.doorbell, std.meta.activeTag(exit));
225 try std.testing.expectEqual(os.abi.channel.DoorbellCode.ready, exit.doorbell.code);
226 try std.testing.expectEqual(instance.RunPhase.draining_activation, machine.phase());
227 try expectRealK0Activation(&machine, selected, execution);
228 const admitted = try admission.prepare(
229 try machine.admissionBasis(),
230 try admission.terminal(0, os.k0.request_bytes),
231 );
232 const committed_root: os.abi.Digest = @splat(0x88);
233 const input_delivery = try admission.bindDelivery(
234 admitted,
235 committed_root,
236 selected.fence,
237 );
238 try machine.deliverAdmitted(&input_delivery);
239 try std.testing.expectEqual(instance.RunPhase.input_delivered, machine.phase());
240 const quiescent = try machine.run();
241 try std.testing.expectEqual(.doorbell, std.meta.activeTag(quiescent));
242 try std.testing.expectEqual(
243 os.abi.channel.DoorbellCode.quiescent,
244 quiescent.doorbell.code,
245 );
246 try std.testing.expectEqual(instance.RunPhase.draining_input, machine.phase());
247 try expectRealK0Completion(&machine);
248 try std.testing.expectEqual(
249 instance.RunPhase.awaiting_acknowledgement,
250 machine.phase(),
251 );
252 try std.testing.expectError(
253 error.QuiescenceUnavailable,
254 machine.quiescenceReceipt(),
255 );
256
257 const requests = layout.requestRing(machine.ram);
258 const clean_requests = requests.*;
259 const request_frontiers = try os.abi.RequestRing.frontiers(
260 requests,
261 selected.fence,
262 );
263 os.abi.wire.write64(
264 requests.header[24..32],
265 request_frontiers.consumed + 1,
266 );
267 os.abi.wire.write64(
268 requests.header[32..40],
269 request_frontiers.produced + 1,
270 );
271 const before_shifted_request_acknowledgement = captureMachine(&machine);
272 try std.testing.expectError(
273 error.EventReceiptMismatch,
274 machine.acknowledge(input_delivery.receipt),
275 );
276 try expectMachine(before_shifted_request_acknowledgement, &machine);
277 try std.testing.expectError(
278 error.QuiescenceUnavailable,
279 machine.quiescenceReceipt(),
280 );
281 requests.* = clean_requests;
282
283 const events = layout.eventRing(machine.ram);
284 const clean_events = events.*;
285 const event_frontiers = try os.abi.EventRing.frontiers(
286 events,
287 selected.fence,
288 );
289 os.abi.wire.write64(
290 events.header[24..32],
291 event_frontiers.consumed + 1,
292 );
293 os.abi.wire.write64(
294 events.header[32..40],
295 event_frontiers.produced + 1,
296 );
297 const before_shifted_event_acknowledgement = captureMachine(&machine);
298 try std.testing.expectError(
299 error.EventReceiptMismatch,
300 machine.acknowledge(input_delivery.receipt),
301 );
302 try expectMachine(before_shifted_event_acknowledgement, &machine);
303 events.* = clean_events;
304
305 try machine.acknowledge(input_delivery.receipt);
306 try std.testing.expectEqual(
307 instance.RunPhase.awaiting_reactivation,
308 machine.phase(),
309 );
310 const first_receipt = try machine.quiescenceReceipt();
311 try std.testing.expect(std.meta.eql(
312 first_receipt,
313 try machine.quiescenceReceipt(),
314 ));
315 try std.testing.expect(os.abi.wire.equalFence(
316 selected.fence,
317 first_receipt.fence,
318 ));
319 try std.testing.expect(std.meta.eql(
320 input_delivery.receipt,
321 first_receipt.delivery,
322 ));
323 try std.testing.expectEqualSlices(
324 u8,
325 &committed_root,
326 &first_receipt.basis.source_root,
327 );
328 try std.testing.expect(first_receipt.basis.outstanding_effect == null);
329 try std.testing.expectEqualSlices(
330 u8,
331 &selected.expected_execution_fingerprint.digest,
332 &first_receipt.execution_fingerprint.digest,
333 );
334 try std.testing.expectEqual(@as(u64, 2), first_receipt.block_root.generation);
335 try std.testing.expectEqualSlices(
336 u8,
337 &rootAfterOne(),
338 &first_receipt.block_root.digest,
339 );
340 try std.testing.expectEqual(@as(u64, 2), first_receipt.boundary.event_consumed);
341 try std.testing.expectEqual(@as(u64, 6), first_receipt.boundary.event_produced);
342 try std.testing.expectEqual(@as(u64, 1), first_receipt.settled.request_cursor);
343 try std.testing.expectEqual(@as(u64, 6), first_receipt.settled.event_cursor);
344 try std.testing.expectEqual(@as(u8, 1), first_receipt.k0.counter);
345 try instance.verifyQuiescenceReceipt(first_receipt, selected.fence);
346 var stale_fence = selected.fence;
347 stale_fence.generation += 1;
348 stale_fence.token = @splat(0x57);
349 try std.testing.expectError(
350 error.InvalidQuiescenceReceipt,
351 instance.verifyQuiescenceReceipt(first_receipt, stale_fence),
352 );
353
354 const settled_requests = layout.requestRing(machine.ram);
355 const clean_settled_requests = settled_requests.*;
356 os.abi.wire.write64(settled_requests.header[24..32], 0);
357 try std.testing.expectError(
358 error.EventReceiptMismatch,
359 machine.quiescenceReceipt(),
360 );
361 settled_requests.* = clean_settled_requests;
362 try std.testing.expect(std.meta.eql(
363 first_receipt,
364 try machine.quiescenceReceipt(),
365 ));
366 const settled_events = layout.eventRing(machine.ram);
367 const clean_settled_events = settled_events.*;
368 os.abi.wire.write64(settled_events.header[24..32], 5);
369 try std.testing.expectError(
370 error.EventReceiptMismatch,
371 machine.quiescenceReceipt(),
372 );
373 settled_events.* = clean_settled_events;
374 try std.testing.expect(std.meta.eql(
375 first_receipt,
376 try machine.quiescenceReceipt(),
377 ));
378 var next_fence = selected.fence;
379 next_fence.generation += 1;
380 next_fence.token = @splat(0x56);
381 const committed = try reactivateAndBasis(
382 &machine,
383 selected.fence,
384 next_fence,
385 );
386 try std.testing.expectEqualSlices(
387 u8,
388 &committed_root,
389 &committed.source_root,
390 );
391 try std.testing.expectEqual(@as(u64, 1), committed.frontiers.input);
392 try std.testing.expectError(
393 error.QuiescenceUnavailable,
394 machine.quiescenceReceipt(),
395 );
396
397 const before_stale_delivery = captureMachine(&machine);
398 try std.testing.expectError(
399 error.DeliveryFenceMismatch,
400 machine.deliverAdmitted(&input_delivery),
401 );
402 try expectMachine(before_stale_delivery, &machine);
403 const admitted_second = try admission.prepare(
404 committed,
405 try admission.terminal(
406 os.k0.request_bytes.len,
407 os.k0.request_bytes,
408 ),
409 );
410 const second_delivery = try admission.bindDelivery(
411 admitted_second,
412 @splat(0x99),
413 next_fence,
414 );
415 try machine.deliverAdmitted(&second_delivery);
416 const second_quiescent = try machine.run();
417 try std.testing.expectEqual(
418 os.abi.channel.DoorbellCode.quiescent,
419 second_quiescent.doorbell.code,
420 );
421 try expectSecondRealK0Completion(&machine);
422 const before_stale_acknowledgement = captureMachine(&machine);
423 try std.testing.expectError(
424 error.DeliveryReceiptMismatch,
425 machine.acknowledge(input_delivery.receipt),
426 );
427 try expectMachine(before_stale_acknowledgement, &machine);
428 try machine.acknowledge(second_delivery.receipt);
429 try std.testing.expectEqual(
430 instance.RunPhase.awaiting_reactivation,
431 machine.phase(),
432 );
433 const second_receipt = try machine.quiescenceReceipt();
434 try std.testing.expect(os.abi.wire.equalFence(
435 next_fence,
436 second_receipt.fence,
437 ));
438 try std.testing.expectEqual(@as(u64, 3), second_receipt.block_root.generation);
439 try std.testing.expectEqual(@as(u8, 2), second_receipt.k0.counter);
440 try std.testing.expect(!std.mem.eql(
441 u8,
442 &first_receipt.fenced_digest,
443 &second_receipt.fenced_digest,
444 ));
445 machine.deinit();
446 try std.testing.expectError(error.Closed, machine.quiescenceReceipt());
447 }
448
449 test "reference transcript and fake accelerator boundary issue identical receipts" {
450 const execution = try manifest.parse(real_k0_manifest);
451 const accelerated = try executeFakeAcceleratorReceipt(execution);
452 const interpreted = try executeInterpretedReceipt(execution);
453 try std.testing.expect(std.meta.eql(accelerated, interpreted));
454 }
455
456 test "quiescent accelerator checkpoint migrates through portable replay" {
457 const execution = try manifest.parse(real_k0_manifest);
458 var checkpoint_storage = checkpoint.Storage.init();
459 const accelerated = try captureFakeAcceleratorBoundary(
460 execution,
461 &checkpoint_storage,
462 );
463 const restored = try replayAcceleratorBoundary(
464 &accelerated,
465 &checkpoint_storage,
466 );
467 const baseline = try executePortableBoundaries(execution, &checkpoint_storage);
468 try std.testing.expectEqualDeep(
469 try receipt_owner.projectSemantic(accelerated.boundary.receipt),
470 try receipt_owner.projectSemantic(baseline.first.receipt),
471 );
472 try std.testing.expectEqualDeep(
473 accelerated.boundary.root.state,
474 baseline.first.root.state,
475 );
476 try std.testing.expect(!std.meta.eql(
477 accelerated.boundary.root.profile,
478 baseline.first.root.profile,
479 ));
480 try std.testing.expect(!std.meta.eql(
481 accelerated.boundary.root,
482 baseline.first.root,
483 ));
484 try std.testing.expectEqualDeep(
485 try receipt_owner.projectSemantic(restored.receipt),
486 try receipt_owner.projectSemantic(baseline.second.receipt),
487 );
488 try std.testing.expectEqualDeep(restored.root, baseline.second.root);
489 }
490
491 test "portable checkpoint migrates through quiescent accelerator replay" {
492 const execution = try manifest.parse(real_k0_manifest);
493 var checkpoint_storage = checkpoint.Storage.init();
494 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
495 const accelerated = try replayPortableBoundaryWithFakeAccelerator(
496 execution,
497 &portable,
498 &checkpoint_storage,
499 );
500 const baseline = try executePortableBoundaries(execution, &checkpoint_storage);
501 try std.testing.expectEqualDeep(portable.boundary.root, baseline.first.root);
502 try std.testing.expectEqualDeep(
503 try receipt_owner.projectSemantic(accelerated.receipt),
504 try receipt_owner.projectSemantic(baseline.second.receipt),
505 );
506 try std.testing.expectEqualDeep(
507 accelerated.root.state,
508 baseline.second.root.state,
509 );
510 try std.testing.expect(!std.meta.eql(
511 accelerated.root.profile,
512 baseline.second.root.profile,
513 ));
514 try std.testing.expect(!std.meta.eql(
515 accelerated.root,
516 baseline.second.root,
517 ));
518 }
519
520 test "accelerator restore validates before host acquisition and preserves unavailable RAM" {
521 const execution = try manifest.parse(real_k0_manifest);
522 var checkpoint_storage = checkpoint.Storage.init();
523 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
524 @memset(&fixture.ram, 0xa5);
525 var operations: fake.Operations = .{ .platform_supported = false };
526 var machine_storage = instance.Storage.init();
527 const fence: os.abi.ActivationFence = .{
528 .world = @splat(0xc1),
529 .generation = 1,
530 .token = @splat(0xc2),
531 };
532 var restore_input = checkpointRestoreInput(
533 &portable,
534 profile.kvmReconstructV1(),
535 fence,
536 );
537 const invalid = harness.restore(
538 &machine_storage,
539 &fixture.ram,
540 restore_input,
541 sys.kvm.operationsFor(&operations),
542 );
543 try std.testing.expectEqual(.rejected, std.meta.activeTag(invalid));
544 try std.testing.expectEqual(error.CheckpointContractMismatch, invalid.rejected);
545 try std.testing.expectEqual(@as(u8, 0), operations.platform_check_count);
546 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
547
548 restore_input.profile = profile.kvmContinuationTestV1();
549 const unavailable = harness.restore(
550 &machine_storage,
551 &fixture.ram,
552 restore_input,
553 sys.kvm.operationsFor(&operations),
554 );
555 try std.testing.expectEqual(.unavailable, std.meta.activeTag(unavailable));
556 try std.testing.expectEqual(
557 instance.BackendAvailability.unsupported,
558 unavailable.unavailable.availability,
559 );
560 try std.testing.expectEqual(@as(u8, 1), operations.platform_check_count);
561 try std.testing.expectEqual(@as(usize, 0), operations.record_count);
562 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
563 }
564
565 test "accelerator restore requires K0 capabilities before materializing RAM" {
566 const execution = try manifest.parse(real_k0_manifest);
567 var checkpoint_storage = checkpoint.Storage.init();
568 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
569 const cases = [_]fake.Operations{
570 .{ .read_only_memory_supported = false },
571 .{ .extended_cpuid_supported = false },
572 .{ .pse_supported = false },
573 .{ .pae_supported = false },
574 .{ .conditional_move_supported = false },
575 .{ .execute_disable_supported = false },
576 .{ .long_mode_supported = false },
577 .{ .physical_address_bits = cpu.physical_address_bits - 1 },
578 .{ .virtual_address_bits = cpu.virtual_address_bits - 1 },
579 };
580 for (cases) |configured| {
581 @memset(&fixture.ram, 0xa5);
582 var operations = configured;
583 var machine_storage = instance.Storage.init();
584 const result = harness.restore(
585 &machine_storage,
586 &fixture.ram,
587 checkpointRestoreInput(
588 &portable,
589 profile.kvmContinuationTestV1(),
590 .{ .world = @splat(0xe1), .generation = 1, .token = @splat(0xe2) },
591 ),
592 sys.kvm.operationsFor(&operations),
593 );
594 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
595 try std.testing.expectEqual(error.BackendUnsupported, result.rejected);
596 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
597 try std.testing.expectEqual(@as(usize, 0), operations.closeCount(20));
598 try std.testing.expectEqual(@as(usize, 0), operations.count(.map));
599 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
600 }
601 }
602
603 test "KVM restore installs the exact K0 CPU before memory and register state" {
604 const execution = try manifest.parse(real_k0_manifest);
605 var checkpoint_storage = checkpoint.Storage.init();
606 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
607 var operations: fake.Operations = .{};
608 var machine_storage = instance.Storage.init();
609 var machine = switch (harness.restore(
610 &machine_storage,
611 &fixture.ram,
612 checkpointRestoreInput(
613 &portable,
614 profile.kvmContinuationTestV1(),
615 .{ .world = @splat(0xd3), .generation = 1, .token = @splat(0xd4) },
616 ),
617 sys.kvm.operationsFor(&operations),
618 )) {
619 .ready => |value| value,
620 .unavailable => return error.UnexpectedBackendUnavailable,
621 .rejected => |failure| return failure,
622 };
623 defer machine.deinit();
624
625 try expectInstalledCpu(&operations);
626 try expectControlOrder(&operations, &.{
627 sys.kvm.abi.request.get_supported_cpuid,
628 sys.kvm.abi.request.create_vm,
629 sys.kvm.abi.request.create_vcpu,
630 sys.kvm.abi.request.set_cpuid,
631 sys.kvm.abi.request.set_user_memory_region,
632 sys.kvm.abi.request.set_special_registers,
633 sys.kvm.abi.request.set_registers,
634 });
635 }
636
637 test "KVM CPU installation failure preserves launch and restore RAM" {
638 const image = fixture.elf();
639 @memset(&fixture.ram, 0xa5);
640 var launch_operations: fake.Operations = .{ .fail_cpuid_write = true };
641 var launch_storage = instance.Storage.init();
642 const launch = harness.init(
643 &launch_storage,
644 &fixture.ram,
645 try input(&image),
646 sys.kvm.operationsFor(&launch_operations),
647 );
648 try std.testing.expectEqual(.rejected, std.meta.activeTag(launch));
649 try std.testing.expectEqual(error.BackendInvalidState, launch.rejected);
650 try expectFakeResourcesClosed(&launch_operations, 1);
651 try std.testing.expectEqual(@as(usize, 0), launch_operations.registeredRegions().len);
652 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
653
654 const execution = try manifest.parse(real_k0_manifest);
655 var checkpoint_storage = checkpoint.Storage.init();
656 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
657 @memset(&fixture.ram, 0x5a);
658 var restore_operations: fake.Operations = .{ .fail_cpuid_write = true };
659 var restore_storage = instance.Storage.init();
660 const restored = harness.restore(
661 &restore_storage,
662 &fixture.ram,
663 checkpointRestoreInput(
664 &portable,
665 profile.kvmContinuationTestV1(),
666 .{ .world = @splat(0xd5), .generation = 1, .token = @splat(0xd6) },
667 ),
668 sys.kvm.operationsFor(&restore_operations),
669 );
670 try std.testing.expectEqual(.rejected, std.meta.activeTag(restored));
671 try std.testing.expectEqual(error.BackendInvalidState, restored.rejected);
672 try expectFakeResourcesClosed(&restore_operations, 1);
673 try std.testing.expectEqual(@as(usize, 0), restore_operations.registeredRegions().len);
674 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0x5a));
675 }
676
677 test "KVM CPU capability query failure precedes VM creation and RAM population" {
678 const image = fixture.elf();
679 @memset(&fixture.ram, 0xa5);
680 var operations: fake.Operations = .{ .fail_supported_cpuid = true };
681 var storage = instance.Storage.init();
682 const result = harness.init(
683 &storage,
684 &fixture.ram,
685 try input(&image),
686 sys.kvm.operationsFor(&operations),
687 );
688 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
689 try std.testing.expectEqual(error.BackendFailure, result.rejected);
690 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
691 try std.testing.expectEqual(@as(usize, 0), operations.closeCount(20));
692 try std.testing.expectEqual(@as(usize, 0), operations.count(.map));
693 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
694 }
695
696 test "accelerator restore closes partial KVM state and retries" {
697 const execution = try manifest.parse(real_k0_manifest);
698 var checkpoint_storage = checkpoint.Storage.init();
699 const portable = try capturePortableBoundary(execution, &checkpoint_storage);
700 var operations: fake.Operations = .{ .fail_register_write = true };
701 var machine_storage = instance.Storage.init();
702 const restore_input = checkpointRestoreInput(
703 &portable,
704 profile.kvmContinuationTestV1(),
705 .{ .world = @splat(0xd1), .generation = 1, .token = @splat(0xd2) },
706 );
707 const failed = harness.restore(
708 &machine_storage,
709 &fixture.ram,
710 restore_input,
711 sys.kvm.operationsFor(&operations),
712 );
713 try std.testing.expectEqual(.rejected, std.meta.activeTag(failed));
714 try std.testing.expectEqual(error.BackendFailure, failed.rejected);
715 try expectFakeResourcesClosed(&operations, 1);
716
717 operations.fail_register_write = false;
718 var machine = switch (harness.restore(
719 &machine_storage,
720 &fixture.ram,
721 restore_input,
722 sys.kvm.operationsFor(&operations),
723 )) {
724 .ready => |value| value,
725 .unavailable => return error.UnexpectedBackendUnavailable,
726 .rejected => |failure| return failure,
727 };
728 try std.testing.expectEqual(instance.RunPhase.booting, machine.phase());
729 machine.deinit();
730 try expectFakeResourcesClosed(&operations, 2);
731 }
732
733 test "KVM reactivation replaces the VM and vCPU execution context" {
734 const execution = try manifest.parse(real_k0_manifest);
735 var operations: fake.Operations = .{};
736 var storage = instance.Storage.init();
737 const selected = realK0Input(execution);
738 var machine = switch (harness.init(
739 &storage,
740 &fixture.ram,
741 selected,
742 sys.kvm.operationsFor(&operations),
743 )) {
744 .ready => |value| value,
745 .unavailable => return error.UnexpectedBackendUnavailable,
746 .rejected => |failure| return failure,
747 };
748 defer machine.deinit();
749
750 var guest = os.k0.Owner.init();
751 try std.testing.expectEqual(
752 os.abi.channel.DoorbellCode.ready,
753 try guest.activate(
754 bootWire(&machine),
755 layout.requestRing(machine.ram),
756 layout.eventRing(machine.ram),
757 ),
758 );
759 const initial_ready = try machine.run();
760 try std.testing.expectEqual(.doorbell, std.meta.activeTag(initial_ready));
761 try std.testing.expectEqual(
762 os.abi.channel.DoorbellCode.ready,
763 initial_ready.doorbell.code,
764 );
765 var batch: instance.EventBatch = undefined;
766 try machine.takeEvents(&batch);
767 const delivery_value = try receiptTestDelivery(&machine, selected.fence);
768 try machine.deliverAdmitted(&delivery_value);
769 try std.testing.expectEqual(
770 os.abi.channel.DoorbellCode.quiescent,
771 try guest.consume(),
772 );
773 operations.run_count = 0;
774 operations.io_code = @backingInt(os.abi.channel.DoorbellCode.quiescent);
775 _ = try machine.run();
776 try std.testing.expect(operations.drain_immediate_exit);
777 try machine.takeEvents(&batch);
778 try machine.acknowledge(delivery_value.receipt);
779
780 const records_before_restart = operations.record_count;
781 const boot_tail: usize = @intCast(
782 layout.boot_frame_address + os.abi.boot.frame_bytes,
783 );
784 const stack_start: usize = @intCast(layout.stack_base);
785 machine.ram[boot_tail] = 0xa5;
786 machine.ram[stack_start] = 0x5a;
787 operations.vm_opaque_state = 0xa55a;
788 operations.vcpu_opaque_state = 0x5aa5;
789 var next = selected.fence;
790 next.generation += 1;
791 next.token = @splat(0x56);
792 try machine.reactivate(next);
793
794 try std.testing.expectEqual(
795 fake.Action.unmap,
796 operations.records[records_before_restart].action,
797 );
798 try std.testing.expectEqual(
799 fake.Action.close,
800 operations.records[records_before_restart + 1].action,
801 );
802 try std.testing.expectEqual(
803 @as(std.posix.fd_t, 30),
804 operations.records[records_before_restart + 1].descriptor,
805 );
806 try std.testing.expectEqual(
807 fake.Action.close,
808 operations.records[records_before_restart + 2].action,
809 );
810 try std.testing.expectEqual(
811 @as(std.posix.fd_t, 20),
812 operations.records[records_before_restart + 2].descriptor,
813 );
814 try expectControlOrderFrom(&operations, records_before_restart, &.{
815 sys.kvm.abi.request.create_vm,
816 sys.kvm.abi.request.create_vcpu,
817 sys.kvm.abi.request.set_cpuid,
818 sys.kvm.abi.request.set_user_memory_region,
819 sys.kvm.abi.request.set_special_registers,
820 sys.kvm.abi.request.set_registers,
821 });
822 try std.testing.expectEqual(@as(u64, 0), operations.vm_opaque_state);
823 try std.testing.expectEqual(@as(u64, 0), operations.vcpu_opaque_state);
824 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(20));
825 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(30));
826 try std.testing.expectEqual(@as(usize, 1), operations.count(.unmap));
827 try std.testing.expectEqual(@as(usize, 2), operations.count(.map));
828 try expectInstalledCpu(&operations);
829 try expectInitialState(&operations, execution.header.facts.initial);
830 try expectRegisteredRam(&machine, &operations, execution);
831 try std.testing.expectEqual(@as(u8, 0), machine.ram[boot_tail]);
832 try std.testing.expectEqual(@as(u8, 0), machine.ram[stack_start]);
833
834 var next_guest = os.k0.Owner.init();
835 try std.testing.expectEqual(
836 os.abi.channel.DoorbellCode.ready,
837 try next_guest.activate(
838 bootWire(&machine),
839 layout.requestRing(machine.ram),
840 layout.eventRing(machine.ram),
841 ),
842 );
843 operations.run_count = 0;
844 operations.io_code = @backingInt(os.abi.channel.DoorbellCode.ready);
845 const ready = try machine.run();
846 try std.testing.expectEqual(
847 os.abi.channel.DoorbellCode.ready,
848 ready.doorbell.code,
849 );
850 try machine.takeEvents(&batch);
851 _ = try machine.admissionBasis();
852 }
853
854 test "partial KVM restart failure is terminal" {
855 const execution = try manifest.parse(real_k0_manifest);
856 var operations: fake.Operations = .{};
857 var storage = instance.Storage.init();
858 const selected = realK0Input(execution);
859 var machine = switch (harness.init(
860 &storage,
861 &fixture.ram,
862 selected,
863 sys.kvm.operationsFor(&operations),
864 )) {
865 .ready => |value| value,
866 .unavailable => return error.UnexpectedBackendUnavailable,
867 .rejected => |failure| return failure,
868 };
869 defer machine.deinit();
870
871 var guest = os.k0.Owner.init();
872 _ = try guest.activate(
873 bootWire(&machine),
874 layout.requestRing(machine.ram),
875 layout.eventRing(machine.ram),
876 );
877 _ = try machine.run();
878 var batch: instance.EventBatch = undefined;
879 try machine.takeEvents(&batch);
880 const delivery_value = try receiptTestDelivery(&machine, selected.fence);
881 try machine.deliverAdmitted(&delivery_value);
882 _ = try guest.consume();
883 operations.run_count = 0;
884 operations.io_code = @backingInt(os.abi.channel.DoorbellCode.quiescent);
885 const quiescent = try machine.run();
886 try std.testing.expectEqual(.doorbell, std.meta.activeTag(quiescent));
887 try std.testing.expectEqual(
888 os.abi.channel.DoorbellCode.quiescent,
889 quiescent.doorbell.code,
890 );
891 try machine.takeEvents(&batch);
892 try machine.acknowledge(delivery_value.receipt);
893
894 operations.fail_register_write = true;
895 const boot_tail: usize = @intCast(
896 layout.boot_frame_address + os.abi.boot.frame_bytes,
897 );
898 const stack_start: usize = @intCast(layout.stack_base);
899 machine.ram[boot_tail] = 0xa5;
900 machine.ram[stack_start] = 0x5a;
901 var next = selected.fence;
902 next.generation += 1;
903 next.token = @splat(0x56);
904 try std.testing.expectError(error.BackendFailure, machine.reactivate(next));
905 try std.testing.expectEqual(instance.RunPhase.failed, machine.phase());
906 try std.testing.expectError(error.Closed, machine.run());
907 try std.testing.expectEqual(@as(u8, 0xa5), machine.ram[boot_tail]);
908 try std.testing.expectEqual(@as(u8, 0x5a), machine.ram[stack_start]);
909 try std.testing.expectEqual(@as(usize, 2), operations.closeCount(20));
910 try std.testing.expectEqual(@as(usize, 2), operations.closeCount(30));
911 try std.testing.expectEqual(@as(usize, 2), operations.count(.unmap));
912 machine.deinit();
913 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
914 try std.testing.expectEqual(@as(usize, 2), operations.closeCount(20));
915 try std.testing.expectEqual(@as(usize, 2), operations.closeCount(30));
916 try std.testing.expectEqual(@as(usize, 2), operations.count(.unmap));
917 }
918
919 test "shifted empty transport frontiers reject basis and delivery atomically" {
920 const execution = try manifest.parse(real_k0_manifest);
921 var storage = instance.Storage.init();
922 var selected = realK0Input(execution);
923 selected.profile = profile.interpretedContinuationTestV1();
924 var machine = switch (instance.Instance.init(
925 &storage,
926 &fixture.ram,
927 selected,
928 )) {
929 .ready => |value| value,
930 .unavailable => return error.UnexpectedBackendUnavailable,
931 .rejected => |failure| return failure,
932 };
933 defer machine.deinit();
934
935 _ = try machine.run();
936 var batch: instance.EventBatch = undefined;
937 try machine.takeEvents(&batch);
938 const trusted_basis = try machine.admissionBasis();
939 const admitted = try admission.prepare(
940 trusted_basis,
941 try admission.terminal(0, os.k0.request_bytes),
942 );
943 const input_delivery = try admission.bindDelivery(
944 admitted,
945 @splat(0x88),
946 selected.fence,
947 );
948
949 const requests = layout.requestRing(machine.ram);
950 const clean_requests = requests.*;
951 os.abi.wire.write64(requests.header[24..32], 1);
952 os.abi.wire.write64(requests.header[32..40], 1);
953 const shifted_requests = captureMachine(&machine);
954 try std.testing.expectError(
955 error.EventReceiptMismatch,
956 machine.admissionBasis(),
957 );
958 try expectMachine(shifted_requests, &machine);
959 try std.testing.expectError(
960 error.EventReceiptMismatch,
961 machine.deliverAdmitted(&input_delivery),
962 );
963 try expectMachine(shifted_requests, &machine);
964 requests.* = clean_requests;
965
966 const events = layout.eventRing(machine.ram);
967 const clean_events = events.*;
968 os.abi.wire.write64(events.header[24..32], 3);
969 os.abi.wire.write64(events.header[32..40], 3);
970 const shifted_events = captureMachine(&machine);
971 try std.testing.expectError(
972 error.EventReceiptMismatch,
973 machine.admissionBasis(),
974 );
975 try expectMachine(shifted_events, &machine);
976 try std.testing.expectError(
977 error.EventReceiptMismatch,
978 machine.deliverAdmitted(&input_delivery),
979 );
980 try expectMachine(shifted_events, &machine);
981 events.* = clean_events;
982
983 try machine.deliverAdmitted(&input_delivery);
984 }
985
986 test "fresh-fence replay survives request-push and pre-acknowledgement crash cuts" {
987 const execution = try manifest.parse(real_k0_manifest);
988 var selected = realK0Input(execution);
989 selected.profile = profile.interpretedContinuationTestV1();
990 selected.fence.generation = 31;
991 selected.fence.token = @splat(0x71);
992 var storage = instance.Storage.init();
993 var machine = try startRealMachine(&storage, selected);
994
995 const admitted = try admission.prepare(
996 try activateAndBasis(&machine),
997 try admission.terminal(0, os.k0.request_bytes),
998 );
999 const first_delivery = try admission.bindDelivery(
1000 admitted,
1001 @splat(0x91),
1002 selected.fence,
1003 );
1004 try machine.deliverAdmitted(&first_delivery);
1005 try std.testing.expectError(
1006 error.QuiescenceUnavailable,
1007 machine.quiescenceReceipt(),
1008 );
1009 machine.deinit();
1010
1011 selected.fence.generation += 1;
1012 selected.fence.token = @splat(0x72);
1013 machine = try startRealMachine(&storage, selected);
1014 try std.testing.expectEqualDeep(
1015 admitted.frontiers,
1016 (try activateAndBasis(&machine)).frontiers,
1017 );
1018 const second_delivery = try admission.bindDelivery(
1019 admitted,
1020 @splat(0x91),
1021 selected.fence,
1022 );
1023 try machine.deliverAdmitted(&second_delivery);
1024 _ = try machine.run();
1025 var after_request_push: instance.EventBatch = undefined;
1026 try machine.takeEvents(&after_request_push);
1027 try std.testing.expectError(
1028 error.QuiescenceUnavailable,
1029 machine.quiescenceReceipt(),
1030 );
1031 machine.deinit();
1032
1033 selected.fence.generation += 1;
1034 selected.fence.token = @splat(0x73);
1035 machine = try startRealMachine(&storage, selected);
1036 try std.testing.expectEqualDeep(
1037 admitted.frontiers,
1038 (try activateAndBasis(&machine)).frontiers,
1039 );
1040 const third_delivery = try admission.bindDelivery(
1041 admitted,
1042 @splat(0x91),
1043 selected.fence,
1044 );
1045 try machine.deliverAdmitted(&third_delivery);
1046 _ = try machine.run();
1047 var after_validated_output: instance.EventBatch = undefined;
1048 try machine.takeEvents(&after_validated_output);
1049 try std.testing.expectError(
1050 error.QuiescenceUnavailable,
1051 machine.quiescenceReceipt(),
1052 );
1053
1054 var normalized_first: instance.EventBatch = undefined;
1055 var normalized_second: instance.EventBatch = undefined;
1056 const comparison_fence: os.abi.ActivationFence = .{
1057 .world = @splat(0x7a),
1058 .generation = 1,
1059 .token = @splat(0x7b),
1060 };
1061 try normalizeBatch(after_request_push, comparison_fence, &normalized_first);
1062 try normalizeBatch(after_validated_output, comparison_fence, &normalized_second);
1063 try std.testing.expectEqualSlices(
1064 u8,
1065 std.mem.asBytes(&normalized_first),
1066 std.mem.asBytes(&normalized_second),
1067 );
1068 const before_prior_fence_acknowledgement = captureMachine(&machine);
1069 try std.testing.expectError(
1070 error.DeliveryReceiptMismatch,
1071 machine.acknowledge(second_delivery.receipt),
1072 );
1073 try expectMachine(before_prior_fence_acknowledgement, &machine);
1074 try std.testing.expectError(
1075 error.QuiescenceUnavailable,
1076 machine.quiescenceReceipt(),
1077 );
1078 try machine.acknowledge(third_delivery.receipt);
1079 const replay_receipt = try machine.quiescenceReceipt();
1080 try std.testing.expect(os.abi.wire.equalFence(
1081 third_delivery.fence,
1082 replay_receipt.fence,
1083 ));
1084 machine.deinit();
1085 }
1086
1087 test "portable delivery owns time entropy and effect completion" {
1088 const time = try executeRealInput(
1089 try admission.virtualTime(7, 11),
1090 null,
1091 1,
1092 0x61,
1093 0x81,
1094 );
1095 try std.testing.expectEqualSlices(u8, &timeRoot(), &time.root);
1096 try std.testing.expectEqual(@as(u64, 11), time.basis.frontiers.virtual_time_tick);
1097 try std.testing.expectEqual(
1098 @as(u64, 11),
1099 time.quiescence.boundary.virtual_time_tick,
1100 );
1101 try std.testing.expect(time.quiescence.basis.outstanding_effect == null);
1102
1103 const replay = try executeRealInput(
1104 try admission.virtualTime(7, 11),
1105 null,
1106 2,
1107 0x62,
1108 0x82,
1109 );
1110 try std.testing.expectEqualSlices(u8, &time.root, &replay.root);
1111 try std.testing.expectEqualSlices(
1112 u8,
1113 &time.receipt.digest,
1114 &replay.receipt.digest,
1115 );
1116
1117 const entropy = try executeRealInput(
1118 try admission.entropy(2, "seed-A"),
1119 null,
1120 3,
1121 0x63,
1122 0x83,
1123 );
1124 try std.testing.expectEqualSlices(u8, &entropyRoot(), &entropy.root);
1125 try std.testing.expectEqual(@as(u64, 2), entropy.basis.frontiers.entropy_generation);
1126 try std.testing.expectEqual(
1127 @as(u64, 2),
1128 entropy.quiescence.boundary.entropy_generation,
1129 );
1130
1131 const effect_request: admission.EffectRequest = .{
1132 .receipt = .{ .digest = @splat(0x91) },
1133 .correlation = 1,
1134 };
1135 const effect = try executeRealInput(
1136 try admission.effectResult(
1137 effect_request.receipt.digest,
1138 effect_request.correlation,
1139 .ok,
1140 @splat(0x66),
1141 "result-A",
1142 ),
1143 effect_request,
1144 4,
1145 0x64,
1146 0x84,
1147 );
1148 try std.testing.expectEqualSlices(u8, &effectRoot(), &effect.root);
1149 try std.testing.expectEqual(@as(u64, 1), effect.basis.frontiers.effect);
1150 try std.testing.expect(effect.basis.outstanding_effect == null);
1151 try std.testing.expectEqual(
1152 @as(u64, 1),
1153 effect.quiescence.boundary.effect_frontier,
1154 );
1155 try std.testing.expect(effect.quiescence.basis.outstanding_effect == null);
1156 }
1157
1158 test "verified K0 artifacts cross the machine admission boundary" {
1159 const execution = try os.boot.kernel.manifest.parse(real_k0_manifest);
1160 var operations: fake.Operations = .{};
1161 var storage = instance.Storage.init();
1162 var machine = switch (harness.init(
1163 &storage,
1164 &fixture.ram,
1165 realK0Input(execution),
1166 sys.kvm.operationsFor(&operations),
1167 )) {
1168 .ready => |value| value,
1169 .unavailable => return error.UnexpectedBackendUnavailable,
1170 .rejected => |failure| return failure,
1171 };
1172 defer machine.deinit();
1173
1174 try expectInitialState(&operations, execution.header.facts.initial);
1175 var load_index: u16 = 0;
1176 while (load_index < execution.header.load_count) : (load_index += 1) {
1177 const load = execution.load(load_index);
1178 const source_start: usize = load.input_offset;
1179 const source_end = source_start + load.file_bytes;
1180 const destination_start: usize = @intCast(
1181 execution.header.facts.physical_base + load.physical_offset,
1182 );
1183 const destination_end = destination_start + load.file_bytes;
1184 try std.testing.expectEqualSlices(
1185 u8,
1186 real_k0_elf[source_start..source_end],
1187 fixture.ram[destination_start..destination_end],
1188 );
1189 }
1190 }
1191
1192 test "real K0 admission rejects incomplete control-flow evidence" {
1193 const execution = try manifest.parse(real_k0_manifest);
1194 const entry_index = findSite(execution, execution.header.facts.entry_offset) orelse
1195 return error.TestExpectedEntrySite;
1196 const without_entry = try mutateManifest(execution, entry_index, false);
1197 try expectPreHostRejection(
1198 realK0InputWithManifest(without_entry),
1199 error.InstructionEntryMissing,
1200 );
1201
1202 const target_index = try directTargetSite(execution);
1203 const without_target = try mutateManifest(execution, target_index, false);
1204 try expectPreHostRejection(
1205 realK0InputWithManifest(without_target),
1206 error.InstructionFlowMismatch,
1207 );
1208
1209 const unused_form = try mutateManifest(execution, null, true);
1210 try expectPreHostRejection(
1211 realK0InputWithManifest(unused_form),
1212 error.InstructionFormUnused,
1213 );
1214 }
1215
1216 test "self-consistent host-sensitive instruction manifests stop before KVM acquisition" {
1217 const cases = [_]struct {
1218 offset: usize,
1219 replacement: []const u8,
1220 }{
1221 .{ .offset = 4, .replacement = &.{ 0x0f, 0xa2 } },
1222 .{ .offset = 4, .replacement = &.{ 0x0f, 0x32 } },
1223 .{ .offset = 4, .replacement = &.{ 0x0f, 0x30 } },
1224 .{ .offset = 6, .replacement = &.{0xfa} },
1225 .{ .offset = 6, .replacement = &.{0xfb} },
1226 .{ .offset = 6, .replacement = &.{0x9c} },
1227 .{ .offset = 4, .replacement = &.{ 0x66, 0xee } },
1228 .{ .offset = 4, .replacement = &.{ 0x66, 0xf4 } },
1229 .{ .offset = 4, .replacement = &.{ 0x66, 0x0f, 0x0b } },
1230 .{ .offset = 4, .replacement = &.{ 0x41, 0x90 } },
1231 };
1232 for (cases) |case| {
1233 var hostile_image = fixture.elf();
1234 const code_start: usize = 0x1000;
1235 @memcpy(
1236 hostile_image[code_start + case.offset ..][0..case.replacement.len],
1237 case.replacement,
1238 );
1239 const hostile_code = hostile_image[code_start..][0..fixture.code.len];
1240 const hostile_execution = try fixture.executionForCode(
1241 &hostile_image,
1242 hostile_code,
1243 );
1244 try expectPreHostRejection(
1245 fixture.input(
1246 instance.Input,
1247 profile.kvmContinuationTestV1(),
1248 &hostile_image,
1249 &hostile_execution,
1250 ),
1251 error.InstructionPolicyDenied,
1252 );
1253 }
1254 }
1255
1256 test "instance initialization failures release every acquired KVM owner" {
1257 var operations: fake.Operations = .{ .fail_register_write = true };
1258 var storage = instance.Storage.init();
1259 const image = fixture.elf();
1260 const result = harness.init(
1261 &storage,
1262 &fixture.ram,
1263 try input(&image),
1264 sys.kvm.operationsFor(&operations),
1265 );
1266 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
1267 try std.testing.expectEqual(error.BackendFailure, result.rejected);
1268 try std.testing.expectEqual(@as(usize, 1), operations.count(.unmap));
1269 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(30));
1270 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(20));
1271 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
1272
1273 operations = .{ .immediate_exit_supported = false };
1274 storage = instance.Storage.init();
1275 const unsupported = harness.init(
1276 &storage,
1277 &fixture.ram,
1278 try input(&image),
1279 sys.kvm.operationsFor(&operations),
1280 );
1281 try std.testing.expectEqual(.rejected, std.meta.activeTag(unsupported));
1282 try std.testing.expectEqual(error.BackendUnsupported, unsupported.rejected);
1283 try std.testing.expectEqual(@as(usize, 1), operations.count(.unmap));
1284 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(30));
1285 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(20));
1286 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
1287 }
1288
1289 test "KVM capability failures precede VM creation and RAM population" {
1290 const cases = [_]fake.Operations{
1291 .{ .read_only_memory_supported = false },
1292 .{ .memory_slots = 1 },
1293 .{ .extended_cpuid_supported = false },
1294 .{ .pse_supported = false },
1295 .{ .pae_supported = false },
1296 .{ .conditional_move_supported = false },
1297 .{ .execute_disable_supported = false },
1298 .{ .long_mode_supported = false },
1299 .{ .physical_address_bits = cpu.physical_address_bits - 1 },
1300 .{ .virtual_address_bits = cpu.virtual_address_bits - 1 },
1301 };
1302 const image = fixture.elf();
1303 for (cases) |configured| {
1304 @memset(&fixture.ram, 0xa5);
1305 var operations = configured;
1306 var storage = instance.Storage.init();
1307 const result = harness.init(
1308 &storage,
1309 &fixture.ram,
1310 try input(&image),
1311 sys.kvm.operationsFor(&operations),
1312 );
1313 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
1314 try std.testing.expectEqual(error.BackendUnsupported, result.rejected);
1315 try std.testing.expectEqual(@as(usize, 1), operations.closeCount(10));
1316 try std.testing.expectEqual(@as(usize, 0), operations.closeCount(20));
1317 try std.testing.expectEqual(@as(usize, 0), operations.count(.map));
1318 try std.testing.expect(std.mem.allEqual(u8, &fixture.ram, 0xa5));
1319 }
1320 }
1321
1322 test "instance rejects every non-KVM contract before host acquisition" {
1323 var operations: fake.Operations = .{};
1324 var storage = instance.Storage.init();
1325 const image = fixture.elf();
1326 var invalid = try input(&image);
1327 invalid.profile = profile.interpretedContinuationTestV1();
1328 const result = harness.init(
1329 &storage,
1330 &fixture.ram,
1331 invalid,
1332 sys.kvm.operationsFor(&operations),
1333 );
1334 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
1335 try std.testing.expectEqual(error.UnsupportedBackend, result.rejected);
1336 try std.testing.expectEqual(@as(usize, 0), operations.count(.open));
1337 }
1338
1339 test "execution admission rejects mismatches before host acquisition" {
1340 const image = fixture.elf();
1341 const execution = try fixture.execution(&image);
1342
1343 var wrong_fingerprint = fixture.input(
1344 instance.Input,
1345 profile.kvmContinuationTestV1(),
1346 &image,
1347 &execution,
1348 );
1349 wrong_fingerprint.expected_execution_fingerprint.digest[0] ^= 1;
1350 try expectPreHostRejection(
1351 wrong_fingerprint,
1352 error.ExecutionFingerprintMismatch,
1353 );
1354
1355 const view = try os.boot.kernel.manifest.parse(execution.bytes());
1356 const first_site = view.site(0);
1357 const wrong_form: u16 = if (first_site.form_index == 0) 1 else 0;
1358 const mismatched = try fixture.withSiteForm(&execution, 0, wrong_form);
1359 try expectPreHostRejection(
1360 fixture.input(
1361 instance.Input,
1362 profile.kvmContinuationTestV1(),
1363 &image,
1364 &mismatched,
1365 ),
1366 error.InstructionFormMismatch,
1367 );
1368
1369 var changed_image = image;
1370 changed_image[0x1000] ^= 1;
1371 try expectPreHostRejection(
1372 fixture.input(
1373 instance.Input,
1374 profile.kvmContinuationTestV1(),
1375 &changed_image,
1376 &execution,
1377 ),
1378 error.LoadedImageMismatch,
1379 );
1380 }
1381
1382 test "instance rejects an ELF alias of guest RAM before mutation" {
1383 @memset(&fixture.ram, 0xa5);
1384 const canonical = fixture.elf();
1385 @memcpy(fixture.ram[0..canonical.len], &canonical);
1386 const image = fixture.ram[0..canonical.len];
1387 const execution = try fixture.execution(image);
1388 var operations: fake.Operations = .{};
1389 var storage = instance.Storage.init();
1390 const result = harness.init(
1391 &storage,
1392 &fixture.ram,
1393 fixture.input(
1394 instance.Input,
1395 profile.kvmContinuationTestV1(),
1396 image,
1397 &execution,
1398 ),
1399 sys.kvm.operationsFor(&operations),
1400 );
1401 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
1402 try std.testing.expectEqual(error.ImageAliasesRam, result.rejected);
1403 try std.testing.expectEqual(@as(usize, 0), operations.count(.open));
1404 try std.testing.expectEqualSlices(u8, &canonical, image);
1405 try std.testing.expectEqual(@as(u8, 0xa5), fixture.ram[canonical.len]);
1406 }
1407
1408 test "instance rejects owner storage inside guest RAM before acquisition" {
1409 @memset(&fixture.ram, 0);
1410 const storage: *instance.Storage = @ptrCast(@alignCast(&fixture.ram));
1411 const image = fixture.elf();
1412 const execution = try fixture.execution(&image);
1413 var operations: fake.Operations = .{};
1414 const result = harness.init(
1415 storage,
1416 &fixture.ram,
1417 fixture.input(
1418 instance.Input,
1419 profile.kvmContinuationTestV1(),
1420 &image,
1421 &execution,
1422 ),
1423 sys.kvm.operationsFor(&operations),
1424 );
1425 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
1426 try std.testing.expectEqual(error.StorageAliasesRam, result.rejected);
1427 try std.testing.expectEqual(@as(usize, 0), operations.count(.open));
1428 }
1429
1430 test "doorbell rejects every wrong shape after completing pending KVM I/O" {
1431 const cases = [_]fake.Operations{
1432 .{ .io_direction = @backingInt(sys.kvm.Direction.input) },
1433 .{ .io_size = 2 },
1434 .{ .io_port = os.abi.channel.doorbell_port + 1 },
1435 .{ .io_count = 2 },
1436 .{ .io_code = 3 },
1437 };
1438 const image = fixture.elf();
1439 for (cases) |configuration| {
1440 var operations = configuration;
1441 var storage = instance.Storage.init();
1442 var machine = switch (harness.init(
1443 &storage,
1444 &fixture.ram,
1445 try input(&image),
1446 sys.kvm.operationsFor(&operations),
1447 )) {
1448 .ready => |value| value,
1449 .unavailable => return error.UnexpectedBackendUnavailable,
1450 .rejected => |failure| return failure,
1451 };
1452 const run_exit = try machine.run();
1453 try std.testing.expectEqual(.fault, std.meta.activeTag(run_exit));
1454 try std.testing.expectEqual(instance.FaultKind.device, run_exit.fault.kind);
1455 try std.testing.expect(operations.drain_immediate_exit);
1456 try std.testing.expectEqual(@as(u8, 2), operations.run_count);
1457 try std.testing.expectEqual(instance.RunPhase.failed, machine.phase());
1458 machine.deinit();
1459 }
1460 }
1461
1462 test "backend stutters are erased and bounded" {
1463 const image = fixture.elf();
1464 const execution = try fixture.execution(&image);
1465
1466 var mixed_operations: fake.Operations = .{
1467 .run_failure = .AGAIN,
1468 .run_stutter_count = 1,
1469 };
1470 var mixed_storage = instance.Storage.init();
1471 var mixed = switch (harness.init(
1472 &mixed_storage,
1473 &fixture.ram,
1474 fixture.input(
1475 instance.Input,
1476 profile.kvmContinuationTestV1(),
1477 &image,
1478 &execution,
1479 ),
1480 sys.kvm.operationsFor(&mixed_operations),
1481 )) {
1482 .ready => |value| value,
1483 .unavailable => return error.UnexpectedBackendUnavailable,
1484 .rejected => |failure| return failure,
1485 };
1486 const mixed_boundary = try mixed.run();
1487 try std.testing.expectEqual(.doorbell, std.meta.activeTag(mixed_boundary));
1488 try std.testing.expectEqual(
1489 os.abi.channel.DoorbellCode.ready,
1490 mixed_boundary.doorbell.code,
1491 );
1492 try std.testing.expectEqual(
1493 instance.RunPhase.draining_activation,
1494 mixed.phase(),
1495 );
1496 mixed.deinit();
1497
1498 const limit: u16 = @intCast(instance.run_stutter_limit);
1499 var bounded_operations: fake.Operations = .{
1500 .run_failure = .INTR,
1501 .run_failure_count = limit + 1,
1502 };
1503 var bounded_storage = instance.Storage.init();
1504 var bounded = switch (harness.init(
1505 &bounded_storage,
1506 &fixture.ram,
1507 fixture.input(
1508 instance.Input,
1509 profile.kvmContinuationTestV1(),
1510 &image,
1511 &execution,
1512 ),
1513 sys.kvm.operationsFor(&bounded_operations),
1514 )) {
1515 .ready => |value| value,
1516 .unavailable => return error.UnexpectedBackendUnavailable,
1517 .rejected => |failure| return failure,
1518 };
1519 try std.testing.expectError(error.WouldBlock, bounded.run());
1520 try std.testing.expectEqual(@as(u16, 1), bounded_operations.run_failure_count);
1521 try std.testing.expectEqual(instance.RunPhase.booting, bounded.phase());
1522 const bounded_boundary = try bounded.run();
1523 try std.testing.expectEqual(.doorbell, std.meta.activeTag(bounded_boundary));
1524 try std.testing.expectEqual(
1525 os.abi.channel.DoorbellCode.ready,
1526 bounded_boundary.doorbell.code,
1527 );
1528 try std.testing.expectEqual(
1529 instance.RunPhase.draining_activation,
1530 bounded.phase(),
1531 );
1532 bounded.deinit();
1533 }
1534
1535 test "fatal backend failures close execution" {
1536 const image = fixture.elf();
1537 const execution = try fixture.execution(&image);
1538
1539 const cases = [_]struct {
1540 failure: std.posix.E,
1541 expected: instance.RunError,
1542 }{
1543 .{ .failure = .IO, .expected = error.BackendFailure },
1544 .{ .failure = .ACCES, .expected = error.BackendAccessDenied },
1545 .{ .failure = .BADF, .expected = error.Closed },
1546 .{ .failure = .INVAL, .expected = error.InvalidRunState },
1547 .{ .failure = .NOSYS, .expected = error.UnsupportedOperation },
1548 };
1549 for (cases) |case| {
1550 var operations: fake.Operations = .{
1551 .run_failure = case.failure,
1552 };
1553 var storage = instance.Storage.init();
1554 var machine = switch (harness.init(
1555 &storage,
1556 &fixture.ram,
1557 fixture.input(
1558 instance.Input,
1559 profile.kvmContinuationTestV1(),
1560 &image,
1561 &execution,
1562 ),
1563 sys.kvm.operationsFor(&operations),
1564 )) {
1565 .ready => |value| value,
1566 .unavailable => return error.UnexpectedBackendUnavailable,
1567 .rejected => |failure| return failure,
1568 };
1569 defer machine.deinit();
1570 try std.testing.expectError(case.expected, machine.run());
1571 try std.testing.expectEqual(instance.RunPhase.failed, machine.phase());
1572 try std.testing.expectError(error.Closed, machine.run());
1573 }
1574
1575 const exit_cases = [_]struct {
1576 operations: fake.Operations,
1577 expected: instance.RunError,
1578 }{
1579 .{
1580 .operations = .{ .io_direction = 0xff },
1581 .expected = error.MalformedBackendExit,
1582 },
1583 .{
1584 .operations = .{ .drain_succeeds = true },
1585 .expected = error.IoCompletionFailed,
1586 },
1587 };
1588 for (exit_cases) |case| {
1589 var operations = case.operations;
1590 var storage = instance.Storage.init();
1591 var machine = switch (harness.init(
1592 &storage,
1593 &fixture.ram,
1594 fixture.input(
1595 instance.Input,
1596 profile.kvmContinuationTestV1(),
1597 &image,
1598 &execution,
1599 ),
1600 sys.kvm.operationsFor(&operations),
1601 )) {
1602 .ready => |value| value,
1603 .unavailable => return error.UnexpectedBackendUnavailable,
1604 .rejected => |failure| return failure,
1605 };
1606 defer machine.deinit();
1607 try std.testing.expectError(case.expected, machine.run());
1608 try std.testing.expectEqual(instance.RunPhase.failed, machine.phase());
1609 try std.testing.expectError(
1610 error.QuiescenceUnavailable,
1611 machine.quiescenceReceipt(),
1612 );
1613 try std.testing.expectError(error.Closed, machine.run());
1614 }
1615 }
1616
1617 test "fixed RAM capacity accepts C and rejects C plus one" {
1618 try layout.validateRamBytes(layout.ram_bytes);
1619 try std.testing.expectError(
1620 error.RamBytesMismatch,
1621 layout.validateRamBytes(layout.ram_bytes + 1),
1622 );
1623 try std.testing.expectEqual(instance.storage_bytes, @sizeOf(instance.Storage));
1624 try instance.validateStorageBytes(instance.storage_bytes);
1625 try std.testing.expectError(
1626 error.StorageBytesMismatch,
1627 instance.validateStorageBytes(instance.storage_bytes + 1),
1628 );
1629 }
1630
1631 test "host KVM availability is an explicit instance result" {
1632 var storage = instance.Storage.init();
1633 const image = fixture.elf();
1634 switch (instance.Instance.init(&storage, &fixture.ram, try input(&image))) {
1635 .ready => |value| {
1636 var machine = value;
1637 machine.deinit();
1638 },
1639 .unavailable => |receipt| {
1640 try std.testing.expect(receipt.availability != .failed or receipt.code != 0);
1641 },
1642 .rejected => |failure| return failure,
1643 }
1644 }
1645
1646 fn expectBootGeometry(
1647 machine: *instance.Instance,
1648 operations: *const fake.Operations,
1649 image: []const u8,
1650 ) !void {
1651 const encoded_execution = try fixture.execution(image);
1652 const execution = try manifest.parse(encoded_execution.bytes());
1653 try expectInstalledCpu(operations);
1654 try expectRegisteredRam(
1655 machine,
1656 operations,
1657 execution,
1658 );
1659 const plan = try os.boot.kernel.inspectElf(image);
1660 try expectInitialState(
1661 operations,
1662 manifest.k0V1Facts(
1663 plan.entry_offset,
1664 plan.payload_bytes,
1665 plan.memory_bytes,
1666 ).initial,
1667 );
1668 try expectMemoryGeometry(image, execution);
1669 try os.abi.RequestRing.validate(
1670 layout.requestRing(machine.ram),
1671 (try input(image)).fence,
1672 );
1673 try os.abi.EventRing.validate(
1674 layout.eventRing(machine.ram),
1675 (try input(image)).fence,
1676 );
1677 }
1678
1679 fn expectInstalledCpu(operations: *const fake.Operations) !void {
1680 const installed = operations.installedCpu();
1681 try std.testing.expectEqual(cpu.model.len, installed.len);
1682 for (cpu.model, installed) |model_entry, actual| {
1683 const expected: sys.kvm.abi.CpuidEntry = .{
1684 .function = model_entry.function,
1685 .index = model_entry.index,
1686 .flags = 0,
1687 .eax = model_entry.eax,
1688 .ebx = model_entry.ebx,
1689 .ecx = model_entry.ecx,
1690 .edx = model_entry.edx,
1691 .padding = @splat(0),
1692 };
1693 try std.testing.expectEqualDeep(expected, actual);
1694 }
1695 }
1696
1697 fn expectControlOrder(
1698 operations: *const fake.Operations,
1699 requests: []const u32,
1700 ) !void {
1701 var previous: usize = 0;
1702 for (requests, 0..) |request, index| {
1703 const current = controlIndex(operations, request) orelse {
1704 try std.testing.expect(false);
1705 return;
1706 };
1707 if (index > 0) try std.testing.expect(previous < current);
1708 previous = current;
1709 }
1710 }
1711
1712 fn expectControlOrderFrom(
1713 operations: *const fake.Operations,
1714 start: usize,
1715 requests: []const u32,
1716 ) !void {
1717 var cursor = start;
1718 for (requests) |request| {
1719 const current = controlIndexFrom(operations, cursor, request) orelse {
1720 try std.testing.expect(false);
1721 return;
1722 };
1723 cursor = current + 1;
1724 }
1725 }
1726
1727 fn controlIndex(operations: *const fake.Operations, request: u32) ?usize {
1728 for (operations.records[0..operations.record_count], 0..) |record, index| {
1729 if (record.action == .control and record.request == request) return index;
1730 }
1731 return null;
1732 }
1733
1734 fn controlIndexFrom(
1735 operations: *const fake.Operations,
1736 start: usize,
1737 request: u32,
1738 ) ?usize {
1739 for (operations.records[start..operations.record_count], start..) |record, index| {
1740 if (record.action == .control and record.request == request) return index;
1741 }
1742 return null;
1743 }
1744
1745 fn expectRegisteredRam(
1746 machine: *const instance.Instance,
1747 operations: *const fake.Operations,
1748 execution: manifest.View,
1749 ) !void {
1750 const plan = try protection.Plan.init(execution);
1751 try std.testing.expectEqual(@as(u64, 0), plan.ram_base);
1752 try std.testing.expectEqual(@as(u64, layout.ram_bytes), plan.ram_bytes);
1753 const regions = operations.registeredRegions();
1754 var region_index: usize = 0;
1755 var cursor: u64 = 0;
1756 for (plan.sealedSpans()) |span| {
1757 if (cursor < span.ram_offset) {
1758 try expectRegisteredRegion(
1759 machine,
1760 regions[region_index],
1761 region_index,
1762 cursor,
1763 span.ram_offset,
1764 false,
1765 );
1766 region_index += 1;
1767 }
1768 const span_end = span.ram_offset + span.bytes;
1769 try expectRegisteredRegion(
1770 machine,
1771 regions[region_index],
1772 region_index,
1773 span.ram_offset,
1774 span_end,
1775 true,
1776 );
1777 region_index += 1;
1778 cursor = span_end;
1779 }
1780 if (cursor < plan.ram_bytes) {
1781 try expectRegisteredRegion(
1782 machine,
1783 regions[region_index],
1784 region_index,
1785 cursor,
1786 plan.ram_bytes,
1787 false,
1788 );
1789 region_index += 1;
1790 cursor = plan.ram_bytes;
1791 }
1792 try std.testing.expectEqual(plan.ram_bytes, cursor);
1793 try std.testing.expectEqual(region_index, regions.len);
1794 }
1795
1796 fn expectRegisteredRegion(
1797 machine: *const instance.Instance,
1798 region: sys.kvm.abi.UserMemoryRegion,
1799 slot: usize,
1800 start: u64,
1801 end: u64,
1802 read_only: bool,
1803 ) !void {
1804 try std.testing.expect(start < end);
1805 try std.testing.expectEqual(@as(u32, @intCast(slot)), region.slot);
1806 try std.testing.expectEqual(start, region.guest_physical_address);
1807 try std.testing.expectEqual(end - start, region.memory_size);
1808 const start_index: usize = @intCast(start);
1809 try std.testing.expectEqual(
1810 @as(u64, @intCast(@intFromPtr(machine.ram.ptr) + start_index)),
1811 region.userspace_address,
1812 );
1813 const flags: sys.kvm.abi.MemoryFlags = .{ .read_only = read_only };
1814 try std.testing.expectEqual(flags.bits(), region.flags);
1815 }
1816
1817 fn expectInitialState(
1818 operations: *const fake.Operations,
1819 initial: manifest.InitialState,
1820 ) !void {
1821 const registers = operations.registers;
1822 try std.testing.expectEqual(initial.rip, registers.rip);
1823 try std.testing.expectEqual(initial.rsp, registers.rsp);
1824 try std.testing.expectEqual(initial.rdi, registers.rdi);
1825 try std.testing.expectEqual(initial.rflags, registers.rflags);
1826 inline for (.{
1827 registers.rax,
1828 registers.rbx,
1829 registers.rcx,
1830 registers.rdx,
1831 registers.rsi,
1832 registers.rbp,
1833 registers.r8,
1834 registers.r9,
1835 registers.r10,
1836 registers.r11,
1837 registers.r12,
1838 registers.r13,
1839 registers.r14,
1840 registers.r15,
1841 }) |value| try std.testing.expectEqual(@as(u64, 0), value);
1842
1843 const special = operations.special_registers;
1844 try std.testing.expectEqual(initial.code_selector, special.cs.selector);
1845 inline for (.{
1846 special.ds.selector,
1847 special.es.selector,
1848 special.fs.selector,
1849 special.gs.selector,
1850 special.ss.selector,
1851 }) |selector| try std.testing.expectEqual(initial.data_selector, selector);
1852 try std.testing.expectEqual(initial.cr0, special.cr0);
1853 try std.testing.expectEqual(initial.cr3, special.cr3);
1854 try std.testing.expectEqual(initial.cr4, special.cr4);
1855 try std.testing.expectEqual(initial.efer, special.efer);
1856 }
1857
1858 fn expectMemoryGeometry(
1859 image: []const u8,
1860 execution: manifest.View,
1861 ) !void {
1862 try std.testing.expectEqual(@as(u64, 0x2003), entry(layout.pml4_address, 0));
1863 try std.testing.expectEqual(@as(u64, 0x3003), entry(layout.pdpt_address, 0));
1864 const plan = try protection.Plan.init(execution);
1865 try expectPageDirectory();
1866 try expectLowPageTable();
1867 try expectKernelPageTables(&plan);
1868 const kernel_start: usize = @intCast(os.boot.kernel.physical_base);
1869 try std.testing.expectEqualSlices(
1870 u8,
1871 &fixture.code,
1872 fixture.ram[kernel_start..][0..fixture.code.len],
1873 );
1874 try std.testing.expectEqual(
1875 @as(u8, 0),
1876 fixture.ram[kernel_start + fixture.code.len],
1877 );
1878 var wire: os.abi.BootWire = undefined;
1879 const frame_start: usize = @intCast(layout.boot_frame_address);
1880 @memcpy(&wire, fixture.ram[frame_start..][0..wire.len]);
1881 const frame = try os.abi.decodeBootFrame(&wire);
1882 const contract = try profile.contractFingerprint(profile.kvmContinuationTestV1());
1883 try std.testing.expectEqualSlices(u8, &contract.digest, &frame.contract_digest);
1884 const elf_plan = try os.boot.kernel.inspectElf(image);
1885 const loaded = os.boot.kernel.loadedImageDigest(image, &elf_plan);
1886 try std.testing.expectEqualSlices(u8, &loaded, &frame.image_digest);
1887 try std.testing.expect(!std.mem.eql(
1888 u8,
1889 &os.boot.kernel.sha256(image),
1890 &frame.image_digest,
1891 ));
1892 try std.testing.expectEqual(@as(u64, 0), frame.input_frontier);
1893 try std.testing.expectEqual(@as(u64, 0), frame.terminal_input_offset);
1894 }
1895
1896 fn expectPageDirectory() !void {
1897 const entries = layout.page_bytes / @sizeOf(u64);
1898 const kernel_index: usize =
1899 os.boot.kernel.physical_base / layout.large_page_bytes;
1900 for (0..entries) |index| {
1901 const expected: u64 = if (index >= layout.identity_large_pages)
1902 0
1903 else if (index == 0)
1904 layout.low_page_table_address |
1905 test_page_present | test_page_writable | test_page_no_execute
1906 else if (index >= kernel_index and
1907 index < kernel_index + layout.kernel_page_table_count)
1908 (layout.kernel_page_table_base +
1909 @as(u64, index - kernel_index) * layout.page_bytes) |
1910 test_page_present | test_page_writable
1911 else
1912 (@as(u64, index) * layout.large_page_bytes) |
1913 test_page_present | test_page_writable |
1914 test_page_large | test_page_no_execute;
1915 try std.testing.expectEqual(
1916 expected,
1917 entry(layout.page_directory_address, index),
1918 );
1919 }
1920 }
1921
1922 fn expectLowPageTable() !void {
1923 const entries = layout.page_bytes / @sizeOf(u64);
1924 for (0..entries) |index| {
1925 const physical = @as(u64, index) * layout.page_bytes;
1926 const expected = if (testPageTableAddress(physical))
1927 0
1928 else
1929 physical | test_page_present | test_page_writable | test_page_no_execute;
1930 try std.testing.expectEqual(
1931 expected,
1932 entry(layout.low_page_table_address, index),
1933 );
1934 }
1935 }
1936
1937 fn expectKernelPageTables(plan: *const protection.Plan) !void {
1938 const entries = layout.page_bytes / @sizeOf(u64);
1939 for (0..layout.kernel_page_table_count) |table_index| {
1940 const table = layout.kernel_page_table_base +
1941 @as(u64, table_index) * layout.page_bytes;
1942 for (0..entries) |index| {
1943 const physical = os.boot.kernel.physical_base +
1944 @as(u64, table_index) * layout.large_page_bytes +
1945 @as(u64, index) * layout.page_bytes;
1946 const expected = if (plan.protectsPage(physical))
1947 physical | test_page_present
1948 else
1949 physical | test_page_present |
1950 test_page_writable | test_page_no_execute;
1951 try std.testing.expectEqual(expected, entry(table, index));
1952 }
1953 }
1954 }
1955
1956 fn testPageTableAddress(address: u64) bool {
1957 for (layout.page_table_addresses) |candidate| {
1958 if (address == candidate) return true;
1959 }
1960 return false;
1961 }
1962
1963 fn captureLaunchState() void {
1964 var destination: usize = 0;
1965 for (layout.page_table_addresses) |address| {
1966 const source: usize = @intCast(address);
1967 @memcpy(
1968 launch_state_snapshot[destination..][0..layout.page_bytes],
1969 fixture.ram[source..][0..layout.page_bytes],
1970 );
1971 destination += layout.page_bytes;
1972 }
1973 const kernel_start: usize = @intCast(os.boot.kernel.physical_base);
1974 @memcpy(
1975 launch_state_snapshot[destination..][0..fixture.code.len],
1976 fixture.ram[kernel_start..][0..fixture.code.len],
1977 );
1978 }
1979
1980 fn expectLaunchStateUnchanged() !void {
1981 var source: usize = 0;
1982 for (layout.page_table_addresses) |address| {
1983 const destination: usize = @intCast(address);
1984 try std.testing.expectEqualSlices(
1985 u8,
1986 launch_state_snapshot[source..][0..layout.page_bytes],
1987 fixture.ram[destination..][0..layout.page_bytes],
1988 );
1989 source += layout.page_bytes;
1990 }
1991 const kernel_start: usize = @intCast(os.boot.kernel.physical_base);
1992 try std.testing.expectEqualSlices(
1993 u8,
1994 launch_state_snapshot[source..][0..fixture.code.len],
1995 fixture.ram[kernel_start..][0..fixture.code.len],
1996 );
1997 }
1998
1999 fn entry(address: u64, index: usize) u64 {
2000 const start: usize = @intCast(address + index * @sizeOf(u64));
2001 return std.mem.readInt(u64, fixture.ram[start..][0..@sizeOf(u64)], .little);
2002 }
2003
2004 fn captureMachine(machine: *const instance.Instance) MachineSnapshot {
2005 return .{
2006 .owner = machine.storage.bytes,
2007 .requests = layout.requestRing(machine.ram).*,
2008 .events = layout.eventRing(machine.ram).*,
2009 .phase = machine.phase(),
2010 };
2011 }
2012
2013 fn expectMachine(expected: MachineSnapshot, machine: *const instance.Instance) !void {
2014 try std.testing.expectEqual(expected.phase, machine.phase());
2015 try std.testing.expectEqualSlices(
2016 u8,
2017 &expected.owner,
2018 &machine.storage.bytes,
2019 );
2020 try std.testing.expectEqualSlices(
2021 u8,
2022 std.mem.asBytes(&expected.requests),
2023 std.mem.asBytes(layout.requestRing(machine.ram)),
2024 );
2025 try std.testing.expectEqualSlices(
2026 u8,
2027 std.mem.asBytes(&expected.events),
2028 std.mem.asBytes(layout.eventRing(machine.ram)),
2029 );
2030 }
2031
2032 fn startRealMachine(
2033 storage: *instance.Storage,
2034 selected: instance.Input,
2035 ) !instance.Instance {
2036 return switch (instance.Instance.init(storage, &fixture.ram, selected)) {
2037 .ready => |value| value,
2038 .unavailable => error.UnexpectedBackendUnavailable,
2039 .rejected => |failure| failure,
2040 };
2041 }
2042
2043 fn checkpointRestoreInput(
2044 captured: *const CapturedBoundary,
2045 selected_profile: profile.Profile,
2046 fence: os.abi.ActivationFence,
2047 ) instance.RestoreInput {
2048 return .{
2049 .checkpoint = .{ .durable = &captured.checkpoint },
2050 .expected_root = captured.boundary.root,
2051 .profile = selected_profile,
2052 .execution_manifest = real_k0_manifest,
2053 .fence = fence,
2054 };
2055 }
2056
2057 fn expectFakeResourcesClosed(
2058 operations: *const fake.Operations,
2059 expected: usize,
2060 ) !void {
2061 try std.testing.expectEqual(expected, operations.count(.unmap));
2062 try std.testing.expectEqual(expected, operations.closeCount(30));
2063 try std.testing.expectEqual(expected, operations.closeCount(20));
2064 try std.testing.expectEqual(expected, operations.closeCount(10));
2065 }
2066
2067 fn captureFakeAcceleratorBoundary(
2068 execution: manifest.View,
2069 storage: *checkpoint.Storage,
2070 ) !CapturedBoundary {
2071 var operations: fake.Operations = .{};
2072 var source_storage = instance.Storage.init();
2073 const selected = realK0Input(execution);
2074 var machine = switch (harness.init(
2075 &source_storage,
2076 &fixture.ram,
2077 selected,
2078 sys.kvm.operationsFor(&operations),
2079 )) {
2080 .ready => |value| value,
2081 .unavailable => return error.UnexpectedBackendUnavailable,
2082 .rejected => |failure| return failure,
2083 };
2084 defer machine.deinit();
2085 const receipt_value = try driveFakeAcceleratorReceipt(
2086 &machine,
2087 &operations,
2088 selected.fence,
2089 0x88,
2090 );
2091 const checkpoint_value = try machine.captureCheckpoint(
2092 storage,
2093 &accelerator_checkpoint_ram,
2094 );
2095 const root = try checkpoint_value.root();
2096 try std.testing.expectEqualDeep(
2097 try profile.profileFingerprint(selected.profile),
2098 root.profile,
2099 );
2100 return .{
2101 .checkpoint = checkpoint_value,
2102 .boundary = .{ .root = root, .receipt = receipt_value },
2103 };
2104 }
2105
2106 fn replayAcceleratorBoundary(
2107 accelerated: *const CapturedBoundary,
2108 storage: *checkpoint.Storage,
2109 ) !CheckpointBoundary {
2110 const fence: os.abi.ActivationFence = .{
2111 .world = @splat(0xa1),
2112 .generation = 1,
2113 .token = @splat(0xa2),
2114 };
2115 var restored_storage = instance.Storage.init();
2116 var machine = switch (instance.Instance.restore(
2117 &restored_storage,
2118 &fixture.ram,
2119 .{
2120 .checkpoint = .{ .durable = &accelerated.checkpoint },
2121 .expected_root = accelerated.boundary.root,
2122 .profile = profile.interpretedContinuationTestV1(),
2123 .execution_manifest = real_k0_manifest,
2124 .fence = fence,
2125 },
2126 )) {
2127 .ready => |value| value,
2128 .unavailable => return error.UnexpectedBackendUnavailable,
2129 .rejected => |failure| return failure,
2130 };
2131 defer machine.deinit();
2132 const receipt_value = try driveInterpretedTerminalReceipt(
2133 &machine,
2134 fence,
2135 0x99,
2136 );
2137 return .{
2138 .root = try recaptureRoot(&machine, storage),
2139 .receipt = receipt_value,
2140 };
2141 }
2142
2143 fn capturePortableBoundary(
2144 execution: manifest.View,
2145 storage: *checkpoint.Storage,
2146 ) !CapturedBoundary {
2147 var selected = realK0Input(execution);
2148 selected.profile = profile.interpretedContinuationTestV1();
2149 var machine_storage = instance.Storage.init();
2150 var machine = try startRealMachine(&machine_storage, selected);
2151 defer machine.deinit();
2152 const receipt_value = try driveInterpretedTerminalReceipt(
2153 &machine,
2154 selected.fence,
2155 0x88,
2156 );
2157 const checkpoint_value = try machine.captureCheckpoint(
2158 storage,
2159 &accelerator_checkpoint_ram,
2160 );
2161 return .{
2162 .checkpoint = checkpoint_value,
2163 .boundary = .{
2164 .root = try checkpoint_value.root(),
2165 .receipt = receipt_value,
2166 },
2167 };
2168 }
2169
2170 fn replayPortableBoundaryWithFakeAccelerator(
2171 execution: manifest.View,
2172 portable: *const CapturedBoundary,
2173 storage: *checkpoint.Storage,
2174 ) !CheckpointBoundary {
2175 const fence: os.abi.ActivationFence = .{
2176 .world = @splat(0xb1),
2177 .generation = 1,
2178 .token = @splat(0xb2),
2179 };
2180 var operations: fake.Operations = .{};
2181 var machine_storage = instance.Storage.init();
2182 var machine = switch (harness.restore(
2183 &machine_storage,
2184 &fixture.ram,
2185 .{
2186 .checkpoint = .{ .durable = &portable.checkpoint },
2187 .expected_root = portable.boundary.root,
2188 .profile = profile.kvmContinuationTestV1(),
2189 .execution_manifest = real_k0_manifest,
2190 .fence = fence,
2191 },
2192 sys.kvm.operationsFor(&operations),
2193 )) {
2194 .ready => |value| value,
2195 .unavailable => return error.UnexpectedBackendUnavailable,
2196 .rejected => |failure| return failure,
2197 };
2198 defer machine.deinit();
2199 try expectRegisteredRam(&machine, &operations, execution);
2200 try expectInitialState(&operations, execution.header.facts.initial);
2201 const receipt_value = try driveFakeAcceleratorReceipt(
2202 &machine,
2203 &operations,
2204 fence,
2205 0x99,
2206 );
2207 const root = try recaptureRoot(&machine, storage);
2208 try std.testing.expectEqualDeep(
2209 try profile.profileFingerprint(profile.kvmContinuationTestV1()),
2210 root.profile,
2211 );
2212 return .{ .root = root, .receipt = receipt_value };
2213 }
2214
2215 fn executePortableBoundaries(
2216 execution: manifest.View,
2217 storage: *checkpoint.Storage,
2218 ) !PortableBoundaries {
2219 var selected = realK0Input(execution);
2220 selected.profile = profile.interpretedContinuationTestV1();
2221 var machine_storage = instance.Storage.init();
2222 var machine = try startRealMachine(&machine_storage, selected);
2223 defer machine.deinit();
2224 const first_receipt = try driveInterpretedTerminalReceipt(
2225 &machine,
2226 selected.fence,
2227 0x88,
2228 );
2229 const first_root = try recaptureRoot(&machine, storage);
2230 var next_fence = selected.fence;
2231 next_fence.generation += 1;
2232 next_fence.token = @splat(0x56);
2233 try machine.reactivate(next_fence);
2234 const second_receipt = try driveInterpretedTerminalReceipt(
2235 &machine,
2236 next_fence,
2237 0x99,
2238 );
2239 return .{
2240 .first = .{ .root = first_root, .receipt = first_receipt },
2241 .second = .{
2242 .root = try recaptureRoot(&machine, storage),
2243 .receipt = second_receipt,
2244 },
2245 };
2246 }
2247
2248 fn recaptureRoot(
2249 machine: *instance.Instance,
2250 storage: *checkpoint.Storage,
2251 ) !checkpoint.Root {
2252 storage.* = checkpoint.Storage.init();
2253 const value = try machine.captureCheckpoint(
2254 storage,
2255 &accelerator_checkpoint_ram,
2256 );
2257 return value.root();
2258 }
2259
2260 fn executeFakeAcceleratorReceipt(
2261 execution: manifest.View,
2262 ) !instance.QuiescenceReceipt {
2263 var operations: fake.Operations = .{};
2264 var storage = instance.Storage.init();
2265 const selected = realK0Input(execution);
2266 var machine = switch (harness.init(
2267 &storage,
2268 &fixture.ram,
2269 selected,
2270 sys.kvm.operationsFor(&operations),
2271 )) {
2272 .ready => |value| value,
2273 .unavailable => return error.UnexpectedBackendUnavailable,
2274 .rejected => |failure| return failure,
2275 };
2276 defer machine.deinit();
2277
2278 return driveFakeAcceleratorReceipt(
2279 &machine,
2280 &operations,
2281 selected.fence,
2282 0x88,
2283 );
2284 }
2285
2286 fn driveFakeAcceleratorReceipt(
2287 machine: *instance.Instance,
2288 operations: *fake.Operations,
2289 fence: os.abi.ActivationFence,
2290 committed_root: u8,
2291 ) !instance.QuiescenceReceipt {
2292 var guest = os.k0.Owner.init();
2293 const boot_offset: usize = @intCast(layout.boot_frame_address);
2294 const boot: *const os.abi.BootWire = @ptrCast(
2295 machine.ram[boot_offset..][0..os.abi.boot.frame_bytes].ptr,
2296 );
2297 try std.testing.expectEqual(
2298 os.abi.channel.DoorbellCode.ready,
2299 try guest.activate(
2300 boot,
2301 layout.requestRing(machine.ram),
2302 layout.eventRing(machine.ram),
2303 ),
2304 );
2305 const ready = try machine.run();
2306 try std.testing.expectEqual(
2307 os.abi.channel.DoorbellCode.ready,
2308 ready.doorbell.code,
2309 );
2310 var batch: instance.EventBatch = undefined;
2311 try machine.takeEvents(&batch);
2312 const basis = try machine.admissionBasis();
2313 const admitted = try admission.prepare(
2314 basis,
2315 try admission.terminal(
2316 basis.frontiers.terminal_input_offset,
2317 os.k0.request_bytes,
2318 ),
2319 );
2320 const delivery_value = try admission.bindDelivery(
2321 admitted,
2322 @splat(committed_root),
2323 fence,
2324 );
2325 try machine.deliverAdmitted(&delivery_value);
2326 try std.testing.expectEqual(
2327 os.abi.channel.DoorbellCode.quiescent,
2328 try guest.consume(),
2329 );
2330 operations.run_count = 0;
2331 operations.io_code = @backingInt(os.abi.channel.DoorbellCode.quiescent);
2332 const quiescent = try machine.run();
2333 try std.testing.expectEqual(
2334 os.abi.channel.DoorbellCode.quiescent,
2335 quiescent.doorbell.code,
2336 );
2337 try std.testing.expect(operations.drain_immediate_exit);
2338 try std.testing.expectEqual(
2339 operations.instruction_pointer_after_io,
2340 operations.instruction_pointer_after_drain,
2341 );
2342 try machine.takeEvents(&batch);
2343 try machine.acknowledge(delivery_value.receipt);
2344 const issued = try machine.quiescenceReceipt();
2345 try std.testing.expectEqualSlices(
2346 u8,
2347 &instance.eventTranscriptDigest(&batch),
2348 &issued.event_transcript_digest,
2349 );
2350 return issued;
2351 }
2352
2353 fn executeInterpretedReceipt(
2354 execution: manifest.View,
2355 ) !instance.QuiescenceReceipt {
2356 var storage = instance.Storage.init();
2357 var selected = realK0Input(execution);
2358 selected.profile = profile.interpretedContinuationTestV1();
2359 var machine = try startRealMachine(&storage, selected);
2360 defer machine.deinit();
2361
2362 return driveInterpretedTerminalReceipt(&machine, selected.fence, 0x88);
2363 }
2364
2365 fn driveInterpretedTerminalReceipt(
2366 machine: *instance.Instance,
2367 fence: os.abi.ActivationFence,
2368 committed_root: u8,
2369 ) !instance.QuiescenceReceipt {
2370 const ready = try machine.run();
2371 try std.testing.expectEqualDeep(
2372 instance.Exit{ .doorbell = .{ .code = .ready } },
2373 ready,
2374 );
2375 var batch: instance.EventBatch = undefined;
2376 try machine.takeEvents(&batch);
2377 const basis = try machine.admissionBasis();
2378 const admitted = try admission.prepare(
2379 basis,
2380 try admission.terminal(
2381 basis.frontiers.terminal_input_offset,
2382 os.k0.request_bytes,
2383 ),
2384 );
2385 const delivery_value = try admission.bindDelivery(
2386 admitted,
2387 @splat(committed_root),
2388 fence,
2389 );
2390 try machine.deliverAdmitted(&delivery_value);
2391 const quiescent = try machine.run();
2392 try std.testing.expectEqualDeep(
2393 instance.Exit{ .doorbell = .{ .code = .quiescent } },
2394 quiescent,
2395 );
2396 try machine.takeEvents(&batch);
2397 try machine.acknowledge(delivery_value.receipt);
2398 const issued = try machine.quiescenceReceipt();
2399 try std.testing.expectEqualSlices(
2400 u8,
2401 &instance.eventTranscriptDigest(&batch),
2402 &issued.event_transcript_digest,
2403 );
2404 return issued;
2405 }
2406
2407 fn receiptTestDelivery(
2408 machine: *const instance.Instance,
2409 fence_value: os.abi.ActivationFence,
2410 ) !admission.Delivery {
2411 const admitted = try admission.prepare(
2412 try machine.admissionBasis(),
2413 try admission.terminal(0, os.k0.request_bytes),
2414 );
2415 return admission.bindDelivery(admitted, @splat(0x88), fence_value);
2416 }
2417
2418 fn activateAndBasis(machine: *instance.Instance) !admission.Basis {
2419 const ready = try machine.run();
2420 try std.testing.expectEqual(.doorbell, std.meta.activeTag(ready));
2421 try std.testing.expectEqual(
2422 os.abi.channel.DoorbellCode.ready,
2423 ready.doorbell.code,
2424 );
2425 var batch: instance.EventBatch = undefined;
2426 try machine.takeEvents(&batch);
2427 try std.testing.expectEqual(
2428 @as(usize, os.k0.events_per_activation),
2429 batch.frames().len,
2430 );
2431 return machine.admissionBasis();
2432 }
2433
2434 fn normalizeBatch(
2435 source: instance.EventBatch,
2436 fence: os.abi.ActivationFence,
2437 output: *instance.EventBatch,
2438 ) !void {
2439 var normalized: instance.EventBatch = .{
2440 .count = source.count,
2441 .storage = @splat(@splat(0)),
2442 };
2443 for (source.frames(), 0..) |*frame, index| {
2444 const decoded = try os.abi.decodeEvent(frame);
2445 var value = decoded.value;
2446 if (std.meta.activeTag(value) == .quiescent) {
2447 value.quiescent.capability_generation = fence.generation;
2448 }
2449 try os.abi.encodeEvent(
2450 fence,
2451 decoded.header.sequence,
2452 decoded.header.correlation,
2453 value,
2454 &normalized.storage[index],
2455 );
2456 }
2457 output.* = normalized;
2458 }
2459
2460 fn executeRealInput(
2461 record: admission.Record,
2462 outstanding_effect: ?admission.EffectRequest,
2463 fence_generation: u64,
2464 fence_token: u8,
2465 committed_root_byte: u8,
2466 ) !InputCompletion {
2467 const execution = try manifest.parse(real_k0_manifest);
2468 var storage = instance.Storage.init();
2469 var selected = realK0Input(execution);
2470 selected.profile = profile.interpretedContinuationTestV1();
2471 selected.fence.generation = fence_generation;
2472 selected.fence.token = @splat(fence_token);
2473 selected.outstanding_effect = outstanding_effect;
2474 var machine = switch (instance.Instance.init(
2475 &storage,
2476 &fixture.ram,
2477 selected,
2478 )) {
2479 .ready => |value| value,
2480 .unavailable => return error.UnexpectedBackendUnavailable,
2481 .rejected => |failure| return failure,
2482 };
2483 defer machine.deinit();
2484
2485 const ready = try machine.run();
2486 try std.testing.expectEqual(.doorbell, std.meta.activeTag(ready));
2487 try std.testing.expectEqual(os.abi.channel.DoorbellCode.ready, ready.doorbell.code);
2488 var batch: instance.EventBatch = undefined;
2489 try machine.takeEvents(&batch);
2490 try std.testing.expectEqual(@as(usize, 2), batch.frames().len);
2491 try std.testing.expectError(error.InputRequired, machine.run());
2492
2493 const admitted = try admission.prepare(
2494 try machine.admissionBasis(),
2495 record,
2496 );
2497 const committed_root: os.abi.Digest = @splat(committed_root_byte);
2498 const input_delivery = try admission.bindDelivery(
2499 admitted,
2500 committed_root,
2501 selected.fence,
2502 );
2503 var tampered = input_delivery;
2504 tampered.admission.receipt.digest[0] ^= 1;
2505 const before_tampered_delivery = captureMachine(&machine);
2506 try std.testing.expectError(
2507 error.NoncanonicalRecord,
2508 machine.deliverAdmitted(&tampered),
2509 );
2510 try expectMachine(before_tampered_delivery, &machine);
2511 try std.testing.expectEqual(instance.RunPhase.awaiting_input, machine.phase());
2512 try machine.deliverAdmitted(&input_delivery);
2513 const before_duplicate_delivery = captureMachine(&machine);
2514 try std.testing.expectError(
2515 error.InputAlreadyDelivered,
2516 machine.deliverAdmitted(&input_delivery),
2517 );
2518 try expectMachine(before_duplicate_delivery, &machine);
2519
2520 const quiescent = try machine.run();
2521 try std.testing.expectEqual(.doorbell, std.meta.activeTag(quiescent));
2522 try std.testing.expectEqual(
2523 os.abi.channel.DoorbellCode.quiescent,
2524 quiescent.doorbell.code,
2525 );
2526 var root: os.abi.Digest = @splat(0);
2527 var root_seen = false;
2528 const event_count: usize = switch (record) {
2529 .terminal => os.k0.events_per_terminal_input,
2530 else => os.k0.events_per_nonterminal_input,
2531 };
2532 try machine.takeEvents(&batch);
2533 try std.testing.expectEqual(event_count, batch.frames().len);
2534 for (batch.frames()) |*frame| {
2535 const decoded = try os.abi.decodeEvent(frame);
2536 if (std.meta.activeTag(decoded.value) == .block_root) {
2537 root = decoded.value.block_root.digest;
2538 root_seen = true;
2539 }
2540 }
2541 try std.testing.expect(root_seen);
2542 try std.testing.expectEqual(
2543 instance.RunPhase.awaiting_acknowledgement,
2544 machine.phase(),
2545 );
2546 var wrong_receipt = input_delivery.receipt;
2547 wrong_receipt.digest[0] ^= 1;
2548 const before_wrong_acknowledgement = captureMachine(&machine);
2549 try std.testing.expectError(
2550 error.DeliveryReceiptMismatch,
2551 machine.acknowledge(wrong_receipt),
2552 );
2553 try expectMachine(before_wrong_acknowledgement, &machine);
2554 try std.testing.expectEqual(
2555 instance.RunPhase.awaiting_acknowledgement,
2556 machine.phase(),
2557 );
2558 try machine.acknowledge(input_delivery.receipt);
2559 try std.testing.expectError(error.InputUnavailable, machine.takeEvents(&batch));
2560 const quiescence_receipt = try machine.quiescenceReceipt();
2561 var next_fence = selected.fence;
2562 next_fence.generation += 1;
2563 next_fence.token = @splat(fence_token +% 1);
2564 const next_basis = try reactivateAndBasis(&machine, selected.fence, next_fence);
2565 return .{
2566 .root = root,
2567 .receipt = admitted.receipt,
2568 .basis = next_basis,
2569 .quiescence = quiescence_receipt,
2570 };
2571 }
2572
2573 fn reactivateAndBasis(
2574 machine: *instance.Instance,
2575 current: os.abi.ActivationFence,
2576 next: os.abi.ActivationFence,
2577 ) !admission.Basis {
2578 try std.testing.expectEqual(
2579 instance.RunPhase.awaiting_reactivation,
2580 machine.phase(),
2581 );
2582 try std.testing.expectError(error.ReactivationRequired, machine.run());
2583 try std.testing.expectError(error.InputUnavailable, machine.admissionBasis());
2584
2585 var invalid = next;
2586 invalid.world[0] ^= 1;
2587 try std.testing.expectError(
2588 error.ActivationWorldMismatch,
2589 machine.reactivate(invalid),
2590 );
2591 invalid = next;
2592 invalid.generation = current.generation;
2593 try std.testing.expectError(
2594 error.ActivationGenerationStale,
2595 machine.reactivate(invalid),
2596 );
2597 invalid = next;
2598 invalid.token = current.token;
2599 try std.testing.expectError(
2600 error.ActivationTokenStale,
2601 machine.reactivate(invalid),
2602 );
2603 try std.testing.expectEqual(
2604 instance.RunPhase.awaiting_reactivation,
2605 machine.phase(),
2606 );
2607
2608 const boot_tail: usize = @intCast(
2609 layout.boot_frame_address + os.abi.boot.frame_bytes,
2610 );
2611 const stack_start: usize = @intCast(layout.stack_base);
2612 machine.ram[boot_tail] = 0xa5;
2613 machine.ram[stack_start] = 0x5a;
2614 try machine.reactivate(next);
2615 try std.testing.expectEqual(@as(u8, 0), machine.ram[boot_tail]);
2616 try std.testing.expectEqual(@as(u8, 0), machine.ram[stack_start]);
2617 try std.testing.expectEqual(instance.RunPhase.booting, machine.phase());
2618 try std.testing.expectError(
2619 error.QuiescenceUnavailable,
2620 machine.quiescenceReceipt(),
2621 );
2622 const ready = try machine.run();
2623 try std.testing.expectEqual(.doorbell, std.meta.activeTag(ready));
2624 try std.testing.expectEqual(os.abi.channel.DoorbellCode.ready, ready.doorbell.code);
2625 var batch: instance.EventBatch = undefined;
2626 try machine.takeEvents(&batch);
2627 try std.testing.expectEqual(@as(usize, 2), batch.frames().len);
2628 return machine.admissionBasis();
2629 }
2630
2631 fn bootWire(machine: *const instance.Instance) *const os.abi.BootWire {
2632 const boot_offset: usize = @intCast(layout.boot_frame_address);
2633 return @ptrCast(
2634 machine.ram[boot_offset..][0..os.abi.boot.frame_bytes].ptr,
2635 );
2636 }
2637
2638 fn expectRealK0Activation(
2639 machine: *instance.Instance,
2640 selected: instance.Input,
2641 execution: manifest.View,
2642 ) !void {
2643 var batch: instance.EventBatch = undefined;
2644 try machine.takeEvents(&batch);
2645 try std.testing.expectEqual(@as(usize, 2), batch.frames().len);
2646 const ready = try os.abi.decodeEvent(&batch.storage[0]);
2647 try std.testing.expectEqual(.ready, std.meta.activeTag(ready.value));
2648 try std.testing.expectEqualSlices(u8, &selected.block_root, &ready.value.ready.block_root);
2649 try std.testing.expectEqualSlices(
2650 u8,
2651 &execution.header.evidence.loaded_image,
2652 &ready.value.ready.image_digest,
2653 );
2654 try std.testing.expectEqual(selected.input_frontier, ready.value.ready.input_frontier);
2655 try std.testing.expectEqual(
2656 selected.terminal_input_offset,
2657 ready.value.ready.terminal_input_offset,
2658 );
2659
2660 const prompt = try os.abi.decodeEvent(&batch.storage[1]);
2661 try std.testing.expectEqual(.terminal_bytes, std.meta.activeTag(prompt.value));
2662 try std.testing.expectEqualStrings(
2663 os.k0.ready_prompt,
2664 prompt.value.terminal_bytes.bytes,
2665 );
2666 try std.testing.expectEqual(@as(u64, 0), prompt.value.terminal_bytes.offset);
2667 }
2668
2669 fn expectRealK0Completion(machine: *instance.Instance) !void {
2670 var batch: instance.EventBatch = undefined;
2671 try machine.takeEvents(&batch);
2672 try std.testing.expectEqual(@as(usize, 4), batch.frames().len);
2673 const semantic = try os.abi.decodeEvent(&batch.storage[0]);
2674 try std.testing.expectEqual(.semantic, std.meta.activeTag(semantic.value));
2675 try std.testing.expectEqualStrings(os.k0.semantic_name, semantic.value.semantic.bytes);
2676 try std.testing.expectEqual(@as(u64, 1), semantic.value.semantic.position);
2677
2678 const terminal = try os.abi.decodeEvent(&batch.storage[1]);
2679 try std.testing.expectEqual(.terminal_bytes, std.meta.activeTag(terminal.value));
2680 try std.testing.expectEqualStrings(
2681 os.k0.incremented_text,
2682 terminal.value.terminal_bytes.bytes,
2683 );
2684 try std.testing.expectEqual(
2685 @as(u64, os.k0.ready_prompt.len),
2686 terminal.value.terminal_bytes.offset,
2687 );
2688
2689 const expected_root = rootAfterOne();
2690 const root = try os.abi.decodeEvent(&batch.storage[2]);
2691 try std.testing.expectEqual(.block_root, std.meta.activeTag(root.value));
2692 try std.testing.expectEqual(@as(u32, 2), root.value.block_root.generation);
2693 try std.testing.expectEqualSlices(u8, &expected_root, &root.value.block_root.digest);
2694
2695 const quiescent = try os.abi.decodeEvent(&batch.storage[3]);
2696 try std.testing.expectEqual(.quiescent, std.meta.activeTag(quiescent.value));
2697 try std.testing.expectEqual(@as(u64, 1), quiescent.value.quiescent.request_sequence);
2698 try std.testing.expectEqual(@as(u64, 1), quiescent.value.quiescent.semantic_frontier);
2699 try std.testing.expectEqual(@as(u64, 6), quiescent.value.quiescent.event_produced);
2700 try std.testing.expectEqual(@as(u64, 19), quiescent.value.quiescent.terminal_offset);
2701 try std.testing.expectEqual(@as(u64, 1), quiescent.value.quiescent.input_frontier);
2702 try std.testing.expectEqual(
2703 @as(u64, os.k0.request_bytes.len),
2704 quiescent.value.quiescent.terminal_input_offset,
2705 );
2706 try std.testing.expectEqualSlices(
2707 u8,
2708 &expected_root,
2709 &quiescent.value.quiescent.block_root,
2710 );
2711 }
2712
2713 fn expectSecondRealK0Completion(machine: *instance.Instance) !void {
2714 var batch: instance.EventBatch = undefined;
2715 try machine.takeEvents(&batch);
2716 try std.testing.expectEqual(@as(usize, 4), batch.frames().len);
2717
2718 const semantic = try os.abi.decodeEvent(&batch.storage[0]);
2719 try std.testing.expectEqual(.semantic, std.meta.activeTag(semantic.value));
2720 try std.testing.expectEqual(@as(u64, 2), semantic.value.semantic.position);
2721
2722 const terminal = try os.abi.decodeEvent(&batch.storage[1]);
2723 try std.testing.expectEqual(.terminal_bytes, std.meta.activeTag(terminal.value));
2724 try std.testing.expectEqual(
2725 @as(u64, 2 * os.k0.ready_prompt.len + os.k0.incremented_text.len),
2726 terminal.value.terminal_bytes.offset,
2727 );
2728
2729 const expected_root = rootAfterTwo();
2730 const root = try os.abi.decodeEvent(&batch.storage[2]);
2731 try std.testing.expectEqual(.block_root, std.meta.activeTag(root.value));
2732 try std.testing.expectEqual(@as(u32, 3), root.value.block_root.generation);
2733 try std.testing.expectEqualSlices(u8, &expected_root, &root.value.block_root.digest);
2734
2735 const quiescent = try os.abi.decodeEvent(&batch.storage[3]);
2736 try std.testing.expectEqual(.quiescent, std.meta.activeTag(quiescent.value));
2737 try std.testing.expectEqual(@as(u64, 2), quiescent.value.quiescent.input_frontier);
2738 try std.testing.expectEqual(
2739 @as(u64, 2 * os.k0.request_bytes.len),
2740 quiescent.value.quiescent.terminal_input_offset,
2741 );
2742 try std.testing.expectEqualSlices(
2743 u8,
2744 &expected_root,
2745 &quiescent.value.quiescent.block_root,
2746 );
2747 }
2748
2749 fn rootAfterOne() os.abi.Digest {
2750 return .{
2751 0xaf, 0x79, 0x67, 0x61, 0xe8, 0xff, 0x01, 0xcd,
2752 0x4a, 0xef, 0x7c, 0x41, 0x3f, 0x7c, 0x4e, 0x7a,
2753 0xa1, 0x08, 0x45, 0x18, 0xda, 0xdc, 0xa7, 0xb7,
2754 0xfe, 0x88, 0xdc, 0x1b, 0xee, 0xee, 0xc2, 0xa8,
2755 };
2756 }
2757
2758 fn rootAfterTwo() os.abi.Digest {
2759 return .{
2760 0x85, 0xd6, 0x22, 0xef, 0x89, 0x7b, 0xe5, 0x8e,
2761 0xcb, 0x4c, 0x58, 0xa9, 0x4b, 0x74, 0xe9, 0xaf,
2762 0x0a, 0x71, 0x1f, 0x5a, 0x43, 0x52, 0xde, 0x11,
2763 0x61, 0xb1, 0xea, 0xb8, 0xad, 0xaf, 0x20, 0xff,
2764 };
2765 }
2766
2767 fn timeRoot() os.abi.Digest {
2768 return .{
2769 0xf3, 0xa3, 0x6c, 0x12, 0x38, 0xf1, 0xcd, 0xd0,
2770 0x3d, 0x8b, 0x69, 0x98, 0x0b, 0x2e, 0xaa, 0x2a,
2771 0x1e, 0xc2, 0x38, 0xea, 0x16, 0x8b, 0x1f, 0xc5,
2772 0x51, 0xb8, 0xeb, 0x2f, 0xb5, 0x29, 0x18, 0xb4,
2773 };
2774 }
2775
2776 fn entropyRoot() os.abi.Digest {
2777 return .{
2778 0xe0, 0x74, 0xde, 0x07, 0xb5, 0x05, 0x73, 0x3c,
2779 0x1e, 0x4b, 0x2d, 0x34, 0x47, 0xba, 0x26, 0x98,
2780 0xc6, 0xe0, 0xe8, 0xcc, 0x1c, 0x78, 0x45, 0x49,
2781 0x27, 0x6d, 0xc2, 0xe6, 0x5c, 0x0d, 0x80, 0xa9,
2782 };
2783 }
2784
2785 fn effectRoot() os.abi.Digest {
2786 return .{
2787 0x78, 0x5c, 0xdd, 0x3d, 0xb9, 0x8c, 0x16, 0x28,
2788 0xd1, 0x84, 0x3e, 0xc4, 0xe8, 0x32, 0xfe, 0xcc,
2789 0x70, 0xaf, 0x81, 0x32, 0xdb, 0x54, 0x81, 0x08,
2790 0x9e, 0x6e, 0x12, 0x3c, 0xc8, 0xf5, 0xed, 0x96,
2791 };
2792 }
2793
2794 fn expectPreHostRejection(input_value: instance.Input, expected: anyerror) !void {
2795 @memset(&fixture.ram, 0xa5);
2796 var operations: fake.Operations = .{};
2797 var storage = instance.Storage.init();
2798 const result = harness.init(
2799 &storage,
2800 &fixture.ram,
2801 input_value,
2802 sys.kvm.operationsFor(&operations),
2803 );
2804 try std.testing.expectEqual(.rejected, std.meta.activeTag(result));
2805 try std.testing.expectEqual(expected, result.rejected);
2806 try std.testing.expectEqual(@as(usize, 0), operations.count(.open));
2807 try std.testing.expectEqual(
2808 @as(u8, 0xa5),
2809 fixture.ram[@intCast(layout.pml4_address)],
2810 );
2811 try std.testing.expectEqual(
2812 @as(u8, 0xa5),
2813 fixture.ram[@intCast(os.boot.kernel.physical_base)],
2814 );
2815 try std.testing.expectEqual(@as(u8, 0xa5), fixture.ram[fixture.ram.len - 1]);
2816 }
2817
2818 fn realK0Input(execution: os.boot.kernel.manifest.View) instance.Input {
2819 return .{
2820 .profile = profile.kvmContinuationTestV1(),
2821 .elf = real_k0_elf,
2822 .execution_manifest = real_k0_manifest,
2823 .expected_execution_fingerprint = execution.header.fingerprint,
2824 .fence = .{
2825 .world = @splat(0x44),
2826 .generation = 1,
2827 .token = @splat(0x55),
2828 },
2829 .initial_time_tick = 7,
2830 .entropy_generation = 1,
2831 .terminal_offset = 0,
2832 .effect_frontier = 0,
2833 .block_root = @splat(0x33),
2834 .source_root = @splat(0x77),
2835 .input_frontier = 0,
2836 .terminal_input_offset = 0,
2837 .outstanding_effect = null,
2838 };
2839 }
2840
2841 fn realK0InputWithManifest(encoded: *const ManifestMutation) instance.Input {
2842 var result = realK0Input(manifest.parse(real_k0_manifest) catch unreachable);
2843 result.execution_manifest = encoded.bytes();
2844 result.expected_execution_fingerprint = encoded.fingerprint;
2845 return result;
2846 }
2847
2848 fn mutateManifest(
2849 source: manifest.View,
2850 removed_site: ?u32,
2851 append_unused_form: bool,
2852 ) !*const ManifestMutation {
2853 var load_index: u16 = 0;
2854 while (load_index < source.header.load_count) : (load_index += 1) {
2855 mutation_loads[load_index] = source.load(load_index);
2856 }
2857 var form_index: u16 = 0;
2858 while (form_index < source.header.form_count) : (form_index += 1) {
2859 mutation_forms[form_index] = source.form(form_index);
2860 }
2861 var form_count: u16 = source.header.form_count;
2862 if (append_unused_form) {
2863 if (form_count == mutation_forms.len) {
2864 return error.TestExpectedSpareFormCapacity;
2865 }
2866 if (source.form(form_count - 1) == std.math.maxInt(u64)) {
2867 return error.TestExpectedSpareFormKey;
2868 }
2869 mutation_forms[form_count] = std.math.maxInt(u64);
2870 form_count += 1;
2871 }
2872 var source_site: u32 = 0;
2873 var site_count: u32 = 0;
2874 while (source_site < source.header.site_count) : (source_site += 1) {
2875 if (source_site == removed_site) continue;
2876 mutation_sites[site_count] = source.site(source_site);
2877 site_count += 1;
2878 }
2879 var evidence = source.header.evidence;
2880 evidence.forms = try manifest.formsDigest(mutation_forms[0..form_count]);
2881 evidence.sites = try manifest.sitesDigest(mutation_sites[0..site_count]);
2882 const encoded = try manifest.encode(.{
2883 .facts = source.header.facts,
2884 .evidence = evidence,
2885 .loads = mutation_loads[0..source.header.load_count],
2886 .forms = mutation_forms[0..form_count],
2887 .sites = mutation_sites[0..site_count],
2888 }, &mutation.storage);
2889 mutation.len = @intCast(encoded.len);
2890 mutation.fingerprint = (try manifest.parse(encoded)).header.fingerprint;
2891 return &mutation;
2892 }
2893
2894 fn directTargetSite(execution: manifest.View) !u32 {
2895 var site_index: u32 = 0;
2896 while (site_index < execution.header.site_count) : (site_index += 1) {
2897 const site = execution.site(site_index);
2898 const decoded = try isa.x86.decode(try instructionBytes(execution, site));
2899 const direct = switch (decoded.operation) {
2900 .ja, .jae, .jb, .jbe, .je, .jne, .call, .jmp => true,
2901 else => false,
2902 };
2903 if (!direct) continue;
2904 const relative = decoded.relative orelse continue;
2905 const next: i64 = @intCast(
2906 @as(u64, site.virtual_offset) + site.instruction_bytes,
2907 );
2908 const target = std.math.add(i64, next, relative) catch continue;
2909 if (target < 0 or target > std.math.maxInt(u32)) continue;
2910 if (findSite(execution, @intCast(target))) |target_index| {
2911 return target_index;
2912 }
2913 }
2914 return error.TestExpectedDirectTarget;
2915 }
2916
2917 fn instructionBytes(
2918 execution: manifest.View,
2919 site: manifest.Site,
2920 ) ![]const u8 {
2921 var load_index: u16 = 0;
2922 while (load_index < execution.header.load_count) : (load_index += 1) {
2923 const load = execution.load(load_index);
2924 if (load.flags & manifest.load_flag_execute == 0) continue;
2925 const site_end = @as(u64, site.virtual_offset) + site.instruction_bytes;
2926 const load_end = load.virtual_offset + load.file_bytes;
2927 if (site.virtual_offset < load.virtual_offset or site_end > load_end) {
2928 continue;
2929 }
2930 const source_start: usize = @intCast(
2931 load.input_offset + (site.virtual_offset - load.virtual_offset),
2932 );
2933 return real_k0_elf[source_start..][0..site.instruction_bytes];
2934 }
2935 return error.TestExpectedExecutableSite;
2936 }
2937
2938 fn findSite(execution: manifest.View, offset: u32) ?u32 {
2939 var low: u32 = 0;
2940 var high = execution.header.site_count;
2941 while (low < high) {
2942 const middle = low + (high - low) / 2;
2943 const candidate = execution.site(middle).virtual_offset;
2944 if (candidate == offset) return middle;
2945 if (offset < candidate) {
2946 high = middle;
2947 } else {
2948 low = middle + 1;
2949 }
2950 }
2951 return null;
2952 }
2953
2954 var execution_fixture: fixture.Execution = undefined;
2955
2956 fn input(image: []const u8) !instance.Input {
2957 execution_fixture = try fixture.execution(image);
2958 return fixture.input(
2959 instance.Input,
2960 profile.kvmContinuationTestV1(),
2961 image,
2962 &execution_fixture,
2963 );
2964 }