2026-08-16
The asker is writing a second-stage bootloader in NASM that must transition the CPU from 32-bit protected mode into 64-bit long mode. The specific failure point: the block that enables paging — from mov eax, cr0 through mov cr0, eax — triple-faults QEMU and reboots the machine.
This is one of the most notoriously fiddly transitions in all of x86 osdev, because long mode has a strict activation sequence and any single mis-ordered step produces the same silent triple fault.
Why it's hard: Long mode activation is a three-way handshake between CR0, CR4, and the EFER MSR, and it must be performed in the correct order:
cr3 with the physical address of the PML4.CR4.PAE (bit 5) — mandatory, long mode uses PAE-style entries.EFER.LME (bit 8) via wrmsr to MSR 0xC0000080.CR0.PG (bit 31) — this is the atomic switch that activates long mode.L bit (bit 53) set to reach 64-bit code.Sketch of the diagnosis: The crash "on the paging block" almost always means one of:
mov cr0, eax. The next instruction fetch must succeed, so RIP must be covered by the map.Debugging approach I'd recommend to the asker: Run QEMU with -d int,cpu_reset -no-reboot -no-shutdown and attach GDB via -s -S. Single-step across the mov cr0, eax. The register dump on the triple fault reveals which invariant failed — a bad CR3, PAE off, or a fetch fault at RIP. Also verify the page tables by hand in the monitor with info mem after loading CR3 but before enabling PG.
Gotcha worth calling out: The far jump must immediately follow enabling PG — no data accesses in between — because your CS is still a 32-bit selector, and the CPU is now in a "compatibility" limbo where anything but that jump is undefined.
