On Mon, 30 May 2016 18:31:17 +0800 Peter Xu <pet...@redhat.com> wrote:
> To enable interrupt remapping for intel IOMMU device, each IOAPIC device > in the system reported via ACPI MADT must be explicitly enumerated under > one specific remapping hardware unit. This patch adds the root-complex > IOAPIC into the default DMAR device. > > Please refer to VT-d spec 8.3.1.1 for more information. > > Signed-off-by: Peter Xu <pet...@redhat.com> > --- > hw/i386/acpi-build.c | 17 +++++++++++++++-- > include/hw/acpi/acpi-defs.h | 15 +++++++++++++++ > include/hw/pci-host/q35.h | 9 +++++++++ > 3 files changed, 39 insertions(+), 2 deletions(-) > > diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c > index ddc6f16..6c572a3 100644 > --- a/hw/i386/acpi-build.c > +++ b/hw/i386/acpi-build.c > @@ -77,6 +77,9 @@ > #define ACPI_BUILD_DPRINTF(fmt, ...) > #endif > > +/* Default IOAPIC ID */ > +#define ACPI_BUILD_IOAPIC_ID 0x0 > + > typedef struct AcpiMcfgInfo { > uint64_t mcfg_base; > uint32_t mcfg_size; > @@ -375,7 +378,6 @@ build_madt(GArray *table_data, GArray *linker, > PCMachineState *pcms) > io_apic = acpi_data_push(table_data, sizeof *io_apic); > io_apic->type = ACPI_APIC_IO; > io_apic->length = sizeof(*io_apic); > -#define ACPI_BUILD_IOAPIC_ID 0x0 > io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; > io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); > io_apic->interrupt = cpu_to_le32(0); > @@ -2561,6 +2563,9 @@ build_dmar_q35(MachineState *ms, GArray *table_data, > GArray *linker) > AcpiTableDmar *dmar; > AcpiDmarHardwareUnit *drhd; > uint8_t dmar_flags = 0; > + AcpiDmarDeviceScope *scope = NULL; > + /* Root complex IOAPIC use one path[0] only */ > + uint16_t scope_size = sizeof(*scope) + sizeof(uint16_t); ioapic_scope_size sizeof(scope->path[0]) /* space for IOxAPIC path */ > if (ms->iommu_intr) { > /* enable INTR for the IOMMU device */ > @@ -2574,11 +2579,19 @@ build_dmar_q35(MachineState *ms, GArray *table_data, > GArray *linker) > /* DMAR Remapping Hardware Unit Definition structure */ > drhd = acpi_data_push(table_data, sizeof(*drhd)); sizeof(*drhd) + ioapic_scope_size > drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); > - drhd->length = cpu_to_le16(sizeof(*drhd)); /* No device scope now */ > + drhd->length = cpu_to_le16(sizeof(*drhd) + scope_size); > drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL; > drhd->pci_segment = cpu_to_le16(0); > drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); > > + /* Scope definition for the root-complex IOAPIC */ > + scope = acpi_data_push(table_data, scope_size); no need for this as previous push took care about all space we need for scope ioapic_scope = &drhd->scope[0]; > + scope->entry_type = cpu_to_le16(ACPI_DMAR_DEV_SCOPE_TYPE_IOAPIC); entry_type is 1 byte long, doing cpu_to_le16 on bigendian host will always result in 0 being written there > + scope->length = scope_size; > + scope->enumeration_id = cpu_to_le16(ACPI_BUILD_IOAPIC_ID); > + scope->bus = cpu_to_le16(Q35_PSEUDO_BUS_PLATFORM); ditto for above 2 fields > + scope->path[0] = cpu_to_le16(Q35_PSEUDO_DEVFN_IOAPIC); > + > build_header(linker, table_data, (void *)(table_data->data + dmar_start), > "DMAR", table_data->len - dmar_start, 1, NULL, NULL); > } > diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h > index 850a962..b46e472 100644 > --- a/include/hw/acpi/acpi-defs.h > +++ b/include/hw/acpi/acpi-defs.h > @@ -569,6 +569,20 @@ enum { > /* > * Sub-structures for DMAR > */ > + > +#define ACPI_DMAR_DEV_SCOPE_TYPE_IOAPIC (0x03) > + > +/* Device scope structure for DRHD. */ > +struct AcpiDmarDeviceScope { > + uint8_t entry_type; > + uint8_t length; > + uint16_t reserved; > + uint8_t enumeration_id; > + uint8_t bus; > + uint16_t path[0]; /* list of dev:func pairs */ > +} QEMU_PACKED; > +typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope; > + > /* Type 0: Hardware Unit Definition */ > struct AcpiDmarHardwareUnit { > uint16_t type; > @@ -577,6 +591,7 @@ struct AcpiDmarHardwareUnit { > uint8_t reserved; > uint16_t pci_segment; /* The PCI Segment associated with this unit */ > uint64_t address; /* Base address of remapping hardware register-set */ > + AcpiDmarDeviceScope scope[0]; > } QEMU_PACKED; > typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; > > diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h > index c5c073d..9afc221 100644 > --- a/include/hw/pci-host/q35.h > +++ b/include/hw/pci-host/q35.h > @@ -175,4 +175,13 @@ typedef struct Q35PCIHost { > > uint64_t mch_mcfg_base(void); > > +/* > + * Arbitary but unique BNF number for IOAPIC device. This is only > + * used when interrupt remapping is enabled. you encode it in DMAR unconditionally so not "only" > + * > + * TODO: make sure there would have no conflict with real PCI bus > + */ > +#define Q35_PSEUDO_BUS_PLATFORM (0xff) > +#define Q35_PSEUDO_DEVFN_IOAPIC (0x00) > + > #endif /* HW_Q35_H */