pci_ahci_init() walks all MAX_PORTS port nodes and skips the ones that
are not configured, so the loop always runs to MAX_PORTS and the
"sc->ports = p" after it is always 32, whatever the configuration.
Before the configuration refactor in 2019 (621b5090487d) the loop
stopped when the option string ran out, so sc->ports was the number of
configured ports, raised to DEF_PORTS (6) at minimum.
As a result every controller advertises CAP.NP = 31 (a guest prints
"AHCI v1.30 with 32 6Gbps ports"), maps a BAR 5 of 0x100 + 32 * 0x80 =
0x1100 bytes, which is rounded up to 8 KiB, and asks for the maximum 16
MSI vectors. PI has only the bits of the configured ports, so guests
never touch the others; the effect is cosmetic and a waste of address
space, not a functional failure.
Remember the highest configured port in a local variable and use it for
sc->ports, keeping the minimum of DEF_PORTS. A controller with one disk
now reports 6 ports, a 1 KiB BAR and 8 MSI vectors. A sparse
configuration still works: with only port 3 configured, ports 0 to 3
exist and the count is raised to 6. The two error paths keep assigning
the port index, which the cleanup loop uses.
ports and cap are saved in the snapshot, so a guest restored from a
checkpoint taken before this change gets the old values back (32 ports
in CAP). The BAR size is chosen at initialization and is now smaller
than the old mapping, but the guest only touches the ports listed in PI,
which lie inside both. Snapshot support is experimental anyway.
I found this while working on a read-only register page for the AHCI
BAR in keelOS, a hypervisor built on bhyve: a 4 KiB page could not hold
the port registers because the controller of a VM with a single disk
reported 32 ports.
Signed-off-by: Wanpeng Qian <wanpengqian@gmail.com>
Sponsored by: keelos.dev