kernel: boot on AMD — SYSRET puts the RPL in the STAR base
A Ryzen 3 3200G triple-faulted on its first timer tick after reaching init. Three defects in a chain, each hiding the one beneath it. STAR's SYSRET base was 0x10, so SS came back as base+8 = 0x18 with RPL 0 while CS carried RPL 3. Intel ORs RPL 3 into SS on SYSRET; AMD only does so for CS. Ring 3 ran fine — RPL is not checked on data access — and died the moment an interrupt tried to IRETQ back, where SS.RPL must equal CS.RPL. The base now carries the RPL (0x13), as Linux does. Two fixes below it, both of which made the first one unreadable: scheduler() read IA32_GS_BASE and dereferenced it without testing for zero, so every fault reporter faulted in turn — a panic inside a panic, and the machine reset before printing anything. Cast after the null test, plus a re-entrancy guard in the panic handler. NT is now masked in SFMASK alongside the rest, and isr.s exports isr_return_iretq at the faulting instruction so a frame dump can say which IRETQ died and print the CS/SS it was about to load. That dump is what identified the RPL mismatch.
This commit is contained in:
@@ -518,4 +518,13 @@ isr_smap_patch:
|
||||
testb $3, 8(%rsp)
|
||||
jz 1f
|
||||
swapgs
|
||||
1: iretq
|
||||
# Named so a fault reporter can recognise its own return instruction. A #GP
|
||||
# here is the frame's fault, not this code's — the five words below RSP are
|
||||
# what the CPU rejected, and they are the only evidence of why. Note that on
|
||||
# the ring-3 path the swapgs above has already run, so a fault at this exact
|
||||
# address re-enters the kernel with the *user's* GS base: per-CPU reads in
|
||||
# that handler are reading user-controlled state and must not be trusted.
|
||||
1:
|
||||
.global isr_return_iretq
|
||||
isr_return_iretq:
|
||||
iretq
|
||||
|
||||
Reference in New Issue
Block a user