diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c
index 9238d39..37c7425 100644
--- a/hw/pci-host/versatile.c
+++ b/hw/pci-host/versatile.c
@@ -75,11 +75,19 @@ typedef struct {
     /* Containers representing the PCI address spaces */
     MemoryRegion pci_io_space;
     MemoryRegion pci_mem_space;
+    /* AddressSpace corresponding to pci_mem_space */
+    AddressSpace pci_mem_as;
     /* Alias regions into PCI address spaces which we expose as sysbus regions.
      * The offsets into pci_mem_space are controlled by the imap registers.
      */
     MemoryRegion pci_io_window;
     MemoryRegion pci_mem_window[3];
+    /* Alias regions into system address space which we map into PCI
+     * memory space under the control of the SMAP registers.
+     * Note that which one of the BARs is the I/O bar differs for
+     * realview and versatile.
+     */
+    MemoryRegion pci_bar[3];
     PCIBus pci_bus;
     PCIDevice pci_dev;
 
@@ -127,10 +135,38 @@ static void pci_vpb_update_all_windows(PCIVPBState *s)
     }
 }
 
+static void pci_vpb_update_smap(PCIVPBState *s, int i)
+{
+    /* After a change to the SMAP registers, we need to update
+     * what part of the system address space we're aliasing
+     * into the PCI memory or IO space. (This is the inverse of
+     * the windows controlled by the IMAP registers, which
+     * map parts of PCI space into system space.)
+     */
+    hwaddr offset;
+
+    if (s->realview) {
+        offset = s->smap[i] & 0xf0000000;
+    } else {
+        offset = s->smap[i] << 28;
+    }
+    memory_region_set_alias_offset(&s->pci_bar[i], offset);
+}
+
+static void pci_vpb_update_all_smaps(PCIVPBState *s)
+{
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        pci_vpb_update_smap(s, i);
+    }
+}
+
 static int pci_vpb_post_load(void *opaque, int version_id)
 {
     PCIVPBState *s = opaque;
     pci_vpb_update_all_windows(s);
+    pci_vpb_update_all_smaps(s);
     return 0;
 }
 
@@ -195,6 +231,7 @@ static void pci_vpb_reg_write(void *opaque, hwaddr addr,
     {
         int win = (addr - PCI_SMAP0) >> 2;
         s->smap[win] = val;
+        pci_vpb_update_smap(s, win);
         break;
     }
     default:
@@ -376,6 +413,13 @@ static void pci_vpb_reset(DeviceState *d)
     pci_vpb_update_all_windows(s);
 }
 
+static AddressSpace *pci_vpb_dma_iommu(PCIBus *bus, void *opaque, int devfn)
+{
+    PCIVPBState *s = opaque;
+
+    return &s->pci_mem_as;
+}
+
 static void pci_vpb_init(Object *obj)
 {
     PCIHostState *h = PCI_HOST_BRIDGE(obj);
@@ -383,12 +427,15 @@ static void pci_vpb_init(Object *obj)
 
     memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32);
     memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32);
+    address_space_init(&s->pci_mem_as, &s->pci_mem_space, "pci_mem");
 
     pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), "pci",
                         &s->pci_mem_space, &s->pci_io_space,
                         PCI_DEVFN(11, 0), TYPE_PCI_BUS);
     h->bus = &s->pci_bus;
 
+    pci_setup_iommu(&s->pci_bus, pci_vpb_dma_iommu, s);
+
     object_initialize(&s->pci_dev, TYPE_VERSATILE_PCI_HOST);
     qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus));
 
@@ -458,9 +505,34 @@ static void pci_vpb_realize(DeviceState *dev, Error **errp)
 
 static int versatile_pci_host_init(PCIDevice *d)
 {
+    int i;
+    PCIVPBState *s = container_of(d->bus, PCIVPBState, pci_bus);
+    int iobar = s->realview ? 2 : 0;
+
     pci_set_word(d->config + PCI_STATUS,
                  PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
     pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
+
+    /* The host allows its MMIO BARs to be mapped at PCI address zero
+     * (even though the PCI spec says 0 isn't a valid address), and
+     * Linux relies on this for PCI bus-mastering DMA to work. So we
+     * have to emulate this out-of-spec behaviour.
+     */
+    d->cap_present |= QEMU_PCI_ADDR0_ALLOWED;
+
+    /* Create alias regions corresponding to our own BARs */
+    for (i = 0; i < 3; i++) {
+        uint8_t bar_type = PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+        memory_region_init_alias(&s->pci_bar[i], OBJECT(s),
+                                 "pci-vpb-bar0", get_system_memory(), 0,
+                                 0x10000000);
+        if (i == iobar) {
+            bar_type = PCI_BASE_ADDRESS_SPACE_IO;
+        }
+        pci_register_bar(&s->pci_dev, i, bar_type, &s->pci_bar[i]);
+    }
+
     return 0;
 }
 
