iommu/amd: enable ACPI hardware ID device support
Subsystems (2)
v3 cover iommu/amd: enable ACPI hardware ID device support
From: Wan Zongshun <[email protected]>
There are some devices indentified using ACPI HID format in AMD chip. This patch series enable iommu support for those ACPI HID device, since the existing AMD iommu only supports PCI bus based device.
The latest public version of AMD IOMMU specification that describes the support for IVHD type 40h and ACPI HID IVHD device type, implemented by this patch series, is available here:
http://support.amd.com/TechDocs/48882_IOMMU.pdf
The V2 added two new patches: patch 5 and patch 8 according to Joerg's comments, there is a little modification in patch 6 to distinguish pci and none pci.
The V3 added the fix for the booting issue Joerg experienced on the CZ platform due to the IOMMU performance counter init code.
Suravee Suthikulpanit (4):
iommu/amd: Adding Extended Feature Register check for PC support iommu/amd: Modify ivhd_header structure to support type 11h and 40h iommu/amd: Use the most comprehensive IVHD type that the driver can
support
iommu/amd: Introduces ivrs_acpihid kernel parameter
Wan Zongshun (5):
iommu/amd: Add new map for storing IVHD dev entry type HID iommu/amd: Make call-sites of get_device_id aware of its return value iommu/amd: Add iommu support for ACPI HID devices iommu/amd: Manage iommu_group for ACPI HID devices iommu/amd: Set AMD iommu callbacks for amba bus
Documentation/kernel-parameters.txt | 7 + drivers/iommu/amd_iommu.c | 167 +++++++++++++++--- drivers/iommu/amd_iommu_init.c | 329 +++++++++++++++++++++++++++++++----- drivers/iommu/amd_iommu_types.h | 14 ++ 4 files changed, 457 insertions(+), 60 deletions(-)
Signature · 1 line
1.9.1
PATCH v3 1/9 iommu/amd: Adding Extended Feature Register check for PC support
From: Suravee Suthikulpanit <[email protected]>
The IVHD header type 11h and 40h introduce the PCSup bit in the EFR Register Image bit fileds. This should be used to determine the IOMMU performance support instead of relying on the PNCounters and PNBanks.
Note also that the PNCouters and PNBanks bits in the IOMMU attributes field of IVHD headers type 11h are incorrectly programmed on some systems.
So, we should not rely on it to determine the performance counter/banks size. Instead, these values should be read from the MMIO Offset 0030h IOMMU Extended Feature Register.
--- drivers/iommu/amd_iommu_init.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-)
drivers/iommu/amd_iommu_init.c +24 -8
@@ -99,7 +99,11 @@ struct ivhd_header {u64 mmio_phys;u16 pci_seg;u16 info; -u32 efr; +u32 efr_attr; + +/* Following only valid on IVHD type 11h and 40h */ +u64 efr_reg;/* Exact copy of MMIO_EXT_FEATURES */ +u64 res; } __attribute__((packed));/*
@@ -1078,13 +1082,25 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) iommu->pci_seg = h->pci_seg; iommu->mmio_phys = h->mmio_phys; -/* Check if IVHD EFR contains proper max banks/counters */ -if ((h->efr !=0 ) && - ((h->efr & (0xF <<13 )) !=0 ) && - ((h->efr & (0x3F <<17 )) !=0 )) { - iommu->mmio_phys_end =MMIO_REG_END_OFFSET ; - }else { - iommu->mmio_phys_end =MMIO_CNTR_CONF_OFFSET ; +switch (h->type) { +case 0x10 : +/* Check if IVHD EFR contains proper max banks/counters */ +if ((h->efr_attr !=0 ) && + ((h->efr_attr & (0xF <<13 )) !=0 ) && + ((h->efr_attr & (0x3F <<17 )) !=0 )) + iommu->mmio_phys_end =MMIO_REG_END_OFFSET ; +else + iommu->mmio_phys_end =MMIO_CNTR_CONF_OFFSET ; +break ; +case 0x11 : +case 0x40 : +if (h->efr_reg & (1 <<9 )) + iommu->mmio_phys_end =MMIO_REG_END_OFFSET ; +else + iommu->mmio_phys_end =MMIO_CNTR_CONF_OFFSET ; +break ; +default : +return -EINVAL ; } iommu->mmio_base = iommu_map_mmio_space(iommu->mmio_phys,
Signature · 1 line
1.9.1
PATCH v3 2/9 iommu/amd: Modify ivhd_header structure to support type 11h and 40h
In reply to Wan ZongshunFrom: Suravee Suthikulpanit <[email protected]>
This patch modifies the existing struct ivhd_header, which currently only support IVHD type 0x10, to add new fields from IVHD type 11h and 40h.
It also modifies the pointer calculation to allow support for IVHD type 11h and 40h
--- drivers/iommu/amd_iommu_init.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
drivers/iommu/amd_iommu_init.c +33 -2
@@ -398,6 +398,22 @@ static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)release_mem_region (iommu->mmio_phys, iommu->mmio_phys_end); } +static inline u32 get_ivhd_header_size (struct ivhd_header *h) +{ +u32 size =0 ; + +switch (h->type) { +case 0x10 : + size =24 ; +break ; +case 0x11 : +case 0x40 : + size =40 ; +break ; + } +return size; +} +/**************************************************************************** * * The functions below belong to the first pass of AMD IOMMU ACPI table
@@ -424,7 +440,14 @@ static int __init find_last_devid_from_ivhd(struct ivhd_header *h)u8 *p = (void *)h, *end = (void *)h;struct ivhd_entry *dev; - p +=sizeof (*h); +u32 ivhd_size =get_ivhd_header_size (h); + +if (!ivhd_size) { +pr_err ("AMD-Vi: Unsupported IVHD type %#x\n" , h->type); +return -EINVAL ; + } + + p += ivhd_size; end += h->length;while (p < end) {
@@ -789,6 +812,7 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,u32 dev_i, ext_flags =0 ;bool alias = false;struct ivhd_entry *e; +u32 ivhd_size;int ret;
@@ -804,7 +828,14 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,/* * Done. Now parse the device entries */ - p +=sizeof (struct ivhd_header ); + ivhd_size =get_ivhd_header_size (h); +if (!ivhd_size) { +pr_err ("AMD-Vi: Unsupported IVHD type %#x\n" , h->type); +return -EINVAL ; + } + + p += ivhd_size; + end += h->length;
Signature · 1 line
1.9.1
PATCH v3 3/9 iommu/amd: Use the most comprehensive IVHD type that the driver can support
In reply to Wan ZongshunFrom: Suravee Suthikulpanit <[email protected]>
The IVRS in more recent AMD system usually contains multiple IVHD block types (e.g. 0x10, 0x11, and 0x40) for each IOMMU. The newer IVHD types provide more information (e.g. new features specified in the IOMMU spec), while maintain compatibility with the older IVHD type.
Having multiple IVHD type allows older IOMMU drivers to still function (e.g. using the older IVHD type 0x10) while the newer IOMMU driver can use the newer IVHD types (e.g. 0x11 and 0x40). Therefore, the IOMMU driver should only make use of the newest IVHD type that it can support.
This patch adds new logic to determine the highest level of IVHD type it can support, and use it throughout the to initialize the driver. This requires adding another pass to the IVRS parsing to determine appropriate IVHD type (see function get_highest_supported_ivhd_type()) before parsing the contents.
[Vincent: fix the build error of IVHD_DEV_ACPI_HID flag not found]
--- drivers/iommu/amd_iommu_init.c | 107 ++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 29 deletions(-)
drivers/iommu/amd_iommu_init.c +78 -29
@@ -44,7 +44,7 @@*/ #define IVRS_HEADER_LENGTH 48 -#define ACPI_IVHD_TYPE 0x10 +#define ACPI_IVHD_TYPE_MAX_SUPPORTED 0x40#define ACPI_IVMD_TYPE_ALL 0x20#define ACPI_IVMD_TYPE 0x21#define ACPI_IVMD_TYPE_RANGE 0x22
@@ -58,6 +58,7 @@#define IVHD_DEV_EXT_SELECT 0x46#define IVHD_DEV_EXT_SELECT_RANGE 0x47#define IVHD_DEV_SPECIAL 0x48 +#define IVHD_DEV_ACPI_HID 0xf0#define IVHD_SPECIAL_IOAPIC 1#define IVHD_SPECIAL_HPET 2
@@ -137,6 +138,7 @@ bool amd_iommu_irq_remap __read_mostly;static bool amd_iommu_detected;static bool __initdata amd_iommu_disabled; +static int amd_iommu_target_ivhd_type;u16 amd_iommu_last_bdf;/* largest PCI device id we have to handle */
@@ -428,7 +430,15 @@ static inline u32 get_ivhd_header_size(struct ivhd_header *h)*/ static inline int ivhd_entry_length (u8 *ivhd) { -return 0x04 << (*ivhd >>6 ); +u32 type = ((struct ivhd_entry *)ivhd)->type; + +if (type <0x80 ) { +return 0x04 << (*ivhd >>6 ); + }else if (type ==IVHD_DEV_ACPI_HID ) { +/* For ACPI_HID, offset 21 is uid len */ +return *((u8 *)ivhd +21 ) +22 ; + } +return 0 ; }/*
@@ -475,6 +485,22 @@ static int __init find_last_devid_from_ivhd(struct ivhd_header *h)return 0 ; } +static int __init check_ivrs_checksum (struct acpi_table_header *table) +{ +int i; +u8 checksum =0 , *p = (u8 *)table; + +for (i =0 ; i < table->length; ++i) + checksum += p[i]; +if (checksum !=0 ) { +/* ACPI table corrupt */ +pr_err (FW_BUG "AMD-Vi: IVRS invalid checksum\n" ); +return -ENODEV ; + } + +return 0 ; +} +/* * Iterate over all IVHD entries in the ACPI table and find the highest device * id which we need to handle. This is the first of three functions which parse
@@ -482,31 +508,19 @@ static int __init find_last_devid_from_ivhd(struct ivhd_header *h)*/ static int __init find_last_devid_acpi (struct acpi_table_header *table) { -int i; -u8 checksum =0 , *p = (u8 *)table, *end = (u8 *)table; +u8 *p = (u8 *)table, *end = (u8 *)table;struct ivhd_header *h; -/* -* Validate checksum here so we don't need to do it when -* we actually parse the table -*/ -for (i =0 ; i < table->length; ++i) - checksum += p[i]; -if (checksum !=0 ) -/* ACPI table corrupt */ -return -ENODEV ; - p +=IVRS_HEADER_LENGTH ; end += table->length;while (p < end) { h = (struct ivhd_header *)p; -switch (h->type) { -case ACPI_IVHD_TYPE : -find_last_devid_from_ivhd (h); -break ; -default : -break ; +if (h->type == amd_iommu_target_ivhd_type) { +int ret =find_last_devid_from_ivhd (h); + +if (ret) +return ret; } p += h->length; }
@@ -1164,6 +1178,32 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)return 0 ; } +/** +* get_highest_supported_ivhd_type - Look up the appropriate IVHD type +* @ivrs Pointer to the IVRS header +* +* This function search through all IVDB of the maximum supported IVHD +*/ +static u8 get_highest_supported_ivhd_type (struct acpi_table_header *ivrs) +{ +u8 *base = (u8 *)ivrs; +struct ivhd_header *ivhd = (struct ivhd_header *) + (base +IVRS_HEADER_LENGTH ); +u8 last_type = ivhd->type; +u16 devid = ivhd->devid; + +while (((u8 *)ivhd - base < ivrs->length) && + (ivhd->type <=ACPI_IVHD_TYPE_MAX_SUPPORTED )) { +u8 *p = (u8 *) ivhd; + +if (ivhd->devid == devid) + last_type = ivhd->type; + ivhd = (struct ivhd_header *)(p + ivhd->length); + } + +return last_type; +} +/* * Iterates over all IOMMU entries in the ACPI table, allocates the * IOMMU structure and initializes it with init_iommu_one()
@@ -1180,8 +1220,7 @@ static int __init init_iommu_all(struct acpi_table_header *table)while (p < end) { h = (struct ivhd_header *)p; -switch (*p) { -case ACPI_IVHD_TYPE : +if (*p == amd_iommu_target_ivhd_type) { DUMP_printk("device: %02x:%02x.%01x cap: %04x " "seg: %d flags: %01x info %04x\n",
@@ -1198,9 +1237,6 @@ static int __init init_iommu_all(struct acpi_table_header *table) ret =init_iommu_one (iommu, h);if (ret)return ret; -break ; -default : -break ; } p += h->length;
@@ -1865,18 +1901,20 @@ static void __init free_dma_resources(void)* remapping setup code. * * This function basically parses the ACPI table for AMD IOMMU (IVRS) -* three times: +* four times: * -* 1 pass) Find the highest PCI device id the driver has to handle. +* 1 pass) Discover the most comprehensive IVHD type to use. +* +* 2 pass) Find the highest PCI device id the driver has to handle. * Upon this information the size of the data structures is * determined that needs to be allocated. * -* 2 pass) Initialize the data structures just allocated with the +* 3 pass) Initialize the data structures just allocated with the * information in the ACPI table about available AMD IOMMUs * in the system. It also maps the PCI devices in the * system to specific IOMMUs * -* 3 pass) After the basic data structures are allocated and +* 4 pass) After the basic data structures are allocated and * initialized we update them with information about memory * remapping requirements parsed out of the ACPI table in * this last pass.
@@ -1904,6 +1942,17 @@ static int __init early_amd_iommu_init(void) }/* +* Validate checksum here so we don't need to do it when +* we actually parse the table +*/ + ret =check_ivrs_checksum (ivrs_base); +if (ret) +return ret; + + amd_iommu_target_ivhd_type =get_highest_supported_ivhd_type (ivrs_base); +DUMP_printk ("Using IVHD type %#x\n" , amd_iommu_target_ivhd_type); + +/* * First parse ACPI tables to find the largest Bus/Dev/Func * we need to handle. Upon this information the shared data * structures for the IOMMUs in the system will be allocated
Signature · 1 line
1.9.1
PATCH v3 4/9 iommu/amd: Add new map for storing IVHD dev entry type HID
In reply to Wan ZongshunFrom: Wan Zongshun <[email protected]>
This patch introduces acpihid_map, which is used to store the new IVHD device entry extracted from BIOS IVRS table.
It also provides a utility function add_acpi_hid_device(), to add this types of devices to the map.
--- drivers/iommu/amd_iommu.c | 1 + drivers/iommu/amd_iommu_init.c | 122 ++++++++++++++++++++++++++++++++++++++++ drivers/iommu/amd_iommu_types.h | 14 +++++ 3 files changed, 137 insertions(+)
drivers/iommu/amd_iommu.c +1 -0
@@ -72,6 +72,7 @@ static DEFINE_SPINLOCK(dev_data_list_lock);LIST_HEAD (ioapic_map);LIST_HEAD (hpet_map); +LIST_HEAD (acpihid_map);/* * Domain for untranslated devices - only allocated
drivers/iommu/amd_iommu_init.c +122 -0
160 lines not on this page · raw message
drivers/iommu/amd_iommu_types.h +14 -0
@@ -527,6 +527,19 @@ struct amd_iommu {#endif }; +#define ACPIHID_UID_LEN 256 +#define ACPIHID_HID_LEN 9 + +struct acpihid_map_entry { +struct list_head list; +u8 uid[ACPIHID_UID_LEN ]; +u8 hid[ACPIHID_HID_LEN ]; +u16 devid; +u16 root_devid; +bool cmd_line; +struct iommu_group *group; +}; +struct devid_map {struct list_head list;u8 id;
@@ -537,6 +550,7 @@ struct devid_map {/* Map HPET and IOAPIC ids to the devid used by the IOMMU */ extern struct list_head ioapic_map;extern struct list_head hpet_map; +extern struct list_head acpihid_map;/* * List with all IOMMUs in the system. This list is not locked because it is
Signature · 1 line
1.9.1
PATCH v3 5/9 iommu/amd: Introduces ivrs_acpihid kernel parameter
In reply to Wan ZongshunFrom: Suravee Suthikulpanit <[email protected]>
This patch introduces a new kernel parameter, ivrs_acpihid. This is used to override existing ACPI-HID IVHD device entry, or add an entry in case it is missing in the IVHD.
--- Documentation/kernel-parameters.txt | 7 +++++++ drivers/iommu/amd_iommu_init.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+)
Documentation/kernel-parameters.txt +7 -0
@@ -1767,6 +1767,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. PCI device 00:14.0 write the parameter as: ivrs_hpet[0]=00:14.0 + ivrs_acpihid [HW,X86_64] + Provide an override to the ACPI-HID:UID<->DEVICE-ID + mapping provided in the IVRS ACPI table. For + example, to map UART-HID:UID AMD0020:0 to + PCI device 00:14.5 write the parameter as: + ivrs_acpihid[00:14.5]=AMD0020:0 + js= [HW,JOY] Analog joystick See Documentation/input/joystick.txt.
drivers/iommu/amd_iommu_init.c +33 -0
@@ -2477,10 +2477,43 @@ static int __init parse_ivrs_hpet(char *str)return 1 ; } +static int __init parse_ivrs_acpihid (char *str) +{ +u32 bus, dev, fn; +char *hid, *uid, *p; +char acpiid[ACPIHID_UID_LEN +ACPIHID_HID_LEN ] = {0 }; +int ret, i; + + ret =sscanf (str,"[%x:%x.%x]=%s" , &bus, &dev, &fn, acpiid); +if (ret !=4 ) { +pr_err ("AMD-Vi: Invalid command line: ivrs_acpihid(%s)\n" , str); +return 1 ; + } + + p = acpiid; + hid =strsep (&p,":" ); + uid = p; + +if (!hid || !(*hid) || !uid) { +pr_err ("AMD-Vi: Invalid command line: hid or uid\n" ); +return 1 ; + } + + i = early_acpihid_map_size++; +memcpy (early_acpihid_map[i].hid, hid,strlen (hid)); +memcpy (early_acpihid_map[i].uid, uid,strlen (uid)); + early_acpihid_map[i].devid = + ((bus &0xff ) <<8 ) | ((dev &0x1f ) <<3 ) | (fn &0x7 ); + early_acpihid_map[i].cmd_line = true; + +return 1 ; +} +__setup ("amd_iommu_dump" , parse_amd_iommu_dump);__setup ("amd_iommu=" , parse_amd_iommu_options);__setup ("ivrs_ioapic" , parse_ivrs_ioapic);__setup ("ivrs_hpet" , parse_ivrs_hpet); +__setup ("ivrs_acpihid" , parse_ivrs_acpihid); IOMMU_INIT_FINISH(amd_iommu_detect, gart_iommu_hole_init,
Signature · 1 line
1.9.1
PATCH v3 6/9 iommu/amd: Make call-sites of get_device_id aware of its return value
In reply to Wan ZongshunFrom: Wan Zongshun <[email protected]>
This patch is to make the call-sites of get_device_id aware of its return value.
--- drivers/iommu/amd_iommu.c | 51 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-)
drivers/iommu/amd_iommu.c +41 -10
@@ -279,9 +279,11 @@ static void init_unity_mappings_for_device(struct device *dev, struct dma_ops_domain *dma_dom) {struct unity_map_entry *e; -u16 devid; +int devid; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ;list_for_each_entry (e, &amd_iommu_unity_map, list) {if (!(devid >= e->devid_start && devid <= e->devid_end))
@@ -296,7 +298,7 @@ static void init_unity_mappings_for_device(struct device *dev,*/ static bool check_device (struct device *dev) { -u16 devid; +int devid;if (!dev || !dev->dma_mask)return false;
@@ -306,6 +308,8 @@ static bool check_device(struct device *dev)return false; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return false;/* Out of our scope? */ if (devid > amd_iommu_last_bdf)
@@ -342,11 +346,16 @@ static int iommu_init_device(struct device *dev) {struct pci_dev *pdev =to_pci_dev (dev);struct iommu_dev_data *dev_data; +int devid;if (dev->archdata.iommu)return 0 ; - dev_data =find_dev_data (get_device_id (dev)); + devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return devid; + + dev_data =find_dev_data (devid);if (!dev_data)return -ENOMEM ;
@@ -367,9 +376,13 @@ static int iommu_init_device(struct device *dev)static void iommu_ignore_device (struct device *dev) { -u16 devid, alias; +u16 alias; +int devid; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ; + alias = amd_iommu_alias_table[devid];memset (&amd_iommu_dev_table[devid],0 ,sizeof (struct dev_table_entry ));
@@ -381,8 +394,14 @@ static void iommu_ignore_device(struct device *dev)static void iommu_uninit_device (struct device *dev) { -struct iommu_dev_data *dev_data =search_dev_data (get_device_id (dev)); +int devid; +struct iommu_dev_data *dev_data; + + devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ; + dev_data =search_dev_data (devid);if (!dev_data)return ;
@@ -2314,13 +2333,15 @@ static int amd_iommu_add_device(struct device *dev)struct iommu_dev_data *dev_data;struct iommu_domain *domain;struct amd_iommu *iommu; -u16 devid; -int ret; +int ret, devid;if (!check_device (dev) ||get_dev_data (dev))return 0 ; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return devid; + iommu = amd_iommu_rlookup_table[devid]; ret =iommu_init_device (dev);
@@ -2358,12 +2379,15 @@ out:static void amd_iommu_remove_device (struct device *dev) {struct amd_iommu *iommu; -u16 devid; +int devid;if (!check_device (dev))return ; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ; + iommu = amd_iommu_rlookup_table[devid];iommu_uninit_device (dev);
@@ -3035,12 +3059,14 @@ static void amd_iommu_detach_device(struct iommu_domain *dom, {struct iommu_dev_data *dev_data = dev->archdata.iommu;struct amd_iommu *iommu; -u16 devid; +int devid;if (!check_device (dev))return ; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ;if (dev_data->domain !=NULL )detach_device (dev);
@@ -3158,9 +3184,11 @@ static void amd_iommu_get_dm_regions(struct device *dev, struct list_head *head) {struct unity_map_entry *entry; -u16 devid; +int devid; devid =get_device_id (dev); +if (IS_ERR_VALUE (devid)) +return ;list_for_each_entry (entry, &amd_iommu_unity_map, list) {struct iommu_dm_region *region;
@@ -3862,6 +3890,9 @@ static struct irq_domain *get_irq_domain(struct irq_alloc_info *info)case X86_IRQ_ALLOC_TYPE_MSI :case X86_IRQ_ALLOC_TYPE_MSIX : devid =get_device_id (&info->msi_dev->dev); +if (IS_ERR_VALUE (devid)) +return NULL ; + iommu = amd_iommu_rlookup_table[devid];if (iommu)return iommu->msi_domain;
Signature · 1 line
1.9.1
PATCH v3 7/9 iommu/amd: Add iommu support for ACPI HID devices
In reply to Wan ZongshunFrom: Wan Zongshun <[email protected]>
Current IOMMU driver make assumption that the downstream devices are PCI. With the newly added ACPI-HID IVHD device entry support, this is no longer true. This patch is to add dev type check and to distinguish the pci and acpihid device code path.
--- drivers/iommu/amd_iommu.c | 69 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 9 deletions(-)
drivers/iommu/amd_iommu.c +60 -9
@@ -19,6 +19,7 @@#include <linux/ratelimit.h> #include <linux/pci.h> +#include <linux/acpi.h> #include <linux/pci-ats.h> #include <linux/bitmap.h> #include <linux/slab.h>
@@ -216,13 +217,60 @@ static struct iommu_dev_data *find_dev_data(u16 devid)return dev_data; } -static inline u16 get_device_id (struct device *dev) +static inline int match_hid_uid (struct device *dev, +struct acpihid_map_entry *entry) +{ +const char *hid, *uid; + + hid =acpi_device_hid (ACPI_COMPANION (dev)); + uid =acpi_device_uid (ACPI_COMPANION (dev)); + +if (!hid || !(*hid)) +return -ENODEV ; + +if (!uid || !(*uid)) +return strcmp (hid, entry->hid); + +if (!(*entry->uid)) +return strcmp (hid, entry->hid); + +return (strcmp (hid, entry->hid) ||strcmp (uid, entry->uid)); +} + +static inline u16 get_pci_device_id (struct device *dev) {struct pci_dev *pdev =to_pci_dev (dev);return PCI_DEVID (pdev->bus->number, pdev->devfn); } +static inline int get_acpihid_device_id (struct device *dev, +struct acpihid_map_entry **entry) +{ +struct acpihid_map_entry *p; + +list_for_each_entry (p, &acpihid_map, list) { +if (!match_hid_uid (dev, p)) { +if (entry) + *entry = p; +return p->devid; + } + } +return -EINVAL ; +} + +static inline int get_device_id (struct device *dev) +{ +int devid; + +if (dev_is_pci (dev)) + devid =get_pci_device_id (dev); +else + devid =get_acpihid_device_id (dev,NULL ); + +return devid; +} +static struct iommu_dev_data *get_dev_data (struct device *dev) {return dev->archdata.iommu;
@@ -303,10 +351,6 @@ static bool check_device(struct device *dev)if (!dev || !dev->dma_mask)return false; -/* No PCI device */ -if (!dev_is_pci (dev)) -return false; - devid =get_device_id (dev);if (IS_ERR_VALUE (devid))return false;
@@ -344,7 +388,6 @@ out:static int iommu_init_device (struct device *dev) { -struct pci_dev *pdev =to_pci_dev (dev);struct iommu_dev_data *dev_data;int devid;
@@ -359,10 +402,10 @@ static int iommu_init_device(struct device *dev)if (!dev_data)return -ENOMEM ; -if (pci_iommuv2_capable (pdev)) { +if (dev_is_pci (dev) &&pci_iommuv2_capable (to_pci_dev (dev))) {struct amd_iommu *iommu; - iommu = amd_iommu_rlookup_table[dev_data->devid]; + iommu = amd_iommu_rlookup_table[dev_data->devid]; dev_data->iommu_v2 = iommu->is_iommu_v2; }
@@ -2239,13 +2282,17 @@ static bool pci_pri_tlp_required(struct pci_dev *pdev)static int attach_device (struct device *dev,struct protection_domain *domain) { -struct pci_dev *pdev =to_pci_dev (dev); +struct pci_dev *pdev;struct iommu_dev_data *dev_data;unsigned long flags;int ret; dev_data =get_dev_data (dev); +if (!dev_is_pci (dev)) + goto skip_ats_check; + + pdev =to_pci_dev (dev);if (domain->flags &PD_IOMMUV2_MASK ) {if (!dev_data->passthrough)return -EINVAL ;
@@ -2264,6 +2311,7 @@ static int attach_device(struct device *dev, dev_data->ats.qdep =pci_ats_queue_depth (pdev); } +skip_ats_check:write_lock_irqsave (&amd_iommu_devtable_lock, flags); ret =__attach_device (dev_data, domain);write_unlock_irqrestore (&amd_iommu_devtable_lock, flags);
@@ -2320,6 +2368,9 @@ static void detach_device(struct device *dev)__detach_device (dev_data);write_unlock_irqrestore (&amd_iommu_devtable_lock, flags); +if (!dev_is_pci (dev)) +return ; +if (domain->flags &PD_IOMMUV2_MASK && dev_data->iommu_v2)pdev_iommuv2_disable (to_pci_dev (dev));else if (dev_data->ats.enabled)
Signature · 1 line
1.9.1
PATCH v3 8/9 iommu/amd: Manage iommu_group for ACPI HID devices
In reply to Wan ZongshunFrom: Wan Zongshun <[email protected]>
This patch creates a new function for finding or creating an IOMMU group for acpihid(ACPI Hardware ID) device.
The acpihid devices with the same devid will be put into same group and there will have the same domain id and share the same page table.
--- drivers/iommu/amd_iommu.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
drivers/iommu/amd_iommu.c +32 -1
@@ -276,6 +276,29 @@ static struct iommu_dev_data *get_dev_data(struct device *dev)return dev->archdata.iommu; } +/* +* Find or create an IOMMU group for a acpihid device. +*/ +static struct iommu_group *acpihid_device_group (struct device *dev) +{ +struct acpihid_map_entry *p, *entry =NULL ; +u16 devid; + + devid =get_acpihid_device_id (dev, &entry); +if (devid <0 ) +return ERR_PTR (devid); + +list_for_each_entry (p, &acpihid_map, list) { +if ((devid == p->devid) && p->group) + entry->group = p->group; + } + +if (!entry->group) + entry->group =generic_device_group (dev); + +return entry->group; +} +static bool pci_iommuv2_capable (struct pci_dev *pdev) {static const int caps[] = {
@@ -2445,6 +2468,14 @@ static void amd_iommu_remove_device(struct device *dev)iommu_completion_wait (iommu); } +static struct iommu_group *amd_iommu_device_group (struct device *dev) +{ +if (dev_is_pci (dev)) +return pci_device_group (dev); + +return acpihid_device_group (dev); +} +/***************************************************************************** * * The next functions belong to the dma_ops mapping/unmapping code.
@@ -3286,7 +3317,7 @@ static const struct iommu_ops amd_iommu_ops = { .iova_to_phys = amd_iommu_iova_to_phys, .add_device = amd_iommu_add_device, .remove_device = amd_iommu_remove_device, - .device_group = pci_device_group, + .device_group = amd_iommu_device_group, .get_dm_regions = amd_iommu_get_dm_regions, .put_dm_regions = amd_iommu_put_dm_regions, .pgsize_bitmap = AMD_IOMMU_PGSIZES,
Signature · 1 line
1.9.1
PATCH v3 9/9 iommu/amd: Set AMD iommu callbacks for amba bus
In reply to Wan ZongshunFrom: Wan Zongshun <[email protected]>
AMD Uart DMA belongs to ACPI HID type device, and its driver is basing on AMBA Bus, need also IOMMU support.
This patch is just to set the AMD iommu callbacks for amba bus.
--- drivers/iommu/amd_iommu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
drivers/iommu/amd_iommu.c +12 -1
@@ -20,6 +20,7 @@#include <linux/ratelimit.h> #include <linux/pci.h> #include <linux/acpi.h> +#include <linux/amba/bus.h> #include <linux/pci-ats.h> #include <linux/bitmap.h> #include <linux/slab.h>
@@ -2969,7 +2970,17 @@ static struct dma_map_ops amd_iommu_dma_ops = {int __init amd_iommu_init_api (void ) { -return bus_set_iommu (&pci_bus_type, &amd_iommu_ops); +int err =0 ; + + err =bus_set_iommu (&pci_bus_type, &amd_iommu_ops); +if (err) +return err; +#ifdef CONFIG_ARM_AMBA + err =bus_set_iommu (&amba_bustype, &amd_iommu_ops); +if (err) +return err; +#endif +return 0 ; }int __init amd_iommu_init_dma_ops (void )
Signature · 1 line
1.9.1
Quoted 7 lines · Wan Zongshun
Suravee Suthikulpanit (4):
iommu/amd: Adding Extended Feature Register check for PC support iommu/amd: Modifyivhd_headerstructure to support type 11h and 40h iommu/amd: Use the most comprehensive IVHD type that the driver can
support
iommu/amd: Introducesivrs_acpihidkernel parameter
Quoted 6 lines
Wan Zongshun (5):
iommu/amd: Add new map for storing IVHD dev entry type HID iommu/amd: Make call-sites ofget_device_idaware of its return value iommu/amd: Add iommu support for ACPI HID devices iommu/amd: Manageiommu_groupfor ACPI HID devices iommu/amd: Set AMD iommu callbacks for amba bus
Applied, thanks. This works for me now on RD890, KV and CZ systems.
Joerg