@@ -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;
}
Rather than assuming that PCI bus master devices can DMA directly into system memory, correctly model the way the hardware does it: * the host controller has three BARs (one I/O and two memory) which can be mapped into PCI memory space in the usual way * each of these BARs is an alias of an area of system memory whose base address is defined by the host controller's SMAP registers * PCI bus master devices see the PCI memory space, in the same way as everybody else Linux programs the BARs and SMAP registers in such a way that (for system RAM) you get the identity mapping. Note that since the QEMU PCI core doesn't forbid PCI devices from attempting to do bus master accesses to their own MMIO BARs it is possible for a malicious guest to configure the controller into a hall-of-mirrors mapping of the PCI and system memory spaces into each other such that an access would loop infinitely. This will probably result in infinite recursion in QEMU in render_memory_region()... Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/pci-host/versatile.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)