~james/scope

scope/guest/guest.c -rw-r--r-- 7.4 KiB
6e057d53 — james chore(import): snapshot james.dad.town/scope at 034e6b67d44c 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#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");
}