lib/machine/src/instance/reference/execute.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

   1 const core = @import("machine_instance_core");
   2 const isa = @import("isa");
   3 const os = @import("os");
   4 const reference = @import("root.zig");
   5 const std = @import("std");
   6 
   7 const backend = core.backend;
   8 const protection = core.protection;
   9 const x86 = isa.x86;
  10 const Cpu = reference.Cpu;
  11 const integer = reference.integer;
  12 const memory = reference.memory;
  13 const manifest = os.boot.kernel.manifest;
  14 
  15 const direction_flag: u64 = 1 << 10;
  16 const divide_exception: u32 = 0;
  17 const invalid_opcode_exception: u32 = 6;
  18 
  19 pub const Error = error{
  20     DivideFault,
  21     InvalidControlTarget,
  22     MemoryFault,
  23     UnsupportedInstruction,
  24 } || core.memory.Error;
  25 
  26 pub const Boundary = union(enum) {
  27     continuing,
  28     io: backend.Io,
  29     halted,
  30 };
  31 
  32 const MemoryTarget = struct {
  33     address: u64,
  34     width: x86.Width,
  35 };
  36 
  37 const Target = union(enum) {
  38     register: x86.Register,
  39     memory: MemoryTarget,
  40 };
  41 
  42 const Explicit = enum(u2) {
  43     register,
  44     operand,
  45     embedded,
  46 };
  47 
  48 pub fn validate(instruction: x86.Instruction) Error!void {
  49     if (instruction.form.prefix.locked()) return error.UnsupportedInstruction;
  50     try validateRegisters(instruction);
  51     try validateOperand(instruction.operand);
  52     switch (instruction.operation) {
  53         .mov => try validateMove(instruction),
  54         .movsx, .movzx => try validateMoveExtend(instruction),
  55         .lea => try validateLea(instruction),
  56         .add, .and_, .or_, .sbb, .sub, .xor_ => try validateBinary(instruction),
  57         .cmp, .test_ => try validateCompare(instruction),
  58         .inc, .dec, .not_ => try validateUnary(instruction),
  59         .rol, .shl, .shr => try validateShift(instruction),
  60         .imul, .mul, .div => try validateMultiplyDivide(instruction),
  61         .ja, .jae, .jb, .jbe, .je, .jmp, .jne => try validateBranch(instruction),
  62         .cmovae, .cmove, .cmovne => try validateConditionalMove(instruction),
  63         .seta, .setae, .setb, .setbe, .sete, .setne => try validateSet(instruction),
  64         .call => try validateCall(instruction),
  65         .ret => try validateRet(instruction),
  66         .push, .pop => try validateStack(instruction),
  67         .movs, .stos => try validateString(instruction),
  68         .bswap => try validateBswap(instruction),
  69         .cmc, .nop, .out, .hlt, .ud2 => try validateBoundary(instruction),
  70         else => return error.UnsupportedInstruction,
  71     }
  72 }
  73 
  74 pub fn step(
  75     cpu: *Cpu,
  76     ram: anytype,
  77     plan: *const protection.Plan,
  78     execution: manifest.View,
  79     instruction: x86.Instruction,
  80     fault: *memory.Fault,
  81 ) Error!Boundary {
  82     fault.* = .{};
  83     var next = cpu.*;
  84     const fallthrough = next.rip +% instruction.bytes;
  85     next.rip = fallthrough;
  86     const boundary = switch (instruction.operation) {
  87         .mov => try move(&next, ram, plan, instruction, fault),
  88         .movsx, .movzx => try moveExtend(&next, ram, plan, instruction, fault),
  89         .lea => try loadAddress(&next, instruction, fault),
  90         .add => try binary(&next, ram, plan, instruction, fault, .add),
  91         .and_ => try binary(&next, ram, plan, instruction, fault, .and_),
  92         .or_ => try binary(&next, ram, plan, instruction, fault, .or_),
  93         .sbb => try binary(&next, ram, plan, instruction, fault, .sbb),
  94         .sub => try binary(&next, ram, plan, instruction, fault, .sub),
  95         .xor_ => try binary(&next, ram, plan, instruction, fault, .xor_),
  96         .cmp => try compare(&next, ram, instruction, fault),
  97         .test_ => try bitTest(&next, ram, instruction, fault),
  98         .inc => try unary(&next, ram, plan, instruction, fault, .increment),
  99         .dec => try unary(&next, ram, plan, instruction, fault, .decrement),
 100         .not_ => try unary(&next, ram, plan, instruction, fault, .bit_not),
 101         .rol => try shift(&next, ram, plan, instruction, fault, .rotate_left),
 102         .shl => try shift(&next, ram, plan, instruction, fault, .left),
 103         .shr => try shift(&next, ram, plan, instruction, fault, .right),
 104         .imul => try signedMultiply(&next, ram, plan, instruction, fault),
 105         .mul => try multiply(&next, ram, instruction, fault),
 106         .div => try divide(&next, ram, instruction, fault),
 107         .ja, .jae, .jb, .jbe, .je, .jmp, .jne => try branch(
 108             &next,
 109             execution,
 110             instruction,
 111         ),
 112         .cmovae, .cmove, .cmovne => try conditionalMove(
 113             &next,
 114             ram,
 115             plan,
 116             instruction,
 117             fault,
 118         ),
 119         .seta, .setae, .setb, .setbe, .sete, .setne => try setCondition(
 120             &next,
 121             ram,
 122             plan,
 123             instruction,
 124             fault,
 125         ),
 126         .call => try call(&next, ram, plan, execution, instruction, fault),
 127         .ret => try ret(&next, ram, execution, instruction, fault),
 128         .push => try push(&next, ram, plan, instruction, fault),
 129         .pop => try pop(&next, ram, instruction, fault),
 130         .movs => try moveString(&next, ram, plan, instruction, fault),
 131         .stos => try storeString(&next, ram, plan, instruction, fault),
 132         .bswap => try byteSwap(&next, instruction),
 133         .cmc => carryComplement(&next),
 134         .nop => Boundary.continuing,
 135         .out => try output(&next),
 136         .hlt => Boundary.halted,
 137         .ud2 => return error.UnsupportedInstruction,
 138         else => return error.UnsupportedInstruction,
 139     };
 140     cpu.* = next;
 141     return boundary;
 142 }
 143 
 144 pub fn exceptionFor(failure: Error) ?u32 {
 145     return switch (failure) {
 146         error.DivideFault => divide_exception,
 147         error.InvalidControlTarget, error.UnsupportedInstruction => invalid_opcode_exception,
 148         error.MemoryAddressOverflow,
 149         error.MemoryAuthenticationFailed,
 150         error.MemoryCapacityExceeded,
 151         error.MemoryFault,
 152         error.MemoryOutOfBounds,
 153         error.MemoryReadFailed,
 154         => null,
 155     };
 156 }
 157 
 158 fn validateRegisters(instruction: x86.Instruction) Error!void {
 159     if (instruction.register) |value| try validateRegister(value);
 160     if (instruction.embedded) |value| try validateRegister(value);
 161     switch (instruction.operand) {
 162         .register => |value| try validateRegister(value),
 163         .memory => |value| {
 164             if (value.base) |base| try validateAddressRegister(base);
 165             if (value.index) |index| try validateAddressRegister(index);
 166         },
 167         .none => {},
 168     }
 169     for (instruction.implicitRegisters()) |value| try validateRegister(value.register);
 170 }
 171 
 172 fn validateRegister(value: x86.Register) Error!void {
 173     if (value.bank != .gpr) return error.UnsupportedInstruction;
 174     switch (value.lane) {
 175         .low8, .word, .dword, .qword => {},
 176         .high8, .vector128 => return error.UnsupportedInstruction,
 177     }
 178 }
 179 
 180 fn validateAddressRegister(value: x86.Register) Error!void {
 181     if (value.bank != .gpr or value.lane != .qword) {
 182         return error.UnsupportedInstruction;
 183     }
 184 }
 185 
 186 fn validateOperand(value: x86.Operand) Error!void {
 187     switch (value) {
 188         .none, .register => {},
 189         .memory => |source| {
 190             if ((source.segment != .none and source.width != .none) or
 191                 source.width == .vector128)
 192             {
 193                 return error.UnsupportedInstruction;
 194             }
 195             const valid = switch (source.kind) {
 196                 .base => source.base != null and source.index == null,
 197                 .base_indexed => source.base != null and source.index != null,
 198                 .absolute => source.base == null and source.index == null,
 199                 .absolute_indexed => source.base == null and source.index != null,
 200                 .none, .rip_relative => false,
 201             };
 202             if (!valid) return error.UnsupportedInstruction;
 203         },
 204     }
 205 }
 206 
 207 fn validateMove(instruction: x86.Instruction) Error!void {
 208     if (countAccess(instruction, .write) != 1 or
 209         countAccess(instruction, .read_write) != 0)
 210     {
 211         return error.UnsupportedInstruction;
 212     }
 213     const sources = countAccess(instruction, .read) +
 214         countBool(instruction.immediate != null);
 215     if (sources != 1 or instruction.relative != null) {
 216         return error.UnsupportedInstruction;
 217     }
 218     try validateScalarWidth(instruction.form.width);
 219 }
 220 
 221 fn validateMoveExtend(instruction: x86.Instruction) Error!void {
 222     if (countAccess(instruction, .write) != 1 or
 223         countAccess(instruction, .read) != 1 or
 224         countAccess(instruction, .read_write) != 0 or
 225         instruction.immediate != null)
 226     {
 227         return error.UnsupportedInstruction;
 228     }
 229     try validateScalarWidth(instruction.form.width);
 230     const source_width = try operandScalarWidth(instruction.operand);
 231     if (source_width.bytes() >= instruction.form.width.bytes()) {
 232         return error.UnsupportedInstruction;
 233     }
 234 }
 235 
 236 fn validateLea(instruction: x86.Instruction) Error!void {
 237     if (instruction.register == null or
 238         instruction.register_access != .write or
 239         std.meta.activeTag(instruction.operand) != .memory or
 240         instruction.operand_access != .none or
 241         instruction.immediate != null or
 242         (instruction.form.width != .dword and
 243             instruction.form.width != .qword))
 244     {
 245         return error.UnsupportedInstruction;
 246     }
 247 }
 248 
 249 fn validateBinary(instruction: x86.Instruction) Error!void {
 250     if (countAccess(instruction, .read_write) != 1 or
 251         countAccess(instruction, .write) != 0)
 252     {
 253         return error.UnsupportedInstruction;
 254     }
 255     const sources = countAccess(instruction, .read) +
 256         countBool(instruction.immediate != null);
 257     if (sources != 1) return error.UnsupportedInstruction;
 258     try validateScalarWidth(instruction.form.width);
 259 }
 260 
 261 fn validateCompare(instruction: x86.Instruction) Error!void {
 262     if (countAccess(instruction, .write) != 0 or
 263         countAccess(instruction, .read_write) != 0)
 264     {
 265         return error.UnsupportedInstruction;
 266     }
 267     const sources = countAccess(instruction, .read) +
 268         countBool(instruction.immediate != null);
 269     if (sources != 2) return error.UnsupportedInstruction;
 270     try validateScalarWidth(instruction.form.width);
 271 }
 272 
 273 fn validateUnary(instruction: x86.Instruction) Error!void {
 274     if (countAccess(instruction, .read_write) != 1 or
 275         countAccess(instruction, .read) != 0 or
 276         countAccess(instruction, .write) != 0 or
 277         instruction.immediate != null)
 278     {
 279         return error.UnsupportedInstruction;
 280     }
 281     try validateScalarWidth(instruction.form.width);
 282 }
 283 
 284 fn validateShift(instruction: x86.Instruction) Error!void {
 285     if (countAccess(instruction, .read_write) != 1 or
 286         countAccess(instruction, .write) != 0 or
 287         countAccess(instruction, .read) != 0)
 288     {
 289         return error.UnsupportedInstruction;
 290     }
 291     const count_sources = countBool(instruction.immediate != null) +
 292         countBool(hasImplicitCl(instruction)) +
 293         countBool(instruction.form.opcode == 0xd0 or instruction.form.opcode == 0xd1);
 294     if (count_sources != 1) return error.UnsupportedInstruction;
 295     try validateScalarWidth(instruction.form.width);
 296 }
 297 
 298 fn validateMultiplyDivide(instruction: x86.Instruction) Error!void {
 299     if (instruction.operation == .imul) {
 300         if ((instruction.form.opcode != 0x69 and instruction.form.opcode != 0x6b) or
 301             instruction.register == null or
 302             instruction.register_access != .write or
 303             instruction.operand_access != .read or
 304             instruction.embedded_access != .none or
 305             explicitAccessCount(instruction) != 2 or
 306             instruction.immediate == null or
 307             instruction.relative != null or
 308             (instruction.form.width != .dword and
 309                 instruction.form.width != .qword))
 310         {
 311             return error.UnsupportedInstruction;
 312         }
 313         return;
 314     }
 315     if (countAccess(instruction, .read) != 1 or
 316         countAccess(instruction, .write) != 0 or
 317         countAccess(instruction, .read_write) != 0 or
 318         instruction.immediate != null)
 319     {
 320         return error.UnsupportedInstruction;
 321     }
 322     const supported = switch (instruction.operation) {
 323         .mul => instruction.form.width == .qword,
 324         .div => instruction.form.width == .dword or instruction.form.width == .qword,
 325         else => false,
 326     };
 327     if (!supported) return error.UnsupportedInstruction;
 328 }
 329 
 330 fn validateConditionalMove(instruction: x86.Instruction) Error!void {
 331     if (instruction.register == null or
 332         instruction.register_access != .read_write or
 333         instruction.operand_access != .read or
 334         instruction.embedded_access != .none or
 335         instruction.immediate != null or
 336         instruction.relative != null or
 337         explicitAccessCount(instruction) != 2)
 338     {
 339         return error.UnsupportedInstruction;
 340     }
 341     try validateScalarWidth(instruction.form.width);
 342 }
 343 
 344 fn validateBranch(instruction: x86.Instruction) Error!void {
 345     if (instruction.relative == null or explicitAccessCount(instruction) != 0) {
 346         return error.UnsupportedInstruction;
 347     }
 348 }
 349 
 350 fn validateSet(instruction: x86.Instruction) Error!void {
 351     if (countAccess(instruction, .write) != 1 or
 352         explicitAccessCount(instruction) != 1 or
 353         instruction.form.width != .byte)
 354     {
 355         return error.UnsupportedInstruction;
 356     }
 357 }
 358 
 359 fn validateCall(instruction: x86.Instruction) Error!void {
 360     if (instruction.relative == null or explicitAccessCount(instruction) != 0 or
 361         !hasImplicitRsp(instruction, .read_write) or
 362         instruction.form.width != .qword or
 363         instruction.form.opcode != 0xe8)
 364     {
 365         return error.UnsupportedInstruction;
 366     }
 367 }
 368 
 369 fn validateRet(instruction: x86.Instruction) Error!void {
 370     if (instruction.relative != null or instruction.immediate != null or
 371         explicitAccessCount(instruction) != 0 or
 372         !hasImplicitRsp(instruction, .read_write) or
 373         instruction.form.width != .qword or
 374         instruction.form.opcode != 0xc3)
 375     {
 376         return error.UnsupportedInstruction;
 377     }
 378 }
 379 
 380 fn validateStack(instruction: x86.Instruction) Error!void {
 381     if (instruction.form.width != .qword or
 382         !hasImplicitRsp(instruction, .read_write) or
 383         instruction.operand != .none)
 384     {
 385         return error.UnsupportedInstruction;
 386     }
 387     if (instruction.operation == .push and instruction.form.opcode == 0x6a) {
 388         if (instruction.immediate == null or instruction.embedded != null or
 389             explicitAccessCount(instruction) != 0)
 390         {
 391             return error.UnsupportedInstruction;
 392         }
 393         return;
 394     }
 395     if (instruction.immediate != null or instruction.embedded == null) {
 396         return error.UnsupportedInstruction;
 397     }
 398     const expected: x86.Access = if (instruction.operation == .push) .read else .write;
 399     if (instruction.embedded_access != expected or explicitAccessCount(instruction) != 1) {
 400         return error.UnsupportedInstruction;
 401     }
 402 }
 403 
 404 fn validateString(instruction: x86.Instruction) Error!void {
 405     if (!instruction.form.prefix.repeated() or
 406         explicitAccessCount(instruction) != 0)
 407     {
 408         return error.UnsupportedInstruction;
 409     }
 410     try validateScalarWidth(instruction.form.width);
 411     if (instruction.implicit_count != 3) return error.UnsupportedInstruction;
 412 }
 413 
 414 fn validateBswap(instruction: x86.Instruction) Error!void {
 415     if (instruction.embedded == null or instruction.embedded_access != .read_write or
 416         explicitAccessCount(instruction) != 1 or
 417         (instruction.form.width != .dword and instruction.form.width != .qword))
 418     {
 419         return error.UnsupportedInstruction;
 420     }
 421 }
 422 
 423 fn validateBoundary(instruction: x86.Instruction) Error!void {
 424     switch (instruction.operation) {
 425         .out => {
 426             if (instruction.form.width != .byte or instruction.implicit_count != 2 or
 427                 explicitAccessCount(instruction) != 0)
 428             {
 429                 return error.UnsupportedInstruction;
 430             }
 431         },
 432         .cmc, .nop, .hlt, .ud2 => {
 433             if (explicitAccessCount(instruction) != 0 or instruction.implicit_count != 0) {
 434                 return error.UnsupportedInstruction;
 435             }
 436         },
 437         else => unreachable,
 438     }
 439 }
 440 
 441 fn validateScalarWidth(width: x86.Width) Error!void {
 442     switch (width) {
 443         .byte, .word, .dword, .qword => {},
 444         .none, .vector128 => return error.UnsupportedInstruction,
 445     }
 446 }
 447 
 448 fn countAccess(instruction: x86.Instruction, access: x86.Access) usize {
 449     return countBool(instruction.register_access == access) +
 450         countBool(instruction.operand_access == access) +
 451         countBool(instruction.embedded_access == access);
 452 }
 453 
 454 fn explicitAccessCount(instruction: x86.Instruction) usize {
 455     return countBool(instruction.register_access != .none) +
 456         countBool(instruction.operand_access != .none) +
 457         countBool(instruction.embedded_access != .none);
 458 }
 459 
 460 fn countBool(value: bool) usize {
 461     return @intFromBool(value);
 462 }
 463 
 464 fn hasImplicitCl(instruction: x86.Instruction) bool {
 465     for (instruction.implicitRegisters()) |value| {
 466         if (value.register.number == 1 and value.register.lane == .low8 and
 467             value.access == .read)
 468         {
 469             return true;
 470         }
 471     }
 472     return false;
 473 }
 474 
 475 fn hasImplicitRsp(instruction: x86.Instruction, access: x86.Access) bool {
 476     for (instruction.implicitRegisters()) |value| {
 477         if (value.register.number == 4 and value.register.lane == .qword and
 478             value.access == access)
 479         {
 480             return true;
 481         }
 482     }
 483     return false;
 484 }
 485 
 486 const BinaryKind = enum(u3) {
 487     add,
 488     and_,
 489     or_,
 490     sbb,
 491     sub,
 492     xor_,
 493 };
 494 
 495 const UnaryKind = enum(u2) {
 496     increment,
 497     decrement,
 498     bit_not,
 499 };
 500 
 501 const ShiftKind = enum(u2) {
 502     left,
 503     right,
 504     rotate_left,
 505 };
 506 
 507 fn move(
 508     cpu: *Cpu,
 509     ram: anytype,
 510     plan: *const protection.Plan,
 511     instruction: x86.Instruction,
 512     fault: *memory.Fault,
 513 ) Error!Boundary {
 514     const destination = try targetForAccess(instruction, .write, cpu, fault);
 515     const value = try sourceValue(instruction, cpu, ram, fault);
 516     try writeTarget(cpu, ram, plan, destination, value, fault);
 517     return .continuing;
 518 }
 519 
 520 fn moveExtend(
 521     cpu: *Cpu,
 522     ram: anytype,
 523     plan: *const protection.Plan,
 524     instruction: x86.Instruction,
 525     fault: *memory.Fault,
 526 ) Error!Boundary {
 527     const destination = try targetForAccess(instruction, .write, cpu, fault);
 528     const source = try targetForAccess(instruction, .read, cpu, fault);
 529     const raw = try readTarget(cpu, ram, source, fault);
 530     const value = switch (instruction.operation) {
 531         .movsx => integer.signExtend(try operandScalarWidth(instruction.operand), raw),
 532         .movzx => raw,
 533         else => return error.UnsupportedInstruction,
 534     };
 535     try writeTarget(cpu, ram, plan, destination, value, fault);
 536     return .continuing;
 537 }
 538 
 539 fn loadAddress(
 540     cpu: *Cpu,
 541     instruction: x86.Instruction,
 542     fault: *memory.Fault,
 543 ) Error!Boundary {
 544     const destination = instruction.register orelse return error.UnsupportedInstruction;
 545     const source = switch (instruction.operand) {
 546         .memory => |value| value,
 547         else => return error.UnsupportedInstruction,
 548     };
 549     const address = memory.effectiveAddress(source, cpu) catch |failure| {
 550         return addressFailure(failure, fault);
 551     };
 552     cpu.write(destination, address) catch return error.UnsupportedInstruction;
 553     return .continuing;
 554 }
 555 
 556 fn binary(
 557     cpu: *Cpu,
 558     ram: anytype,
 559     plan: *const protection.Plan,
 560     instruction: x86.Instruction,
 561     fault: *memory.Fault,
 562     kind: BinaryKind,
 563 ) Error!Boundary {
 564     const destination = try targetForAccess(instruction, .read_write, cpu, fault);
 565     const lhs = try readTarget(cpu, ram, destination, fault);
 566     const rhs = try sourceValue(instruction, cpu, ram, fault);
 567     const result = switch (kind) {
 568         .add => integer.add(instruction.form.width, lhs, rhs),
 569         .and_ => integer.bitAnd(instruction.form.width, lhs, rhs),
 570         .or_ => integer.bitOr(instruction.form.width, lhs, rhs),
 571         .sbb => integer.sbb(
 572             instruction.form.width,
 573             lhs,
 574             rhs,
 575             cpu.flag(integer.flag.carry),
 576         ),
 577         .sub => integer.sub(instruction.form.width, lhs, rhs),
 578         .xor_ => integer.bitXor(instruction.form.width, lhs, rhs),
 579     };
 580     try writeTarget(cpu, ram, plan, destination, result.value, fault);
 581     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 582     return .continuing;
 583 }
 584 
 585 fn compare(
 586     cpu: *Cpu,
 587     ram: anytype,
 588     instruction: x86.Instruction,
 589     fault: *memory.Fault,
 590 ) Error!Boundary {
 591     var lhs: u64 = undefined;
 592     var rhs: u64 = undefined;
 593     if (instruction.immediate) |value| {
 594         const target = try targetForAccess(instruction, .read, cpu, fault);
 595         lhs = try readTarget(cpu, ram, target, fault);
 596         rhs = immediateValue(value);
 597     } else switch (instruction.form.opcode) {
 598         0x38, 0x39 => {
 599             lhs = try readExplicit(cpu, ram, instruction, .operand, fault);
 600             rhs = try readExplicit(cpu, ram, instruction, .register, fault);
 601         },
 602         0x3a, 0x3b => {
 603             lhs = try readExplicit(cpu, ram, instruction, .register, fault);
 604             rhs = try readExplicit(cpu, ram, instruction, .operand, fault);
 605         },
 606         else => return error.UnsupportedInstruction,
 607     }
 608     const result = integer.cmp(instruction.form.width, lhs, rhs);
 609     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 610     return .continuing;
 611 }
 612 
 613 fn bitTest(
 614     cpu: *Cpu,
 615     ram: anytype,
 616     instruction: x86.Instruction,
 617     fault: *memory.Fault,
 618 ) Error!Boundary {
 619     var values: [2]u64 = undefined;
 620     var count: usize = 0;
 621     if (instruction.register_access == .read) {
 622         values[count] = try readExplicit(cpu, ram, instruction, .register, fault);
 623         count += 1;
 624     }
 625     if (instruction.operand_access == .read) {
 626         values[count] = try readExplicit(cpu, ram, instruction, .operand, fault);
 627         count += 1;
 628     }
 629     if (instruction.embedded_access == .read) {
 630         values[count] = try readExplicit(cpu, ram, instruction, .embedded, fault);
 631         count += 1;
 632     }
 633     if (instruction.immediate) |value| {
 634         if (count >= values.len) return error.UnsupportedInstruction;
 635         values[count] = immediateValue(value);
 636         count += 1;
 637     }
 638     if (count != values.len) return error.UnsupportedInstruction;
 639     const result = integer.bitTest(instruction.form.width, values[0], values[1]);
 640     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 641     return .continuing;
 642 }
 643 
 644 fn unary(
 645     cpu: *Cpu,
 646     ram: anytype,
 647     plan: *const protection.Plan,
 648     instruction: x86.Instruction,
 649     fault: *memory.Fault,
 650     kind: UnaryKind,
 651 ) Error!Boundary {
 652     const destination = try targetForAccess(instruction, .read_write, cpu, fault);
 653     const value = try readTarget(cpu, ram, destination, fault);
 654     const result = switch (kind) {
 655         .increment => integer.increment(instruction.form.width, value),
 656         .decrement => integer.decrement(instruction.form.width, value),
 657         .bit_not => integer.bitNot(instruction.form.width, value),
 658     };
 659     try writeTarget(cpu, ram, plan, destination, result.value, fault);
 660     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 661     return .continuing;
 662 }
 663 
 664 fn shift(
 665     cpu: *Cpu,
 666     ram: anytype,
 667     plan: *const protection.Plan,
 668     instruction: x86.Instruction,
 669     fault: *memory.Fault,
 670     kind: ShiftKind,
 671 ) Error!Boundary {
 672     const destination = try targetForAccess(instruction, .read_write, cpu, fault);
 673     const value = try readTarget(cpu, ram, destination, fault);
 674     const count = try shiftCount(cpu, instruction);
 675     const result = switch (kind) {
 676         .left => integer.shl(instruction.form.width, value, count),
 677         .right => integer.shr(instruction.form.width, value, count),
 678         .rotate_left => integer.rol(instruction.form.width, value, count),
 679     };
 680     try writeTarget(cpu, ram, plan, destination, result.value, fault);
 681     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 682     return .continuing;
 683 }
 684 
 685 fn signedMultiply(
 686     cpu: *Cpu,
 687     ram: anytype,
 688     plan: *const protection.Plan,
 689     instruction: x86.Instruction,
 690     fault: *memory.Fault,
 691 ) Error!Boundary {
 692     const source = try targetForAccess(instruction, .read, cpu, fault);
 693     const destination = try targetForAccess(instruction, .write, cpu, fault);
 694     const lhs = try readTarget(cpu, ram, source, fault);
 695     const rhs = immediateValue(instruction.immediate orelse
 696         return error.UnsupportedInstruction);
 697     const result = integer.signedMultiplyLow(instruction.form.width, lhs, rhs);
 698     try writeTarget(cpu, ram, plan, destination, result.value, fault);
 699     cpu.writeFlagsDefined(result.flags.defined, result.flags.bits);
 700     return .continuing;
 701 }
 702 
 703 fn multiply(
 704     cpu: *Cpu,
 705     ram: anytype,
 706     instruction: x86.Instruction,
 707     fault: *memory.Fault,
 708 ) Error!Boundary {
 709     const source = try targetForAccess(instruction, .read, cpu, fault);
 710     const rhs = try readTarget(cpu, ram, source, fault);
 711     const accumulator = try scalarRegister(0, instruction.form.width);
 712     const high = try scalarRegister(2, instruction.form.width);
 713     const lhs = cpu.read(accumulator) catch return error.UnsupportedInstruction;
 714     const product = integer.unsignedMultiply(instruction.form.width, lhs, rhs);
 715     cpu.write(accumulator, product.low) catch return error.UnsupportedInstruction;
 716     cpu.write(high, product.high) catch return error.UnsupportedInstruction;
 717     cpu.writeFlagsDefined(product.flags.defined, product.flags.bits);
 718     return .continuing;
 719 }
 720 
 721 fn divide(
 722     cpu: *Cpu,
 723     ram: anytype,
 724     instruction: x86.Instruction,
 725     fault: *memory.Fault,
 726 ) Error!Boundary {
 727     const source = try targetForAccess(instruction, .read, cpu, fault);
 728     const divisor = try readTarget(cpu, ram, source, fault);
 729     const accumulator = try scalarRegister(0, instruction.form.width);
 730     const high = try scalarRegister(2, instruction.form.width);
 731     const low_value = cpu.read(accumulator) catch return error.UnsupportedInstruction;
 732     const high_value = cpu.read(high) catch return error.UnsupportedInstruction;
 733     const result = integer.unsignedDivide(
 734         instruction.form.width,
 735         high_value,
 736         low_value,
 737         divisor,
 738     ) catch return error.DivideFault;
 739     cpu.write(accumulator, result.quotient) catch return error.UnsupportedInstruction;
 740     cpu.write(high, result.remainder) catch return error.UnsupportedInstruction;
 741     return .continuing;
 742 }
 743 
 744 fn branch(
 745     cpu: *Cpu,
 746     execution: manifest.View,
 747     instruction: x86.Instruction,
 748 ) Error!Boundary {
 749     const taken = condition(cpu, instruction.operation);
 750     const target = if (taken)
 751         relativeTarget(cpu.rip, instruction.relative orelse
 752             return error.UnsupportedInstruction)
 753     else
 754         cpu.rip;
 755     try requireSite(execution, target);
 756     cpu.rip = target;
 757     return .continuing;
 758 }
 759 
 760 fn conditionalMove(
 761     cpu: *Cpu,
 762     ram: anytype,
 763     plan: *const protection.Plan,
 764     instruction: x86.Instruction,
 765     fault: *memory.Fault,
 766 ) Error!Boundary {
 767     const source = try targetForAccess(instruction, .read, cpu, fault);
 768     const destination = try targetForAccess(instruction, .read_write, cpu, fault);
 769     const value = try readTarget(cpu, ram, source, fault);
 770     if (condition(cpu, instruction.operation)) {
 771         try writeTarget(cpu, ram, plan, destination, value, fault);
 772     }
 773     return .continuing;
 774 }
 775 
 776 fn setCondition(
 777     cpu: *Cpu,
 778     ram: anytype,
 779     plan: *const protection.Plan,
 780     instruction: x86.Instruction,
 781     fault: *memory.Fault,
 782 ) Error!Boundary {
 783     const destination = try targetForAccess(instruction, .write, cpu, fault);
 784     const value: u8 = @intFromBool(condition(cpu, instruction.operation));
 785     try writeTarget(cpu, ram, plan, destination, value, fault);
 786     return .continuing;
 787 }
 788 
 789 fn call(
 790     cpu: *Cpu,
 791     ram: anytype,
 792     plan: *const protection.Plan,
 793     execution: manifest.View,
 794     instruction: x86.Instruction,
 795     fault: *memory.Fault,
 796 ) Error!Boundary {
 797     const target = relativeTarget(
 798         cpu.rip,
 799         instruction.relative orelse return error.UnsupportedInstruction,
 800     );
 801     try requireSite(execution, target);
 802     const rsp = cpu.read(.{ .number = 4 }) catch return error.UnsupportedInstruction;
 803     const next_rsp = rsp -% @sizeOf(u64);
 804     try writeMemory(ram, plan, next_rsp, .qword, cpu.rip, fault);
 805     cpu.write(.{ .number = 4 }, next_rsp) catch return error.UnsupportedInstruction;
 806     cpu.rip = target;
 807     return .continuing;
 808 }
 809 
 810 fn ret(
 811     cpu: *Cpu,
 812     ram: anytype,
 813     execution: manifest.View,
 814     _: x86.Instruction,
 815     fault: *memory.Fault,
 816 ) Error!Boundary {
 817     const rsp = cpu.read(.{ .number = 4 }) catch return error.UnsupportedInstruction;
 818     const target = readMemory(ram, rsp, .qword, fault) catch |failure| return failure;
 819     try requireSite(execution, target);
 820     cpu.write(.{ .number = 4 }, rsp +% @sizeOf(u64)) catch
 821         return error.UnsupportedInstruction;
 822     cpu.rip = target;
 823     return .continuing;
 824 }
 825 
 826 fn push(
 827     cpu: *Cpu,
 828     ram: anytype,
 829     plan: *const protection.Plan,
 830     instruction: x86.Instruction,
 831     fault: *memory.Fault,
 832 ) Error!Boundary {
 833     const value = if (instruction.immediate) |immediate|
 834         immediateValue(immediate)
 835     else
 836         try readExplicit(cpu, ram, instruction, .embedded, fault);
 837     const rsp = cpu.read(.{ .number = 4 }) catch return error.UnsupportedInstruction;
 838     const next_rsp = rsp -% @sizeOf(u64);
 839     try writeMemory(ram, plan, next_rsp, .qword, value, fault);
 840     cpu.write(.{ .number = 4 }, next_rsp) catch return error.UnsupportedInstruction;
 841     return .continuing;
 842 }
 843 
 844 fn pop(
 845     cpu: *Cpu,
 846     ram: anytype,
 847     instruction: x86.Instruction,
 848     fault: *memory.Fault,
 849 ) Error!Boundary {
 850     const rsp = cpu.read(.{ .number = 4 }) catch return error.UnsupportedInstruction;
 851     const value = try readMemory(ram, rsp, .qword, fault);
 852     const destination = instruction.embedded orelse return error.UnsupportedInstruction;
 853     cpu.write(destination, value) catch return error.UnsupportedInstruction;
 854     if (destination.number != 4) {
 855         cpu.write(.{ .number = 4 }, rsp +% @sizeOf(u64)) catch
 856             return error.UnsupportedInstruction;
 857     }
 858     return .continuing;
 859 }
 860 
 861 fn moveString(
 862     cpu: *Cpu,
 863     ram: anytype,
 864     plan: *const protection.Plan,
 865     instruction: x86.Instruction,
 866     fault: *memory.Fault,
 867 ) Error!Boundary {
 868     const source = cpu.read(.{ .number = 6 }) catch return error.UnsupportedInstruction;
 869     const destination = cpu.read(.{ .number = 7 }) catch return error.UnsupportedInstruction;
 870     const count = cpu.read(.{ .number = 1 }) catch return error.UnsupportedInstruction;
 871     const backwards = cpu.flag(direction_flag);
 872     const element_bytes = instruction.form.width.bytes();
 873     const source_range = try stringRange(
 874         ram,
 875         source,
 876         count,
 877         element_bytes,
 878         backwards,
 879         fault,
 880     );
 881     const destination_range = try stringRange(
 882         ram,
 883         destination,
 884         count,
 885         element_bytes,
 886         backwards,
 887         fault,
 888     );
 889     try protectRange(plan, destination_range, fault);
 890     try prepareMemoryWrite(ram, destination_range, fault);
 891     const iterations: usize = @intCast(count);
 892     for (0..iterations) |index| {
 893         const source_address = stringElementAddress(
 894             source,
 895             index,
 896             element_bytes,
 897             backwards,
 898         );
 899         const destination_address = stringElementAddress(
 900             destination,
 901             index,
 902             element_bytes,
 903             backwards,
 904         );
 905         const value = try readMemory(
 906             ram,
 907             source_address,
 908             instruction.form.width,
 909             fault,
 910         );
 911         try writeMemory(
 912             ram,
 913             plan,
 914             destination_address,
 915             instruction.form.width,
 916             value,
 917             fault,
 918         );
 919     }
 920     const total_bytes: u64 = @intCast(source_range.end - source_range.start);
 921     updateStringRegisters(cpu, source, destination, total_bytes, backwards, true);
 922     return .continuing;
 923 }
 924 
 925 fn storeString(
 926     cpu: *Cpu,
 927     ram: anytype,
 928     plan: *const protection.Plan,
 929     instruction: x86.Instruction,
 930     fault: *memory.Fault,
 931 ) Error!Boundary {
 932     const destination = cpu.read(.{ .number = 7 }) catch return error.UnsupportedInstruction;
 933     const count = cpu.read(.{ .number = 1 }) catch return error.UnsupportedInstruction;
 934     const accumulator = try scalarRegister(0, instruction.form.width);
 935     const value = cpu.read(accumulator) catch return error.UnsupportedInstruction;
 936     const backwards = cpu.flag(direction_flag);
 937     const element_bytes = instruction.form.width.bytes();
 938     const destination_range = try stringRange(
 939         ram,
 940         destination,
 941         count,
 942         element_bytes,
 943         backwards,
 944         fault,
 945     );
 946     try protectRange(plan, destination_range, fault);
 947     try prepareMemoryWrite(ram, destination_range, fault);
 948     const iterations: usize = @intCast(count);
 949     for (0..iterations) |index| {
 950         const address = stringElementAddress(
 951             destination,
 952             index,
 953             element_bytes,
 954             backwards,
 955         );
 956         try writeMemory(
 957             ram,
 958             plan,
 959             address,
 960             instruction.form.width,
 961             value,
 962             fault,
 963         );
 964     }
 965     const total_bytes: u64 = @intCast(destination_range.end - destination_range.start);
 966     updateStringRegisters(cpu, 0, destination, total_bytes, backwards, false);
 967     return .continuing;
 968 }
 969 
 970 fn byteSwap(cpu: *Cpu, instruction: x86.Instruction) Error!Boundary {
 971     const register = instruction.embedded orelse return error.UnsupportedInstruction;
 972     const value = cpu.read(register) catch return error.UnsupportedInstruction;
 973     const swapped: u64 = switch (instruction.form.width) {
 974         .dword => @byteSwap(@as(u32, @truncate(value))),
 975         .qword => @byteSwap(value),
 976         else => return error.UnsupportedInstruction,
 977     };
 978     cpu.write(register, swapped) catch return error.UnsupportedInstruction;
 979     return .continuing;
 980 }
 981 
 982 fn carryComplement(cpu: *Cpu) Boundary {
 983     const bits = if (cpu.flag(integer.flag.carry)) 0 else integer.flag.carry;
 984     cpu.writeFlagsDefined(integer.flag.carry, bits);
 985     return .continuing;
 986 }
 987 
 988 fn output(cpu: *Cpu) Error!Boundary {
 989     const port = cpu.read(.{ .number = 2, .lane = .word }) catch
 990         return error.UnsupportedInstruction;
 991     const value = cpu.read(.{ .number = 0, .lane = .low8 }) catch
 992         return error.UnsupportedInstruction;
 993     return .{ .io = .{
 994         .direction = .output,
 995         .port = @intCast(port),
 996         .size = 1,
 997         .count = 1,
 998         .data_bytes = 1,
 999         .first_byte = @truncate(value),
1000     } };
1001 }
1002 
1003 fn targetForAccess(
1004     instruction: x86.Instruction,
1005     access: x86.Access,
1006     cpu: *const Cpu,
1007     fault: *memory.Fault,
1008 ) Error!Target {
1009     var result: ?Target = null;
1010     if (instruction.register_access == access) {
1011         try storeTarget(&result, .{ .register = instruction.register orelse
1012             return error.UnsupportedInstruction });
1013     }
1014     if (instruction.operand_access == access) {
1015         try storeTarget(&result, try operandTarget(cpu, instruction.operand, fault));
1016     }
1017     if (instruction.embedded_access == access) {
1018         try storeTarget(&result, .{ .register = instruction.embedded orelse
1019             return error.UnsupportedInstruction });
1020     }
1021     return result orelse error.UnsupportedInstruction;
1022 }
1023 
1024 fn storeTarget(destination: *?Target, value: Target) Error!void {
1025     if (destination.* != null) return error.UnsupportedInstruction;
1026     destination.* = value;
1027 }
1028 
1029 fn operandTarget(
1030     cpu: *const Cpu,
1031     operand: x86.Operand,
1032     fault: *memory.Fault,
1033 ) Error!Target {
1034     return switch (operand) {
1035         .none => error.UnsupportedInstruction,
1036         .register => |value| .{ .register = value },
1037         .memory => |value| .{ .memory = .{
1038             .address = memory.effectiveAddress(value, cpu) catch |failure| {
1039                 return addressFailure(failure, fault);
1040             },
1041             .width = value.width,
1042         } },
1043     };
1044 }
1045 
1046 fn readTarget(
1047     cpu: *const Cpu,
1048     ram: anytype,
1049     target: Target,
1050     fault: *memory.Fault,
1051 ) Error!u64 {
1052     return switch (target) {
1053         .register => |value| cpu.read(value) catch error.UnsupportedInstruction,
1054         .memory => |value| readMemory(ram, value.address, value.width, fault),
1055     };
1056 }
1057 
1058 fn writeTarget(
1059     cpu: *Cpu,
1060     ram: anytype,
1061     plan: *const protection.Plan,
1062     target: Target,
1063     value: u64,
1064     fault: *memory.Fault,
1065 ) Error!void {
1066     switch (target) {
1067         .register => |destination| cpu.write(destination, value) catch
1068             return error.UnsupportedInstruction,
1069         .memory => |destination| try writeMemory(
1070             ram,
1071             plan,
1072             destination.address,
1073             destination.width,
1074             value,
1075             fault,
1076         ),
1077     }
1078 }
1079 
1080 fn sourceValue(
1081     instruction: x86.Instruction,
1082     cpu: *const Cpu,
1083     ram: anytype,
1084     fault: *memory.Fault,
1085 ) Error!u64 {
1086     if (instruction.immediate) |value| return immediateValue(value);
1087     const source = try targetForAccess(instruction, .read, cpu, fault);
1088     return readTarget(cpu, ram, source, fault);
1089 }
1090 
1091 fn readExplicit(
1092     cpu: *const Cpu,
1093     ram: anytype,
1094     instruction: x86.Instruction,
1095     field: Explicit,
1096     fault: *memory.Fault,
1097 ) Error!u64 {
1098     const target: Target = switch (field) {
1099         .register => .{ .register = instruction.register orelse
1100             return error.UnsupportedInstruction },
1101         .operand => try operandTarget(cpu, instruction.operand, fault),
1102         .embedded => .{ .register = instruction.embedded orelse
1103             return error.UnsupportedInstruction },
1104     };
1105     return readTarget(cpu, ram, target, fault);
1106 }
1107 
1108 fn immediateValue(value: x86.Immediate) u64 {
1109     return switch (value.extension) {
1110         .none => value.value,
1111         .sign => @bitCast(value.signed()),
1112     };
1113 }
1114 
1115 fn readMemory(
1116     ram: anytype,
1117     address: u64,
1118     width: x86.Width,
1119     fault: *memory.Fault,
1120 ) Error!u64 {
1121     return memory.read(ram, address, width, fault) catch |failure| {
1122         if (fault.valid) return error.MemoryFault;
1123         return switch (failure) {
1124             error.UnsupportedWidth, error.RamBytesMismatch => error.UnsupportedInstruction,
1125             error.MemoryAddressOverflow,
1126             error.MemoryAuthenticationFailed,
1127             error.MemoryCapacityExceeded,
1128             error.MemoryOutOfBounds,
1129             error.MemoryReadFailed,
1130             => |access_failure| access_failure,
1131             else => error.MemoryFault,
1132         };
1133     };
1134 }
1135 
1136 fn writeMemory(
1137     ram: anytype,
1138     plan: *const protection.Plan,
1139     address: u64,
1140     width: x86.Width,
1141     value: u64,
1142     fault: *memory.Fault,
1143 ) Error!void {
1144     const bytes = width.bytes();
1145     _ = memory.check(ram, address, bytes, fault) catch {
1146         if (!fault.valid) fault.* = .{ .address = address, .valid = true };
1147         return error.MemoryFault;
1148     };
1149     const overlaps = plan.overlaps(address, bytes) catch {
1150         fault.* = .{ .address = address, .valid = true };
1151         return error.MemoryFault;
1152     };
1153     if (overlaps) {
1154         fault.* = .{ .address = address, .valid = true };
1155         return error.MemoryFault;
1156     }
1157     memory.write(ram, address, width, value, fault) catch |failure| {
1158         switch (failure) {
1159             error.MemoryAddressOverflow,
1160             error.MemoryAuthenticationFailed,
1161             error.MemoryCapacityExceeded,
1162             error.MemoryOutOfBounds,
1163             error.MemoryReadFailed,
1164             => |access_failure| return access_failure,
1165             else => {},
1166         }
1167         if (!fault.valid) return error.UnsupportedInstruction;
1168         return error.MemoryFault;
1169     };
1170 }
1171 
1172 fn prepareMemoryWrite(
1173     ram: anytype,
1174     range: memory.Range,
1175     fault: *memory.Fault,
1176 ) Error!void {
1177     memory.prepareWrite(
1178         ram,
1179         range.start,
1180         range.end - range.start,
1181         fault,
1182     ) catch |failure| return switch (failure) {
1183         error.MemoryAddressOverflow,
1184         error.MemoryAuthenticationFailed,
1185         error.MemoryCapacityExceeded,
1186         error.MemoryOutOfBounds,
1187         error.MemoryReadFailed,
1188         => |access_failure| access_failure,
1189         else => error.MemoryFault,
1190     };
1191 }
1192 
1193 fn addressFailure(
1194     failure: memory.AddressError,
1195     _: *memory.Fault,
1196 ) Error {
1197     return switch (failure) {
1198         error.InvalidAddress,
1199         error.UnsupportedRegister,
1200         error.UnsupportedSegment,
1201         error.UnsupportedWidth,
1202         => error.UnsupportedInstruction,
1203     };
1204 }
1205 
1206 fn shiftCount(cpu: *const Cpu, instruction: x86.Instruction) Error!u8 {
1207     if (instruction.immediate) |value| return @truncate(value.value);
1208     if (hasImplicitCl(instruction)) {
1209         return @truncate(cpu.read(.{ .number = 1, .lane = .low8 }) catch
1210             return error.UnsupportedInstruction);
1211     }
1212     if (instruction.form.opcode == 0xd0 or instruction.form.opcode == 0xd1) return 1;
1213     return error.UnsupportedInstruction;
1214 }
1215 
1216 fn scalarRegister(number: u4, width: x86.Width) Error!x86.Register {
1217     const lane: x86.RegisterLane = switch (width) {
1218         .byte => .low8,
1219         .word => .word,
1220         .dword => .dword,
1221         .qword => .qword,
1222         .none, .vector128 => return error.UnsupportedInstruction,
1223     };
1224     return .{ .number = number, .lane = lane };
1225 }
1226 
1227 fn operandScalarWidth(operand: x86.Operand) Error!x86.Width {
1228     const width = switch (operand) {
1229         .none => return error.UnsupportedInstruction,
1230         .register => |value| switch (value.lane) {
1231             .low8, .high8 => x86.Width.byte,
1232             .word => x86.Width.word,
1233             .dword => x86.Width.dword,
1234             .qword => x86.Width.qword,
1235             .vector128 => x86.Width.vector128,
1236         },
1237         .memory => |value| value.width,
1238     };
1239     try validateScalarWidth(width);
1240     return width;
1241 }
1242 
1243 fn condition(cpu: *const Cpu, operation: x86.Operation) bool {
1244     const carry = cpu.flag(integer.flag.carry);
1245     const zero = cpu.flag(integer.flag.zero);
1246     return switch (operation) {
1247         .ja, .seta => !carry and !zero,
1248         .jae, .cmovae, .setae => !carry,
1249         .jb, .setb => carry,
1250         .jbe, .setbe => carry or zero,
1251         .je, .cmove, .sete => zero,
1252         .jne, .cmovne, .setne => !zero,
1253         .jmp => true,
1254         else => false,
1255     };
1256 }
1257 
1258 fn relativeTarget(fallthrough: u64, displacement: i32) u64 {
1259     const signed: i64 = displacement;
1260     return fallthrough +% @as(u64, @bitCast(signed));
1261 }
1262 
1263 fn requireSite(
1264     execution: manifest.View,
1265     target: u64,
1266 ) Error!void {
1267     _ = reference.fetch.findSite(execution, target) catch
1268         return error.InvalidControlTarget;
1269 }
1270 
1271 fn stringRange(
1272     ram: anytype,
1273     address: u64,
1274     count: u64,
1275     element_bytes: u64,
1276     backwards: bool,
1277     fault: *memory.Fault,
1278 ) Error!memory.Range {
1279     if (count == 0) return memory.check(ram, address, 0, fault) catch
1280         return error.MemoryFault;
1281     const total_bytes = std.math.mul(u64, count, element_bytes) catch {
1282         fault.* = .{ .address = address, .valid = true };
1283         return error.MemoryFault;
1284     };
1285     const start = if (backwards)
1286         std.math.sub(u64, address, total_bytes - element_bytes) catch {
1287             fault.* = .{ .address = 0, .valid = true };
1288             return error.MemoryFault;
1289         }
1290     else
1291         address;
1292     return memory.check(ram, start, total_bytes, fault) catch {
1293         if (!fault.valid) fault.* = .{ .address = start, .valid = true };
1294         return error.MemoryFault;
1295     };
1296 }
1297 
1298 fn stringElementAddress(
1299     initial: u64,
1300     index: usize,
1301     element_bytes: u64,
1302     backwards: bool,
1303 ) u64 {
1304     const offset = @as(u64, @intCast(index)) * element_bytes;
1305     return if (backwards) initial - offset else initial + offset;
1306 }
1307 
1308 fn protectRange(
1309     plan: *const protection.Plan,
1310     range: memory.Range,
1311     fault: *memory.Fault,
1312 ) Error!void {
1313     const address: u64 = @intCast(range.start);
1314     const bytes: u64 = @intCast(range.end - range.start);
1315     const overlaps = plan.overlaps(address, bytes) catch {
1316         fault.* = .{ .address = address, .valid = true };
1317         return error.MemoryFault;
1318     };
1319     if (overlaps) {
1320         fault.* = .{ .address = address, .valid = true };
1321         return error.MemoryFault;
1322     }
1323 }
1324 
1325 fn updateStringRegisters(
1326     cpu: *Cpu,
1327     source: u64,
1328     destination: u64,
1329     bytes: u64,
1330     backwards: bool,
1331     update_source: bool,
1332 ) void {
1333     const next_source = if (backwards) source -% bytes else source +% bytes;
1334     const next_destination = if (backwards)
1335         destination -% bytes
1336     else
1337         destination +% bytes;
1338     if (update_source) cpu.write(.{ .number = 6 }, next_source) catch unreachable;
1339     cpu.write(.{ .number = 7 }, next_destination) catch unreachable;
1340     cpu.write(.{ .number = 1 }, 0) catch unreachable;
1341 }
1342 
1343 var test_ram: [memory.ram_bytes]u8 align(core.layout.page_bytes) = undefined;
1344 
1345 fn testExecution(encoded_output: []u8) !manifest.View {
1346     const instruction = try x86.decode(&.{0x90});
1347     const loads = [_]manifest.Load{.{
1348         .input_offset = 0,
1349         .source_offset = 0,
1350         .file_bytes = 1,
1351         .memory_bytes = core.layout.page_bytes,
1352         .physical_offset = 0,
1353         .virtual_offset = 0,
1354         .flags = manifest.load_flag_read | manifest.load_flag_execute,
1355     }};
1356     const forms = [_]u64{instruction.form.key()};
1357     const sites = [_]manifest.Site{.{
1358         .virtual_offset = 0,
1359         .form_index = 0,
1360         .instruction_bytes = 1,
1361     }};
1362     const facts = manifest.k0V1Facts(0, 1, core.layout.page_bytes);
1363     const evidence: manifest.Evidence = .{
1364         .loaded_image = @splat(0),
1365         .executable_image = @splat(0),
1366         .load_plan = try manifest.loadPlanDigest(facts.entry_offset, &loads),
1367         .noncode = @splat(0),
1368         .policy = manifest.k0_v1_policy_digest,
1369         .forms = try manifest.formsDigest(&forms),
1370         .sites = try manifest.sitesDigest(&sites),
1371     };
1372     const encoded = try manifest.encode(.{
1373         .facts = facts,
1374         .evidence = evidence,
1375         .loads = &loads,
1376         .forms = &forms,
1377         .sites = &sites,
1378     }, encoded_output);
1379     return manifest.parse(encoded);
1380 }
1381 
1382 test "qword sign immediates retain their architectural extension" {
1383     var encoded: [1_024]u8 = undefined;
1384     const execution = try testExecution(&encoded);
1385     const plan = try protection.Plan.init(execution);
1386     var cpu = Cpu.init(execution.header.facts.initial);
1387     try cpu.write(.{ .number = 4 }, 0x123f);
1388     const instruction = try x86.decode(&.{ 0x48, 0x83, 0xe4, 0xf0 });
1389     try validate(instruction);
1390     var fault: memory.Fault = .{};
1391     try std.testing.expectEqual(
1392         Boundary.continuing,
1393         try step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1394     );
1395     try std.testing.expectEqual(@as(u64, 0x1230), try cpu.read(.{ .number = 4 }));
1396     try std.testing.expect(!fault.valid);
1397 }
1398 
1399 test "dword LEA zero extends the modular effective address" {
1400     var encoded: [1_024]u8 = undefined;
1401     const execution = try testExecution(&encoded);
1402     const plan = try protection.Plan.init(execution);
1403     var cpu = Cpu.init(execution.header.facts.initial);
1404     const source: u64 = 0xffff_ffff_8000_0001;
1405     try cpu.write(.{ .number = 2 }, source);
1406     try cpu.write(.{ .number = 0 }, std.math.maxInt(u64));
1407     const instruction = try x86.decode(&.{ 0x8d, 0x04, 0x92 });
1408     try validate(instruction);
1409     var fault: memory.Fault = .{};
1410     try std.testing.expectEqual(
1411         Boundary.continuing,
1412         try step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1413     );
1414     const expected = (source +% source *% 4) & std.math.maxInt(u32);
1415     try std.testing.expectEqual(expected, try cpu.read(.{ .number = 0 }));
1416     try std.testing.expect(!fault.valid);
1417 }
1418 
1419 test "compiler scalar forms execute exact register and flag transitions" {
1420     var encoded: [1_024]u8 = undefined;
1421     const execution = try testExecution(&encoded);
1422     const plan = try protection.Plan.init(execution);
1423     var fault: memory.Fault = .{};
1424 
1425     {
1426         var cpu = Cpu.init(execution.header.facts.initial);
1427         try cpu.write(.{ .number = 6 }, 0x1234_5678_9abc_8001);
1428         const instruction = try x86.decode(&.{ 0x48, 0x0f, 0xbf, 0xf6 });
1429         try validate(instruction);
1430         try std.testing.expectEqual(
1431             Boundary.continuing,
1432             try step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1433         );
1434         try std.testing.expectEqual(
1435             @as(u64, 0xffff_ffff_ffff_8001),
1436             try cpu.read(.{ .number = 6 }),
1437         );
1438     }
1439 
1440     {
1441         var cpu = Cpu.init(execution.header.facts.initial);
1442         cpu.rflags |= integer.flag.carry;
1443         try cpu.write(.{ .number = 0 }, std.math.maxInt(u64));
1444         const instruction = try x86.decode(&.{ 0x48, 0xff, 0xc0 });
1445         try validate(instruction);
1446         _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1447         try std.testing.expectEqual(@as(u64, 0), try cpu.read(.{ .number = 0 }));
1448         try std.testing.expect(cpu.flag(integer.flag.carry));
1449         try std.testing.expect(cpu.flag(integer.flag.zero));
1450     }
1451 
1452     {
1453         var cpu = Cpu.init(execution.header.facts.initial);
1454         try cpu.write(.{ .number = 2 }, 0);
1455         const instruction = try x86.decode(&.{ 0x48, 0xff, 0xca });
1456         try validate(instruction);
1457         _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1458         try std.testing.expectEqual(
1459             std.math.maxInt(u64),
1460             try cpu.read(.{ .number = 2 }),
1461         );
1462         try std.testing.expect(!cpu.flag(integer.flag.carry));
1463         try std.testing.expect(cpu.flag(integer.flag.sign));
1464     }
1465 
1466     {
1467         var cpu = Cpu.init(execution.header.facts.initial);
1468         try cpu.write(.{ .number = 6 }, 0xffff_ffff_00ff_00ff);
1469         const instruction = try x86.decode(&.{ 0xf7, 0xd6 });
1470         try validate(instruction);
1471         _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1472         try std.testing.expectEqual(
1473             @as(u64, 0xff00_ff00),
1474             try cpu.read(.{ .number = 6 }),
1475         );
1476     }
1477 }
1478 
1479 test "compiler scalar forms execute exact rotate and multiply transitions" {
1480     var encoded: [1_024]u8 = undefined;
1481     const execution = try testExecution(&encoded);
1482     const plan = try protection.Plan.init(execution);
1483     var fault: memory.Fault = .{};
1484 
1485     var cpu = Cpu.init(execution.header.facts.initial);
1486     const before: u32 = 0x8000_0001;
1487     try cpu.write(.{ .number = 6 }, before);
1488     const rotate = try x86.decode(&.{ 0xc1, 0xc6, 0x19 });
1489     try validate(rotate);
1490     _ = try step(&cpu, &test_ram, &plan, execution, rotate, &fault);
1491     const expected = before << 25 | before >> 7;
1492     try std.testing.expectEqual(
1493         @as(u64, expected),
1494         try cpu.read(.{ .number = 6 }),
1495     );
1496 
1497     cpu = Cpu.init(execution.header.facts.initial);
1498     try cpu.write(.{ .number = 2 }, 2);
1499     try cpu.write(.{ .number = 13 }, 0xa5a5_a5a5_a5a5_a5a5);
1500     const multiply_instruction = try x86.decode(&.{
1501         0x4c, 0x69, 0xea, 0x00, 0xf1, 0xff, 0xff,
1502     });
1503     try validate(multiply_instruction);
1504     _ = try step(
1505         &cpu,
1506         &test_ram,
1507         &plan,
1508         execution,
1509         multiply_instruction,
1510         &fault,
1511     );
1512     try std.testing.expectEqual(
1513         @as(u64, @bitCast(@as(i64, -0x1e00))),
1514         try cpu.read(.{ .number = 13 }),
1515     );
1516     try std.testing.expect(!cpu.flag(integer.flag.carry));
1517     try std.testing.expect(!cpu.flag(integer.flag.overflow));
1518 }
1519 
1520 test "conditional moves read sources and commit only on their condition" {
1521     var encoded: [1_024]u8 = undefined;
1522     const execution = try testExecution(&encoded);
1523     const plan = try protection.Plan.init(execution);
1524     var fault: memory.Fault = .{};
1525     var cpu = Cpu.init(execution.header.facts.initial);
1526     try cpu.write(.{ .number = 0 }, 0x11);
1527     try cpu.write(.{ .number = 2 }, 0x22);
1528     const above_equal = try x86.decode(&.{ 0x48, 0x0f, 0x43, 0xc2 });
1529     try validate(above_equal);
1530     _ = try step(&cpu, &test_ram, &plan, execution, above_equal, &fault);
1531     try std.testing.expectEqual(@as(u64, 0x22), try cpu.read(.{ .number = 0 }));
1532 
1533     try cpu.write(.{ .number = 0 }, 0x33);
1534     cpu.rflags |= integer.flag.carry;
1535     _ = try step(&cpu, &test_ram, &plan, execution, above_equal, &fault);
1536     try std.testing.expectEqual(@as(u64, 0x33), try cpu.read(.{ .number = 0 }));
1537 
1538     try cpu.write(.{ .number = 0 }, 0x44);
1539     try cpu.write(.{ .number = 1 }, 0x55);
1540     cpu.rflags |= integer.flag.zero;
1541     const equal = try x86.decode(&.{ 0x0f, 0x44, 0xc1 });
1542     try validate(equal);
1543     _ = try step(&cpu, &test_ram, &plan, execution, equal, &fault);
1544     try std.testing.expectEqual(@as(u64, 0x55), try cpu.read(.{ .number = 0 }));
1545 
1546     try cpu.write(.{ .number = 0 }, 0x66);
1547     const not_equal = try x86.decode(&.{ 0x0f, 0x45, 0xc1 });
1548     try validate(not_equal);
1549     _ = try step(&cpu, &test_ram, &plan, execution, not_equal, &fault);
1550     try std.testing.expectEqual(@as(u64, 0x66), try cpu.read(.{ .number = 0 }));
1551 
1552     try cpu.write(.{ .number = 0 }, memory.ram_bytes);
1553     cpu.rflags |= integer.flag.carry;
1554     const cpu_before = cpu;
1555     const faulting_source = try x86.decode(&.{ 0x48, 0x0f, 0x43, 0x00 });
1556     try validate(faulting_source);
1557     try std.testing.expectError(
1558         error.MemoryFault,
1559         step(&cpu, &test_ram, &plan, execution, faulting_source, &fault),
1560     );
1561     try std.testing.expectEqualDeep(cpu_before, cpu);
1562     try std.testing.expectEqual(@as(u64, memory.ram_bytes), fault.address);
1563 }
1564 
1565 test "immediate pushes sign extend and commit one qword" {
1566     var encoded: [1_024]u8 = undefined;
1567     const execution = try testExecution(&encoded);
1568     const plan = try protection.Plan.init(execution);
1569     var cpu = Cpu.init(execution.header.facts.initial);
1570     try cpu.write(.{ .number = 4 }, 0x9000);
1571     const instruction = try x86.decode(&.{ 0x6a, 0xf8 });
1572     try validate(instruction);
1573     var fault: memory.Fault = .{};
1574     _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1575     try std.testing.expectEqual(@as(u64, 0x8ff8), try cpu.read(.{ .number = 4 }));
1576     try std.testing.expectEqual(
1577         @as(u64, @bitCast(@as(i64, -8))),
1578         try memory.read(&test_ram, 0x8ff8, .qword, &fault),
1579     );
1580 }
1581 
1582 test "widened REP forms preserve element width and direction" {
1583     var encoded: [1_024]u8 = undefined;
1584     const execution = try testExecution(&encoded);
1585     const plan = try protection.Plan.init(execution);
1586     var fault: memory.Fault = .{};
1587 
1588     {
1589         var cpu = Cpu.init(execution.header.facts.initial);
1590         try cpu.write(.{ .number = 6 }, 0x10_000);
1591         try cpu.write(.{ .number = 7 }, 0x11_000);
1592         try cpu.write(.{ .number = 1 }, 2);
1593         try memory.write(&test_ram, 0x10_000, .qword, 0x1122_3344_5566_7788, &fault);
1594         try memory.write(&test_ram, 0x10_008, .qword, 0x99aa_bbcc_ddee_ff00, &fault);
1595         const instruction = try x86.decode(&.{ 0xf3, 0x48, 0xa5 });
1596         try validate(instruction);
1597         _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1598         try std.testing.expectEqual(
1599             @as(u64, 0x1122_3344_5566_7788),
1600             try memory.read(&test_ram, 0x11_000, .qword, &fault),
1601         );
1602         try std.testing.expectEqual(
1603             @as(u64, 0x99aa_bbcc_ddee_ff00),
1604             try memory.read(&test_ram, 0x11_008, .qword, &fault),
1605         );
1606         try std.testing.expectEqual(@as(u64, 0x10_010), try cpu.read(.{ .number = 6 }));
1607         try std.testing.expectEqual(@as(u64, 0x11_010), try cpu.read(.{ .number = 7 }));
1608         try std.testing.expectEqual(@as(u64, 0), try cpu.read(.{ .number = 1 }));
1609     }
1610 
1611     {
1612         var cpu = Cpu.init(execution.header.facts.initial);
1613         try cpu.write(.{ .number = 0 }, 0xdead_beef_1122_3344);
1614         try cpu.write(.{ .number = 7 }, 0x12_000);
1615         try cpu.write(.{ .number = 1 }, 3);
1616         const instruction = try x86.decode(&.{ 0xf3, 0xab });
1617         try validate(instruction);
1618         _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1619         for (0..3) |index| {
1620             try std.testing.expectEqual(
1621                 @as(u64, 0x1122_3344),
1622                 try memory.read(
1623                     &test_ram,
1624                     0x12_000 + @as(u64, @intCast(index)) * 4,
1625                     .dword,
1626                     &fault,
1627                 ),
1628             );
1629         }
1630         try std.testing.expectEqual(@as(u64, 0x12_00c), try cpu.read(.{ .number = 7 }));
1631         try std.testing.expectEqual(@as(u64, 0), try cpu.read(.{ .number = 1 }));
1632     }
1633 }
1634 
1635 test "widened REP forms preserve reverse direction" {
1636     var encoded: [1_024]u8 = undefined;
1637     const execution = try testExecution(&encoded);
1638     const plan = try protection.Plan.init(execution);
1639     var fault: memory.Fault = .{};
1640     var cpu = Cpu.init(execution.header.facts.initial);
1641     cpu.rflags |= direction_flag;
1642     try cpu.write(.{ .number = 6 }, 0x13_018);
1643     try cpu.write(.{ .number = 7 }, 0x14_018);
1644     try cpu.write(.{ .number = 1 }, 2);
1645     try memory.write(&test_ram, 0x13_010, .qword, 0x0123_4567_89ab_cdef, &fault);
1646     try memory.write(&test_ram, 0x13_018, .qword, 0xfedc_ba98_7654_3210, &fault);
1647     const instruction = try x86.decode(&.{ 0xf3, 0x48, 0xa5 });
1648     try validate(instruction);
1649     _ = try step(&cpu, &test_ram, &plan, execution, instruction, &fault);
1650     try std.testing.expectEqual(
1651         @as(u64, 0x0123_4567_89ab_cdef),
1652         try memory.read(&test_ram, 0x14_010, .qword, &fault),
1653     );
1654     try std.testing.expectEqual(
1655         @as(u64, 0xfedc_ba98_7654_3210),
1656         try memory.read(&test_ram, 0x14_018, .qword, &fault),
1657     );
1658     try std.testing.expectEqual(@as(u64, 0x13_008), try cpu.read(.{ .number = 6 }));
1659     try std.testing.expectEqual(@as(u64, 0x14_008), try cpu.read(.{ .number = 7 }));
1660     try std.testing.expectEqual(@as(u64, 0), try cpu.read(.{ .number = 1 }));
1661 }
1662 
1663 test "protected guest writes fault before CPU or RAM commit" {
1664     var encoded: [1_024]u8 = undefined;
1665     const execution = try testExecution(&encoded);
1666     const plan = try protection.Plan.init(execution);
1667     var cpu = Cpu.init(execution.header.facts.initial);
1668     const cpu_before = cpu;
1669     const address: usize = @intCast(execution.header.facts.physical_base);
1670     test_ram[address] = 0xc6;
1671     const instruction = try x86.decode(&.{
1672         0xc6, 0x04, 0x25, 0x00, 0x00, 0x00, 0x02, 0x90,
1673     });
1674     try validate(instruction);
1675     var fault: memory.Fault = .{};
1676     try std.testing.expectError(
1677         error.MemoryFault,
1678         step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1679     );
1680     try std.testing.expectEqualDeep(cpu_before, cpu);
1681     try std.testing.expectEqual(@as(u8, 0xc6), test_ram[address]);
1682     try std.testing.expectEqual(execution.header.facts.physical_base, fault.address);
1683 }
1684 
1685 test "divide faults preserve the complete scalar transaction" {
1686     var encoded: [1_024]u8 = undefined;
1687     const execution = try testExecution(&encoded);
1688     const plan = try protection.Plan.init(execution);
1689     var cpu = Cpu.init(execution.header.facts.initial);
1690     try cpu.write(.{ .number = 0 }, 7);
1691     try cpu.write(.{ .number = 2 }, 0);
1692     try cpu.write(.{ .number = 3 }, 0);
1693     const cpu_before = cpu;
1694     const instruction = try x86.decode(&.{ 0x48, 0xf7, 0xf3 });
1695     try validate(instruction);
1696     var fault: memory.Fault = .{};
1697     try std.testing.expectError(
1698         error.DivideFault,
1699         step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1700     );
1701     try std.testing.expectEqualDeep(cpu_before, cpu);
1702     try std.testing.expect(!fault.valid);
1703 }
1704 
1705 test "REP source faults are whole-instruction atomic" {
1706     var encoded: [1_024]u8 = undefined;
1707     const execution = try testExecution(&encoded);
1708     const plan = try protection.Plan.init(execution);
1709     var cpu = Cpu.init(execution.header.facts.initial);
1710     try cpu.write(.{ .number = 6 }, memory.ram_bytes - 1);
1711     try cpu.write(.{ .number = 7 }, 0x15_000);
1712     try cpu.write(.{ .number = 1 }, 2);
1713     const cpu_before = cpu;
1714     test_ram[0x15_000] = 0xa5;
1715     const instruction = try x86.decode(&.{ 0xf3, 0xa4 });
1716     try validate(instruction);
1717     var fault: memory.Fault = .{};
1718     try std.testing.expectError(
1719         error.MemoryFault,
1720         step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1721     );
1722     try std.testing.expectEqualDeep(cpu_before, cpu);
1723     try std.testing.expectEqual(@as(u8, 0xa5), test_ram[0x15_000]);
1724     try std.testing.expectEqual(@as(u64, memory.ram_bytes), fault.address);
1725 }
1726 
1727 test "widened REP faults are whole-instruction atomic" {
1728     var encoded: [1_024]u8 = undefined;
1729     const execution = try testExecution(&encoded);
1730     const plan = try protection.Plan.init(execution);
1731     var fault: memory.Fault = .{};
1732 
1733     {
1734         var cpu = Cpu.init(execution.header.facts.initial);
1735         try cpu.write(.{ .number = 6 }, memory.ram_bytes - 4);
1736         try cpu.write(.{ .number = 7 }, 0x16_000);
1737         try cpu.write(.{ .number = 1 }, 1);
1738         const cpu_before = cpu;
1739         try memory.write(&test_ram, 0x16_000, .qword, 0xa5a5_a5a5_a5a5_a5a5, &fault);
1740         const instruction = try x86.decode(&.{ 0xf3, 0x48, 0xa5 });
1741         try validate(instruction);
1742         try std.testing.expectError(
1743             error.MemoryFault,
1744             step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1745         );
1746         const fault_address = fault.address;
1747         try std.testing.expectEqualDeep(cpu_before, cpu);
1748         try std.testing.expectEqual(
1749             @as(u64, 0xa5a5_a5a5_a5a5_a5a5),
1750             try memory.read(&test_ram, 0x16_000, .qword, &fault),
1751         );
1752         try std.testing.expectEqual(@as(u64, memory.ram_bytes), fault_address);
1753     }
1754 
1755     {
1756         var cpu = Cpu.init(execution.header.facts.initial);
1757         try cpu.write(.{ .number = 0 }, 0x1122_3344_5566_7788);
1758         try cpu.write(.{ .number = 7 }, execution.header.facts.physical_base);
1759         try cpu.write(.{ .number = 1 }, 2);
1760         const cpu_before = cpu;
1761         const start: usize = @intCast(execution.header.facts.physical_base);
1762         const ram_before = test_ram[start..][0..16].*;
1763         const instruction = try x86.decode(&.{ 0xf3, 0x48, 0xab });
1764         try validate(instruction);
1765         try std.testing.expectError(
1766             error.MemoryFault,
1767             step(&cpu, &test_ram, &plan, execution, instruction, &fault),
1768         );
1769         try std.testing.expectEqualDeep(cpu_before, cpu);
1770         try std.testing.expectEqualSlices(
1771             u8,
1772             &ram_before,
1773             test_ram[start..][0..ram_before.len],
1774         );
1775         try std.testing.expectEqual(execution.header.facts.physical_base, fault.address);
1776     }
1777 }