From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Michael S. Tsirkin" Subject: Re: [PATCH 2/3] AMD IOMMU support Date: Sun, 6 Feb 2011 13:47:57 +0200 Message-ID: <20110206114757.GF26242@redhat.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: kvm@vger.kernel.org, joro@8bytes.org, seabios@seabios.org, qemu-devel@nongnu.org, blauwirbel@gmail.com, yamahata@valinux.co.jp, av1474@comtv.ru, avi@redhat.com, paul@codesourcery.com To: Eduard - Gabriel Munteanu Return-path: Content-Disposition: inline In-Reply-To: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: seabios-bounces+gcbcs-seabios=m.gmane.org@seabios.org Sender: seabios-bounces+gcbcs-seabios=m.gmane.org@seabios.org List-Id: kvm.vger.kernel.org On Fri, Feb 04, 2011 at 01:24:14AM +0200, Eduard - Gabriel Munteanu wrote: > This initializes the AMD IOMMU and creates ACPI tables for it. > > Signed-off-by: Eduard - Gabriel Munteanu > --- > src/acpi.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > src/config.h | 3 ++ > src/pci_ids.h | 1 + > src/pci_regs.h | 1 + > src/pciinit.c | 29 +++++++++++++++++++ > 5 files changed, 118 insertions(+), 0 deletions(-) > > diff --git a/src/acpi.c b/src/acpi.c > index 18830dc..fca152c 100644 > --- a/src/acpi.c > +++ b/src/acpi.c > @@ -196,6 +196,36 @@ struct srat_memory_affinity > u32 reserved3[2]; > } PACKED; > > +/* > + * IVRS (I/O Virtualization Reporting Structure) table. > + * > + * Describes the AMD IOMMU, as per: > + * "AMD I/O Virtualization Technology (IOMMU) Specification", rev 1.26 > + */ > + > +struct ivrs_ivhd > +{ > + u8 type; > + u8 flags; > + u16 length; > + u16 devid; > + u16 capab_off; > + u32 iommu_base_low; > + u32 iommu_base_high; > + u16 pci_seg_group; > + u16 iommu_info; > + u32 reserved; > + u8 entry[0]; > +} PACKED; > + > +struct ivrs_table > +{ > + ACPI_TABLE_HEADER_DEF /* ACPI common table header. */ > + u32 iv_info; > + u32 reserved[2]; > + struct ivrs_ivhd ivhd; > +} PACKED; > + prefix with amd_iommu_ or amd_ then ? > #include "acpi-dsdt.hex" > > static void > @@ -579,6 +609,59 @@ build_srat(void) > return srat; > } > > +#define IVRS_SIGNATURE 0x53525649 // IVRS > +#define IVRS_MAX_DEVS 32 > +static void * > +build_ivrs(void) > +{ > + int iommu_bdf, iommu_cap; > + int bdf, max, i; > + struct ivrs_table *ivrs; > + struct ivrs_ivhd *ivhd; > + > + /* Note this currently works for a single IOMMU! */ Meant to be a FIXME? How hard is it to fix? Just stick this in a loop? > + iommu_bdf = pci_find_class(PCI_CLASS_SYSTEM_IOMMU); > + if (iommu_bdf < 0) > + return NULL; > + iommu_cap = pci_find_capability(iommu_bdf, PCI_CAP_ID_SEC); > + if (iommu_cap < 0) > + return NULL; > + > + ivrs = malloc_high(sizeof(struct ivrs_table) + 4 * IVRS_MAX_DEVS); > + ivrs->iv_info = pci_config_readw(iommu_bdf, iommu_cap + 0x12) & ~0x000F; > + > + ivhd = &ivrs->ivhd; > + ivhd->type = 0x10; > + ivhd->flags = 0; > + ivhd->length = sizeof(struct ivrs_ivhd); > + ivhd->devid = iommu_bdf; > + ivhd->capab_off = iommu_cap; > + ivhd->iommu_base_low = pci_config_readl(iommu_bdf, iommu_cap + 0x04) & > + 0xFFFFFFFE; > + ivhd->iommu_base_high = pci_config_readl(iommu_bdf, iommu_cap + 0x08); > + ivhd->pci_seg_group = 0; > + ivhd->iommu_info = 0; > + ivhd->reserved = 0; > + > + i = 0; > + foreachpci(bdf, max) { > + if (bdf == ivhd->devid) > + continue; > + ivhd->entry[4 * i + 0] = 2; > + ivhd->entry[4 * i + 1] = bdf & 0xFF; > + ivhd->entry[4 * i + 2] = (bdf >> 8) & 0xFF; > + ivhd->entry[4 * i + 3] = ~(1 << 3); > + ivhd->length += 4; > + if (++i >= IVRS_MAX_DEVS) > + break; > + } > + > + build_header((void *) ivrs, IVRS_SIGNATURE, > + sizeof(struct ivrs_table) + 4 * i, 1); > + > + return ivrs; > +} > + > static const struct pci_device_id acpi_find_tbl[] = { > /* PIIX4 Power Management device. */ > PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), > @@ -625,6 +708,7 @@ acpi_bios_init(void) > ACPI_INIT_TABLE(build_madt()); > ACPI_INIT_TABLE(build_hpet()); > ACPI_INIT_TABLE(build_srat()); > + ACPI_INIT_TABLE(build_ivrs()); > > u16 i, external_tables = qemu_cfg_acpi_additional_tables(); > > diff --git a/src/config.h b/src/config.h > index 6356941..0ba5723 100644 > --- a/src/config.h > +++ b/src/config.h > @@ -172,6 +172,9 @@ > #define BUILD_APIC_ADDR 0xfee00000 > #define BUILD_IOAPIC_ADDR 0xfec00000 > > +#define BUILD_AMD_IOMMU_START 0xfed00000 > +#define BUILD_AMD_IOMMU_END 0xfee00000 /* BUILD_APIC_ADDR */ > + > #define BUILD_SMM_INIT_ADDR 0x38000 > #define BUILD_SMM_ADDR 0xa8000 > #define BUILD_SMM_SIZE 0x8000 > diff --git a/src/pci_ids.h b/src/pci_ids.h > index e1cded2..3cc3c6e 100644 > --- a/src/pci_ids.h > +++ b/src/pci_ids.h > @@ -72,6 +72,7 @@ > #define PCI_CLASS_SYSTEM_RTC 0x0803 > #define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 > #define PCI_CLASS_SYSTEM_SDHCI 0x0805 > +#define PCI_CLASS_SYSTEM_IOMMU 0x0806 > #define PCI_CLASS_SYSTEM_OTHER 0x0880 > > #define PCI_BASE_CLASS_INPUT 0x09 > diff --git a/src/pci_regs.h b/src/pci_regs.h > index e5effd4..bfac824 100644 > --- a/src/pci_regs.h > +++ b/src/pci_regs.h > @@ -208,6 +208,7 @@ > #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ > #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ > #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ > +#define PCI_CAP_ID_SEC 0x0F /* Secure Device (AMD IOMMU) */ > #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ > #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ > #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ > diff --git a/src/pciinit.c b/src/pciinit.c > index ee2e72d..4ebcfbe 100644 > --- a/src/pciinit.c > +++ b/src/pciinit.c > @@ -21,6 +21,8 @@ static struct pci_region pci_bios_io_region; > static struct pci_region pci_bios_mem_region; > static struct pci_region pci_bios_prefmem_region; > > +static u32 amd_iommu_addr; > + > /* host irqs corresponding to PCI irqs A-D */ > const u8 pci_irqs[4] = { > 10, 10, 11, 11 > @@ -256,6 +258,27 @@ static void apple_macio_init(u16 bdf, void *arg) > pci_set_io_region_addr(bdf, 0, 0x80800000); > } > > +static void amd_iommu_init(u16 bdf, void *arg) > +{ > + int cap; > + u32 base_addr; > + > + cap = pci_find_capability(bdf, PCI_CAP_ID_SEC); > + if (cap < 0) { > + return; > + } There actually can be multiple instances of this capability according to spec. Do we care? > + > + if (amd_iommu_addr >= BUILD_AMD_IOMMU_END) { > + return; > + } > + base_addr = amd_iommu_addr; > + amd_iommu_addr += 0x4000; > + > + pci_config_writel(bdf, cap + 0x0C, 0); > + pci_config_writel(bdf, cap + 0x08, 0); > + pci_config_writel(bdf, cap + 0x04, base_addr | 1); > +} > + > static const struct pci_device_id pci_class_tbl[] = { > /* STORAGE IDE */ > PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, > @@ -279,6 +302,10 @@ static const struct pci_device_id pci_class_tbl[] = { > PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, > pci_bios_init_device_bridge), > > + /* AMD IOMMU */ Makes sense to limit to AMD vendor id? > + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SYSTEM_IOMMU, > + amd_iommu_init), > + > /* default */ > PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID, pci_bios_allocate_regions), > > @@ -408,6 +435,8 @@ pci_setup(void) > pci_region_init(&pci_bios_prefmem_region, > BUILD_PCIPREFMEM_START, BUILD_PCIPREFMEM_END - 1); > > + amd_iommu_addr = BUILD_AMD_IOMMU_START; > + > pci_bios_init_bus(); > > int bdf, max; > -- > 1.7.3.4 From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=33155 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pm36i-0008HH-Av for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:48:45 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pm36f-0006t3-Ry for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:48:44 -0500 Received: from mx1.redhat.com ([209.132.183.28]:51890) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pm36f-0006se-Kw for qemu-devel@nongnu.org; Sun, 06 Feb 2011 06:48:41 -0500 Date: Sun, 6 Feb 2011 13:47:57 +0200 From: "Michael S. Tsirkin" Message-ID: <20110206114757.GF26242@redhat.com> References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: Subject: [Qemu-devel] Re: [PATCH 2/3] AMD IOMMU support List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Eduard - Gabriel Munteanu Cc: kvm@vger.kernel.org, joro@8bytes.org, seabios@seabios.org, qemu-devel@nongnu.org, blauwirbel@gmail.com, yamahata@valinux.co.jp, kevin@koconnor.net, avi@redhat.com, paul@codesourcery.com On Fri, Feb 04, 2011 at 01:24:14AM +0200, Eduard - Gabriel Munteanu wrote: > This initializes the AMD IOMMU and creates ACPI tables for it. > > Signed-off-by: Eduard - Gabriel Munteanu > --- > src/acpi.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > src/config.h | 3 ++ > src/pci_ids.h | 1 + > src/pci_regs.h | 1 + > src/pciinit.c | 29 +++++++++++++++++++ > 5 files changed, 118 insertions(+), 0 deletions(-) > > diff --git a/src/acpi.c b/src/acpi.c > index 18830dc..fca152c 100644 > --- a/src/acpi.c > +++ b/src/acpi.c > @@ -196,6 +196,36 @@ struct srat_memory_affinity > u32 reserved3[2]; > } PACKED; > > +/* > + * IVRS (I/O Virtualization Reporting Structure) table. > + * > + * Describes the AMD IOMMU, as per: > + * "AMD I/O Virtualization Technology (IOMMU) Specification", rev 1.26 > + */ > + > +struct ivrs_ivhd > +{ > + u8 type; > + u8 flags; > + u16 length; > + u16 devid; > + u16 capab_off; > + u32 iommu_base_low; > + u32 iommu_base_high; > + u16 pci_seg_group; > + u16 iommu_info; > + u32 reserved; > + u8 entry[0]; > +} PACKED; > + > +struct ivrs_table > +{ > + ACPI_TABLE_HEADER_DEF /* ACPI common table header. */ > + u32 iv_info; > + u32 reserved[2]; > + struct ivrs_ivhd ivhd; > +} PACKED; > + prefix with amd_iommu_ or amd_ then ? > #include "acpi-dsdt.hex" > > static void > @@ -579,6 +609,59 @@ build_srat(void) > return srat; > } > > +#define IVRS_SIGNATURE 0x53525649 // IVRS > +#define IVRS_MAX_DEVS 32 > +static void * > +build_ivrs(void) > +{ > + int iommu_bdf, iommu_cap; > + int bdf, max, i; > + struct ivrs_table *ivrs; > + struct ivrs_ivhd *ivhd; > + > + /* Note this currently works for a single IOMMU! */ Meant to be a FIXME? How hard is it to fix? Just stick this in a loop? > + iommu_bdf = pci_find_class(PCI_CLASS_SYSTEM_IOMMU); > + if (iommu_bdf < 0) > + return NULL; > + iommu_cap = pci_find_capability(iommu_bdf, PCI_CAP_ID_SEC); > + if (iommu_cap < 0) > + return NULL; > + > + ivrs = malloc_high(sizeof(struct ivrs_table) + 4 * IVRS_MAX_DEVS); > + ivrs->iv_info = pci_config_readw(iommu_bdf, iommu_cap + 0x12) & ~0x000F; > + > + ivhd = &ivrs->ivhd; > + ivhd->type = 0x10; > + ivhd->flags = 0; > + ivhd->length = sizeof(struct ivrs_ivhd); > + ivhd->devid = iommu_bdf; > + ivhd->capab_off = iommu_cap; > + ivhd->iommu_base_low = pci_config_readl(iommu_bdf, iommu_cap + 0x04) & > + 0xFFFFFFFE; > + ivhd->iommu_base_high = pci_config_readl(iommu_bdf, iommu_cap + 0x08); > + ivhd->pci_seg_group = 0; > + ivhd->iommu_info = 0; > + ivhd->reserved = 0; > + > + i = 0; > + foreachpci(bdf, max) { > + if (bdf == ivhd->devid) > + continue; > + ivhd->entry[4 * i + 0] = 2; > + ivhd->entry[4 * i + 1] = bdf & 0xFF; > + ivhd->entry[4 * i + 2] = (bdf >> 8) & 0xFF; > + ivhd->entry[4 * i + 3] = ~(1 << 3); > + ivhd->length += 4; > + if (++i >= IVRS_MAX_DEVS) > + break; > + } > + > + build_header((void *) ivrs, IVRS_SIGNATURE, > + sizeof(struct ivrs_table) + 4 * i, 1); > + > + return ivrs; > +} > + > static const struct pci_device_id acpi_find_tbl[] = { > /* PIIX4 Power Management device. */ > PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), > @@ -625,6 +708,7 @@ acpi_bios_init(void) > ACPI_INIT_TABLE(build_madt()); > ACPI_INIT_TABLE(build_hpet()); > ACPI_INIT_TABLE(build_srat()); > + ACPI_INIT_TABLE(build_ivrs()); > > u16 i, external_tables = qemu_cfg_acpi_additional_tables(); > > diff --git a/src/config.h b/src/config.h > index 6356941..0ba5723 100644 > --- a/src/config.h > +++ b/src/config.h > @@ -172,6 +172,9 @@ > #define BUILD_APIC_ADDR 0xfee00000 > #define BUILD_IOAPIC_ADDR 0xfec00000 > > +#define BUILD_AMD_IOMMU_START 0xfed00000 > +#define BUILD_AMD_IOMMU_END 0xfee00000 /* BUILD_APIC_ADDR */ > + > #define BUILD_SMM_INIT_ADDR 0x38000 > #define BUILD_SMM_ADDR 0xa8000 > #define BUILD_SMM_SIZE 0x8000 > diff --git a/src/pci_ids.h b/src/pci_ids.h > index e1cded2..3cc3c6e 100644 > --- a/src/pci_ids.h > +++ b/src/pci_ids.h > @@ -72,6 +72,7 @@ > #define PCI_CLASS_SYSTEM_RTC 0x0803 > #define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 > #define PCI_CLASS_SYSTEM_SDHCI 0x0805 > +#define PCI_CLASS_SYSTEM_IOMMU 0x0806 > #define PCI_CLASS_SYSTEM_OTHER 0x0880 > > #define PCI_BASE_CLASS_INPUT 0x09 > diff --git a/src/pci_regs.h b/src/pci_regs.h > index e5effd4..bfac824 100644 > --- a/src/pci_regs.h > +++ b/src/pci_regs.h > @@ -208,6 +208,7 @@ > #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ > #define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ > #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ > +#define PCI_CAP_ID_SEC 0x0F /* Secure Device (AMD IOMMU) */ > #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ > #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ > #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ > diff --git a/src/pciinit.c b/src/pciinit.c > index ee2e72d..4ebcfbe 100644 > --- a/src/pciinit.c > +++ b/src/pciinit.c > @@ -21,6 +21,8 @@ static struct pci_region pci_bios_io_region; > static struct pci_region pci_bios_mem_region; > static struct pci_region pci_bios_prefmem_region; > > +static u32 amd_iommu_addr; > + > /* host irqs corresponding to PCI irqs A-D */ > const u8 pci_irqs[4] = { > 10, 10, 11, 11 > @@ -256,6 +258,27 @@ static void apple_macio_init(u16 bdf, void *arg) > pci_set_io_region_addr(bdf, 0, 0x80800000); > } > > +static void amd_iommu_init(u16 bdf, void *arg) > +{ > + int cap; > + u32 base_addr; > + > + cap = pci_find_capability(bdf, PCI_CAP_ID_SEC); > + if (cap < 0) { > + return; > + } There actually can be multiple instances of this capability according to spec. Do we care? > + > + if (amd_iommu_addr >= BUILD_AMD_IOMMU_END) { > + return; > + } > + base_addr = amd_iommu_addr; > + amd_iommu_addr += 0x4000; > + > + pci_config_writel(bdf, cap + 0x0C, 0); > + pci_config_writel(bdf, cap + 0x08, 0); > + pci_config_writel(bdf, cap + 0x04, base_addr | 1); > +} > + > static const struct pci_device_id pci_class_tbl[] = { > /* STORAGE IDE */ > PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, > @@ -279,6 +302,10 @@ static const struct pci_device_id pci_class_tbl[] = { > PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_PCI, > pci_bios_init_device_bridge), > > + /* AMD IOMMU */ Makes sense to limit to AMD vendor id? > + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SYSTEM_IOMMU, > + amd_iommu_init), > + > /* default */ > PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID, pci_bios_allocate_regions), > > @@ -408,6 +435,8 @@ pci_setup(void) > pci_region_init(&pci_bios_prefmem_region, > BUILD_PCIPREFMEM_START, BUILD_PCIPREFMEM_END - 1); > > + amd_iommu_addr = BUILD_AMD_IOMMU_START; > + > pci_bios_init_bus(); > > int bdf, max; > -- > 1.7.3.4