Every one of these works under QEMU and stops a real machine:
- MADT processors that are "online capable" but not enabled were
counted as CPUs. AMD firmware lists absent cores that way (so does
QEMU with maxcpus), and startothers() then waited for ever for an
AP that does not exist. Only enabled entries count now, x2APIC
(type 9) entries are read too, and the wait is bounded to a second;
an AP that misses it is parked with INIT and the boot goes on.
- The PIT calibration loop had no bound. Recent boards clock-gate the
8254: the output never rises (a hang) or floats high (TSC "0 MHz"
and every clock thousands of times fast). The loop is bounded and
checked, and falls back to the ACPI PM timer, then CPUID 0x15/0x16.
Under QEMU pit=off the PM timer gives the same figures the PIT did.
- Firmware that hands over in x2APIC mode left lapic[] reading
garbage: cpunum() panicked or the timer never started. Each CPU is
switched back to xAPIC first (x2APIC -> disabled -> xAPIC).
- The IOAPIC was always at 0xFEC00000 and interrupts were aimed at
APIC ID "0". Both now come from the firmware's tables.
- INIT-to-STARTUP was 100 us ("Bochs is slow"); it is the 10 ms the
MP specification asks for.
- mouseinit() drained the 8042 with an unbounded loop. With no
controller the port reads 0xFF and the loop never ends. It now
notices there is no controller and skips the mouse.
Tested in QEMU: i440fx and q35, -smp 4, -smp 2,maxcpus=4, pit=off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HH7xukt4aaiGj1nQEAoQf1
59 lines
2.3 KiB
C
59 lines
2.3 KiB
C
// See MultiProcessor Specification Version 1.[14]
|
|
|
|
struct mp { // floating pointer
|
|
uchar signature[4]; // "_MP_"
|
|
uint32 physaddr; // 32-bit phys addr of MP config table
|
|
uchar length; // 1
|
|
uchar specrev; // [14]
|
|
uchar checksum; // all bytes must add up to 0
|
|
uchar type; // MP system config type
|
|
uchar imcrp;
|
|
uchar reserved[3];
|
|
};
|
|
|
|
struct mpconf { // configuration table header
|
|
uchar signature[4]; // "PCMP"
|
|
ushort length; // total table length
|
|
uchar version; // [14]
|
|
uchar checksum; // all bytes must add up to 0
|
|
uchar product[20]; // product id
|
|
uint32 oemtable_p; // OEM table pointer
|
|
ushort oemlength; // OEM table length
|
|
ushort entry; // entry count
|
|
uint32 lapicaddr_p; // address of local APIC
|
|
ushort xlength; // extended table length
|
|
uchar xchecksum; // extended table checksum
|
|
uchar reserved;
|
|
};
|
|
|
|
struct mpproc { // processor table entry
|
|
uchar type; // entry type (0)
|
|
uchar apicid; // local APIC id
|
|
uchar version; // local APIC verison
|
|
uchar flags; // CPU flags
|
|
#define MPBOOT 0x02 // This proc is the bootstrap processor.
|
|
uchar signature[4]; // CPU signature
|
|
uint feature; // feature flags from CPUID instruction
|
|
uchar reserved[8];
|
|
};
|
|
|
|
struct mpioapic { // I/O APIC table entry
|
|
uchar type; // entry type (2)
|
|
uchar apicno; // I/O APIC id
|
|
uchar version; // I/O APIC version
|
|
uchar flags; // I/O APIC flags
|
|
uint32 addr_p; // I/O APIC address
|
|
};
|
|
|
|
// Table entry types
|
|
#define MPPROC 0x00 // One per processor
|
|
#define MPBUS 0x01 // One per bus
|
|
#define MPIOAPIC 0x02 // One per I/O APIC
|
|
#define MPIOINTR 0x03 // One per bus interrupt source
|
|
#define MPLINTR 0x04 // One per system interrupt source
|
|
|
|
|
|
// The I/O APIC's registers, as the firmware described them. 0xFEC00000 is
|
|
// only where they usually are; see kernel/arch/x86_64/mp.c.
|
|
#define IOAPIC_DEFAULT_PHYS 0xFEC00000ULL
|
|
extern uint64 ioapic_phys;
|