From 122517435641384e4f5e36eaad8302ff273648e8 Mon Sep 17 00:00:00 2001
From: Wei Wang
Date: Wed, 26 Sep 2012 16:43:40 +0200
Subject: [PATCH] Add amd iommu emulation for Xen.
To passthrough amd southern islands series gpu to guest, a virtual iommu device must
be registered on qemu pci bus. It uses a new hypercall xc_domain_update_iommu_msi
to notify xen the msi vector of iommu.
Signed-off-by: Wei Wang
---
hw/i386/Makefile.objs | 2 +-
hw/pc_piix.c | 6 ++
hw/xen_iommu.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/xen_pt.h | 1 +
4 files changed, 199 insertions(+), 1 deletions(-)
create mode 100644 hw/xen_iommu.c
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 8c764bb..8b231ab 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -8,7 +8,7 @@ obj-y += pc_piix.o
obj-y += pc_sysfw.o
obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
-obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
+obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_iommu.o
obj-y += kvm/
obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index fd5898f..0b5d034 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -46,6 +46,9 @@
#ifdef CONFIG_XEN
# include
#endif
+#ifdef CONFIG_XEN_PCI_PASSTHROUGH
+# include "xen_pt.h"
+#endif
#define MAX_IDE_BUS 2
@@ -228,6 +231,9 @@ static void pc_init1(MemoryRegion *system_memory,
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
if (xen_enabled()) {
pci_create_simple(pci_bus, -1, "xen-platform");
+#ifdef CONFIG_XEN_PCI_PASSTHROUGH
+ xen_pt_iommu_create(pci_bus);
+#endif
}
/* init basic PC hardware */
diff --git a/hw/xen_iommu.c b/hw/xen_iommu.c
new file mode 100644
index 0000000..9a9ede4
--- /dev/null
+++ b/hw/xen_iommu.c
@@ -0,0 +1,191 @@
+/*
+ * amd iommu support
+ *
+ * Copyright (C) 2012 Advanced Micro Devices, Inc.
+ * Author: Wei Wang
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "xen_pt.h"
+#include "xen_backend.h"
+
+#pragma pack(1)
+
+typedef struct iommu_capability_block {
+ uint8_t id;
+ uint8_t next_ptr;
+ uint8_t cap_info;
+ uint8_t flags;
+ uint32_t base_low;
+ uint32_t base_high;
+ uint32_t range;
+ uint32_t misc;
+} iommu_capability_t;
+
+typedef struct msi_capability_block {
+ uint8_t id;
+ uint8_t next_ptr;
+ uint16_t msg_ctrl;
+ uint32_t addr_low;
+ uint32_t addr_high;
+ uint32_t msi_data;
+} msi_capability_t;
+
+struct amd_iommu_config {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t command;
+ uint16_t status;
+ uint8_t revision;
+ uint8_t api;
+ uint8_t subclass;
+ uint8_t class;
+ uint8_t cache_line_size;
+ uint8_t latency_timer;
+ uint8_t header_type;
+ uint8_t bist;
+ uint32_t base_address_regs[6];
+ uint32_t reserved1;
+ uint16_t subsystem_vendor_id;
+ uint16_t subsystem_id;
+ uint32_t rom_addr;
+ uint8_t cap_ptr;
+ uint8_t reserved3[3];
+ uint32_t reserved4;
+ uint8_t interrupt_line;
+ uint8_t interrupt_pin;
+ uint8_t min_gnt;
+ uint8_t max_lat;
+ iommu_capability_t cap;
+ msi_capability_t msi;
+};
+#pragma pack()
+
+#ifndef PCI_CAP_ID_SEC
+#define PCI_CAP_ID_SEC 0x0F
+#endif
+#define PCI_CLASS_SYSTEM_AMD_IOMMU 0x0806
+#define PCI_DEVICE_AMD_IOMMU_V2 0xFFFF
+#define IOMMU_CAP_FLAGS_IOTLB 0
+#define IOMMU_CAP_FLAGS_EFRSUP 3
+#define IOMMU_CAP_TYPE 0x3
+#define IOMMU_CAP_REV 0x1
+
+#define MSI_DATA_VECTOR_SHIFT 0
+#define MSI_DATA_DELIVERY_SHIFT 8
+#define MSI_DATA_LEVEL_SHIFT 14
+#define MSI_DATA_TRIGGER_SHIFT 15
+#define MSI_ADDR_DESTID_MASK 0xfff0000f
+#define MSI_ADDR_DESTMODE_SHIFT 2
+#define MSI_ADDR_REDIRECTION_SHIFT 3
+#define MSI_TARGET_CPU_SHIFT 12
+#define PCI_STATUS_CAPABILITIES 0x010
+
+static void amd_iommu_pci_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ struct amd_iommu_config *iommu_config;
+ uint64_t msi_addr;
+ uint32_t msi_data;
+ uint8_t offset_msi_data, vector, en;
+ uint8_t dest_mode, dest, delivery_mode, trig_mode;
+
+ pci_default_write_config(d, address, val, len);
+
+ iommu_config = (struct amd_iommu_config *)d->config;
+
+ offset_msi_data = iommu_config->cap.next_ptr + sizeof(uint32_t) +
+ sizeof(uint64_t);
+
+ if ( address == offset_msi_data )
+ {
+ msi_addr = (uint64_t)iommu_config->msi.addr_high << 32 |
+ iommu_config->msi.addr_low;
+ msi_data = val;
+ vector = msi_data & 0xFF;
+ en = iommu_config->msi.msg_ctrl & 0x1;
+
+ if ( !en )
+ return;
+
+ dest_mode = (msi_addr >> MSI_ADDR_DESTMODE_SHIFT) & 0x1;
+ dest = (msi_addr >> MSI_TARGET_CPU_SHIFT) & 0xff;
+ delivery_mode = (msi_data >> MSI_DATA_DELIVERY_SHIFT) & 0x7;
+ trig_mode = (msi_data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
+
+ xc_domain_update_iommu_msi(xen_xc, xen_domid, vector, dest,
+ dest_mode, delivery_mode, trig_mode);
+ }
+}
+
+static int amd_iommu_initfn(PCIDevice *d)
+{
+ struct amd_iommu_config *cfg;
+
+ cfg = (struct amd_iommu_config *)d->config;
+
+ cfg->status = PCI_STATUS_CAPABILITIES;
+ cfg->cap_ptr = sizeof(struct amd_iommu_config) -
+ sizeof(iommu_capability_t)- sizeof(msi_capability_t);
+
+ cfg->cap.id = PCI_CAP_ID_SEC;
+ cfg->cap.cap_info = IOMMU_CAP_REV << 3 | IOMMU_CAP_TYPE;
+
+ cfg->cap.flags |= 1 << IOMMU_CAP_FLAGS_IOTLB;
+ cfg->cap.flags |= 1 << IOMMU_CAP_FLAGS_EFRSUP;
+
+ cfg->cap.next_ptr = cfg->cap_ptr + sizeof(iommu_capability_t);
+ cfg->msi.id = PCI_CAP_ID_MSI;
+ cfg->msi.msg_ctrl = PCI_MSI_FLAGS_64BIT;
+
+ return 0;
+}
+
+static void xen_iommu_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = amd_iommu_initfn;
+ k->vendor_id = PCI_VENDOR_ID_AMD;
+ k->device_id = PCI_DEVICE_AMD_IOMMU_V2;
+ k->class_id = PCI_CLASS_SYSTEM_AMD_IOMMU;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_AMD;
+ k->subsystem_id = PCI_DEVICE_AMD_IOMMU_V2;
+ k->config_write = amd_iommu_pci_write_config;
+};
+
+typedef struct XenIommuState {
+ PCIDevice dev;
+} XenIommuState;
+
+static TypeInfo xen_iommu_info = {
+ .name = "xen-iommu",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(XenIommuState),
+ .class_init = xen_iommu_class_init,
+};
+
+static void xen_iommu_register_types(void)
+{
+ type_register_static(&xen_iommu_info);
+}
+
+type_init(xen_iommu_register_types)
+
+void xen_pt_iommu_create(PCIBus *pci_bus)
+{
+ char *path;
+ char *iommu;
+ struct xs_handle *xs = xs_open(0);
+
+ path = xs_get_domain_path(xs, xen_domid);
+ iommu = xenstore_read_str(path, "guest_iommu");
+
+ if ( !strcmp(iommu, "1") )
+ pci_create_simple(pci_bus, -1, "xen-iommu");
+
+ free(path);
+ xs_close(xs);
+}
diff --git a/hw/xen_pt.h b/hw/xen_pt.h
index 112477a..b54a0fb 100644
--- a/hw/xen_pt.h
+++ b/hw/xen_pt.h
@@ -291,6 +291,7 @@ void xen_pt_msix_delete(XenPCIPassthroughState *s);
int xen_pt_msix_update(XenPCIPassthroughState *s);
int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index);
void xen_pt_msix_disable(XenPCIPassthroughState *s);
+void xen_pt_iommu_create(PCIBus *pci_bus);
static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar)
{
--
1.7.4