Hey all,
I’ve been trying to get Haiku running on an old Sony VAIO P netbook (Intel
Atom Z520, Intel US15W “Poulsbo” chipset — yeah, I know, ancient and
extremely fringe hardware) and ran into a chain of boot/USB problems that
took a while to untangle. Figured I’d write up what I found in case any of
it rings a bell for other hardware, or is worth turning into proper tickets.
Quick context: with ACPI on and no safe mode, the machine would either hang
before the boot screen finished lighting up its icons, or panic partway
through boot, or boot but never get USB working (which also broke
installing from a USB stick, since it never enumerated). Chasing each of
these one at a time turned up five separate bugs, roughly in the order I
hit them:
1. Boot loader hangs forever if a CPU from ACPI’s MADT never wakes up
src/system/boot/platform/bios_ia32/smp.cpp
This one’s a plain hang — no panic, the boot screen just never finishes
lighting up its icons. Turns out this machine’s ACPI MADT reports two
Local APIC entries, but there’s only one real CPU (it’s a single-core
Atom). smp_boot_other_cpus() sends the usual INIT-SIPI-SIPI sequence to
wake up “CPU 1” and then spins waiting for each step to finish — and
none of those waits have a timeout. Since the second CPU obviously never
answers, the boot loader just sits there forever.
I added bounded timeouts to all four wait loops in that function (INIT
assert/deassert delivery, STARTUP IPI delivery, and the final “AP cleared
its handoff slot” wait). If a CPU doesn’t respond in time, it gets dropped
and boot continues with whatever CPU count did respond, instead of hanging.
Shouldn’t affect any machine where SMP actually works normally — those
waits already resolve near-instantly there.
2. ACPICA: NULL pointer deref if the Global Lock SCI fires while its handler is being installed
src/add-ons/kernel/bus_managers/acpi/acpica/components/events/evglock.c
Once #1 was out of the way, boot got further and then hit PANIC: page fault, but interrupts were disabled, stack trace through
AcpiOsAcquireLock → AcpiEvGlobalLockHandler → AcpiEvFixedEventDetect
→ AcpiEvSciXruptHandler, touching address 0.
AcpiEvInitGlobalLockHandler() installs the Global Lock’s fixed event
handler before it creates AcpiGbl_GlobalLockPendingLock (that only
happens on the next line, after the install call returns). Apparently on
this hardware the GBL_STATUS bit is already set at that point in boot, so
the SCI fires synchronously during the install call, the handler runs
immediately, and it tries to lock something that doesn’t exist yet →
crash. I just swapped the order — create the lock first, then install the
handler — and cleaned up the two failure paths after that so they don’t
leak the lock. Not sure if this is already fixed in a newer ACPICA snapshot
than what I was on; might be worth checking against upstream Intel ACPICA.
3. ACPI PCI IRQ routing trusts edge/high-active from a Link Device, which isn’t legal for PCI
src/system/kernel/arch/generic/acpi_irq_routing_table.cpp
This was the sneaky one. USB devices had the right GSI, right vector,
correctly unmasked — but I dumped the actual IO-APIC redirection entry
(added a small ioapic KDL command for this, see below) and it was
configured edge-triggered / active-high. PCI interrupts are always
level/low per spec, no exceptions — but this machine’s DSDT describes one
of its PCI Link Devices using a plain ACPI IRQ resource that claims
edge/high, and choose_link_device_configurations() just copied that
straight into the redirection entry, no questions asked.
Fix was to stop trusting the descriptor for polarity/trigger and just force
level/low whenever we’re programming an interrupt that came from a _PRT
entry (which is unconditionally true for everything flowing through this
function) — same as the sibling code path already does for hardwired GSIs.
Feels like a legit general bug rather than a quirk of this one DSDT, since
nothing in the PCI spec allows what that Link Device was claiming.
4. PCI config space read/write refuses non-dword-aligned 4-byte access, breaks EHCI’s legacy handoff
src/add-ons/kernel/bus_managers/pci/pci.cpp
Got past #3, USB still didn’t work — PCI: can't read config for domain 0, 0:29:7, offset 226, size 4, then the EHCI driver logs “extended
capability is not a legacy support register” and fails to reset the
controller. The EHCI driver locates its legacy-support capability via
HCCPARAMS’s EECP field, which per the EHCI spec can be any byte offset —
it landed on 0xE2 here, which is 2-aligned but not 4-aligned.
PCI::ReadConfig/WriteConfig reject any 4-byte access that isn’t
dword-aligned, which is correct for the standard PCI capability list
(always dword-aligned by spec) but wrong for capability mechanisms with
looser rules, like this one. So the read just failed, the driver never saw
a valid capability, and skipped asking the BIOS to hand over the
controller.
Fixed by having both functions split a 4-byte access into two word
accesses when the offset is 2-aligned but not 4-aligned, instead of
rejecting it outright — checked against the actual x86 0xCF8/0xCFC I/O
port mechanism to make sure two word reads at offset and offset+2 actually
land on the right bytes (they do, via the existing + (offset & 3)
handling on the data port). Byte-misaligned access is still rejected, since
nothing can do that one. This feels like a general bus-manager
correctness fix too, not something specific to my hardware.
5. This specific chipset’s EHCI controller has an unassigned BAR0 and Haiku doesn’t fill it in
src/add-ons/kernel/bus_managers/pci/pci_fixup.cpp
Last one, and this one is hardware-specific. Even with #4 fixed, EHCI
still failed to reset. Turned out the PCI dump showed its BAR0 (the 1KB
MMIO range holding all its registers) as host 00000000 — the BIOS never
assigned it a real address. Every register access the driver made was
therefore hitting physical address 0. The sibling UHCI controllers (I/O
port based) were completely unaffected, only the memory-BAR one.
Haiku’s PCI bus manager doesn’t currently reassign/fill in resources the
BIOS left blank — I noticed there’s already a // TODO(bga): disabled until the PCI manager can assign new resources. sitting in
intel_fixup_ahci() in the same file for basically the same reason. So as
a stopgap I added a fixup matched specifically to this device ID (Intel
0x8086:0x8117, the Poulsbo EHCI controller) that fills in a fixed address
(0xf0000000, inside this machine’s own ACPI-declared, otherwise-empty PCI
memory aperture) only if BAR0 reads back as exactly 0. Obviously not a
real general fix — that’d be giving the PCI bus manager actual resource
allocation, which is apparently already wanted for other devices too — but
wanted to flag it in case someone wants to tackle that properly at some
point, and to explain why this specific workaround exists if anyone
stumbles on it.
Two smaller things I added alongside all this, not bugs by themselves:
- A
ioapic [gsi]KDL command inioapic.cppthat dumps IO-APIC
redirection table entries (mask/trigger/polarity/vector/IRR). This is
what let me actually find #3 without rebuilding a dozen times — figured
it’s generically useful enough to be worth keeping around. - A bounded retry (10s, 250ms steps) around
get_boot_partitions()in
vfs_boot.cppbefore it panics with “did not find any boot partitions!”
— my working theory going in was that a slow USB controller might not
have registered with the disk device manager yet by the time that check
runs. Turned out that wasn’t actually the root cause here (that was #4/#5
— USB wasn’t coming up at all), but seems like reasonable defensive
hardening regardless, so I left it in.
I hope my trial and error will help you!