test(guest): compare predicted faults with executed qemu accesses
5 files changed, +505 −0
- guest/Makefile added +30 −0
- guest/boot.S added +173 −0
- guest/guest.c added +223 −0
- guest/layout.ld added +13 −0
- guest/verify.py added +66 −0
guest/Makefile added
| @@ -0,0 +1,30 @@ | ||
| 1 | +CC ?= cc | |
| 2 | +LD ?= ld | |
| 3 | +OBJCOPY ?= objcopy | |
| 4 | +CFLAGS = -std=c11 -O2 -Wall -Wextra -Werror -ffreestanding -fno-pie -fno-stack-protector -fno-builtin -mno-red-zone -mgeneral-regs-only | |
| 5 | + | |
| 6 | +.PHONY: all check clean | |
| 7 | + | |
| 8 | +all: build/disk | |
| 9 | + | |
| 10 | +build: | |
| 11 | + mkdir -p build | |
| 12 | + | |
| 13 | +build/boot.o: boot.S | build | |
| 14 | + $(CC) -c -m64 -fno-pie $< -o $@ | |
| 15 | + | |
| 16 | +build/guest.o: guest.c | build | |
| 17 | + $(CC) $(CFLAGS) -c $< -o $@ | |
| 18 | + | |
| 19 | +build/guest: build/boot.o build/guest.o layout.ld | |
| 20 | + $(LD) -T layout.ld build/boot.o build/guest.o -o $@ | |
| 21 | + | |
| 22 | +build/disk: build/guest | |
| 23 | + $(OBJCOPY) -O binary $< $@ | |
| 24 | + python3 -c 'from pathlib import Path; p = Path("$@"); b = p.read_bytes(); assert len(b) <= 61440; p.write_bytes(b.ljust(61952, b"\0"))' | |
| 25 | + | |
| 26 | +check: build/disk | |
| 27 | + python3 verify.py ../_build/default/bin/main.exe build/disk | |
| 28 | + | |
| 29 | +clean: | |
| 30 | + rm -rf build | |
guest/boot.S added
| @@ -0,0 +1,173 @@ | ||
| 1 | +.section .boot,"ax" | |
| 2 | +.code16 | |
| 3 | +.global boot | |
| 4 | +boot: | |
| 5 | + cli | |
| 6 | + xor %ax, %ax | |
| 7 | + mov %ax, %ds | |
| 8 | + mov %ax, %es | |
| 9 | + mov %ax, %ss | |
| 10 | + mov $0x7c00, %sp | |
| 11 | + mov $packet, %si | |
| 12 | + mov $0x42, %ah | |
| 13 | + int $0x13 | |
| 14 | + jc stopped | |
| 15 | + in $0x92, %al | |
| 16 | + or $2, %al | |
| 17 | + out %al, $0x92 | |
| 18 | + lgdt descriptor | |
| 19 | + mov %cr0, %eax | |
| 20 | + or $1, %eax | |
| 21 | + mov %eax, %cr0 | |
| 22 | + ljmpl $8, $protected | |
| 23 | +stopped: | |
| 24 | + hlt | |
| 25 | + jmp stopped | |
| 26 | +.balign 8 | |
| 27 | +packet: | |
| 28 | + .byte 16, 0 | |
| 29 | + .word 120 | |
| 30 | + .word 0, 0x1000 | |
| 31 | + .quad 1 | |
| 32 | +.global gdt | |
| 33 | +gdt: | |
| 34 | + .quad 0 | |
| 35 | + .quad 0x00cf9a000000ffff | |
| 36 | + .quad 0x00cf92000000ffff | |
| 37 | + .quad 0x00af9a000000ffff | |
| 38 | + .quad 0x00affa000000ffff | |
| 39 | + .quad 0x00cff2000000ffff | |
| 40 | + .quad 0, 0 | |
| 41 | +descriptor: | |
| 42 | + .word 63 | |
| 43 | + .long gdt | |
| 44 | +.org 510 | |
| 45 | +.word 0xaa55 | |
| 46 | + | |
| 47 | +.section .text | |
| 48 | +.code32 | |
| 49 | +protected: | |
| 50 | + mov $0x10, %ax | |
| 51 | + mov %ax, %ds | |
| 52 | + mov %ax, %es | |
| 53 | + mov %ax, %ss | |
| 54 | + mov $0x90000, %esp | |
| 55 | + cld | |
| 56 | + mov $0x70000, %edi | |
| 57 | + xor %eax, %eax | |
| 58 | + mov $3072, %ecx | |
| 59 | + rep stosl | |
| 60 | + movl $0x71003, 0x70000 | |
| 61 | + movl $0x72003, 0x71000 | |
| 62 | + mov $0x72000, %edi | |
| 63 | + mov $0x83, %eax | |
| 64 | + mov $512, %ecx | |
| 65 | +1: | |
| 66 | + mov %eax, (%edi) | |
| 67 | + add $0x200000, %eax | |
| 68 | + add $8, %edi | |
| 69 | + loop 1b | |
| 70 | + mov $0x20, %eax | |
| 71 | + mov %eax, %cr4 | |
| 72 | + mov $0x70000, %eax | |
| 73 | + mov %eax, %cr3 | |
| 74 | + mov $0xc0000080, %ecx | |
| 75 | + rdmsr | |
| 76 | + or $0x900, %eax | |
| 77 | + wrmsr | |
| 78 | + mov %cr0, %eax | |
| 79 | + or $0x80010000, %eax | |
| 80 | + mov %eax, %cr0 | |
| 81 | + ljmp $0x18, $longmode | |
| 82 | + | |
| 83 | +.code64 | |
| 84 | +longmode: | |
| 85 | + mov $0x10, %ax | |
| 86 | + mov %ax, %ds | |
| 87 | + mov %ax, %es | |
| 88 | + mov %ax, %ss | |
| 89 | + mov $0x90000, %rsp | |
| 90 | + lea __bss_start(%rip), %rdi | |
| 91 | + lea __bss_end(%rip), %rcx | |
| 92 | + sub %rdi, %rcx | |
| 93 | + xor %eax, %eax | |
| 94 | + rep stosb | |
| 95 | + call guest | |
| 96 | +2: | |
| 97 | + hlt | |
| 98 | + jmp 2b | |
| 99 | + | |
| 100 | +.global probe | |
| 101 | +probe: | |
| 102 | + mov %rsp, recovery_stack(%rip) | |
| 103 | + lea 5f(%rip), %rax | |
| 104 | + mov %rax, recovery_address(%rip) | |
| 105 | + test %edi, %edi | |
| 106 | + jz 3f | |
| 107 | + cmp $1, %edi | |
| 108 | + je 4f | |
| 109 | + call *%rsi | |
| 110 | + jmp 5f | |
| 111 | +3: | |
| 112 | + mov (%rsi), %rax | |
| 113 | + jmp 5f | |
| 114 | +4: | |
| 115 | + movq $0xc3, (%rsi) | |
| 116 | +5: | |
| 117 | + ret | |
| 118 | + | |
| 119 | +.global probe_user | |
| 120 | +probe_user: | |
| 121 | + mov %rsp, recovery_stack(%rip) | |
| 122 | + lea 9f(%rip), %rax | |
| 123 | + mov %rax, recovery_address(%rip) | |
| 124 | + pushq $0x2b | |
| 125 | + pushq $0x86000 | |
| 126 | + pushq $2 | |
| 127 | + pushq $0x23 | |
| 128 | + lea 6f(%rip), %rax | |
| 129 | + push %rax | |
| 130 | + iretq | |
| 131 | +6: | |
| 132 | + test %edi, %edi | |
| 133 | + jz 7f | |
| 134 | + cmp $1, %edi | |
| 135 | + je 8f | |
| 136 | + call *%rsi | |
| 137 | + int $0x80 | |
| 138 | +7: | |
| 139 | + mov (%rsi), %rax | |
| 140 | + int $0x80 | |
| 141 | +8: | |
| 142 | + movq $0xc3, (%rsi) | |
| 143 | + int $0x80 | |
| 144 | +9: | |
| 145 | + ret | |
| 146 | + | |
| 147 | +.global completed | |
| 148 | +completed: | |
| 149 | + pushq $0 | |
| 150 | + jmp recover | |
| 151 | + | |
| 152 | +.global pagefault | |
| 153 | +pagefault: | |
| 154 | + movq $14, observed_vector(%rip) | |
| 155 | + jmp recover | |
| 156 | +.global protection | |
| 157 | +protection: | |
| 158 | + movq $13, observed_vector(%rip) | |
| 159 | +recover: | |
| 160 | + push %rax | |
| 161 | + mov 8(%rsp), %rax | |
| 162 | + mov %rax, observed_error(%rip) | |
| 163 | + mov recovery_address(%rip), %rax | |
| 164 | + mov %rax, 16(%rsp) | |
| 165 | + mov recovery_stack(%rip), %rax | |
| 166 | + mov %rax, 40(%rsp) | |
| 167 | + movq $0x18, 24(%rsp) | |
| 168 | + movq $0x10, 48(%rsp) | |
| 169 | + pop %rax | |
| 170 | + add $8, %rsp | |
| 171 | + iretq | |
| 172 | + | |
| 173 | +.section .note.GNU-stack,"",@progbits | |
guest/guest.c added
| @@ -0,0 +1,223 @@ | ||
| 1 | +#include <stdint.h> | |
| 2 | +#include <stddef.h> | |
| 3 | + | |
| 4 | +static uint64_t root[512] __attribute__((aligned(4096))); | |
| 5 | +static uint64_t directory[512] __attribute__((aligned(4096))); | |
| 6 | +static uint64_t middle[512] __attribute__((aligned(4096))); | |
| 7 | +static uint64_t table[512] __attribute__((aligned(4096))); | |
| 8 | +static uint8_t payload[4096] __attribute__((aligned(4096))); | |
| 9 | + | |
| 10 | +volatile uint64_t recovery_stack; | |
| 11 | +volatile uint64_t recovery_address; | |
| 12 | +volatile uint64_t observed_vector; | |
| 13 | +volatile uint64_t observed_error; | |
| 14 | + | |
| 15 | +extern void probe(int access, uint64_t address); | |
| 16 | +extern void pagefault(void); | |
| 17 | +extern void protection(void); | |
| 18 | +extern void completed(void); | |
| 19 | +extern void probe_user(int access, uint64_t address); | |
| 20 | +extern uint64_t gdt[]; | |
| 21 | + | |
| 22 | +struct gate { | |
| 23 | + uint16_t low; | |
| 24 | + uint16_t selector; | |
| 25 | + uint8_t ist; | |
| 26 | + uint8_t flags; | |
| 27 | + uint16_t middle; | |
| 28 | + uint32_t high; | |
| 29 | + uint32_t reserved; | |
| 30 | +} __attribute__((packed)); | |
| 31 | + | |
| 32 | +static struct gate idt[256]; | |
| 33 | +static int phys_bits; | |
| 34 | +static int nx; | |
| 35 | +static int gb_pages; | |
| 36 | +static int user_mode; | |
| 37 | +static uint64_t root_bits; | |
| 38 | + | |
| 39 | +static struct { | |
| 40 | + uint32_t reserved; | |
| 41 | + uint64_t rsp[3]; | |
| 42 | + uint64_t reserved1; | |
| 43 | + uint64_t ist[7]; | |
| 44 | + uint64_t reserved2; | |
| 45 | + uint16_t reserved3; | |
| 46 | + uint16_t iomap; | |
| 47 | +} __attribute__((packed)) tss; | |
| 48 | + | |
| 49 | +static void put(char c) { | |
| 50 | + __asm__ volatile("outb %0, $0xe9" : : "a"(c)); | |
| 51 | +} | |
| 52 | + | |
| 53 | +static void text(const char *s) { | |
| 54 | + while (*s) put(*s++); | |
| 55 | +} | |
| 56 | + | |
| 57 | +static void hex(uint64_t n) { | |
| 58 | + text("0x"); | |
| 59 | + for (int shift = 60; shift >= 0; shift -= 4) put("0123456789abcdef"[(n >> shift) & 15]); | |
| 60 | +} | |
| 61 | + | |
| 62 | +static void decimal(int n) { | |
| 63 | + if (n >= 10) decimal(n / 10); | |
| 64 | + put('0' + n % 10); | |
| 65 | +} | |
| 66 | + | |
| 67 | +static uint64_t cr0(void) { | |
| 68 | + uint64_t value; | |
| 69 | + __asm__ volatile("mov %%cr0, %0" : "=r"(value)); | |
| 70 | + return value; | |
| 71 | +} | |
| 72 | + | |
| 73 | +static uint64_t cr4(void) { | |
| 74 | + uint64_t value; | |
| 75 | + __asm__ volatile("mov %%cr4, %0" : "=r"(value)); | |
| 76 | + return value; | |
| 77 | +} | |
| 78 | + | |
| 79 | +static void set_cr0(uint64_t value) { | |
| 80 | + __asm__ volatile("mov %0, %%cr0" : : "r"(value) : "memory"); | |
| 81 | +} | |
| 82 | + | |
| 83 | +static void set_cr4(uint64_t value) { | |
| 84 | + __asm__ volatile("mov %0, %%cr4" : : "r"(value) : "memory"); | |
| 85 | +} | |
| 86 | + | |
| 87 | +static uint64_t efer(void) { | |
| 88 | + uint32_t low, high; | |
| 89 | + __asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0xc0000080)); | |
| 90 | + return (uint64_t)high << 32 | low; | |
| 91 | +} | |
| 92 | + | |
| 93 | +static void set_efer(uint64_t value) { | |
| 94 | + __asm__ volatile("wrmsr" : : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "c"(0xc0000080)); | |
| 95 | +} | |
| 96 | + | |
| 97 | +static void set_ac(int enabled) { | |
| 98 | + uint64_t flags; | |
| 99 | + __asm__ volatile("pushfq; pop %0" : "=r"(flags)); | |
| 100 | + flags = enabled ? flags | (1u << 18) : flags & ~(1u << 18); | |
| 101 | + __asm__ volatile("push %0; popfq" : : "r"(flags) : "cc", "memory"); | |
| 102 | +} | |
| 103 | + | |
| 104 | +static void flush(void) { | |
| 105 | + __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)root | root_bits) : "memory"); | |
| 106 | +} | |
| 107 | + | |
| 108 | +static void gate(int vector, void (*handler)(void)) { | |
| 109 | + uint64_t address = (uint64_t)handler; | |
| 110 | + idt[vector] = (struct gate){address, 0x18, 0, 0x8e, address >> 16, address >> 32, 0}; | |
| 111 | +} | |
| 112 | + | |
| 113 | +static void reset(void) { | |
| 114 | + __asm__ volatile("mov %0, %%cr3" : : "r"(0x70000ull) : "memory"); | |
| 115 | + set_cr4(0x20); | |
| 116 | + set_cr0(cr0() | (1u << 16)); | |
| 117 | + set_efer(efer() | (1u << 11)); | |
| 118 | + set_ac(0); | |
| 119 | + user_mode = 0; | |
| 120 | + root_bits = 0; | |
| 121 | + for (int i = 0; i < 512; i++) root[i] = directory[i] = middle[i] = table[i] = 0; | |
| 122 | + root[0] = (uint64_t)directory | 7; | |
| 123 | + directory[0] = (uint64_t)middle | 7; | |
| 124 | + middle[0] = 0x83; | |
| 125 | + middle[2] = (uint64_t)table | 7; | |
| 126 | + table[0] = (uint64_t)payload | 3; | |
| 127 | + payload[0] = 0xc3; | |
| 128 | + flush(); | |
| 129 | +} | |
| 130 | + | |
| 131 | +static void page(uint64_t *frame) { | |
| 132 | + text("page "); | |
| 133 | + hex((uint64_t)frame); | |
| 134 | + put(' '); | |
| 135 | + const uint8_t *bytes = (const uint8_t *)frame; | |
| 136 | + for (int i = 0; i < 4096; i++) { | |
| 137 | + put("0123456789abcdef"[bytes[i] >> 4]); | |
| 138 | + put("0123456789abcdef"[bytes[i] & 15]); | |
| 139 | + } | |
| 140 | + put('\n'); | |
| 141 | +} | |
| 142 | + | |
| 143 | +static void run(const char *name, int access, uint64_t address) { | |
| 144 | + uint64_t flags, current_cr2, current_cr3; | |
| 145 | + flush(); | |
| 146 | + __asm__ volatile("pushfq; pop %0" : "=r"(flags)); | |
| 147 | + __asm__ volatile("mov %%cr2, %0" : "=r"(current_cr2)); | |
| 148 | + __asm__ volatile("mov %%cr3, %0" : "=r"(current_cr3)); | |
| 149 | + text("begin "); text(name); put(' '); decimal(access); put(' '); hex(address); | |
| 150 | + text(user_mode ? " user\n" : " supervisor\n"); | |
| 151 | + text("context "); hex(cr0()); put(' '); hex(current_cr2); put(' '); hex(current_cr3); | |
| 152 | + put(' '); hex(cr4()); put(' '); hex(efer()); put(' '); hex(flags); text(" 0x0 "); | |
| 153 | + decimal(phys_bits); put(' '); decimal(nx); put(' '); decimal(gb_pages); put('\n'); | |
| 154 | + page(root); page(directory); page(middle); page(table); | |
| 155 | + observed_vector = 0; | |
| 156 | + observed_error = 0; | |
| 157 | + flush(); | |
| 158 | + if (user_mode) probe_user(access, address); | |
| 159 | + else probe(access, address); | |
| 160 | + text("observed "); decimal(observed_vector); put(' '); decimal(observed_error); put('\n'); | |
| 161 | + text("end\n"); | |
| 162 | +} | |
| 163 | + | |
| 164 | +static void run_user(const char *name, int access) { | |
| 165 | + middle[0] |= 4; | |
| 166 | + user_mode = 1; | |
| 167 | + run(name, access, 0x400000); | |
| 168 | +} | |
| 169 | + | |
| 170 | +void guest(void) { | |
| 171 | + uint32_t a, b, c, d; | |
| 172 | + __asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000008), "c"(0)); | |
| 173 | + phys_bits = a & 255; | |
| 174 | + __asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000001), "c"(0)); | |
| 175 | + nx = (d >> 20) & 1; | |
| 176 | + gb_pages = (d >> 26) & 1; | |
| 177 | + gate(13, protection); | |
| 178 | + gate(14, pagefault); | |
| 179 | + gate(0x80, completed); | |
| 180 | + idt[0x80].flags = 0xee; | |
| 181 | + tss.rsp[0] = 0x88000; | |
| 182 | + tss.iomap = sizeof(tss); | |
| 183 | + uint64_t base = (uint64_t)&tss; | |
| 184 | + gdt[6] = (sizeof(tss) - 1) | ((base & 0xffffff) << 16) | (0x89ull << 40) | ((base >> 24) << 56); | |
| 185 | + gdt[7] = base >> 32; | |
| 186 | + __asm__ volatile("ltr %0" : : "r"((uint16_t)0x30)); | |
| 187 | + struct { uint16_t limit; uint64_t base; } __attribute__((packed)) idtr = {sizeof(idt) - 1, (uint64_t)idt}; | |
| 188 | + __asm__ volatile("lidt %0" : : "m"(idtr)); | |
| 189 | + reset(); run("read", 0, 0x400000); | |
| 190 | + reset(); run("write", 1, 0x400000); | |
| 191 | + reset(); run("execute", 2, 0x400000); | |
| 192 | + reset(); middle[2] &= ~2ull; run("parent", 1, 0x400000); | |
| 193 | + reset(); middle[2] &= ~2ull; set_cr0(cr0() & ~(1u << 16)); run("wp", 1, 0x400000); | |
| 194 | + reset(); table[0] |= 1ull << 63; run("nx", 2, 0x400000); | |
| 195 | + reset(); middle[2] |= 1ull << 63; run("ancestor", 2, 0x400000); | |
| 196 | + reset(); table[0] = 0; run("absent", 0, 0x400000); | |
| 197 | + reset(); table[0] = 0; run("absentwrite", 1, 0x400000); | |
| 198 | + reset(); table[0] = 0; run("absentfetch", 2, 0x400000); | |
| 199 | + reset(); table[0] |= 1ull << phys_bits; run("reserved", 0, 0x400000); | |
| 200 | + reset(); table[0] |= 1ull << 63; set_efer(efer() & ~(1u << 11)); run("nxe", 0, 0x400000); | |
| 201 | + reset(); middle[2] = 0x2083; run("alignment", 0, 0x400000); | |
| 202 | + reset(); middle[2] = 0x1083; run("huge", 0, 0x401000); | |
| 203 | + reset(); directory[1] = 0x83; run("giga", 0, 0x40001000); | |
| 204 | + reset(); table[0] |= 0x80; run("pat", 0, 0x400000); | |
| 205 | + reset(); root[1] = 0x83; run("pml", 0, 0x8000000000); | |
| 206 | + reset(); run("canonical", 0, 0x800000000000); | |
| 207 | + reset(); table[0] |= 4; set_cr4(cr4() | (1u << 20)); run("smep", 2, 0x400000); | |
| 208 | + reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); run("smap", 0, 0x400000); | |
| 209 | + reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); run("smapwrite", 1, 0x400000); | |
| 210 | + reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); set_ac(1); run("ac", 1, 0x400000); | |
| 211 | + reset(); middle[2] &= ~2ull; table[0] = 0; run("precedence", 1, 0x400000); | |
| 212 | + reset(); run_user("userdenied", 0); | |
| 213 | + reset(); table[0] |= 4; run_user("userread", 0); | |
| 214 | + reset(); table[0] |= 4; run_user("userwrite", 1); | |
| 215 | + reset(); table[0] |= 4; run_user("userfetch", 2); | |
| 216 | + reset(); table[0] |= 4; middle[2] &= ~2ull; set_cr0(cr0() & ~(1u << 16)); run_user("userwp", 1); | |
| 217 | + reset(); table[0] |= (1ull << 63) | 4; run_user("usernx", 2); | |
| 218 | + reset(); root_bits = 0xabc; run("rootbits", 0, 0x400000); | |
| 219 | + reset(); set_cr4(cr4() | (1u << 17)); root_bits = 0xabc; run("pcid", 0, 0x400000); | |
| 220 | + text("done\n"); | |
| 221 | + __asm__ volatile("outl %0, $0xf4" : : "a"(0x10)); | |
| 222 | + for (;;) __asm__ volatile("hlt"); | |
| 223 | +} | |
guest/layout.ld added
| @@ -0,0 +1,13 @@ | ||
| 1 | +ENTRY(boot) | |
| 2 | +SECTIONS { | |
| 3 | + .boot 0x7c00 : AT(0) { *(.boot) } | |
| 4 | + .text 0x10000 : AT(512) { *(.text*) } | |
| 5 | + .rodata : AT(LOADADDR(.text) + SIZEOF(.text)) { *(.rodata*) } | |
| 6 | + .data : AT(LOADADDR(.rodata) + SIZEOF(.rodata)) { *(.data*) } | |
| 7 | + .bss (NOLOAD) : ALIGN(4096) { | |
| 8 | + __bss_start = .; | |
| 9 | + *(.bss*) *(COMMON) | |
| 10 | + __bss_end = .; | |
| 11 | + } | |
| 12 | + /DISCARD/ : { *(.eh_frame*) *(.note*) *(.comment*) } | |
| 13 | +} | |
guest/verify.py added
| @@ -0,0 +1,66 @@ | ||
| 1 | +import json | |
| 2 | +from pathlib import Path | |
| 3 | +import subprocess | |
| 4 | +import sys | |
| 5 | +import tempfile | |
| 6 | + | |
| 7 | + | |
| 8 | +scope = Path(sys.argv[1]).resolve() | |
| 9 | +disk = Path(sys.argv[2]).resolve() | |
| 10 | +with tempfile.TemporaryDirectory(prefix="scopeguest") as directory: | |
| 11 | + root = Path(directory) | |
| 12 | + transcript = root / "transcript" | |
| 13 | + command = [ | |
| 14 | + "qemu-system-x86_64", "-machine", "pc,accel=tcg", "-cpu", "max", | |
| 15 | + "-m", "32M", "-display", "none", "-serial", "none", "-monitor", "none", | |
| 16 | + "-no-reboot", "-drive", f"format=raw,file={disk}", | |
| 17 | + "-debugcon", f"file:{transcript}", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", | |
| 18 | + ] | |
| 19 | + try: | |
| 20 | + result = subprocess.run(command, capture_output=True, text=True, timeout=45) | |
| 21 | + except subprocess.TimeoutExpired: | |
| 22 | + print(transcript.read_text()[-2000:] if transcript.exists() else "no guest output", file=sys.stderr) | |
| 23 | + raise | |
| 24 | + assert result.returncode == 33, (result.returncode, result.stderr, transcript.read_text()[-2000:]) | |
| 25 | + lines = transcript.read_text().splitlines() | |
| 26 | + assert lines[-1] == "done", "guest did not finish" | |
| 27 | + current = None | |
| 28 | + checks = 0 | |
| 29 | + discrepancies = 0 | |
| 30 | + for line in lines: | |
| 31 | + if line.startswith("begin "): | |
| 32 | + _, name, access, address, privilege = line.split() | |
| 33 | + current = [] | |
| 34 | + elif line.startswith("context ") or line.startswith("page "): | |
| 35 | + assert current is not None | |
| 36 | + current.append(line) | |
| 37 | + elif line.startswith("observed "): | |
| 38 | + _, vector, error = line.split() | |
| 39 | + observed = int(vector), int(error) | |
| 40 | + elif line == "end": | |
| 41 | + assert current is not None | |
| 42 | + source = root / "source" | |
| 43 | + capture = root / "capture" | |
| 44 | + source.write_text("\n".join(current) + "\n") | |
| 45 | + subprocess.run([str(scope), "import", str(source), str(capture), "--source", f"qemu guest case {name}"], | |
| 46 | + check=True, capture_output=True, text=True) | |
| 47 | + result = subprocess.run([str(scope), "explain", str(capture), address, "--access", | |
| 48 | + ["read", "write", "execute"][int(access)], "--privilege", privilege, "--json"], | |
| 49 | + capture_output=True, text=True, timeout=10) | |
| 50 | + assert result.returncode in (0, 1), (name, result.stderr) | |
| 51 | + prediction = json.loads(result.stdout)["result"] | |
| 52 | + if prediction["status"] == "mapped": | |
| 53 | + predicted = 0, 0 | |
| 54 | + else: | |
| 55 | + predicted = prediction["fault"]["vector"], prediction["fault"]["error_code"] | |
| 56 | + if observed != predicted: | |
| 57 | + known = name in {"reserved", "nxe", "alignment", "pml"} and observed == (14, predicted[1] & ~1) and predicted == (14, 9) | |
| 58 | + assert known, (name, "observed", observed, "predicted", predicted, prediction) | |
| 59 | + discrepancies += 1 | |
| 60 | + print(f"{name}: known tcg discrepancy, observed=0x{observed[1]:x} architectural=0x{predicted[1]:x}") | |
| 61 | + else: | |
| 62 | + print(f"{name}: vector={observed[0]} error=0x{observed[1]:x}") | |
| 63 | + checks += 1 | |
| 64 | + current = None | |
| 65 | + assert checks == 31, checks | |
| 66 | + print(f"{checks - discrepancies} exact guest comparisons, {discrepancies} reserved-bit tcg discrepancies") | |