summaryrefslogtreecommitdiff
path: root/hw/pci.c
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2009-11-12 13:47:17 +0200
committerMichael S. Tsirkin <mst@redhat.com>2009-12-01 17:52:35 +0200
commit876a350d3daed88c5677397c287c6210cf163f6c (patch)
tree4c57a0a523c9b052dc17011666fb4cce750e39c0 /hw/pci.c
parentec50344230aad67a867ac59e4a0cd7233149484c (diff)
downloadqemu-876a350d3daed88c5677397c287c6210cf163f6c.zip
pci: split up up pci_update mappings
Split bar address math into a separate function. In particular, this gets rid of an ugly forward goto into scope that we have there. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: Isaku Yamahata <yamahata@valinux.co.jp>
Diffstat (limited to 'hw/pci.c')
-rw-r--r--hw/pci.c125
1 files changed, 68 insertions, 57 deletions
diff --git a/hw/pci.c b/hw/pci.c
index d6f647cc25..49d06489e0 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -721,14 +721,77 @@ static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
}
}
+static pcibus_t pci_bar_address(PCIDevice *d,
+ int reg, uint8_t type, pcibus_t size)
+{
+ pcibus_t new_addr, last_addr;
+ int bar = pci_bar(d, reg);
+ uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ if (!(cmd & PCI_COMMAND_IO)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ new_addr = pci_get_long(d->config + bar) & ~(size - 1);
+ last_addr = new_addr + size - 1;
+ /* NOTE: we have only 64K ioports on PC */
+ if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+ return new_addr;
+ }
+
+ if (!(cmd & PCI_COMMAND_MEMORY)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ new_addr = pci_get_quad(d->config + bar);
+ } else {
+ new_addr = pci_get_long(d->config + bar);
+ }
+ /* the ROM slot has a specific enable bit */
+ if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ new_addr &= ~(size - 1);
+ last_addr = new_addr + size - 1;
+ /* NOTE: we do not support wrapping */
+ /* XXX: as we cannot support really dynamic
+ mappings, we handle specific values as invalid
+ mappings. */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr == PCI_BAR_UNMAPPED) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ /* Now pcibus_t is 64bit.
+ * Check if 32 bit BAR wraps around explicitly.
+ * Without this, PC ide doesn't work well.
+ * TODO: remove this work around.
+ */
+ if (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ /*
+ * OS is allowed to set BAR beyond its addressable
+ * bits. For example, 32 bit OS can set 64bit bar
+ * to >4G. Check it. TODO: we might need to support
+ * it in the future for e.g. PAE.
+ */
+ if (last_addr >= TARGET_PHYS_ADDR_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ return new_addr;
+}
+
static void pci_update_mappings(PCIDevice *d)
{
PCIIORegion *r;
- int cmd, i;
- pcibus_t last_addr, new_addr;
- pcibus_t filtered_size;
+ int i;
+ pcibus_t new_addr, filtered_size;
- cmd = pci_get_word(d->config + PCI_COMMAND);
for(i = 0; i < PCI_NUM_REGIONS; i++) {
r = &d->io_regions[i];
@@ -736,59 +799,7 @@ static void pci_update_mappings(PCIDevice *d)
if (!r->size)
continue;
- if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- if (cmd & PCI_COMMAND_IO) {
- new_addr = pci_get_long(d->config + pci_bar(d, i));
- new_addr = new_addr & ~(r->size - 1);
- last_addr = new_addr + r->size - 1;
- /* NOTE: we have only 64K ioports on PC */
- if (last_addr <= new_addr || new_addr == 0 ||
- last_addr >= 0x10000) {
- new_addr = PCI_BAR_UNMAPPED;
- }
- } else {
- new_addr = PCI_BAR_UNMAPPED;
- }
- } else {
- if (cmd & PCI_COMMAND_MEMORY) {
- if (r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
- new_addr = pci_get_quad(d->config + pci_bar(d, i));
- } else {
- new_addr = pci_get_long(d->config + pci_bar(d, i));
- }
- /* the ROM slot has a specific enable bit */
- if (i == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE))
- goto no_mem_map;
- new_addr = new_addr & ~(r->size - 1);
- last_addr = new_addr + r->size - 1;
- /* NOTE: we do not support wrapping */
- /* XXX: as we cannot support really dynamic
- mappings, we handle specific values as invalid
- mappings. */
- if (last_addr <= new_addr || new_addr == 0 ||
- last_addr == PCI_BAR_UNMAPPED ||
-
- /* Now pcibus_t is 64bit.
- * Check if 32 bit BAR wrap around explicitly.
- * Without this, PC ide doesn't work well.
- * TODO: remove this work around.
- */
- (!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) &&
- last_addr >= UINT32_MAX) ||
-
- /*
- * OS is allowed to set BAR beyond its addressable
- * bits. For example, 32 bit OS can set 64bit bar
- * to >4G. Check it.
- */
- last_addr >= TARGET_PHYS_ADDR_MAX) {
- new_addr = PCI_BAR_UNMAPPED;
- }
- } else {
- no_mem_map:
- new_addr = PCI_BAR_UNMAPPED;
- }
- }
+ new_addr = pci_bar_address(d, i, r->type, r->size);
/* bridge filtering */
filtered_size = r->size;