boot: the loader keeps reporting after ExitBootServices

"Leaving the firmware, and the bar is full" -- so on the laptop
ExitBootServices was reached, and everything after it was silent. die() draws
on screen, so a failed ExitBootServices would have said so; it did not, which
means the firmware call succeeded and the machine stopped somewhere in the
handful of instructions between there and the kernel's first.

That window had no diagnostics because of a claim in a comment: "nothing below
can report anything". Half true. ConOut is gone, but the *framebuffer* is not
-- it is memory, the firmware has stopped using it, and ui_progress_label() is
a write to it and nothing more. So the window is now split: "Starting the
kernel" is drawn after the page tables are built and immediately before the
jump. A machine that stops on "Leaving the firmware" stopped in
ExitBootServices or the page-table build; one that stops on "Starting the
kernel" stopped in the jump or in the kernel itself, before it clears the
screen.

**And one speculative change, reverted.** On the theory that hardcoded low
memory is a poor assumption under UEFI -- the firmware is entitled to have
anything at 0x1000 -- the page tables were moved to pages asked of
AllocatePages. It cost two triple faults: cr3 still loaded the constant, and
then the PML4 entries still pointed at the constant PDPT. Both were fixed, and
it still reboot-looped, which is when the actual objection turned up:

    kernel/arch/x86_64/entry.S:85   # bootstrap page tables (0x1000 - 0x3000)
    kernel/arch/x86_64/entry.S:111  mov $0x1000, %eax
                                    mov %eax, %cr3

Those addresses are a *contract between the loader and the kernel's assembly*,
not this file's private choice. Moving them in one of the two places was never
going to work, and the theory that started it had no evidence behind it -- no
report says 0x1000 is the laptop's problem. So it is reverted, with the
contract written down where the constants are, and if it ever does move it
moves in both files at once.

Both supported configurations: one boot each, no traps, no panics, socktest
and memfdtest green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-23 20:18:34 +02:00
commit 3eb2e97f7b

View file

@ -41,6 +41,14 @@
// Page tables are built here, exactly where mboot_entry builds them, so the // Page tables are built here, exactly where mboot_entry builds them, so the
// kernel's entry sees the state it was written for. // kernel's entry sees the state it was written for.
//
// **These are a contract, not this file's choice.** kernel/arch/x86_64/entry.S
// builds its own bootstrap tables at the same two addresses and loads cr3 with
// 0x1000 by name. Moving them here alone was tried, on the theory that
// hardcoded low memory is a poor assumption under UEFI: the machine
// triple-faulted one instruction after the jump, twice, for two different
// reasons -- and would have needed the kernel's assembly to agree even once
// both were fixed. If it ever does move, it moves in both files at once.
#define PML4_ADDR 0x1000 #define PML4_ADDR 0x1000
#define PDPT_ADDR 0x2000 #define PDPT_ADDR 0x2000
@ -1751,11 +1759,12 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
// ---- no firmware services exist past this line ---- // ---- no firmware services exist past this line ----
// //
// Nothing below can report anything: ConOut is gone, and ui_progress_label // ConOut is gone, but the *framebuffer* is not: it is memory, the firmware
// would be drawing into a framebuffer the firmware may no longer own. The // has stopped using it, and ui_progress_label() is a write to it and
// last label on screen is therefore "Leaving the firmware", and a machine // nothing more. That distinction is worth making rather than assuming --
// stopped on that one stopped somewhere in the handful of instructions // an earlier version of this comment claimed nothing could report from
// between here and the kernel's first. // here, and that claim cost a round trip to a laptop and back to learn
// which of three steps a machine had stopped in.
__asm__ volatile("cli"); __asm__ volatile("cli");
@ -1776,6 +1785,8 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
for (int i = 0; i < 4; i++) // 4 GB in 1 GB pages for (int i = 0; i < 4; i++) // 4 GB in 1 GB pages
pdpt[i] = ((UINT64)i << 30) | PTE_P | PTE_PS | PTE_W; pdpt[i] = ((UINT64)i << 30) | PTE_P | PTE_PS | PTE_W;
ui_progress_label("Starting the kernel");
// RBX carries the framebuffer address into kmain, and must never be 1 -- // RBX carries the framebuffer address into kmain, and must never be 1 --
// that value marks an application processor in entry64high. A physical // that value marks an application processor in entry64high. A physical
// MMIO address or zero, so it cannot collide. // MMIO address or zero, so it cannot collide.