Files

224 lines
7.4 KiB
C

#include <stdint.h>
#include <stddef.h>
static uint64_t root[512] __attribute__((aligned(4096)));
static uint64_t directory[512] __attribute__((aligned(4096)));
static uint64_t middle[512] __attribute__((aligned(4096)));
static uint64_t table[512] __attribute__((aligned(4096)));
static uint8_t payload[4096] __attribute__((aligned(4096)));
volatile uint64_t recovery_stack;
volatile uint64_t recovery_address;
volatile uint64_t observed_vector;
volatile uint64_t observed_error;
extern void probe(int access, uint64_t address);
extern void pagefault(void);
extern void protection(void);
extern void completed(void);
extern void probe_user(int access, uint64_t address);
extern uint64_t gdt[];
struct gate {
uint16_t low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t middle;
uint32_t high;
uint32_t reserved;
} __attribute__((packed));
static struct gate idt[256];
static int phys_bits;
static int nx;
static int gb_pages;
static int user_mode;
static uint64_t root_bits;
static struct {
uint32_t reserved;
uint64_t rsp[3];
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint16_t reserved3;
uint16_t iomap;
} __attribute__((packed)) tss;
static void put(char c) {
__asm__ volatile("outb %0, $0xe9" : : "a"(c));
}
static void text(const char *s) {
while (*s) put(*s++);
}
static void hex(uint64_t n) {
text("0x");
for (int shift = 60; shift >= 0; shift -= 4) put("0123456789abcdef"[(n >> shift) & 15]);
}
static void decimal(int n) {
if (n >= 10) decimal(n / 10);
put('0' + n % 10);
}
static uint64_t cr0(void) {
uint64_t value;
__asm__ volatile("mov %%cr0, %0" : "=r"(value));
return value;
}
static uint64_t cr4(void) {
uint64_t value;
__asm__ volatile("mov %%cr4, %0" : "=r"(value));
return value;
}
static void set_cr0(uint64_t value) {
__asm__ volatile("mov %0, %%cr0" : : "r"(value) : "memory");
}
static void set_cr4(uint64_t value) {
__asm__ volatile("mov %0, %%cr4" : : "r"(value) : "memory");
}
static uint64_t efer(void) {
uint32_t low, high;
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0xc0000080));
return (uint64_t)high << 32 | low;
}
static void set_efer(uint64_t value) {
__asm__ volatile("wrmsr" : : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "c"(0xc0000080));
}
static void set_ac(int enabled) {
uint64_t flags;
__asm__ volatile("pushfq; pop %0" : "=r"(flags));
flags = enabled ? flags | (1u << 18) : flags & ~(1u << 18);
__asm__ volatile("push %0; popfq" : : "r"(flags) : "cc", "memory");
}
static void flush(void) {
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)root | root_bits) : "memory");
}
static void gate(int vector, void (*handler)(void)) {
uint64_t address = (uint64_t)handler;
idt[vector] = (struct gate){address, 0x18, 0, 0x8e, address >> 16, address >> 32, 0};
}
static void reset(void) {
__asm__ volatile("mov %0, %%cr3" : : "r"(0x70000ull) : "memory");
set_cr4(0x20);
set_cr0(cr0() | (1u << 16));
set_efer(efer() | (1u << 11));
set_ac(0);
user_mode = 0;
root_bits = 0;
for (int i = 0; i < 512; i++) root[i] = directory[i] = middle[i] = table[i] = 0;
root[0] = (uint64_t)directory | 7;
directory[0] = (uint64_t)middle | 7;
middle[0] = 0x83;
middle[2] = (uint64_t)table | 7;
table[0] = (uint64_t)payload | 3;
payload[0] = 0xc3;
flush();
}
static void page(uint64_t *frame) {
text("page ");
hex((uint64_t)frame);
put(' ');
const uint8_t *bytes = (const uint8_t *)frame;
for (int i = 0; i < 4096; i++) {
put("0123456789abcdef"[bytes[i] >> 4]);
put("0123456789abcdef"[bytes[i] & 15]);
}
put('\n');
}
static void run(const char *name, int access, uint64_t address) {
uint64_t flags, current_cr2, current_cr3;
flush();
__asm__ volatile("pushfq; pop %0" : "=r"(flags));
__asm__ volatile("mov %%cr2, %0" : "=r"(current_cr2));
__asm__ volatile("mov %%cr3, %0" : "=r"(current_cr3));
text("begin "); text(name); put(' '); decimal(access); put(' '); hex(address);
text(user_mode ? " user\n" : " supervisor\n");
text("context "); hex(cr0()); put(' '); hex(current_cr2); put(' '); hex(current_cr3);
put(' '); hex(cr4()); put(' '); hex(efer()); put(' '); hex(flags); text(" 0x0 ");
decimal(phys_bits); put(' '); decimal(nx); put(' '); decimal(gb_pages); put('\n');
page(root); page(directory); page(middle); page(table);
observed_vector = 0;
observed_error = 0;
flush();
if (user_mode) probe_user(access, address);
else probe(access, address);
text("observed "); decimal(observed_vector); put(' '); decimal(observed_error); put('\n');
text("end\n");
}
static void run_user(const char *name, int access) {
middle[0] |= 4;
user_mode = 1;
run(name, access, 0x400000);
}
void guest(void) {
uint32_t a, b, c, d;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000008), "c"(0));
phys_bits = a & 255;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x80000001), "c"(0));
nx = (d >> 20) & 1;
gb_pages = (d >> 26) & 1;
gate(13, protection);
gate(14, pagefault);
gate(0x80, completed);
idt[0x80].flags = 0xee;
tss.rsp[0] = 0x88000;
tss.iomap = sizeof(tss);
uint64_t base = (uint64_t)&tss;
gdt[6] = (sizeof(tss) - 1) | ((base & 0xffffff) << 16) | (0x89ull << 40) | ((base >> 24) << 56);
gdt[7] = base >> 32;
__asm__ volatile("ltr %0" : : "r"((uint16_t)0x30));
struct { uint16_t limit; uint64_t base; } __attribute__((packed)) idtr = {sizeof(idt) - 1, (uint64_t)idt};
__asm__ volatile("lidt %0" : : "m"(idtr));
reset(); run("read", 0, 0x400000);
reset(); run("write", 1, 0x400000);
reset(); run("execute", 2, 0x400000);
reset(); middle[2] &= ~2ull; run("parent", 1, 0x400000);
reset(); middle[2] &= ~2ull; set_cr0(cr0() & ~(1u << 16)); run("wp", 1, 0x400000);
reset(); table[0] |= 1ull << 63; run("nx", 2, 0x400000);
reset(); middle[2] |= 1ull << 63; run("ancestor", 2, 0x400000);
reset(); table[0] = 0; run("absent", 0, 0x400000);
reset(); table[0] = 0; run("absentwrite", 1, 0x400000);
reset(); table[0] = 0; run("absentfetch", 2, 0x400000);
reset(); table[0] |= 1ull << phys_bits; run("reserved", 0, 0x400000);
reset(); table[0] |= 1ull << 63; set_efer(efer() & ~(1u << 11)); run("nxe", 0, 0x400000);
reset(); middle[2] = 0x2083; run("alignment", 0, 0x400000);
reset(); middle[2] = 0x1083; run("huge", 0, 0x401000);
reset(); directory[1] = 0x83; run("giga", 0, 0x40001000);
reset(); table[0] |= 0x80; run("pat", 0, 0x400000);
reset(); root[1] = 0x83; run("pml", 0, 0x8000000000);
reset(); run("canonical", 0, 0x800000000000);
reset(); table[0] |= 4; set_cr4(cr4() | (1u << 20)); run("smep", 2, 0x400000);
reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); run("smap", 0, 0x400000);
reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); run("smapwrite", 1, 0x400000);
reset(); table[0] |= 4; set_cr4(cr4() | (1u << 21)); set_ac(1); run("ac", 1, 0x400000);
reset(); middle[2] &= ~2ull; table[0] = 0; run("precedence", 1, 0x400000);
reset(); run_user("userdenied", 0);
reset(); table[0] |= 4; run_user("userread", 0);
reset(); table[0] |= 4; run_user("userwrite", 1);
reset(); table[0] |= 4; run_user("userfetch", 2);
reset(); table[0] |= 4; middle[2] &= ~2ull; set_cr0(cr0() & ~(1u << 16)); run_user("userwp", 1);
reset(); table[0] |= (1ull << 63) | 4; run_user("usernx", 2);
reset(); root_bits = 0xabc; run("rootbits", 0, 0x400000);
reset(); set_cr4(cr4() | (1u << 17)); root_bits = 0xabc; run("pcid", 0, 0x400000);
text("done\n");
__asm__ volatile("outl %0, $0xf4" : : "a"(0x10));
for (;;) __asm__ volatile("hlt");
}