diff --git a/hw/pass-through.c b/hw/pass-through.c index 919937f..d6f35d8 100644 --- a/hw/pass-through.c +++ b/hw/pass-through.c @@ -4402,5 +4414,94 @@ int pt_init(PCIBus *e_bus) memset(&dpci_infos, 0, sizeof(struct dpci_infos)); dpci_infos.e_bus = e_bus; + amd_iommu_init(e_bus); return 0; } + +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 ( !vector || !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(xc_handle, domid, vector, dest, + dest_mode, delivery_mode, trig_mode); + } +} + +#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 + +extern int guest_iommu; +void amd_iommu_init(PCIBus *bus) +{ + PCIDevice *amd_iommu; + struct amd_iommu_config *cfg; + struct pt_dev *assigned_device = NULL; + + if ( !guest_iommu ) + return; + + amd_iommu = pci_register_device(bus, "AMD IOMMU v2", + sizeof(struct PCIDevice), -1, + NULL, amd_iommu_pci_write_config); + if ( amd_iommu == NULL ) + { + PT_LOG("Error: couldn't register amd iommu device\n"); + return; + } + + cfg = (struct amd_iommu_config *)amd_iommu->config; + + pci_config_set_vendor_id((uint8_t *)cfg, PCI_VENDOR_ID_AMD); + pci_config_set_device_id((uint8_t *)cfg, PCI_DEVICE_AMD_IOMMU_V2); + pci_config_set_class((uint8_t *)cfg, PCI_CLASS_SYSTEM_AMD_IOMMU); + + 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; +} + diff --git a/hw/pass-through.h b/hw/pass-through.h index 884139c..c089987 100644 --- a/hw/pass-through.h +++ b/hw/pass-through.h @@ -402,6 +402,58 @@ struct pt_reg_info_tbl { } u; }; +#pragma pack(1) +/* AMD IOMMU v2 support */ +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() + static inline pciaddr_t pt_pci_base_addr(pciaddr_t base) { if ( base & PCI_ADDRESS_SPACE_IO ) @@ -422,6 +474,6 @@ PCIBus *intel_pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, const char *name, uint16_t revision); void igd_pci_write(PCIDevice *pci_dev, uint32_t config_addr, uint32_t val, int len); uint32_t igd_pci_read(PCIDevice *pci_dev, uint32_t config_addr, int len); - +void amd_iommu_init(PCIBus *bus); #endif /* __PASSTHROUGH_H__ */ diff --git a/vl.c b/vl.c index be8587a..43a0111 100644 --- a/vl.c +++ b/vl.c @@ -217,6 +217,7 @@ int cirrus_vga_enabled = 1; int std_vga_enabled = 0; int vmsvga_enabled = 0; int gfx_passthru = 0; +int guest_iommu = 0; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; @@ -4295,6 +4296,7 @@ enum { QEMU_OPTION_acpi, QEMU_OPTION_vcpus, QEMU_OPTION_vcpu_avail, + QEMU_OPTION_guest_iommu, /* Debug/Expert options: */ QEMU_OPTION_serial, @@ -4468,6 +4470,7 @@ static const QEMUOption qemu_options[] = { { "vncunused", 0, QEMU_OPTION_vncunused }, { "vcpus", HAS_ARG, QEMU_OPTION_vcpus }, { "vcpu_avail", HAS_ARG, QEMU_OPTION_vcpu_avail }, + { "iommu", 0, QEMU_OPTION_guest_iommu}, #if defined(CONFIG_XEN) && !defined(CONFIG_DM) { "xen-domid", HAS_ARG, QEMU_OPTION_xen_domid }, { "xen-create", 0, QEMU_OPTION_xen_create }, @@ -5595,6 +5598,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_gfx_passthru: select_vgahw("passthrough"); break; + case QEMU_OPTION_guest_iommu: + guest_iommu = 1; + break; } } }