#include #include #include #include #include #include #include #include #include #include #include /* addresses spread across different PML4 slots. OVMF maps the first PML4 entry (0-512 GiB), so low candidates may be present; the high ones are not. we READ rather than write so a mapped candidate cannot corrupt firmware. */ static const uint64_t probe_addrs[] = { 0x0000700000000000ull, /* ~123 TiB (PML4 slot 224, canonical) */ 0xFFFF800000000000ull, /* kernel half (PML4 slot 256, canonical) */ 0x0000008000000000ull, /* 512 GiB (PML4 slot 1) */ }; /* * self-IPI round trip: proves the LAPIC can deliver, the IDT dispatches, and * iretq returns us cleanly. on real hardware this is also how we would verify * a vector before trusting it for inter-CPU traffic. */ static volatile uint32_t ipi_count; static volatile uint64_t ipi_sender_apic_id; static void on_ipi(void) { ipi_sender_apic_id = lapic_id(); ipi_count++; } static bool test_self_ipi(void) { const uint32_t spins = 2000000; uint32_t i; ipi_count = 0; idt_set_handler(VECTOR_IPI_TIMER, on_ipi); /* * interrupts must be enabled for the IPI to be delivered: our gates are * interrupt gates, so IF clear means the request is latched, not taken. */ cpu_sti(); lapic_ipi_send(LAPIC_IPI_FIXED, LAPIC_SHORTHAND_SELF, VECTOR_IPI_TIMER, 0); for (i = 0; i < spins && ipi_count == 0; i++) { /* wait for the interrupt to arrive */ } cpu_cli(); serial_puts("apic: self-IPI on vector "); serial_put_hex(VECTOR_IPI_TIMER); serial_puts(" -> count = "); serial_put_hex(ipi_count); serial_puts("\n"); if (ipi_count == 0) { serial_puts("apic: FAIL (no interrupt delivered)\n"); return false; } serial_puts("apic: ipi sender apic id = "); serial_put_hex(ipi_sender_apic_id); serial_puts(ipi_sender_apic_id == lapic_id() ? " (ok)\n" : " (WRONG)\n"); idt_set_handler(VECTOR_IPI_TIMER, NULL); return true; } static bool report_madt(const EFI_SYSTEM_TABLE *st, struct acpi_madt_info *madt) { uint32_t i; if (!acpi_madt_parse(st, madt)) { serial_puts("madt: not found\n"); return false; } serial_puts("madt: cpus = "); serial_put_hex(madt->cpu_count); serial_puts(", ioapics = "); serial_put_hex(madt->ioapic_count); serial_puts("\n apic ids:"); for (i = 0; i < madt->cpu_count; i++) { serial_puts(" "); serial_put_hex(madt->apic_ids[i]); } serial_puts("\n"); return true; } void kernel_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *st) { uint64_t trampoline_base = 0; struct acpi_madt_info madt; uint32_t started = 0; uint64_t gs_check; uint32_t count; uint32_t i; (void)image; /* firmware console first: once our own GDT/IDT are live we must not call UEFI boot services again */ st->ConOut->OutputString(st->ConOut, (CHAR16 *)L"meow :3 hello world!\r\n"); cpu_cli(); serial_init(); pic_disarm(); ioapic_disarm(); serial_puts("furix: phase 0\n"); /* * the LAPIC timer MUST be masked before the first UEFI boot service call * below, not merely before we enable the LAPIC. boot services re-enable * interrupts on return, so the firmware's timer is live across the window * where our IDT is installed but carries no handler for vector 0x20; a tick * there lands on the default gate and halts the kernel, at a point that * wanders with boot timing. observed roughly 1 run in 10 on smp=8. */ lapic_enable(); serial_puts("lapic: base = "); serial_put_hex(lapic_base()); serial_puts(", id = "); serial_put_hex(lapic_id()); serial_puts("\n"); /* * the boot services below can leave IF set on the way out, so re-assert it * here rather than trusting the state we handed them. */ cpu_cli(); /* * must happen before we install our own GDT/IDT: afterwards we must not call * UEFI boot services again, and we need GetMemoryMap to find a low page. */ if (!ap_boot_reserve(st, &trampoline_base)) { serial_puts("ERROR: ap trampoline reservation failed\n"); for (;;) { cpu_hlt(); } } gdt_init(); /* GDT + TSS + IST stacks */ idt_init(); /* IDT, IST1 on #DF and IST2 on NMI */ serial_puts("gdt + tss + ist + idt ok\n"); percpu_init(); serial_puts("percpu: gs load = "); gs_check = percpu_gs_selftest(0xfeedfacecafebeefull); serial_put_hex(gs_check); serial_puts(gs_check == 0xfeedfacecafebeefull ? " (ok)\n" : " (BROKEN)\n"); serial_puts("percpu: c load = "); gs_check = percpu_gs_selftest_via_c(0x1111222233334444ull); serial_put_hex(gs_check); serial_puts(gs_check == 0x1111222233334444ull ? " (ok)\n" : " (BROKEN)\n"); percpu_set_id(lapic_id()); test_self_ipi(); if (report_madt(st, &madt)) { /* * bring the APs up before the deliberate page fault below: that probe halts * the kernel, so it has to be the last thing that happens. * * interrupts are OFF for the duration. AP startup is driven entirely by * IPIs sent FROM this CPU, so nothing here needs an interrupt serviced, * and the spin that waits for an AP to report in is long enough (tens of * millions of iterations) for the timer to fire. nothing routes vector * 0x20 yet, so it lands on the default gate, which reports and halts, * which stopped the BSP mid-bringup, making an AP failure and a BSP death * indistinguishable from the outside. */ cpu_cli(); started = ap_boot_start(&madt); serial_puts("ap: "); serial_put_hex(started); serial_puts(" of "); serial_put_hex(acpi_madt_ap_count(&madt)); serial_puts(" aps started\n"); } cpu_cli(); serial_puts("probing unmapped addresses...\n"); count = (uint32_t)(sizeof(probe_addrs) / sizeof(probe_addrs[0])); for (i = 0; i < count; i++) { volatile uint64_t sink; serial_puts(" probing "); serial_put_hex(probe_addrs[i]); serial_puts(" ...\n"); sink = *(volatile uint64_t *)probe_addrs[i]; (void)sink; } serial_puts("ERROR: none of the probe addresses faulted\n"); /* do not return to firmware with our GDT/IDT installed */ for (;;) { cpu_hlt(); } }