diff mbox

[V6,3/4] hw/i386: ACPI table for AMD IOMMU

Message ID 1456078260-6669-4-git-send-email-davidkiarie4@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

David Kiarie Feb. 21, 2016, 6:10 p.m. UTC
Add IVRS table for AMD IOMMU. Generate IVRS or DMAR
depending on emulated IOMMU

Signed-off-by: David Kiarie <davidkiarie4@gmail.com>
---
 hw/i386/acpi-build.c        | 208 +++++++++++++++++++++++++++++++++++++++++---
 include/hw/acpi/acpi-defs.h |  55 ++++++++++++
 2 files changed, 252 insertions(+), 11 deletions(-)

Comments

Jan Kiszka Feb. 21, 2016, 6:20 p.m. UTC | #1
On 2016-02-21 19:10, David Kiarie wrote:
> Add IVRS table for AMD IOMMU. Generate IVRS or DMAR
> depending on emulated IOMMU
> 
> Signed-off-by: David Kiarie <davidkiarie4@gmail.com>
> ---
>  hw/i386/acpi-build.c        | 208 +++++++++++++++++++++++++++++++++++++++++---
>  include/hw/acpi/acpi-defs.h |  55 ++++++++++++
>  2 files changed, 252 insertions(+), 11 deletions(-)
> 
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index 4554eb8..fa1310f 100644
> --- a/hw/i386/acpi-build.c
> +++ b/hw/i386/acpi-build.c
> @@ -51,6 +51,7 @@
>  #include "hw/pci/pci_bus.h"
>  #include "hw/pci-host/q35.h"
>  #include "hw/i386/intel_iommu.h"
> +#include "hw/i386/amd_iommu.h"
>  #include "hw/timer/hpet.h"
>  
>  #include "hw/acpi/aml-build.h"
> @@ -121,6 +122,12 @@ typedef struct AcpiBuildPciBusHotplugState {
>      bool pcihp_bridge_en;
>  } AcpiBuildPciBusHotplugState;
>  
> +typedef enum iommu_type {
> +    TYPE_AMD,
> +    TYPE_INTEL,
> +    TYPE_NONE
> +} iommu_type;
> +
>  static
>  int acpi_add_cpu_info(Object *o, void *opaque)
>  {
> @@ -2513,6 +2520,188 @@ build_dmar_q35(GArray *table_data, GArray *linker)
>                   "DMAR", table_data->len - dmar_start, 1, NULL, NULL);
>  }
>  
> +static void
> +build_amd_iommu(GArray *table_data, GArray *linker)
> +{
> +    int iommu_start = table_data->len;
> +    bool iommu_ambig;
> +
> +    AcpiAMDIOMMUIVRS *ivrs;
> +    AcpiAMDIOMMUHardwareUnit *iommu;
> +
> +    /* IVRS definition */
> +    ivrs = acpi_data_push(table_data, sizeof(*ivrs));
> +    ivrs->revision = cpu_to_le16(ACPI_IOMMU_IVRS_TYPE);
> +    ivrs->length = cpu_to_le16((sizeof(*ivrs) + sizeof(*iommu)));
> +    ivrs->v_common_info = cpu_to_le64(AMD_IOMMU_HOST_ADDRESS_WIDTH << 8);
> +
> +    AMDIOMMUState *s = (AMDIOMMUState *)object_resolve_path_type("",
> +                        TYPE_AMD_IOMMU_DEVICE, &iommu_ambig);
> +
> +    /* IVDB definition - type 10h */
> +    iommu = acpi_data_push(table_data, sizeof(*iommu));
> +    if (!iommu_ambig) {
> +        iommu->type = cpu_to_le16(0x10);
> +        /* IVHD flags */
> +        iommu->flags = cpu_to_le16(iommu->flags);
> +        iommu->flags = cpu_to_le16(IVHD_HT_TUNEN | IVHD_PPRSUP | IVHD_IOTLBSUP
> +                       | IVHD_PREFSUP);
> +        iommu->length = cpu_to_le16(sizeof(*iommu));
> +        iommu->device_id = cpu_to_le16(PCI_DEVICE_ID_RD890_IOMMU);
> +        iommu->capability_offset = cpu_to_le16(s->capab_offset);
> +        iommu->mmio_base = cpu_to_le64(s->mmio.addr);
> +        iommu->pci_segment = 0;
> +        iommu->interrupt_info = 0;
> +        /* EFR features */
> +        iommu->efr_register = cpu_to_le64(IVHD_EFR_GTSUP | IVHD_EFR_HATS
> +                              | IVHD_EFR_GATS);
> +        iommu->efr_register = cpu_to_le64(iommu->efr_register);
> +        /* device entries */
> +        memset(iommu->dev_entries, 0, 20);
> +        /* Add device flags here
> +         *  This is are 4-byte device entries currently reporting the range of
> +         *  devices 00h - ffffh; all devices
> +         *
> +         *  Device setting affecting all devices should be made here
> +         *
> +         *  Refer to
> +         *  (http://developer.amd.com/wordpress/media/2012/10/488821.pdf)
> +         *  5.2.2.1
> +         */
> +        iommu->dev_entries[12] = 3;
> +        iommu->dev_entries[16] = 4;
> +        iommu->dev_entries[17] = 0xff;
> +        iommu->dev_entries[18] = 0xff;
> +    }
> +
> +    build_header(linker, table_data, (void *)(table_data->data + iommu_start),
> +                 "IVRS", table_data->len - iommu_start, 1, NULL);
> +}
> +
> +static iommu_type has_iommu(void)
> +{
> +    bool ambiguous;
> +
> +    if (object_resolve_path_type("", TYPE_AMD_IOMMU_DEVICE, &ambiguous)
> +            && !ambiguous)
> +        return TYPE_AMD;
> +    else if (object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, &ambiguous)
> +            && !ambiguous)
> +        return TYPE_INTEL;
> +    else
> +        return TYPE_NONE;
> +}
> +
> +static void
> +build_dsdt(GArray *table_data, GArray *linker,
> +           AcpiPmInfo *pm, AcpiMiscInfo *misc)
> +{
> +    Aml *dsdt, *sb_scope, *scope, *dev, *method, *field;
> +    MachineState *machine = MACHINE(qdev_get_machine());
> +    uint32_t nr_mem = machine->ram_slots;
> +
> +    dsdt = init_aml_allocator();
> +
> +    /* Reserve space for header */
> +    acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
> +
> +    build_dbg_aml(dsdt);
> +    if (misc->is_piix4) {
> +        sb_scope = aml_scope("_SB");
> +        dev = aml_device("PCI0");
> +        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
> +        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
> +        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
> +        aml_append(sb_scope, dev);
> +        aml_append(dsdt, sb_scope);
> +
> +        build_hpet_aml(dsdt);
> +        build_piix4_pm(dsdt);
> +        build_piix4_isa_bridge(dsdt);
> +        build_isa_devices_aml(dsdt);
> +        build_piix4_pci_hotplug(dsdt);
> +        build_piix4_pci0_int(dsdt);
> +    } else {
> +        sb_scope = aml_scope("_SB");
> +        aml_append(sb_scope,
> +            aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x0c));
> +        aml_append(sb_scope,
> +            aml_operation_region("PCSB", AML_SYSTEM_IO, 0xae0c, 0x01));
> +        field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
> +        aml_append(field, aml_named_field("PCIB", 8));
> +        aml_append(sb_scope, field);
> +        aml_append(dsdt, sb_scope);
> +
> +        sb_scope = aml_scope("_SB");
> +        dev = aml_device("PCI0");
> +        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
> +        aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
> +        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
> +        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
> +        aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
> +        aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
> +        aml_append(dev, build_q35_osc_method());
> +        aml_append(sb_scope, dev);
> +        aml_append(dsdt, sb_scope);
> +
> +        build_hpet_aml(dsdt);
> +        build_q35_isa_bridge(dsdt);
> +        build_isa_devices_aml(dsdt);
> +        build_q35_pci0_int(dsdt);
> +    }
> +
> +    build_cpu_hotplug_aml(dsdt);
> +    build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base,
> +                             pm->mem_hp_io_len);
> +
> +    scope =  aml_scope("_GPE");
> +    {
> +        aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
> +
> +        aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED));
> +
> +        if (misc->is_piix4) {
> +            method = aml_method("_E01", 0, AML_NOTSERIALIZED);
> +            aml_append(method,
> +                aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
> +            aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
> +            aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
> +            aml_append(scope, method);
> +        } else {
> +            aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED));
> +        }
> +
> +        method = aml_method("_E02", 0, AML_NOTSERIALIZED);
> +        aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD));
> +        aml_append(scope, method);
> +
> +        method = aml_method("_E03", 0, AML_NOTSERIALIZED);
> +        aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH));
> +        aml_append(scope, method);
> +
> +        aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED));
> +        aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED));
> +    }
> +    aml_append(dsdt, scope);
> +
> +    /* copy AML table into ACPI tables blob and patch header there */
> +    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
> +    build_header(linker, table_data,
> +        (void *)(table_data->data + table_data->len - dsdt->buf->len),
> +        "DSDT", dsdt->buf->len, 1, NULL);

