iommu/amd: Fix the overwritten field in IVMD header
Commit 387caf0b759a ("iommu/amd: Treat per-device exclusion ranges as r/w unity-mapped regions") accidentally overwrites the 'flags' field in IVMD (struct ivmd_header) when the I/O virtualization memory definition is associated with the exclusion range entry. This leads to the corrupted IVMD table (incorrect checksum). The kdump kernel reports the invalid checksum: ACPI BIOS Warning (bug): Incorrect checksum in table [IVRS] - 0x5C, should be 0x60 (20200717/tbprint-177) AMD-Vi: [Firmware Bug]: IVRS invalid checksum Fix the above-mentioned issue by modifying the 'struct unity_map_entry' member instead of the IVMD header. Cleanup: The *exclusion_range* functions are not used anymore, so get rid of them. Fixes: 387caf0b759a ("iommu/amd: Treat per-device exclusion ranges as r/w unity-mapped regions") Reported-and-tested-by: Baoquan He <bhe@redhat.com> Signed-off-by: Adrian Huang <ahuang12@lenovo.com> Cc: Jerry Snitselaar <jsnitsel@redhat.com> Link: https://lore.kernel.org/r/20200926102602.19177-1-adrianhuang0701@gmail.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
1a26044954
commit
0bbe4ced53
@ -1103,25 +1103,6 @@ static int __init add_early_maps(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads the device exclusion range from ACPI and initializes the IOMMU with
|
|
||||||
* it
|
|
||||||
*/
|
|
||||||
static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m)
|
|
||||||
{
|
|
||||||
if (!(m->flags & IVMD_FLAG_EXCL_RANGE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Treat per-device exclusion ranges as r/w unity-mapped regions
|
|
||||||
* since some buggy BIOSes might lead to the overwritten exclusion
|
|
||||||
* range (exclusion_start and exclusion_length members). This
|
|
||||||
* happens when there are multiple exclusion ranges (IVMD entries)
|
|
||||||
* defined in ACPI table.
|
|
||||||
*/
|
|
||||||
m->flags = (IVMD_FLAG_IW | IVMD_FLAG_IR | IVMD_FLAG_UNITY_MAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Takes a pointer to an AMD IOMMU entry in the ACPI table and
|
* Takes a pointer to an AMD IOMMU entry in the ACPI table and
|
||||||
* initializes the hardware and our data structures with it.
|
* initializes the hardware and our data structures with it.
|
||||||
@ -2073,30 +2054,6 @@ static void __init free_unity_maps(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* called when we find an exclusion range definition in ACPI */
|
|
||||||
static int __init init_exclusion_range(struct ivmd_header *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
switch (m->type) {
|
|
||||||
case ACPI_IVMD_TYPE:
|
|
||||||
set_device_exclusion_range(m->devid, m);
|
|
||||||
break;
|
|
||||||
case ACPI_IVMD_TYPE_ALL:
|
|
||||||
for (i = 0; i <= amd_iommu_last_bdf; ++i)
|
|
||||||
set_device_exclusion_range(i, m);
|
|
||||||
break;
|
|
||||||
case ACPI_IVMD_TYPE_RANGE:
|
|
||||||
for (i = m->devid; i <= m->aux; ++i)
|
|
||||||
set_device_exclusion_range(i, m);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* called for unity map ACPI definition */
|
/* called for unity map ACPI definition */
|
||||||
static int __init init_unity_map_range(struct ivmd_header *m)
|
static int __init init_unity_map_range(struct ivmd_header *m)
|
||||||
{
|
{
|
||||||
@ -2107,9 +2064,6 @@ static int __init init_unity_map_range(struct ivmd_header *m)
|
|||||||
if (e == NULL)
|
if (e == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (m->flags & IVMD_FLAG_EXCL_RANGE)
|
|
||||||
init_exclusion_range(m);
|
|
||||||
|
|
||||||
switch (m->type) {
|
switch (m->type) {
|
||||||
default:
|
default:
|
||||||
kfree(e);
|
kfree(e);
|
||||||
@ -2133,6 +2087,16 @@ static int __init init_unity_map_range(struct ivmd_header *m)
|
|||||||
e->address_end = e->address_start + PAGE_ALIGN(m->range_length);
|
e->address_end = e->address_start + PAGE_ALIGN(m->range_length);
|
||||||
e->prot = m->flags >> 1;
|
e->prot = m->flags >> 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Treat per-device exclusion ranges as r/w unity-mapped regions
|
||||||
|
* since some buggy BIOSes might lead to the overwritten exclusion
|
||||||
|
* range (exclusion_start and exclusion_length members). This
|
||||||
|
* happens when there are multiple exclusion ranges (IVMD entries)
|
||||||
|
* defined in ACPI table.
|
||||||
|
*/
|
||||||
|
if (m->flags & IVMD_FLAG_EXCL_RANGE)
|
||||||
|
e->prot = (IVMD_FLAG_IW | IVMD_FLAG_IR) >> 1;
|
||||||
|
|
||||||
DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x"
|
DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x"
|
||||||
" range_start: %016llx range_end: %016llx flags: %x\n", s,
|
" range_start: %016llx range_end: %016llx flags: %x\n", s,
|
||||||
PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start),
|
PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user