furix
cute os
arch/x86_64/ap_boot.c
25002 bytes raw
1#include <ap_boot.h>
2#include <gdt.h>
3
4#include <idt.h>
5#include <io.h>
6#include <lapic.h>
7#include <percpu.h>
8#include <serial.h>
9
10/* provided by ap_boot.S */
11extern const uint8_t ap_trampoline[];
12extern const uint64_t ap_trampoline_bytes;
13
14/* patch slots in the trampoline, written by the BSP and read by another core */
15extern volatile uint64_t ap_percpu;
16extern volatile uint64_t ap_stack_top;
17extern volatile uint64_t ap_entry;
18extern volatile uint64_t ap_slot;
19extern const uint8_t ap_gdt_sym[];
20extern const uint8_t ap_gdt_end[];
21extern const uint8_t ap_gdt_tss[];
22
23/*
24 * GDT pointer record, read by both LGDTs in the trampoline. it holds the limit
25 * and base, NOT the descriptor table. pointing LGDT at the descriptors
26 * themselves leaves GDTR.limit zero and the far jump triple faults.
27 *
28 * one record serves both loads: LGDT's memory layout follows the operand size,
29 * and both loads use a 32-bit operand size.
30 */
31extern volatile uint8_t ap_gdt_ptr[];
32extern const uint8_t ap_gdt_pm_ptr[];
33extern const uint8_t ap_gdt_end[];
34
35/*
36 * IDT pointer record, read by the LIDT in ap_lm. both fields are absolute
37 * addresses: the table lives in the kernel image, not in this page.
38 */
39extern volatile uint8_t ap_idt_pm_ptr[];
40
41/* byte offsets within the GDT pointer record, which is limit-first */
42#define AP_GDT_PM_LIMIT 0u
43#define AP_GDT_PM_BASE 2u
44
45#define AP_IDT_PM_LIMIT 0u
46#define AP_IDT_PM_BASE 2u
47
48/*
49 * far-jump offset slots and the labels they must name.
50 *
51 * a far jump computes CS.base + offset, and the flat GDT gives every
52 * descriptor base 0, so the offset field must hold the ABSOLUTE linear address
53 * of the target. using the within-page offset alone sends the AP to linear
54 * 0x23, inside the real-mode IVT, where it executes vector bytes forever.
55 *
56 * the 16-bit form is emitted as raw bytes because `ljmp $0x08, sym` in .code16
57 * produces an ADDR32 relocation the linker fills with the image address.
58 */
59extern const uint8_t ap_pm32_off16[];
60extern const uint8_t ap_pm32[];
61extern const uint8_t ap_lm_off32[];
62extern const uint8_t ap_lm[];
63
64/*
65 * the real-mode prologue's idea of where it lives.
66 *
67 * the AP cannot work this out for itself: INIT leaves SP=0, so the classic
68 * `call $+5 ; popw %bx` sequence reads zeros back and every page-relative
69 * reference then addresses the wrong place. the BSP knows the answer, it is
70 * the base of the page it just wrote, so it is patched straight in.
71 *
72 * two words are needed because the load is a moffs16, i.e. an absolute address
73 * baked into the instruction rather than a [bx+disp16]:
74 *
75 * - ap_rm_base_addr16 is the operand the AP reads FROM.
76 * - ap_rm_base_slot is the value the AP reads, the page base itself.
77 *
78 * both are 16 bits because the page is below 1 MiB.
79 */
80extern const uint8_t ap_rm_base_addr16[];
81extern const uint8_t ap_rm_base_slot[];
82extern const uint8_t ap_rm_sp_slot[];
83extern const uint8_t ap_rm_stack_top[];
84extern const uint8_t ap_stage[];
85
86extern const uint8_t ap_gdt_sym[];
87
88/*
89 * the PML4 base for CR3, a slot rather than a label because CR3 names a page
90 * table outside this page. patched with an absolute physical address.
91 */
92extern volatile uint64_t ap_cr3_off32;
93
94#define AP_PM32_OFF16_OFF ((uint64_t)(&ap_pm32_off16[0] - (uint64_t)ap_trampoline))
95#define AP_LM_OFF32_OFF ((uint64_t)(&ap_lm_off32[0] - (uint64_t)ap_trampoline))
96#define AP_PM32_WITHIN ((uint64_t)(&ap_pm32[0] - (uint64_t)ap_trampoline))
97#define AP_LM_WITHIN ((uint64_t)(&ap_lm[0] - (uint64_t)ap_trampoline))
98#define AP_RM_SP_WITHIN ((uint64_t)(&ap_rm_stack_top[0] - (uint64_t)ap_trampoline))
99/* the AP's TSS descriptor inside its own copy of the flat GDT */
100#define AP_GDT_TSS_WITHIN ((uint64_t)(&ap_gdt_tss[0] - (uint64_t)ap_trampoline))
101
102/* the reserved trampoline page, set by ap_boot_reserve() */
103static uint64_t trampoline_base;
104
105/*
106 * where the trampoline's progress marker lives inside the page, and the highest
107 * value any AP has reached there. debug aid only, see delay_and_latch().
108 */
109static volatile uint8_t *ap_stage_ptr;
110static uint8_t ap_stage_best;
111
112/* the APs' PML4, built by ap_boot_reserve() */
113static uint64_t ap_pml4_base;
114
115static void ap_main(void);
116
117/*
118 * APs that have come up and initialised their per-CPU area.
119 *
120 * incremented with a locked add, not `++`: several APs reach ap_main() at once
121 * and a plain read-modify-write lets two of them read the same old value, so
122 * one increment is lost and the BSP waits forever for an AP that is already
123 * online. the BSP only reads it through atomic_load_u32().
124 */
125volatile uint32_t ap_online_count;
126
127#define PAGE_SIZE 4096u
128
129/* a real-mode CS:IP cannot reach past 1 MiB */
130#define TRAMPOLINE_MAX_ADDR 0x100000ull
131
132/* bounded so a lost SIPI surfaces as a failed bringup, never a hang */
133#define AP_START_SPINS 40000000u
134
135/*
136 * memory types we may take a page from. EfiLoaderCode/Data is where our own
137 * image lives and EfiRuntimeServices* is firmware-owned, so both are excluded.
138 */
139static bool memory_type_usable(uint32_t type) {
140 return type == EfiBootServicesCode ||
141 type == EfiBootServicesData ||
142 type == EfiConventionalMemory;
143}
144
145/*
146 * find a free page below 1 MiB from the UEFI memory map.
147 *
148 * AllocatePages alone would be simpler but gives no upper bound, and the AP's
149 * real-mode entry point must be somewhere the CPU can reach. so read the map
150 * and choose an address, then claim that exact address below.
151 */
152static bool find_low_page(const EFI_SYSTEM_TABLE *st, uint64_t *out_addr) {
153 EFI_BOOT_SERVICES *bs = st->BootServices;
154 EFI_MEMORY_DESCRIPTOR *map = NULL;
155 UINTN map_size = 0;
156 UINTN desc_size = 0;
157 UINTN key = 0;
158 UINTN entry_size;
159 UINTN count;
160 UINTN i;
161 uint32_t version = 0;
162 EFI_STATUS status;
163 bool found = false;
164
165 /*
166 * the sizing call passes a NULL buffer and is SUPPOSED to fail with
167 * EFI_BUFFER_TOO_SMALL while reporting the size needed. testing it with
168 * EFIERR() would reject the only outcome that is not an error, so require
169 * exactly that status instead.
170 */
171 status = bs->GetMemoryMap(&map_size, NULL, &key, &desc_size, &version);
172
173 if (status != EFI_BUFFER_TOO_SMALL) {
174 serial_puts("ap: GetMemoryMap size probe failed, status=");
175 serial_put_hex((uint64_t)status);
176 serial_puts("\n");
177 return false;
178 }
179
180 /* leave headroom: the map can grow between calls as firmware allocates */
181 map_size += desc_size * 8;
182
183 if (EFIERR(bs->AllocatePool(EfiLoaderData, map_size, (void **)&map))) {
184 serial_puts("ap: AllocatePool failed for the memory map\n");
185 return false;
186 }
187
188 if (EFIERR(bs->GetMemoryMap(&map_size, map, &key, &desc_size, &version))) {
189 bs->FreePool(map);
190 serial_puts("ap: GetMemoryMap failed\n");
191 return false;
192 }
193
194 entry_size = desc_size;
195 count = map_size / entry_size;
196
197 for (i = 0; i < count && !found; i++) {
198 const EFI_MEMORY_DESCRIPTOR *d = &map[i];
199 uint64_t start = d->PhysicalStart;
200 uint64_t end;
201 uint64_t first_page;
202
203 if (!memory_type_usable(d->Type)) {
204 continue;
205 }
206
207 if (start >= TRAMPOLINE_MAX_ADDR) {
208 continue;
209 }
210
211 end = start + d->NumberOfPages * PAGE_SIZE;
212
213 first_page = (start + PAGE_SIZE - 1) & ~(uint64_t)(PAGE_SIZE - 1);
214
215 /*
216 * physical page 0 holds the real-mode IVT and BIOS data area, and the AP
217 * jumps there on INIT, so never hand out page 0. the region itself may
218 * legitimately START at 0, so skip the page rather than the region.
219 */
220 if (first_page == 0) {
221 first_page = PAGE_SIZE;
222 }
223
224 if (first_page + PAGE_SIZE <= end && first_page + PAGE_SIZE <= TRAMPOLINE_MAX_ADDR) {
225 *out_addr = first_page;
226 found = true;
227 }
228 }
229
230 bs->FreePool(map);
231 return found;
232}
233
234/* entry attribute bits for the page tables below */
235#define PTE_PRESENT (1ull << 0)
236#define PTE_WRITE (1ull << 1)
237#define PTE_LARGE (1ull << 7)
238
239/* 512 entries per table, each 8 bytes wide */
240#define PAGE_TABLE_ENTRIES 512u
241
242/*
243 * identity-map the low 4 GiB with 2 MiB pages, for the APs only.
244 *
245 * long mode cannot be entered with paging off, since EFER.LME is not honoured
246 * until CR0.PG is set. page tables are required to *enter* long mode, not
247 * merely a later refinement. the AP needs a translation regime before it can
248 * fetch a single 64-bit instruction.
249 *
250 * three levels suffice for 4 GiB of identity mapping:
251 *
252 * PML4[0] -> PDPT (covers 512 GiB)
253 * PDPT[0] -> PD[0] (covers 1 GiB)
254 * PD[i][j] -> 2 MiB page (512 pages of 2 MiB each)
255 *
256 * the BSP does NOT use these tables; it keeps running on the firmware's CR3
257 * until it builds its own. only the APs load this PML4. that is temporary and
258 * a known asymmetry to be resolved with the paging layer.
259 */
260#define AP_MAPPED_GIB 4u
261
262/* bytes needed: PML4 + PDPT + AP_MAPPED_GIB page directories */
263#define AP_PT_PAGES (2u + AP_MAPPED_GIB)
264
265static void zero_page(uint64_t addr) {
266 uint64_t *p = (uint64_t *)addr;
267 uint32_t i;
268
269 for (i = 0; i < PAGE_TABLE_ENTRIES; i++) {
270 p[i] = 0;
271 }
272}
273
274/*
275 * build the identity map and return the PML4 physical address, or 0 on failure.
276 *
277 * the tables are allocated by the firmware and left as ordinary physical
278 * memory: they are only ever read by the hardware, never by C code, so a
279 * virtual mapping is unnecessary.
280 */
281static uint64_t build_ap_page_tables(const EFI_SYSTEM_TABLE *st) {
282 EFI_BOOT_SERVICES *bs = st->BootServices;
283 uint64_t base = 0;
284 uint64_t pml4;
285 uint64_t pdpt;
286 uint64_t *pd;
287 uint32_t gi;
288 uint32_t i;
289
290 if (EFIERR(bs->AllocatePages(AllocateAnyPages, EfiBootServicesData,
291 AP_PT_PAGES, &base))) {
292 serial_puts("ap: could not allocate page tables\n");
293 return 0;
294 }
295
296 pml4 = base;
297 pdpt = base + PAGE_SIZE;
298
299 zero_page(pml4);
300 zero_page(pdpt);
301
302 /*
303 * entries are physical addresses with the attribute bits ORed in, so the low
304 * 12 bits, always zero for a page-aligned table, are free to hold flags.
305 */
306 *(volatile uint64_t *)pml4 = pdpt | PTE_PRESENT | PTE_WRITE;
307
308 for (gi = 0; gi < AP_MAPPED_GIB; gi++) {
309 pd = (uint64_t *)(pdpt + PAGE_SIZE + (uint64_t)gi * PAGE_SIZE);
310 zero_page(pdpt + PAGE_SIZE + (uint64_t)gi * PAGE_SIZE);
311
312 /*
313 * every PDPT entry has to be filled in, not just entry 0.
314 *
315 * one page directory covers 512 * 2 MiB = 1 GiB, so a PDPT with only
316 * PDPT[0] populated maps exactly one gibibyte no matter how many
317 * directories were filled in. the others were built, fully populated, and
318 * simply unreachable. the first access above 1 GiB is lapic_id(), whose
319 * #PF lands on the LAPIC's own MMIO window at 0xfee00000 and so reads like
320 * a broken APIC.
321 *
322 * PDPT entries are 8 BYTES APART. `gi * PAGE_SIZE` is wrong here even
323 * though the page directories themselves really are PAGE_SIZE apart:
324 * indexing by PAGE_SIZE writes gi=1 over PD[0][0], which chains the
325 * directories into each other and overwrites the large pages. the walk
326 * then resolves the trampoline's own address to an unrelated physical
327 * page, the AP fetches zeros instead of its own instructions, and the
328 * failure looks like the trampoline page was corrupted.
329 */
330 *(volatile uint64_t *)(pdpt + (uint64_t)gi * sizeof(uint64_t)) =
331 (uint64_t)pd | PTE_PRESENT | PTE_WRITE;
332
333 for (i = 0; i < PAGE_TABLE_ENTRIES; i++) {
334 uint64_t phys = ((uint64_t)gi * PAGE_TABLE_ENTRIES + i) * (2u * 1024u * 1024u);
335
336 pd[i] = phys | PTE_PRESENT | PTE_WRITE | PTE_LARGE;
337 }
338 }
339
340 return pml4;
341}
342
343/*
344 * write the page-relative displacements used by the trampoline.
345 *
346 * everything here is an offset from the start of the image, because the AP
347 * adds it to the page base it recovers at run time. the exception is CR3,
348 * which names a page table outside the page and so holds an absolute address.
349 */
350static bool patch_page_relatives(volatile uint8_t *page, uint64_t base) {
351 const uint64_t image = (uint64_t)ap_trampoline;
352 const uint32_t gdt_limit = (uint32_t)((uint64_t)ap_gdt_end - (uint64_t)ap_gdt_sym) - 1u;
353 volatile uint8_t *pm = &page[(uint64_t)ap_gdt_pm_ptr - image];
354
355 /*
356 * the GDT lives inside the page, so its runtime address is the page base
357 * plus its within-page offset. this is the one place a true absolute address
358 * is needed: the CPU reads the base out of the pointer record rather than
359 * adding a displacement itself.
360 */
361 const uint32_t gdt_base = (uint32_t)(base + ((uint64_t)ap_gdt_sym - image));
362
363 /* limit:2 then base:4 then 2 reserved */
364 *(volatile uint16_t *)&pm[AP_GDT_PM_LIMIT] = (uint16_t)gdt_limit;
365 *(volatile uint32_t *)&pm[AP_GDT_PM_BASE] = gdt_base;
366
367 /*
368 * the IDT pointer, unlike the GDT this one is not inside the page: the table
369 * is the AP's own copy in the kernel image, already built by idt_init(), so
370 * both fields are the address the BSP published.
371 */
372 {
373 volatile uint8_t *idt_ptr = &page[(uint64_t)ap_idt_pm_ptr - image];
374
375 *(volatile uint16_t *)&idt_ptr[AP_IDT_PM_LIMIT] = idt_ap_pointer.limit;
376 *(volatile uint32_t *)&idt_ptr[AP_IDT_PM_BASE] =
377 (uint32_t)idt_ap_pointer.base;
378 }
379
380 /*
381 * GDTR.limit is the largest usable OFFSET, so it must cover the last byte of
382 * the descriptor the long-mode far jump selects, selector 0x18, whose
383 * descriptor ends at offset 0x1f. too small and the far jump raises #GP,
384 * triple faults and resets the AP, which looks exactly like "it never ran".
385 */
386 if (gdt_limit < 0x2fu) {
387 serial_puts("ap: gdt limit too small, ");
388 serial_put_hex(gdt_limit);
389 serial_puts("\n");
390 return false;
391 }
392
393 /* CR3 gets the PML4's ABSOLUTE physical address */
394 *(volatile uint64_t *)&page[(uint64_t)&ap_cr3_off32 - image] = ap_pml4_base;
395
396 /*
397 * the two far-jump offsets, plus the real-mode page base and stack pointer.
398 * absolute addresses, not within-page offsets. both targets are inside this
399 * page, which the BSP places below 1 MiB, so 16 bits suffices for the first
400 * and 32 for the second.
401 */
402 {
403 const uint64_t pm32_lin = base + AP_PM32_WITHIN;
404 const uint64_t lm_lin = base + AP_LM_WITHIN;
405 const uint64_t rm_sp = base + AP_RM_SP_WITHIN;
406 const uint64_t base_slot_addr = base + (uint64_t)&ap_rm_base_slot - image;
407
408 if (base > 0xffffu || base_slot_addr > 0xffffu || pm32_lin > 0xffffu ||
409 rm_sp > 0xffffu || lm_lin > 0xffffffffu) {
410 serial_puts("ap: trampoline page too high for its 16-bit offsets\n");
411 return false;
412 }
413
414 *(volatile uint16_t *)&page[AP_PM32_OFF16_OFF] = (uint16_t)pm32_lin;
415 *(volatile uint32_t *)&page[AP_LM_OFF32_OFF] = (uint32_t)lm_lin;
416
417 /* both the operand AND the value are patched */
418 *(volatile uint16_t *)&page[(uint64_t)ap_rm_base_addr16 - image] =
419 (uint16_t)base_slot_addr;
420 *(volatile uint16_t *)&page[(uint64_t)ap_rm_base_slot - image] =
421 (uint16_t)base;
422
423 /*
424 * the real-mode stack pointer is an absolute linear address: SS is 0, so
425 * SP is the physical address itself.
426 */
427 *(volatile uint16_t *)&page[(uint64_t)ap_rm_sp_slot - image] =
428 (uint16_t)rm_sp;
429 }
430
431 return true;
432}
433
434/*
435 * refresh the trampoline page from the image and re-apply every patch.
436 *
437 * done once per AP rather than once at reserve time. the page runs below 1 MiB
438 * with paging off, which is shared space: an AP that faults there has no IDT
439 * to report through, so it triple faults and QEMU resets it, and a reset AP
440 * starts executing whatever sits at the reset vector. on an OVMF-only machine
441 * that is uninitialised low RAM, and the resulting garbage stores land in this
442 * page. a single failure then leaves the next AP with a trampoline whose
443 * prologue and far-jump opcode have been overwritten. re-copying makes each AP
444 * start from a page known to be identical to the image.
445 */
446static void reload_trampoline(void) {
447 volatile uint8_t *page = (volatile uint8_t *)trampoline_base;
448 uint64_t i;
449
450 for (i = 0; i < ap_trampoline_bytes; i++) {
451 page[i] = ap_trampoline[i];
452 }
453
454 if (!patch_page_relatives(page, trampoline_base)) {
455 serial_puts("ap: could not patch trampoline\n");
456 }
457}
458
459bool ap_boot_reserve(const EFI_SYSTEM_TABLE *st, uint64_t *out_base) {
460 uint64_t base = 0;
461 volatile uint8_t *page;
462 uint64_t bytes;
463 uint64_t i;
464
465 if (!find_low_page(st, &base)) {
466 serial_puts("ap: no free page below 1 MiB\n");
467 return false;
468 }
469
470 bytes = ap_trampoline_bytes;
471
472 /*
473 * build the APs' identity map BEFORE patching, so ap_cr3_off32 has something
474 * real to point at. long mode cannot be entered with paging disabled, so a
475 * failure here is fatal to AP bringup.
476 */
477 ap_pml4_base = build_ap_page_tables(st);
478
479 if (ap_pml4_base == 0) {
480 return false;
481 }
482
483 if (bytes > PAGE_SIZE) {
484 serial_puts("ap: trampoline too large (");
485 serial_put_hex(bytes);
486 serial_puts(" bytes)\n");
487 return false;
488 }
489
490 /* claim it before writing, so firmware cannot hand it out underneath us */
491 if (EFIERR(st->BootServices->AllocatePages(AllocateAddress, EfiBootServicesData, 1, &base))) {
492 serial_puts("ap: could not claim page at ");
493 serial_put_hex(base);
494 serial_puts("\n");
495 return false;
496 }
497
498 page = (volatile uint8_t *)base;
499
500 for (i = 0; i < bytes; i++) {
501 page[i] = ap_trampoline[i];
502 }
503
504 /* fill in every page-relative displacement now that the image is placed */
505 if (!patch_page_relatives(page, base)) {
506 return false;
507 }
508
509 serial_puts("ap: trampoline page at ");
510 serial_put_hex(base);
511 serial_puts(" (");
512 serial_put_hex(bytes);
513 serial_puts(" bytes), sipi vector ");
514 serial_put_hex(base >> 12);
515 serial_puts(", pml4 ");
516 serial_put_hex(ap_pml4_base);
517 serial_puts("\n");
518
519 trampoline_base = base;
520 ap_stage_ptr = &page[(uint64_t)ap_stage - (uint64_t)ap_trampoline];
521 *out_base = base;
522 return true;
523}
524
525/*
526 * point the trampoline at this AP's per-CPU area, stack and entry point.
527 * done per AP immediately before its SIPI, since one shared trampoline page
528 * serves all of them and they are brought up one at a time.
529 */
530static void patch_trampoline(uint8_t slot) {
531 /* offsets of the patch slots, relative to the start of the image */
532 const uint64_t percpu_off = (uint64_t)&ap_percpu - (uint64_t)ap_trampoline;
533 const uint64_t stack_off = (uint64_t)&ap_stack_top - (uint64_t)ap_trampoline;
534 const uint64_t entry_off = (uint64_t)&ap_entry - (uint64_t)ap_trampoline;
535 const uint64_t slot_off = (uint64_t)&ap_slot - (uint64_t)ap_trampoline;
536
537 /* the page is written here and executed by another core: keep it volatile */
538 volatile uint8_t *page = (volatile uint8_t *)trampoline_base;
539
540 /*
541 * the per-CPU area and stack are chosen by `slot`, a dense index, rather
542 * than by APIC ID: an x2APIC MADT can report an ID larger than the number of
543 * per-CPU slots reserved, and using it as an index would run off the array.
544 */
545 *(volatile uint64_t *)&page[percpu_off] = percpu_area(slot);
546 *(volatile uint64_t *)&page[stack_off] = percpu_stack_top(slot);
547 *(volatile uint64_t *)&page[entry_off] = (uint64_t)&ap_main;
548
549 /*
550 * the AP re-reads its own APIC ID from the LAPIC rather than being told, so
551 * the slot travels with it and is handed to percpu_attach() there.
552 */
553 *(volatile uint64_t *)&page[slot_off] = (uint64_t)slot;
554
555 /*
556 * point this AP's GDT TSS descriptor at the TSS for its slot.
557 *
558 * the descriptor lives inside the copied page, so it is addressed by
559 * page-base plus its within-page offset, exactly like the pointer record
560 * patched in patch_page_relatives(). the descriptor is 16 bytes: base low 2,
561 * base mid 1, access 1, limit/flags 1, base high 1, then the upper base.
562 *
563 * access byte 0x89 marks it present, ring0 and available; without it the LTR
564 * in the trampoline raises #GP, which faults, and the AP triple faults into a
565 * reset that is indistinguishable from "it never ran".
566 */
567 {
568 volatile uint8_t *desc = &page[AP_GDT_TSS_WITHIN];
569 uint64_t tss = gdt_ap_tss_base(slot);
570
571 desc[2] = (uint8_t)((tss >> 16) & 0xFF); /* base mid */
572 desc[3] = 0x89; /* present, ring0, 64-bit TSS */
573 desc[5] = (uint8_t)((tss >> 24) & 0xFF); /* base high */
574 desc[6] = (uint8_t)(tss & 0xFF); /* base bits 32..39 */
575 desc[7] = (uint8_t)((tss >> 40) & 0xFF); /* base bits 40..47 */
576 }
577}
578
579/* runs on the AP, in long mode */
580static void ap_main(void) {
581 uint32_t id = lapic_id();
582
583 /*
584 * the BSP patched this AP's slot into the trampoline before the SIPI. read it
585 * through the same volatile slot the trampoline image declares, so the
586 * compiler cannot cache it or fold the load.
587 */
588 percpu_attach(id, (uint8_t)ap_slot);
589
590 /*
591 * tell the BSP we are alive and have our own GS base. this must be the
592 * locked add and must come before the serial output below, because the BSP
593 * treats the counter as the completion signal: if it observed the counter
594 * first it may start patching the trampoline page for the next AP while this
595 * one is still printing, and the two would interleave on the UART.
596 */
597 (void)atomic_fetch_add_u32(&ap_online_count, 1);
598
599 serial_puts("ap: ");
600 serial_put_hex(id);
601 serial_puts(" online\n");
602
603 /*
604 * nothing to schedule yet. park with interrupts masked: an unhandled
605 * interrupt would reach the default dispatcher and halt the whole kernel.
606 */
607 for (;;) {
608 cpu_cli();
609 cpu_hlt();
610 }
611}
612
613static void delay_loops(uint32_t n) {
614 volatile uint32_t i;
615
616 for (i = 0; i < n; i++) {
617 }
618}
619
620/*
621 * busy-wait while latching the trampoline's progress marker.
622 *
623 * this has to cover the wait AFTER the SIPIs, not just the wait after them in
624 * the caller: an AP runs its whole trampoline and faults within nanoseconds of
625 * the SIPI landing, and a fault with no IDT becomes a triple fault and a reset,
626 * after which the reset processor overwrites the page. by the time the caller
627 * starts sampling, the evidence is usually already gone.
628 */
629static void delay_and_latch(uint32_t n) {
630 uint32_t i;
631
632 for (i = 0; i < n; i++) {
633 if (ap_stage_ptr != NULL && *ap_stage_ptr > ap_stage_best) {
634 ap_stage_best = *ap_stage_ptr;
635 }
636 }
637}
638
639static uint32_t start_one_ap(uint32_t apic_id, uint8_t sipi_vector) {
640 uint32_t rc = 0;
641 /*
642 * INIT is level-triggered and needs both halves.
643 *
644 * the assert (ICR bit 14 set) drives the processor into the reset-hold
645 * state. only once it is held does the deassert mean anything: it releases
646 * the AP into real mode at the reset vector, spinning there until a SIPI
647 * redirects it. sending the deassert alone, with bit 14 clear as this code
648 * originally did, leaves the AP wherever the firmware had it and
649 * the subsequent SIPIs go nowhere, so the AP never runs the trampoline.
650 */
651 rc |= lapic_ipi_init_assert(apic_id);
652 delay_loops(200000);
653
654 rc |= lapic_ipi_init_deassert(apic_id);
655 delay_loops(200000);
656
657 /*
658 * two SIPIs are required: the first may be ignored if the processor has not
659 * finished resetting, the second always lands. the vector selects the page
660 * to jump to, so it must be the page we copied the trampoline into.
661 */
662 rc |= lapic_ipi_sipi(apic_id, sipi_vector);
663 delay_and_latch(100000);
664
665 rc |= lapic_ipi_sipi(apic_id, sipi_vector);
666 delay_and_latch(100000);
667
668 return rc;
669}
670
671uint32_t ap_boot_start(const struct acpi_madt_info *madt) {
672 uint32_t bsp_id = lapic_id();
673 uint8_t sipi_vector = (uint8_t)(trampoline_base >> 12);
674 uint32_t started = 0;
675 uint32_t i;
676 const uint64_t stage_off = (uint64_t)ap_stage - (uint64_t)ap_trampoline;
677
678 ap_online_count = 0;
679
680 serial_puts("ap: starting from apic ");
681 serial_put_hex(bsp_id);
682 serial_puts(", vector ");
683 serial_put_hex(sipi_vector);
684 serial_puts("\n");
685
686 for (i = 0; i < madt->cpu_count; i++) {
687 uint32_t id = madt->apic_ids[i];
688 uint8_t slot = madt->slots[i];
689 uint32_t before;
690 uint32_t spins;
691 uint8_t stage = 0;
692
693 if (id == bsp_id) {
694 continue;
695 }
696
697 before = atomic_load_u32(&ap_online_count);
698
699 /* fresh page per AP, see reload_trampoline() */
700 reload_trampoline();
701 patch_trampoline(slot);
702
703 ap_stage_best = 0;
704 ap_stage_ptr = &((volatile uint8_t *)trampoline_base)[stage_off];
705
706 /*
707 * a non-zero return means the destination ID was outside what the xAPIC
708 * ICR can name, so no INIT or SIPI was sent at all. report that as a
709 * failure to start rather than waiting out the spin below for an AP that
710 * was never going to run.
711 */
712 if (start_one_ap(id, sipi_vector) != 0) {
713 serial_puts("ap: ");
714 serial_put_hex(id);
715 serial_puts(" UNADDRESSABLE (x2apic id needs IA32_X2APIC_ICR)\n");
716 continue;
717 }
718
719 for (spins = 0; spins < AP_START_SPINS; spins++) {
720 if (atomic_load_u32(&ap_online_count) > before) {
721 break;
722 }
723 }
724
725 stage = ap_stage_best;
726
727 if (atomic_load_u32(&ap_online_count) > before) {
728 started++;
729 } else {
730 serial_puts("ap: ");
731 serial_put_hex(id);
732 serial_puts(" TIMEOUT stage=");
733 serial_put_hex(stage);
734 serial_puts("\n");
735 }
736 }
737
738 return started;
739}