boot: stop assuming the machine is an emulator
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
This commit is contained in:
parent
f1798e7443
commit
94c936ed98
7 changed files with 355 additions and 23 deletions
|
|
@ -386,9 +386,16 @@ kmain(uint32 fb_addr)
|
|||
}
|
||||
|
||||
// Other CPUs jump here from entryother.S.
|
||||
void lapic_xapic_mode(void);
|
||||
void lapic_park(uchar apicid);
|
||||
|
||||
void
|
||||
mpenter(void)
|
||||
{
|
||||
// Before anything reads the APIC -- seginit() and lapicinit() both do. An
|
||||
// AP keeps whatever mode the firmware left its APIC in across INIT, so the
|
||||
// boot processor having switched itself back says nothing about this one.
|
||||
lapic_xapic_mode();
|
||||
switchkvm();
|
||||
seginit();
|
||||
lapicinit();
|
||||
|
|
@ -500,9 +507,24 @@ startothers(void)
|
|||
|
||||
lapicstartap(c->apicid, V2P(code));
|
||||
|
||||
// wait for cpu to finish mpmain()
|
||||
while(c->started == 0)
|
||||
// Wait for it to finish mpmain() -- for a second, not for ever.
|
||||
//
|
||||
// This had no bound, so one processor that did not answer stopped the
|
||||
// machine here for good, after the last line of hardware detection and
|
||||
// before anything else. A processor the firmware listed that is not
|
||||
// there, one that is disabled in setup, or one that simply fails to start
|
||||
// is a machine running on fewer CPUs, not a machine that cannot boot.
|
||||
// ncpu_online below counts only the ones that answered, and procfs
|
||||
// already reports the difference.
|
||||
uint64 deadline = rdtsc() + tsc_hz;
|
||||
while(c->started == 0 && rdtsc() < deadline)
|
||||
;
|
||||
if(c->started == 0){
|
||||
cprintf("smp: cpu with apic id %d did not start; continuing without it\n",
|
||||
c->apicid);
|
||||
lapic_park(c->apicid);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Counted here, on the boot processor, after the AP has actually reported
|
||||
// in -- not by the AP itself. This is serialised by the loop, so it needs
|
||||
|
|
|
|||
Loading…
Reference in a new issue