Grabbed an old version by accident? This one still cannot build over
current master (c3bce9d5f9).

In fact, there are more build error with this file. Please check
carefully before posting.

Jan
David Kiarie Feb. 21, 2016, 7 p.m. UTC | #2
On Sun, Feb 21, 2016 at 9:20 PM, Jan Kiszka <jan.kiszka@web.de> wrote:
> On 2016-02-21 19:10, David Kiarie wrote:
>> Add IVRS table for AMD IOMMU. Generate IVRS or DMAR
>> depending on emulated IOMMU
>>
>> Signed-off-by: David Kiarie <davidkiarie4@gmail.com>
>> ---
>>  hw/i386/acpi-build.c        | 208 +++++++++++++++++++++++++++++++++++++++++---
>>  include/hw/acpi/acpi-defs.h |  55 ++++++++++++
>>  2 files changed, 252 insertions(+), 11 deletions(-)
>>
>> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
>> index 4554eb8..fa1310f 100644
>> --- a/hw/i386/acpi-build.c
>> +++ b/hw/i386/acpi-build.c
>> @@ -51,6 +51,7 @@
>>  #include "hw/pci/pci_bus.h"
>>  #include "hw/pci-host/q35.h"
>>  #include "hw/i386/intel_iommu.h"
>> +#include "hw/i386/amd_iommu.h"
>>  #include "hw/timer/hpet.h"
>>
>>  #include "hw/acpi/aml-build.h"
>> @@ -121,6 +122,12 @@ typedef struct AcpiBuildPciBusHotplugState {
>>      bool pcihp_bridge_en;
>>  } AcpiBuildPciBusHotplugState;
>>
>> +typedef enum iommu_type {
>> +    TYPE_AMD,
>> +    TYPE_INTEL,
>> +    TYPE_NONE
>> +} iommu_type;
>> +
>>  static
>>  int acpi_add_cpu_info(Object *o, void *opaque)
>>  {
>> @@ -2513,6 +2520,188 @@ build_dmar_q35(GArray *table_data, GArray *linker)
>>                   "DMAR", table_data->len - dmar_start, 1, NULL, NULL);
>>  }
>>
>> +static void
>> +build_amd_iommu(GArray *table_data, GArray *linker)
>> +{
>> +    int iommu_start = table_data->len;
>> +    bool iommu_ambig;
>> +
>> +    AcpiAMDIOMMUIVRS *ivrs;
>> +    AcpiAMDIOMMUHardwareUnit *iommu;
>> +
>> +    /* IVRS definition */
>> +    ivrs = acpi_data_push(table_data, sizeof(*ivrs));
>> +    ivrs->revision = cpu_to_le16(ACPI_IOMMU_IVRS_TYPE);
>> +    ivrs->length = cpu_to_le16((sizeof(*ivrs) + sizeof(*iommu)));
>> +    ivrs->v_common_info = cpu_to_le64(AMD_IOMMU_HOST_ADDRESS_WIDTH << 8);
>> +
>> +    AMDIOMMUState *s = (AMDIOMMUState *)object_resolve_path_type("",
>> +                        TYPE_AMD_IOMMU_DEVICE, &iommu_ambig);
>> +
>> +    /* IVDB definition - type 10h */
>> +    iommu = acpi_data_push(table_data, sizeof(*iommu));
>> +    if (!iommu_ambig) {
>> +        iommu->type = cpu_to_le16(0x10);
>> +        /* IVHD flags */
>> +        iommu->flags = cpu_to_le16(iommu->flags);
>> +        iommu->flags = cpu_to_le16(IVHD_HT_TUNEN | IVHD_PPRSUP | IVHD_IOTLBSUP
>> +                       | IVHD_PREFSUP);
>> +        iommu->length = cpu_to_le16(sizeof(*iommu));
>> +        iommu->device_id = cpu_to_le16(PCI_DEVICE_ID_RD890_IOMMU);
>> +        iommu->capability_offset = cpu_to_le16(s->capab_offset);
>> +        iommu->mmio_base = cpu_to_le64(s->mmio.addr);
>> +        iommu->pci_segment = 0;
>> +        iommu->interrupt_info = 0;
>> +        /* EFR features */
>> +        iommu->efr_register = cpu_to_le64(IVHD_EFR_GTSUP | IVHD_EFR_HATS
>> +                              | IVHD_EFR_GATS);
>> +        iommu->efr_register = cpu_to_le64(iommu->efr_register);
>> +        /* device entries */
>> +        memset(iommu->dev_entries, 0, 20);
>> +        /* Add device flags here
>> +         *  This is are 4-byte device entries currently reporting the range of
>> +         *  devices 00h - ffffh; all devices
>> +         *
>> +         *  Device setting affecting all devices should be made here
>> +         *
>> +         *  Refer to
>> +         *  (http://developer.amd.com/wordpress/media/2012/10/488821.pdf)
>> +         *  5.2.2.1
>> +         */
>> +        iommu->dev_entries[12] = 3;
>> +        iommu->dev_entries[16] = 4;
>> +        iommu->dev_entries[17] = 0xff;
>> +        iommu->dev_entries[18] = 0xff;
>> +    }
>> +
>> +    build_header(linker, table_data, (void *)(table_data->data + iommu_start),
>> +                 "IVRS", table_data->len - iommu_start, 1, NULL);
>> +}
>> +
>> +static iommu_type has_iommu(void)
>> +{
>> +    bool ambiguous;
>> +
>> +    if (object_resolve_path_type("", TYPE_AMD_IOMMU_DEVICE, &ambiguous)
>> +            && !ambiguous)
>> +        return TYPE_AMD;
>> +    else if (object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, &ambiguous)
>> +            && !ambiguous)
>> +        return TYPE_INTEL;
>> +    else
>> +        return TYPE_NONE;
>> +}
>> +
>> +static void
>> +build_dsdt(GArray *table_data, GArray *linker,
>> +           AcpiPmInfo *pm, AcpiMiscInfo *misc)
>> +{
>> +    Aml *dsdt, *sb_scope, *scope, *dev, *method, *field;
>> +    MachineState *machine = MACHINE(qdev_get_machine());
>> +    uint32_t nr_mem = machine->ram_slots;
>> +
>> +    dsdt = init_aml_allocator();
>> +
>> +    /* Reserve space for header */
>> +    acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
>> +
>> +    build_dbg_aml(dsdt);
>> +    if (misc->is_piix4) {
>> +        sb_scope = aml_scope("_SB");
>> +        dev = aml_device("PCI0");
>> +        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
>> +        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
>> +        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
>> +        aml_append(sb_scope, dev);
>> +        aml_append(dsdt, sb_scope);
>> +
>> +        build_hpet_aml(dsdt);
>> +        build_piix4_pm(dsdt);
>> +        build_piix4_isa_bridge(dsdt);
>> +        build_isa_devices_aml(dsdt);
>> +        build_piix4_pci_hotplug(dsdt);
>> +        build_piix4_pci0_int(dsdt);
>> +    } else {
>> +        sb_scope = aml_scope("_SB");
>> +        aml_append(sb_scope,
>> +            aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x0c));
>> +        aml_append(sb_scope,
>> +            aml_operation_region("PCSB", AML_SYSTEM_IO, 0xae0c, 0x01));
>> +        field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
>> +        aml_append(field, aml_named_field("PCIB", 8));
>> +        aml_append(sb_scope, field);
>> +        aml_append(dsdt, sb_scope);
>> +
>> +        sb_scope = aml_scope("_SB");
>> +        dev = aml_device("PCI0");
>> +        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
>> +        aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
>> +        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
>> +        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
>> +        aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
>> +        aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
>> +        aml_append(dev, build_q35_osc_method());
>> +        aml_append(sb_scope, dev);
>> +        aml_append(dsdt, sb_scope);
>> +
>> +        build_hpet_aml(dsdt);
>> +        build_q35_isa_bridge(dsdt);
>> +        build_isa_devices_aml(dsdt);
>> +        build_q35_pci0_int(dsdt);
>> +    }
>> +
>> +    build_cpu_hotplug_aml(dsdt);
>> +    build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base,
>> +                             pm->mem_hp_io_len);
>> +
>> +    scope =  aml_scope("_GPE");
>> +    {
>> +        aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
>> +
>> +        aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED));
>> +
>> +        if (misc->is_piix4) {
>> +            method = aml_method("_E01", 0, AML_NOTSERIALIZED);
>> +            aml_append(method,
>> +                aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
>> +            aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
>> +            aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
>> +            aml_append(scope, method);
>> +        } else {
>> +            aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED));
>> +        }
>> +
>> +        method = aml_method("_E02", 0, AML_NOTSERIALIZED);
>> +        aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD));
>> +        aml_append(scope, method);
>> +
>> +        method = aml_method("_E03", 0, AML_NOTSERIALIZED);
>> +        aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH));
>> +        aml_append(scope, method);
>> +
>> +        aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED));
>> +        aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED));
>> +    }
>> +    aml_append(dsdt, scope);
>> +
>> +    /* copy AML table into ACPI tables blob and patch header there */
>> +    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
>> +    build_header(linker, table_data,
>> +        (void *)(table_data->data + table_data->len - dsdt->buf->len),
>> +        "DSDT", dsdt->buf->len, 1, NULL);
>
> Grabbed an old version by accident? This one still cannot build over
> current master (c3bce9d5f9).

