|
@@ -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.
|
|
@@ -1903,6 +1941,17 @@ static int __init early_amd_iommu_init(void)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * 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
|