furix
cute os
arch/x86_64/ap_boot.S
14195 bytes raw
1/*
2 * x86_64 application-processor startup trampoline
3 *
4 * a SIPI starts an AP in 16-bit real mode at a low-memory page chosen by the
5 * BSP. this trampoline transitions through protected mode into long mode and
6 * finally calls the AP's C entry point.
7 *
8 * the BSP copies this image below 1 MiB and patches the fields marked below.
9 *
10 * the trampoline is position-independent with respect to its copied page:
11 * %bx/%ebx/%rbx holds the runtime page base, and references within the page
12 * use page-relative displacements. do not replace them with link-time absolute
13 * addresses.
14 *
15 * raw byte encodings are used where the assembler would otherwise emit
16 * absolute ADDR32 relocations. after ANY change you make, verify:
17 *
18 * - llvm-readelf -r shows no relocation for ap_lm or far-jump targets.
19 * - BSP-patched slot offsets still match this layout.
20 */
21
22.text
23
24/*
25 * selectors from the flat GDT at the bottom of this file. kept as names so a
26 * change to the table layout cannot drift away from the far jumps that use it.
27 */
28.set GDT_SEL_CODE32, 0x08
29.set GDT_SEL_DATA32, 0x10
30.set GDT_SEL_CODE64, 0x18
31.set GDT_SEL_TSS, 0x20
32
33/* model-specific registers, named because the raw numbers are untraceable */
34.set MSR_EFER, 0xC0000080
35.set MSR_KERNEL_GS_BASE, 0xC0000102
36
37.set CR0_PE, (1 << 0)
38.set CR0_PG, (1 << 31)
39.set CR4_PAE, (1 << 5)
40
41/* EFER.LME: sets LMA once CR0.PG is also on */
42
43.set EFER_LME, (1 << 8)
44
45/*
46 * bump the progress marker, so a BSP sampling ap_stage can tell how far an AP
47 * got. one macro per address size because the displacement width is encoded
48 * separately from the opcode (0x87/0x83 are incb with different ModRM).
49 * the base register is not encoded here either, so the disp32 form serves the
50 * 64-bit section too: incb disp32(%ebx) and disp32(%rbx) are the same bytes.
51 */
52.macro AP_BUMP_STAGE16
53 .byte 0xfe, 0x87 /* incb disp16(%bx) */
54 .word ap_stage - ap_trampoline
55.endm
56
57.macro AP_BUMP_STAGE
58 .byte 0xfe, 0x83 /* incb disp32(%ebx) */
59 .long ap_stage - ap_trampoline
60.endm
61
62.globl ap_trampoline
63
64
65ap_trampoline:
66
67 .code16
68
69 /* entry is real mode, paging off, interrupts masked. */
70 .byte 0xfa /* cli */
71 .byte 0xfc /* cld */
72
73 .byte 0x31, 0xc0 /* xorw %ax, %ax */
74 .byte 0x8e, 0xd8 /* movw %ax, %ds */
75 .byte 0x8e, 0xc0 /* movw %ax, %es */
76 .byte 0x8e, 0xd0 /* movw %ax, %ss */
77
78 /*
79 * load the runtime page base into %bx.
80 *
81 * INIT leaves SP=0, so a call/pop position-discovery trick is unsafe here.
82 * the BSP already knows the copied page address, so it patches that value
83 * directly into ap_rm_base_slot and patches this instruction's absolute
84 * 16-bit operand to point at the slot.
85 *
86 * slots embedded in the instruction stream must be jumped over.
87 */
88 jmp 1f
89.globl ap_rm_base_slot
90
91
92ap_rm_base_slot:
93
94 .word 0 /* patched: trampoline page base */
95
96
971:
98
99 .byte 0x8b, 0x1e /* movw abs16, %bx */
100.globl ap_rm_base_addr16
101
102
103ap_rm_base_addr16:
104
105 .word 0 /* patched: address of ap_rm_base_slot */
106
107 /*
108 * install a real-mode stack within this page
109 *
110 * SS=0, so SP must contain the absolute low-memory address of the stack
111 * top. the displacement and loaded value are distinct words.
112 *
113 * ModRM is 0x87 (mod=10, reg=SP, rm=111) so the disp16 is present. 0x27
114 * drops the displacement and loads SP from the code bytes instead.
115 */
116 jmp 1f
117.globl ap_rm_sp_slot
118
119
120ap_rm_sp_slot:
121
122 .word 0 /* patched: page base + stack-top offset */
123
124
1251:
126
127 .byte 0x8b, 0x87 /* movw disp16(%bx), %sp */
128.globl ap_rm_sp_disp16
129
130
131ap_rm_sp_disp16:
132
133 .word ap_rm_sp_slot - ap_trampoline
134
135 /*
136 * load our GDT pointer.
137 *
138 * 0x66 selects the 32-bit LGDT operand format: limit:2, base:4.
139 * addressing remains 16-bit.
140 *
141 * ModRM 0x97:
142 * mod=10 disp16
143 * reg=010 LGDT (/2)
144 * rm=111 [BX]
145 *
146 * rm=011 would be [BP+DI], not [BX], and reg=001 is SIDT rather than LGDT.
147 */
148 .byte 0x66
149 .byte 0x0f, 0x01, 0x97 /* lgdt disp16(%bx) */
150 .word ap_gdt_pm_ptr - ap_trampoline
151 AP_BUMP_STAGE16
152
153 /*
154 * enter protected mode before the far jump. without CR0.PE, the jump uses
155 * real-mode CS semantics instead of loading selector 0x08 from the GDT.
156 */
157 .byte 0x66, 0x0f, 0x20, 0xc0 /* mov %cr0, %eax */
158 .byte 0x66, 0x83, 0xc8, 0x01 /* or $CR0_PE, %eax */
159 .byte 0x66, 0x0f, 0x22, 0xc0 /* mov %eax, %cr0 */
160 AP_BUMP_STAGE16
161
162 /*
163 * far jump to flat 32-bit code.
164 *
165 * since the GDT code-segment base is zero, the jump offset must be the
166 * absolute runtime address, not a within-page offset. the BSP patches it.
167 */
168 .byte 0xea
169.globl ap_pm32_off16
170
171
172ap_pm32_off16:
173
174 .word 0 /* patched: page base + ap_pm32 offset */
175 .word GDT_SEL_CODE32
176
177 .code32
178.globl ap_pm32
179
180
181ap_pm32:
182
183 /* load flat data selectors. */
184 movw $GDT_SEL_DATA32, %ax
185 movw %ax, %ds
186 movw %ax, %es
187 movw %ax, %ss
188
189 /*
190 * reload the GDT. required because the far jump out of this block loads
191 * CS with a 64-bit (L=1) descriptor.
192 *
193 * same pointer record as the real-mode load: the memory layout follows the
194 * operand size, not the execution mode. here that size is 32-bit.
195 *
196 * ModRM 0x93: mod=10, reg=010 (LGDT), rm=011 ([EBX]).
197 */
198 .byte 0x0f, 0x01, 0x93 /* lgdt disp32(%ebx) */
199 .long ap_gdt_pm_ptr - ap_trampoline
200 AP_BUMP_STAGE
201
202 /*
203 * CR4.PAE, required for the 4-level tables long mode uses.
204 *
205 * CR4 is zeroed rather than OR-ed into %ecx: INIT leaves %ecx with
206 * whatever the delivery path last used, and a stray CR4.VMXE/SMXE bit puts
207 * QEMU into nested virtualisation and spins it on the next CR write.
208 */
209 xorl %eax, %eax
210 movl %eax, %cr4
211 orl $CR4_PAE, %eax
212 movl %eax, %cr4
213 AP_BUMP_STAGE
214
215 movl $MSR_EFER, %ecx
216 rdmsr
217 AP_BUMP_STAGE
218 orl $EFER_LME, %eax
219 wrmsr
220 AP_BUMP_STAGE
221
222 /*
223 * load CR3 with the PML4 the BSP built. paging is still off, so this does
224 * not change the translation regime yet.
225 */
226 .byte 0x8b, 0x83 /* movl disp32(%ebx), %eax */
227 .long ap_cr3_off32 - ap_trampoline
228 movl %eax, %cr3
229 AP_BUMP_STAGE
230
231 /*
232 * enable paging, which is what actually turns long mode on: EFER.LMA is
233 * only set once LME and CR0.PG are both set.
234 *
235 * CR0 is read-modify-written rather than zeroed, because zeroing would
236 * briefly drop PE. the mask clears CR0.NW/CD, which firmware leaves set.
237 */
238 movl %eax, %cr0 /* read CR0 */
239 andl $0x9fffffff, %eax /* clear NW (29), CD (30) */
240 orl $(CR0_PG | CR0_PE), %eax
241 movl %eax, %cr0 /* write CR0 */
242 AP_BUMP_STAGE
243
244 /*
245 * CR4.PSE is unnecessary in long mode (PAE already provides 2 MiB pages),
246 * and CR4.FSGSBASE is left clear because qemu64 does not advertise it,
247 * which is why per-CPU state below goes through swapgs.
248 */
249
250 /*
251 * far jump to the long-mode (L=1) segment. same absolute-offset rule as
252 * the 16-bit far jump above ; the BSP patches this one with the page base.
253 */
254 .byte 0xea
255.globl ap_lm_off32
256
257
258ap_lm_off32:
259
260 .long 0 /* patched: page base + ap_lm offset */
261 .word GDT_SEL_CODE64
262
263 .code64
264.globl ap_lm
265
266
267ap_lm:
268
269 /*
270 * long mode, paging live on the identity map the BSP built.
271 *
272 * select the AP's per-CPU area via IA32_KERNEL_GS_BASE + swapgs. GS.base
273 * is still 0 on a freshly INIT'd AP, so the swap cannot corrupt anything.
274 */
275 movl $MSR_KERNEL_GS_BASE, %ecx
276 .byte 0x48, 0x8b, 0x83 /* movq disp32(%rbx), %rax */
277 .long ap_percpu - ap_trampoline
278
279 /*
280 * WRMSR writes (RDX << 32) | RAX, so the HIGH half is what needs zeroing.
281 * clearing %eax instead puts the pointer at bit 32 and yields a
282 * non-canonical GS base that faults in C much later.
283 */
284 xorl %edx, %edx
285 wrmsr
286 swapgs
287 AP_BUMP_STAGE
288
289 /*
290 * load the AP's own TSS before the IDT.
291 *
292 * ORDER MATTERS: the IDT below names IST stacks, and an IST gate raises
293 * #GP (which itself faults, giving a triple fault and a reset) unless the
294 * processor already has a TSS loaded. the descriptor lives in this AP's own
295 * GDT at GDT_SEL_TSS and its base was patched per AP, because the TSS holds
296 * that AP's IST stack pointers and cannot be shared.
297 *
298 * ModRM 0x9b here is reg=011 (LTR /3), the same addressing as the LIDT.
299 */
300 .byte 0x0f, 0x00, 0x9b, 0x83 /* ltr disp32(%rbx) */
301 .long ap_tss_sel - ap_trampoline
302 AP_BUMP_STAGE
303
304 /*
305 * load the AP IDT the BSP built.
306 *
307 * separate from the BSP's table because its gates select 0x08, which is a
308 * 32-bit code segment ; a long-mode interrupt gate must name a 64-bit one.
309 * the IST entries the BSP uses ARE mirrored here now that the AP has a TSS,
310 * so a #DF on an AP lands on a dedicated stack instead of a dead one.
311 *
312 * ModRM 0x9b: mod=10, reg=011 (LIDT), rm=011 ([RBX]).
313 */
314 .byte 0x0f, 0x01, 0x9b, 0x83 /* lidt disp32(%rbx) */
315 .long ap_idt_pm_ptr - ap_trampoline
316 AP_BUMP_STAGE
317
318 /*
319 * move to this AP's own stack, leaving the firmware stack behind.
320 *
321 * ModRM is 0xa3 (reg=rsp), not 0x83 (reg=rax) which silently discards the
322 * value and leaves RSP at 0.
323 */
324 .byte 0x48, 0x8b, 0xa3 /* movq disp32(%rbx), %rsp */
325 .long ap_stack_top - ap_trampoline
326 xorl %ebp, %ebp /* terminate frame-pointer chains */
327 AP_BUMP_STAGE
328
329 /* call the AP entry point, patched by the BSP. */
330 .byte 0xff, 0x93 /* call *disp32(%rbx) */
331 .long ap_entry - ap_trampoline
332
333 /* park if it ever returns. */
334
335
3361:
337
338 hlt
339 jmp 1b
340
341/*
342 * GDT pointer record, read by both LGDTs above. LGDT loads a (limit, base)
343 * pair, not the descriptor table, so this is kept apart from the table itself.
344 * the BSP fills both fields ; the base is only known at run time.
345 */
346 .balign 8
347.globl ap_gdt_ptr
348
349
350ap_gdt_ptr:
351
352.globl ap_gdt_pm_ptr
353
354
355ap_gdt_pm_ptr:
356
357 .word 0 /* patched: GDTR limit */
358 .long 0 /* patched: GDTR base */
359 .word 0 /* pad */
360
361/*
362 * IDT pointer record, read by the LIDT in ap_lm. limit then base, same as the
363 * GDT record. both fields are absolute addresses: the table lives in the
364 * kernel image rather than in this page.
365 */
366 .balign 8
367.globl ap_idt_pm_ptr
368
369
370ap_idt_pm_ptr:
371
372 .word 0 /* patched: IDT limit */
373 .long 0 /* patched: IDT base */
374 .word 0 /* pad */
375
376/*
377 * BSP-patched slots. each holds an absolute value, never a within-page offset:
378 * reading the offset where an address belongs installs a small integer as a
379 * stack pointer or function pointer. ap_cr3_off32 is a slot for the same
380 * reason, since CR3 names a page table outside this page.
381 */
382 .balign 8
383.globl ap_cr3_off32
384
385
386ap_cr3_off32:
387
388 .quad 0 /* patched: absolute physical address of the PML4 */
389
390.globl ap_percpu
391
392
393ap_percpu:
394
395 .quad 0 /* patched: &percpu[slot] */
396
397.globl ap_stack_top
398
399
400ap_stack_top:
401
402 .quad 0 /* patched: top of this AP's stack */
403
404.globl ap_entry
405
406
407ap_entry:
408
409 .quad 0 /* patched: ap_main() */
410
411.globl ap_slot
412
413
414ap_slot:
415
416 .quad 0 /* patched: dense per-CPU slot for this AP */
417
418.globl ap_tss_sel
419
420
421ap_tss_sel:
422
423 .word GDT_SEL_TSS /* selector, read by the LTR above */
424
425 .quad 0
426
427/*
428 * progress marker, bumped at each step above so the BSP can report how far an
429 * AP got. the page is reloaded before every AP, so the value always belongs to
430 * the AP being started. debug aid ; the BSP only reads it.
431 */
432 .globl ap_stage
433
434
435ap_stage:
436
437 .byte 0
438 .space 3
439
440/*
441 * minimal flat GDT: null, 32-bit code, 32-bit data, 64-bit code. loaded by the
442 * real-mode prologue and again before the long-mode far jump, which needs the
443 * L=1 descriptor at selector 0x18.
444 *
445 * a selector is the descriptor's byte offset from the table base, so the .set
446 * names above and the layout below have to agree. the assembler resolves
447 * `.if` at build time, so a mismatch fails the build rather than the boot.
448 * the checks compare label differences because `.if` requires an absolute
449 * expression, which `label + constant` is not.
450 */
451 .balign 8
452.globl ap_gdt_sym
453
454
455ap_gdt_sym:
456
457
458
459ap_gdt:
460
461 .quad 0x0000000000000000 /* null */
462
463
464ap_gdt_code32:
465
466 .quad 0x00cf9a000000ffff /* 0x08: 32-bit code, base 0, limit 4G */
467
468
469ap_gdt_data32:
470
471 .quad 0x00cf92000000ffff /* 0x10: 32-bit data, base 0, limit 4G */
472
473
474ap_gdt_code64:
475
476 .quad 0x00af9a000000ffff /* 0x18: 64-bit code, base 0, limit 4G */
477
478.globl ap_gdt_tss
479
480
481ap_gdt_tss:
482
483 /*
484 * 64-bit available TSS descriptor, 16 bytes, base patched per AP by
485 * patch_trampoline(): the TSS holds that AP's IST stack pointers, so it
486 * cannot be a fixed address.
487 */
488 .quad 0
489 .quad 0
490.globl ap_gdt_end
491
492
493ap_gdt_end:
494
495.if (ap_gdt_code32 - ap_gdt) != GDT_SEL_CODE32
496 .error "ap_gdt: 32-bit code descriptor is not at GDT_SEL_CODE32"
497.endif
498.if (ap_gdt_data32 - ap_gdt) != GDT_SEL_DATA32
499 .error "ap_gdt: 32-bit data descriptor is not at GDT_SEL_DATA32"
500.endif
501.if (ap_gdt_code64 - ap_gdt) != GDT_SEL_CODE64
502 .error "ap_gdt: 64-bit code descriptor is not at GDT_SEL_CODE64"
503.endif
504.if (ap_gdt_tss - ap_gdt) != GDT_SEL_TSS
505 .error "ap_gdt: TSS descriptor is not at GDT_SEL_TSS"
506.endif
507.if (ap_gdt_end - ap_gdt) != 48
508 .error "ap_gdt: table is not 5 descriptors; a selector above is wrong"
509.endif
510
511/*
512 * top of the 16-bit real-mode stack. SS=0 here, so this is an offset within
513 * the page ; keep it above all code and patched fields.
514 */
515 .balign 16
516
517
518ap_rm_stack:
519
520 .space 64
521.globl ap_rm_stack_top
522
523
524ap_rm_stack_top:
525
526 .balign 8
527.globl ap_trampoline_end
528
529
530ap_trampoline_end:
531
532/* size of the image, for the BSP to copy. */
533.globl ap_trampoline_bytes
534
535
536ap_trampoline_bytes:
537
538 .quad ap_trampoline_end - ap_trampoline