heh, no, I sent the current version. Current workflow; Fix code, test
, rebase on master (and blow up everything), send.

Am replying with the correct patch, in case someone wants to test.

>
> In fact, there are more build error with this file. Please check
> carefully before posting.
>
> Jan
>
>
diff mbox

Patch

diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 4554eb8..fa1310f 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -51,6 +51,7 @@ 
 #include "hw/pci/pci_bus.h"
 #include "hw/pci-host/q35.h"
 #include "hw/i386/intel_iommu.h"
+#include "hw/i386/amd_iommu.h"
 #include "hw/timer/hpet.h"
 
 #include "hw/acpi/aml-build.h"
@@ -121,6 +122,12 @@  typedef struct AcpiBuildPciBusHotplugState {
     bool pcihp_bridge_en;
 } AcpiBuildPciBusHotplugState;
 
+typedef enum iommu_type {
+    TYPE_AMD,
+    TYPE_INTEL,
+    TYPE_NONE
+} iommu_type;
+
 static
 int acpi_add_cpu_info(Object *o, void *opaque)
 {
@@ -2513,6 +2520,188 @@  build_dmar_q35(GArray *table_data, GArray *linker)
                  "DMAR", table_data->len - dmar_start, 1, NULL, NULL);
 }
 
+static void
+build_amd_iommu(GArray *table_data, GArray *linker)
+{
+    int iommu_start = table_data->len;
+    bool iommu_ambig;
+
+    AcpiAMDIOMMUIVRS *ivrs;
+    AcpiAMDIOMMUHardwareUnit *iommu;
+
+    /* IVRS definition */
+    ivrs = acpi_data_push(table_data, sizeof(*ivrs));
+    ivrs->revision = cpu_to_le16(ACPI_IOMMU_IVRS_TYPE);
+    ivrs->length = cpu_to_le16((sizeof(*ivrs) + sizeof(*iommu)));
+    ivrs->v_common_info = cpu_to_le64(AMD_IOMMU_HOST_ADDRESS_WIDTH << 8);
+
+    AMDIOMMUState *s = (AMDIOMMUState *)object_resolve_path_type("",
+                        TYPE_AMD_IOMMU_DEVICE, &iommu_ambig);
+
+    /* IVDB definition - type 10h */
+    iommu = acpi_data_push(table_data, sizeof(*iommu));
+    if (!iommu_ambig) {
+        iommu->type = cpu_to_le16(0x10);
+        /* IVHD flags */
+        iommu->flags = cpu_to_le16(iommu->flags);
+        iommu->flags = cpu_to_le16(IVHD_HT_TUNEN | IVHD_PPRSUP | IVHD_IOTLBSUP
+                       | IVHD_PREFSUP);
+        iommu->length = cpu_to_le16(sizeof(*iommu));
+        iommu->device_id = cpu_to_le16(PCI_DEVICE_ID_RD890_IOMMU);
+        iommu->capability_offset = cpu_to_le16(s->capab_offset);
+        iommu->mmio_base = cpu_to_le64(s->mmio.addr);
+        iommu->pci_segment = 0;
+        iommu->interrupt_info = 0;
+        /* EFR features */
+        iommu->efr_register = cpu_to_le64(IVHD_EFR_GTSUP | IVHD_EFR_HATS
+                              | IVHD_EFR_GATS);
+        iommu->efr_register = cpu_to_le64(iommu->efr_register);
+        /* device entries */
+        memset(iommu->dev_entries, 0, 20);
+        /* Add device flags here
+         *  This is are 4-byte device entries currently reporting the range of
+         *  devices 00h - ffffh; all devices
+         *
+         *  Device setting affecting all devices should be made here
+         *
+         *  Refer to
+         *  (http://developer.amd.com/wordpress/media/2012/10/488821.pdf)
+         *  5.2.2.1
+         */
+        iommu->dev_entries[12] = 3;
+        iommu->dev_entries[16] = 4;
+        iommu->dev_entries[17] = 0xff;
+        iommu->dev_entries[18] = 0xff;
+    }
+
+    build_header(linker, table_data, (void *)(table_data->data + iommu_start),
+                 "IVRS", table_data->len - iommu_start, 1, NULL);
+}
+
+static iommu_type has_iommu(void)
+{
+    bool ambiguous;
+
+    if (object_resolve_path_type("", TYPE_AMD_IOMMU_DEVICE, &ambiguous)
+            && !ambiguous)
+        return TYPE_AMD;
+    else if (object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, &ambiguous)
+            && !ambiguous)
+        return TYPE_INTEL;
+    else
+        return TYPE_NONE;
+}
+
+static void
+build_dsdt(GArray *table_data, GArray *linker,
+           AcpiPmInfo *pm, AcpiMiscInfo *misc)
+{
+    Aml *dsdt, *sb_scope, *scope, *dev, *method, *field;
+    MachineState *machine = MACHINE(qdev_get_machine());
+    uint32_t nr_mem = machine->ram_slots;
+
+    dsdt = init_aml_allocator();
+
+    /* Reserve space for header */
+    acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
+
+    build_dbg_aml(dsdt);
+    if (misc->is_piix4) {
+        sb_scope = aml_scope("_SB");
+        dev = aml_device("PCI0");
+        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
+        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+        aml_append(sb_scope, dev);
+        aml_append(dsdt, sb_scope);
+
+        build_hpet_aml(dsdt);
+        build_piix4_pm(dsdt);
+        build_piix4_isa_bridge(dsdt);
+        build_isa_devices_aml(dsdt);
+        build_piix4_pci_hotplug(dsdt);
+        build_piix4_pci0_int(dsdt);
+    } else {
+        sb_scope = aml_scope("_SB");
+        aml_append(sb_scope,
+            aml_operation_region("PCST", AML_SYSTEM_IO, 0xae00, 0x0c));
+        aml_append(sb_scope,
+            aml_operation_region("PCSB", AML_SYSTEM_IO, 0xae0c, 0x01));
+        field = aml_field("PCSB", AML_ANY_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS);
+        aml_append(field, aml_named_field("PCIB", 8));
+        aml_append(sb_scope, field);
+        aml_append(dsdt, sb_scope);
+
+        sb_scope = aml_scope("_SB");
+        dev = aml_device("PCI0");
+        aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
+        aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
+        aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+        aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+        aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
+        aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
+        aml_append(dev, build_q35_osc_method());
+        aml_append(sb_scope, dev);
+        aml_append(dsdt, sb_scope);
+
+        build_hpet_aml(dsdt);
+        build_q35_isa_bridge(dsdt);
+        build_isa_devices_aml(dsdt);
+        build_q35_pci0_int(dsdt);
+    }
+
+    build_cpu_hotplug_aml(dsdt);
+    build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base,
+                             pm->mem_hp_io_len);
+
+    scope =  aml_scope("_GPE");
+    {
+        aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
+
+        aml_append(scope, aml_method("_L00", 0, AML_NOTSERIALIZED));
+
+        if (misc->is_piix4) {
+            method = aml_method("_E01", 0, AML_NOTSERIALIZED);
+            aml_append(method,
+                aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
+            aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
+            aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
+            aml_append(scope, method);
+        } else {
+            aml_append(scope, aml_method("_L01", 0, AML_NOTSERIALIZED));
+        }
+
+        method = aml_method("_E02", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_call0("\\_SB." CPU_SCAN_METHOD));
+        aml_append(scope, method);
+
+        method = aml_method("_E03", 0, AML_NOTSERIALIZED);
+        aml_append(method, aml_call0(MEMORY_HOTPLUG_HANDLER_PATH));
+        aml_append(scope, method);
+
+        aml_append(scope, aml_method("_L04", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L05", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L06", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L07", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L08", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L09", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0A", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0B", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0C", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0D", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0E", 0, AML_NOTSERIALIZED));
+        aml_append(scope, aml_method("_L0F", 0, AML_NOTSERIALIZED));
+    }
+    aml_append(dsdt, scope);
+
+    /* copy AML table into ACPI tables blob and patch header there */
+    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+    build_header(linker, table_data,
+        (void *)(table_data->data + table_data->len - dsdt->buf->len),
+        "DSDT", dsdt->buf->len, 1, NULL);
+    free_aml_allocator();
+}
+
 static GArray *
 build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
 {
@@ -2570,16 +2759,6 @@  static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
     return true;
 }
 
