-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsetup.c
460 lines (378 loc) · 11.8 KB
/
setup.c
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
#include "setup.h"
#include "screen.h"
/* The address space is split into sections that are spread out so
* that each is allocated using separate page table (i.e. all start
* at a multiple of 0x00400000). */
#define STACK_ADDRESS 0x00000000
#define INST_ADDRESS 0x00400000
#define X86_BASE_ADDRESS 0x00c00000
#define GDT_ADDRESS 0x01800000
#define X86_PD_ADDRESS 0x07c00000
#define PROG_BASE_ADDR 0x08000000
/* The IDT is a part of the instruction, and is mapped as the first page
* in the instruction range. */
#define IDT_ADDRESS (INST_ADDRESS)
/* Page number for the first page in the movdbz program, and translation
* to virtual addresses for the program's pages.
*
* This address range use direct mapping, so the translation is trivial. */
#define PROG_BASE_PAGE ((PROG_BASE_ADDR) >> 12)
#define PROGPAGE2VIRT(x) ((unsigned int *)((PROG_BASE_ADDR) + ((x) << 12)))
/* The movdbz binary starts with 12 pages containing
* - Stack PT and a page for the actual stack.
* - The GTD PT and four pages for the GDT.
* - An initial partial instruction whose PD is used when calling the
* first instruction in the movdbz program.
* - A register (initialized to 1) used as input to all internal NOP
* instructions.
* - A register used as output to all internal NOPs (and for 'discard'
* register in the assembler.
*
* This is followed by the registers, constants (which are read-only
* registers initialized with the constant's value + 1), and the
* instructions. */
#define STACK_PAGE 0
#define STACK_PT_PAGE 1
#define GTD_PT_PAGE 2
#define GDT_PAGE0 3
#define GDT_PAGE1 4
#define GDT_PAGE2 5
#define GDT_PAGE3 6
#define INIT_0 7 // PD
#define INIT_1 8 // PT for INST_ADDRESS
#define INIT_2 9 // INST
#define REG_CONST_ONE_PAGE 10 // Must be (REG_R0_PAGE - 2).
#define REG_DISCARD_PAGE 11 // Must be (REG_R0_PAGE - 1).
#define REG_R0_PAGE 12
/* Each instruction is encoded in four pages. */
#define PAGES_PER_INST 4
#define PD_OFF 0
#define INST_PT_OFF 1
#define INST_OFF 2
#define IDT_OFF 3
/* The register numbers for the two special registers (i.e. the two registers
* before r0). */
#define REG_DISCARD -1
#define REG_CONSTANT_ONE -2
static void gen_reg(int, unsigned int);
static void gen_movdbz(int, int, int, int, int);
#include "generated-prog.c"
static unsigned int x86_tss[26];
extern void set_gdtr(unsigned int base_addr, unsigned short table_limit);
#define PG_P 0x001 // Present (i.e. all other ignored if this is not set)
#define PG_W 0x002 // Write enable
#define PG_PS 0x080 // 4MB pages if CR4.PSE=1, ignored if CR4.PSE=0
static void
write_cr0(unsigned int data)
{
__asm __volatile("movl %0,%%cr0" : : "r" (data) : "memory");
}
static unsigned int
read_cr0(void)
{
unsigned int data;
__asm __volatile("movl %%cr0,%0" : "=r" (data));
return data;
}
static void
write_cr3(unsigned int data)
{
__asm __volatile("movl %0,%%cr3" : : "r" (data) : "memory");
}
static unsigned int
read_eflags(void)
{
unsigned int data;
__asm __volatile("pushf\npop %0" : "=r" (data));
return data;
}
static void
write_cr4(unsigned int data)
{
__asm __volatile("movl %0,%%cr4" : : "r" (data) : "memory");
}
static unsigned int
read_cr4(void)
{
unsigned int data;
__asm __volatile("movl %%cr4,%0" : "=r" (data));
return data;
}
static void
set_idtr(unsigned int base_addr, unsigned short table_limit)
{
struct __attribute__((__packed__))
{
unsigned short table_limit;
unsigned int base_addr;
} idtr;
idtr.table_limit = table_limit;
idtr.base_addr = base_addr;
__asm __volatile("lidtl %0" : : "m" (idtr) : "memory");
}
static void
init_paging(void)
{
unsigned int *pde = (unsigned int *)X86_PD_ADDRESS;
for (int i = 0; i < 512; i++)
pde[i] = PG_P | PG_PS | PG_W | (i << 22); // Direct mapping.
write_cr3(X86_PD_ADDRESS);
write_cr4(read_cr4() | 0x10);
write_cr0(read_cr0() | (1 << 31));
}
/* Encode a segment descriptor. */
static void
encode_seg_descr(
unsigned int *p,
unsigned int type,
unsigned int g,
unsigned int base,
unsigned int limit)
{
p[0] = ((base & 0xffff) << 16) | (limit & 0xffff);
p[1] = ((base & 0xff000000)
| 0x00400000 | (g << 23) | (limit & 0x000f0000)
| (type << 8)
| ((base & 0x00ff0000) >> 16));
}
static void
init_gdt(unsigned int *gdt)
{
memset(gdt, 0, 4096 * 4);
encode_seg_descr(&gdt[2], 0x9A, 1, 0, 0xfffff); // code 0x08
encode_seg_descr(&gdt[4], 0x92, 1, 0, 0xfffff); // data 0x10
encode_seg_descr(&gdt[6], 0x89, 0, (unsigned int)x86_tss, 0x67); // TSS 0x18
encode_seg_descr(&gdt[0x7fe], 0x89, 0, 0x40ffd0, 0x67);
encode_seg_descr(&gdt[0xbfe], 0x89, 0, 0x41ffd0, 0x67);
encode_seg_descr(&gdt[0xffe], 0x89, 0, 0x42ffd0, 0x67);
}
static void
init_tss(void)
{
x86_tss[7] = X86_PD_ADDRESS;
}
unsigned int
read_reg(unsigned int reg_nr)
{
unsigned int val = *(PROGPAGE2VIRT(reg_nr + REG_R0_PAGE) + 2);
return val >> 2;
}
void
write_reg(unsigned int reg_nr, unsigned int val)
{
*(PROGPAGE2VIRT(reg_nr + REG_R0_PAGE) + 2) = val << 2;
}
/* Populate the register page (i.e. the last part of the TSS). */
static void
gen_reg(int reg_nr, unsigned int value)
{
unsigned int *p = PROGPAGE2VIRT(REG_R0_PAGE + reg_nr);
p[ 2] = value << 2; // ESP
p[ 6] = 0x10; // ES
p[ 7] = 0x08; // CS
p[ 8] = 0x10; // SS
p[ 9] = 0x10; // DS
p[10] = 0x10; // FS
p[11] = 0x10; // GS
p[12] = 0; // LDT Segment Selector
}
static void
generate_pagetable(
unsigned int pd_page)
{
unsigned int *pde_ptr = PROGPAGE2VIRT(pd_page);
/* Map stack at 0x00000000. */
unsigned int *p0 = PROGPAGE2VIRT(STACK_PT_PAGE);
p0[0] = PG_P | PG_W | ((PROG_BASE_PAGE + STACK_PAGE) << 12);
pde_ptr[0] = PG_P | PG_W | ((PROG_BASE_PAGE + STACK_PT_PAGE) << 12);
/* Map the current instruction (including IDT) at 0x00400000. */
unsigned int *p1 = PROGPAGE2VIRT(pd_page + INST_PT_OFF);
p1[0] = PG_P | PG_W | ((PROG_BASE_PAGE + pd_page + IDT_OFF) << 12);
pde_ptr[1] = PG_P | PG_W | ((PROG_BASE_PAGE + pd_page + INST_PT_OFF) << 12);
/* Map the memory used for the x86 at 0x00c00000. */
pde_ptr[3] = PG_P | PG_PS | PG_W | (3 << 22);
/* Map the GDT on four consecutive pages at 0x01800000. */
unsigned int *p6 = PROGPAGE2VIRT(GTD_PT_PAGE);
for (int i = 0; i < 4; i++)
p6[i] = PG_P | PG_W | ((PROG_BASE_PAGE + GDT_PAGE0 + i) << 12);
pde_ptr[6] = PG_P | PG_W | ((PROG_BASE_PAGE + GTD_PT_PAGE) << 12);
}
static unsigned int
inst_to_tss_seg_descr(
int inst_nr)
{
if (inst_nr < 0)
return 0x18;
else
switch (inst_nr % 3)
{
case 0:
return 0x1ff8;
case 1:
return 0x2ff8;
default:
return 0x3ff8;
}
}
static unsigned int
inst_to_tss_addr(
int inst_nr)
{
switch (inst_nr % 3)
{
case 0:
return INST_ADDRESS + 0x0ffd0;
case 1:
return INST_ADDRESS + 0x1ffd0;
default:
return INST_ADDRESS + 0x2ffd0;
}
}
/* Populate the IDT with the destination instructions. */
static void
generate_idt_page(
unsigned int pd_page,
int dest1_inst_nr,
int dest2_inst_nr)
{
unsigned int *p = PROGPAGE2VIRT(pd_page + IDT_OFF);
unsigned int tss_seg_selector1 = inst_to_tss_seg_descr(dest1_inst_nr);
unsigned int tss_seg_selector2 = inst_to_tss_seg_descr(dest2_inst_nr);
/* #DF - Double Fault */
p[16] = tss_seg_selector2 << 16;
p[17] = 0xe500; /* Task gate present,
DPL (Descriptor Privilege Level) = 3
(i.e. application privilege level) */
/* #PF - Page Fault */
p[28] = tss_seg_selector1 << 16;
p[29] = 0xe500;
}
/* Populate the instruction page (i.e. the first part of the "source TSS"). */
static void
generate_inst_page(
unsigned int pd_page,
int inst_nr)
{
unsigned int *p = PROGPAGE2VIRT(pd_page + INST_OFF);
unsigned int tss_addr = inst_to_tss_addr(inst_nr);
p[1019] = (PROG_BASE_PAGE + pd_page) << 12; /* CR3 */
p[1020] = 0xfffefff; /* EIP */
p[1021] = read_eflags(); /* EFLAGS */
encode_seg_descr(&p[1022], 137, 0, tss_addr, 0x67); /* EAX and EDX */
}
/* Map the "destination TSS" (GDT page and destination register) into the
* address space. */
static void
map_dest_tss(
unsigned int pd_page,
int inst_nr,
int reg_nr)
{
unsigned int *p1 = PROGPAGE2VIRT(pd_page + INST_PT_OFF);
unsigned int tss_addr = inst_to_tss_addr(inst_nr);
unsigned int seg_descr = inst_to_tss_seg_descr(inst_nr);
p1[((tss_addr & 0x003ff000) >> 12)]
= PG_P | PG_W | ((PROG_BASE_PAGE + GDT_PAGE0 + (seg_descr >> 12)) << 12);
p1[((tss_addr & 0x003ff000) >> 12) + 1]
= PG_P | PG_W | ((PROG_BASE_PAGE + REG_R0_PAGE + reg_nr) << 12);
}
/* Map the "source TSS" (instruction page and source register) into the
* address space. */
static void
map_src_tss(
unsigned int pd_page,
int inst_nr,
int reg_nr)
{
unsigned int *p1 = PROGPAGE2VIRT(pd_page + INST_PT_OFF);
unsigned int tss_addr = inst_to_tss_addr(inst_nr);
unsigned int inst_off = inst_nr * PAGES_PER_INST + INST_OFF;
p1[((tss_addr & 0x003ff000) >> 12)]
= PG_P | PG_W | ((PROG_BASE_PAGE + FIRST_INST_PAGE + inst_off) << 12);
p1[((tss_addr & 0x003ff000) >> 12) + 1]
= PG_P | PG_W | ((PROG_BASE_PAGE + REG_R0_PAGE + reg_nr) << 12);
}
/* Generate one instruction. */
static void
gen_inst(
int inst_nr,
int dest1_inst_nr,
int dest2_inst_nr,
int dest_reg,
int dest1_input_reg,
int dest2_input_reg)
{
unsigned int pd_page = FIRST_INST_PAGE + inst_nr * PAGES_PER_INST + PD_OFF;
generate_pagetable(pd_page);
generate_idt_page(pd_page, dest1_inst_nr, dest2_inst_nr);
generate_inst_page(pd_page, inst_nr);
map_dest_tss(pd_page, inst_nr, dest_reg);
if (dest1_inst_nr >= 0)
map_src_tss(pd_page, dest1_inst_nr, dest1_input_reg);
if (dest2_inst_nr >= 0)
map_src_tss(pd_page, dest2_inst_nr, dest2_input_reg);
}
/* Generate one movdbz assembler instruction. */
static void
gen_movdbz(
int asminst_nr,
int dest_reg,
int source_reg,
int asmdest1_nr,
int asmdest2_nr)
{
/* The movdbz assembler instruction is implemented as three real
* instructions; two NOP instructions followed by an instruction doing
* the work. This simplifies the implementation by automatically handling
* the restrictions of the X86 hardware:
* - A task cannot switch to itself
* - There are limited number of GDTs (because of the workaround for the
* busy bit), and the GDTs for the instructions and target instructions
* must be distinct.
* - The input register of the instruction need to be encoded in the
* predecessor.
* Adding the NOPs means that we can generate one assembler instruction
* at a time, without needing to fix up the code for these issues. */
gen_inst(asminst_nr * 3, asminst_nr * 3 + 2, asminst_nr * 3 + 2,
REG_DISCARD, source_reg, source_reg);
gen_inst(asminst_nr * 3 + 1, asminst_nr * 3 + 2, asminst_nr * 3 + 2,
REG_DISCARD, source_reg, source_reg);
gen_inst(asminst_nr * 3 + 2, asmdest1_nr * 3, asmdest2_nr * 3 + 1,
dest_reg, REG_CONSTANT_ONE, REG_CONSTANT_ONE);
}
static void
setup_x86(void)
{
init_paging();
init_tss();
init_gdt((void *)GDT_ADDRESS);
set_gdtr(GDT_ADDRESS, 0xffff); // Sets TSS to 0x18
set_idtr(IDT_ADDRESS, 0x7ff);
}
static void
setup_movdbz_program(void)
{
memset(PROGPAGE2VIRT(0), 0, (LAST_INST_PAGE + 1) * 4096);
gen_reg(REG_CONSTANT_ONE, 1);
gen_reg(REG_DISCARD, 0);
gen_program();
/* Set up an initial page table and inst needed to call the first
* instruction in the movdbz program. */
generate_pagetable(INIT_0);
map_src_tss(INIT_0, 0, REG_CONSTANT_ONE);
init_gdt(PROGPAGE2VIRT(GDT_PAGE0));
}
void
start_movdbz_program(void)
{
write_cr3((PROG_BASE_PAGE + INIT_0) << 12);
__asm __volatile ("ljmp $0x1ff8, $0x0;add $4,%%esp" : : : "memory");
}
void
kmain(void)
{
clear_screen();
setup_x86();
setup_movdbz_program();
run_movdbz_program();
}