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