Map device registers with ioremap instead of assuming a low BAR
Every driver reached its registers with P2V(bar.base), which assumes the BAR sits inside the 0-4 GB the direct map covers. Under SeaBIOS it always does. Under OVMF it does not: firmware may place a 64-bit BAR anywhere, and it puts virtio-gpu's at about 768 GB. P2V produced an address with no translation behind it, so the driver's first register write took a page fault, then a double fault, then a triple fault and a reboot loop -- which, coming from a driver bind, reads as broken hardware rather than a missing mapping. ioremap() carries each region on a 1 GB page in PDPT entries 4-511, which were unused: the direct map occupies 0-3, and kpml4[0], kpml4[KERNBASE] and every process's PML4 all share that one table, so a mapping appears in every address space at once with no page table to allocate. The pages are cache-disabled and write-through for the same reason the top GB of the direct map is -- these are registers, and a write sitting in a cache line is a write the device never sees. A physical address below 4 GB returns the direct-map address unchanged, so the BIOS path, where every BAR is low, is byte for byte what it was. virtio-gpu now initialises under OVMF (1280x800, queue 64) instead of triple faulting. e1000 and xhci had the identical latent bug; xhci was already refusing to bind above 4 GB with a comment saying the mapping did not exist yet, and now it does. BIOS unchanged: bootcheck 108 [ok] with -Fb -Tty -Usb -Sock -Evdev -Ps, e1000 and xhci both binding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
6e23d2a466
commit
b768f5ded9
5 changed files with 106 additions and 12 deletions
|
|
@ -1219,18 +1219,17 @@ xhci_probe(struct pci_dev *d)
|
|||
if(d->bar[0].base == 0)
|
||||
return -1;
|
||||
|
||||
// The kernel's identity map only marks 3-4 GB uncacheable, and only covers
|
||||
// the first 4 GB at all. A BAR above that needs its own mapping, which does
|
||||
// not exist yet -- refuse rather than read garbage from a stale mapping.
|
||||
if(d->bar[0].base + d->bar[0].size > 0x100000000ULL){
|
||||
cprintf("xhci: BAR0 at 0x%p is above 4 GB and cannot be mapped yet\n",
|
||||
d->bar[0].base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
x = &xhcis[nxhci];
|
||||
x->pci = d;
|
||||
x->cap = (volatile uchar*)P2V(d->bar[0].base);
|
||||
// A BAR above 4 GB used to be refused here: the direct map covers only the
|
||||
// first 4 GB, so there was nowhere to put it. ioremap() gives it somewhere,
|
||||
// and returns the direct-map address unchanged when the BAR is low -- which
|
||||
// every BAR is under SeaBIOS.
|
||||
x->cap = (volatile uchar*)ioremap(d->bar[0].base, d->bar[0].size);
|
||||
if(x->cap == 0){
|
||||
cprintf("xhci: cannot map BAR0 at 0x%p\n", d->bar[0].base);
|
||||
return -1;
|
||||
}
|
||||
initlock(&x->lock, "xhci");
|
||||
|
||||
// Bus mastering is not optional: every structure below is read by the
|
||||
|
|
|
|||
Loading…
Reference in a new issue