When an AP starts, apboot32 loads the null IDT with

        mov     apboot_idt_ptr, %ebx
        lidt    (%ebx)

The first instruction loads the contents of apboot_idt_ptr, which is zero,
so lidt then loads the pseudo-descriptor from DS:0.  The AP is running with
the early per-CPU segment base of -KERNELBASE (0x40000000), so that is
linear address 0x40000000, i.e. physical address 0x40000000.

That physical address is RAM only when the guest has more than 1 GiB.  On a
two-CPU i386 PAE machine with 512 or 1024 MiB it is the start of the PCI
hole, the AP never retires lidt, and the BSP spins forever in the unbounded
"Waiting for AP 1" loop in mp_desc.c.  With 1280 MiB or more the read lands
in RAM and the AP happens to continue.

Load the address instead, matching the amd64 path:

        movl    $apboot_idt_ptr, %ebx
        lidt    (%ebx)

so that the null pseudo-descriptor (limit 0, base 0) is read from
apboot_idt_ptr itself.

Tested by booting a two-CPU i386 PAE QEMU/KVM guest at 512, 1024 and
1536 MiB: before this change the 512 and 1024 MiB guests hang in
"Waiting for AP 1", after it they reach userland with the AP online.  A
single-CPU i386 guest and two-CPU amd64 guests at the same sizes are
unaffected.  Only i386 PAE boot-to-userland was exercised; no long-running
behaviour was tested.
---
 i386/i386/cpuboot.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/i386/i386/cpuboot.S b/i386/i386/cpuboot.S
index fd3d6323..b939100a 100644
--- a/i386/i386/cpuboot.S
+++ b/i386/i386/cpuboot.S
@@ -214,7 +214,7 @@ apboot32:
        movw    %ax, %gs
 
        /* Load null Interrupt descriptor table */
-       mov     apboot_idt_ptr, %ebx
+       movl    $apboot_idt_ptr, %ebx
        lidt    (%ebx)
 
        /* Enable local apic in xAPIC mode */

Reply via email to