If like me you went for a UEFI only kernel, I didn't go through the real -> protected -> long sequence initially. Instead I started in long mode already and just needed to set my own PML4.
While doing this, I started off by utilizing the NX/XD bit on all my pages except those I tag as executable. UEFI must enable the XD bit in the EFER for you because I didn't.
The fun comes when you attempt to bring up the APs, go through all the dance, then set the PML4 you know works only to discover your code is executing fine, but your stack is not!
My clue came from the error code on the page fault only to discover that the value was 0xB. One of the bits indicates that a reserved bit is set. After checking against the reserved bits, it wasn't set and I was thoroughly confused.
After much head scratching and rubber duck programming with ChatGPT, it kept telling me that bit 63 was reserved, and I told it back that this is where the NX/XD flag is set. It replied that this bit is only valid when NX/XD is enabled in the EFER.
I think it's worth signposting this in a few places:
- In the section on Page Fault error codes I would include a note in both the row for Reserved write, and the the list of conditions under which a page fault occurs.
- On the SMP page I would link to the page fault error codes section saying there is more info here if you are struggling with page faults.
- Finally a note on the Setting Up Long Mode about how the NX/XD bit may set by UEFI but it won't be set on the APs, with a link to the above, and perhaps a bit where you manipulate the EFER in the section on switching from protected mode.