-static bool acpi_has_iommu(void)
-{
-    bool ambiguous;
-    Object *intel_iommu;
-
-    intel_iommu = object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE,
-                                           &ambiguous);
-    return intel_iommu && !ambiguous;
-}
-
 static bool acpi_has_nvdimm(void)
 {
     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
@@ -2600,6 +2779,7 @@  void acpi_build(AcpiBuildTables *tables)
     AcpiMcfgInfo mcfg;
     PcPciInfo pci;
     uint8_t *u;
+    iommu_type type = has_iommu();
     size_t aml_len = 0;
     GArray *tables_blob = tables->table_data;
     AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL };
@@ -2666,7 +2846,13 @@  void acpi_build(AcpiBuildTables *tables)
         acpi_add_table(table_offsets, tables_blob);
         build_mcfg_q35(tables_blob, tables->linker, &mcfg);
     }
-    if (acpi_has_iommu()) {
+
+    if (type == TYPE_AMD) {
+        acpi_add_table(table_offsets, tables_blob);
+        build_amd_iommu(tables_blob, tables->linker);
+    }
+
+    if (type == TYPE_INTEL) {
         acpi_add_table(table_offsets, tables_blob);
         build_dmar_q35(tables_blob, tables->linker);
     }
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index c7a03d4..a161358 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -570,4 +570,59 @@  typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit;
 /* Masks for Flags field above */
 #define ACPI_DMAR_INCLUDE_PCI_ALL   1
 
+/* IVRS constants */
+#define ACPI_IOMMU_HARDWAREUNIT_TYPE 0x10
+#define ACPI_IOMMU_IVRS_TYPE 0x1
+#define AMD_IOMMU_HOST_ADDRESS_WIDTH 39UL
+
+/* AMD IOMMU IVRS table */
+struct AcpiAMDIOMMUIVRS {
+    ACPI_TABLE_HEADER_DEF
+    uint32_t v_common_info; /* common virtualization information */
+    uint64_t reserved;      /* reserved                          */
+} QEMU_PACKED;
+typedef struct AcpiAMDIOMMUIVRS AcpiAMDIOMMUIVRS;
+
+/* flags in the IVHD headers */
+#define IVHD_HT_TUNEN    (1UL << 0)
+#define IVHD_PASS_PW     (1UL << 1)
+#define IVHD_RESPASS_PW  (1UL << 2)
+#define IVHD_ISOC        (1UL << 3)
+#define IVHD_IOTLBSUP    (1UL << 4)
+#define IVHD_COHERENT    (1UL << 5)
+#define IVHD_PREFSUP     (1UL << 6)
+#define IVHD_PPRSUP      (1UL << 7)
+
+/* features in the IVHD headers */
+#define IVHD_EFR_HATS       48
+#define IVHD_EFR_GATS       48
+#define IVHD_EFR_MSI_NUM
+#define IVHD_EFR_PNBANKS
+#define IVHD_EFR_PNCOUNTERS
+#define IVHD_EFR_PASMAX
+#define IVHD_EFR_HESUP  (1UL << 7)
+#define IVHD_EFR_GASUP  (1UL << 6)
+#define IVHD_EFR_IASUP  (1UL << 5)
+#define IVHD_EFR_GLXSUP (3UL << 3)
+#define IVHD_EFR_GTSUP  (1UL << 2)
+#define IVHD_EFR_NXSUP  (1UL << 1)
+#define IVHD_EFR_XTSUP  (1UL << 0)
+
+/* IVDB type 10h */
+struct AcpiAMDIOMMUHardwareUnit {
+    uint8_t type;
+    uint8_t flags;
+    uint16_t length;
+    uint16_t device_id;
+    uint16_t capability_offset;
+    uint64_t mmio_base;
+    uint16_t pci_segment;
+    uint16_t interrupt_info;
+    uint32_t features;
+    uint64_t efr_register;
+    uint64_t reserved;
+    uint8_t dev_entries[20];
+} QEMU_PACKED;
+typedef struct AcpiAMDIOMMUHardwareUnit AcpiAMDIOMMUHardwareUnit;
+
 #endif