The pci_bar_address() function calculates the start address of a given PCI BAR. Its main purpose is supporting MemoryRegion mapping / unmapping in pci_update_mappings(), which is guest-observable.
For that reason it contains heavy logic to enforce the validity of the BAR, and this logic includes checks for the PCI command register: - If the PCI_COMMAND_IO bit is clear in the command register, then all IO space BARs will be reported unmapped (all bits one), - If the PCI_COMMAND_MEMORY bit is clear in the command register, then all MMIO space BARs will be reported unmapped (all bits one). For an upcoming use case, we'd like pci_bar_address() to calculate the BAR address from the config space *regardless* of the command register. Add a new parameter that allows the caller to cut out the command register verification. All current callers preserve their behaviors. Cc: Marcel Apfelbaum <mar...@redhat.com> Cc: Michael S. Tsirkin <m...@redhat.com> Signed-off-by: Laszlo Ersek <ler...@redhat.com> --- include/hw/pci/pci.h | 2 ++ hw/pci/pci.c | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d44bc84..16b467b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -314,6 +314,8 @@ void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, MemoryRegion *io_lo, MemoryRegion *io_hi); void pci_unregister_vga(PCIDevice *pci_dev); pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num); +pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size, + bool respect_command); int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t offset, uint8_t size); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 750f3da..61a70de 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1054,15 +1054,19 @@ pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) return pci_dev->io_regions[region_num].addr; } -static pcibus_t pci_bar_address(PCIDevice *d, - int reg, uint8_t type, pcibus_t size) +pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size, + bool respect_command) { pcibus_t new_addr, last_addr; int bar = pci_bar(d, reg); - uint16_t cmd = pci_get_word(d->config + PCI_COMMAND); + uint16_t cmd; + + if (respect_command) { + cmd = pci_get_word(d->config + PCI_COMMAND); + } if (type & PCI_BASE_ADDRESS_SPACE_IO) { - if (!(cmd & PCI_COMMAND_IO)) { + if (respect_command && !(cmd & PCI_COMMAND_IO)) { return PCI_BAR_UNMAPPED; } new_addr = pci_get_long(d->config + bar) & ~(size - 1); @@ -1076,7 +1080,7 @@ static pcibus_t pci_bar_address(PCIDevice *d, return new_addr; } - if (!(cmd & PCI_COMMAND_MEMORY)) { + if (respect_command && !(cmd & PCI_COMMAND_MEMORY)) { return PCI_BAR_UNMAPPED; } if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { @@ -1134,7 +1138,7 @@ static void pci_update_mappings(PCIDevice *d) if (!r->size) continue; - new_addr = pci_bar_address(d, i, r->type, r->size); + new_addr = pci_bar_address(d, i, r->type, r->size, true); /* This bar isn't changed */ if (new_addr == r->addr) @@ -2427,7 +2431,7 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) !(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { continue; } - region_range.begin = pci_bar_address(dev, i, r->type, r->size); + region_range.begin = pci_bar_address(dev, i, r->type, r->size, true); region_range.end = region_range.begin + r->size; if (region_range.begin == PCI_BAR_UNMAPPED) { -- 1.8.3.1