fix: write compliant bit pattern for BAR sizing#37
fix: write compliant bit pattern for BAR sizing#37IsaacWoods merged 1 commit intorust-osdev:mainfrom
Conversation
According to the PCI spec [1], "[t]o determine how much address space a Function requires, system software should write a value of all 1's to each BAR register and then read the value back." QEMU (and possibly others) mask the provided address based on the size of the address space [2], which is always larger than 128 bytes for memory BARs, so the value of the last nibble has no effect. However, cloud-hypervisor (with possibly others) is more strict in its interpretation of the specification and check for exactly the all-bits-set pattern [3]. On the latter platforms, the current pattern can be erroneously interpreted as a BAR relocation instead of sizing. [1]: PCI Express Base Specification Revision 6.0, page 930 [2]: v10.1.2:hw/pci/pci.c:1658 [3]: v49.0:pci/src/configuration.rs:979
|
I don't have a copy of the pci spec (not worth it to pay for one and couldn't find any copies online, just a bunch of wiki pages, linux source code and some presentation slides), but I would have assumed that the writing all 1s to a BAR register would also change the BAR location on real hardware. Just to something outside of the range the parent bridge allows, hence effectively disabling the BAR until you write back the original value. I wouldn't expect there to be a special case for the literal all 1s value that keeps a shadow copy of the BAR value. |
|
Ah, that makes sense. We should definitely make sure to disable the address spaces before sizing the bar, but I think that is separate from this PR. This PR should be a strict improvement over the status quo by closer aligning the code with the specification and would still be required regardless of disabling address spaces. Writing back the value does happen here (unrelated to this PR): Line 312 in b35f742 Line 319 in b35f742 Line 326 in b35f742 |
|
Thanks for the PR. Looking at the spec (I have the PCI Express spec rev 5.0 v1.0; relevant section is 7.5.1.2.1), this is correct - we should be writing all-ones; I'm not sure why we were manually zeroing the bottom bits before. However, I think an implementation that misbehaves with The same section does mention disabling address decoding via the function's command register - I've interpreted this as meaning we should be clearing bit 1 of the command register before sizing the BAR and restoring it afterwards. I would be very happy to accept another PR to do so (or I'll try to remember to do so when I next work on this crate). |
|
Could you publish a new release containing this? :) |
|
Sure - published as |
According to the PCI spec (v6.0, p 930), "[t]o determine how much address space a Function requires, system software should write a value of all 1's to each BAR register and then read the value back." QEMU (and possibly others) mask the provided address based on the size of the address space [2], which is always larger than 128 bytes for memory BARs, so the value of the last nibble has no effect. However, cloud-hypervisor (with possibly others) is more strict in its interpretation of the specification and check for exactly the all-bits-set pattern [3]. On the latter platforms, the current pattern can be erroneously interpreted as a BAR relocation instead of sizing.