AMD/IOMMU: add "ivmd=" command line option

Just like VT-d's "rmrr=" it can be used to cover for firmware omissions.
Since systems surfacing IVMDs seem to be rare, it is also meant to allow
testing of the involved code.

Only the IVMD flavors actually understood by the IVMD parsing logic can
be generated, and for this initial implementation there's also no way to
control the flags field - unity r/w mappings are assumed.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Paul Durrant <paul@xen.org>
This commit is contained in:
Jan Beulich committed 2021-09-22 16:17:04 +02:00
1 parent 2209c36007
commit c7146535d1
2 files changed
+114 -3

No files matched your search

+27 -2
View File
@@ -836,12 +836,12 @@ Controls for the dom0 IOMMU setup.
Typically, some devices in a system use bits of RAM for communication, and
these areas should be listed as reserved in the E820 table and identified
via RMRR or IVMD entries in the APCI tables, so Xen can ensure that they
via RMRR or IVMD entries in the ACPI tables, so Xen can ensure that they
are identity-mapped in the IOMMU. However, some firmware makes mistakes,
and this option is a coarse-grain workaround for those errors.
Where possible, finer grain corrections should be made with the `rmrr=`,
`ivrs_hpet=` or `ivrs_ioapic=` command line options.
`ivmd=`, `ivrs_hpet[]=`, or `ivrs_ioapic[]=` command line options.
This option is disabled by default, and deprecated and intended for
removal in future versions of Xen. If specifying `map-inclusive` is the
@@ -1523,6 +1523,31 @@ _dom0-iommu=map-inclusive_ - using both options in combination is undefined.**
> `= <integer>`
### irq_vector_map (x86)
### ivmd (x86)
> `= <start>[-<end>][=<bdf1>[-<bdf1'>][,<bdf2>[-<bdf2'>][,...]]][;<start>...]`
Define IVMD-like ranges that are missing from ACPI tables along with the
device(s) they belong to, and use them for 1:1 mapping. End addresses can be
omitted when exactly one page is meant. The ranges are inclusive when start
and end are specified. Note that only PCI segment 0 is supported at this time,
but it is fine to specify it explicitly.
'start' and 'end' values are page numbers (not full physical addresses),
in hexadecimal format (can optionally be preceded by "0x").
Omitting the optional (range of) BDF spcifiers signals that the range is to
be applied to all devices.
Usage example: If device 0:0:1d.0 requires one page (0xd5d45) to be
reserved, and devices 0:0:1a.0...0:0:1a.3 collectively require three pages
(0xd5d46 thru 0xd5d48) to be reserved, one usage would be:
ivmd=d5d45=0:1d.0;0xd5d46-0xd5d48=0:1a.0-0:1a.3
Note: grub2 requires to escape or quote special characters, like ';' when
multiple ranges are specified - refer to the grub2 documentation.
### ivrs_hpet[`<hpet>`] (AMD)
> `=[<seg>:]<bus>:<device>.<func>`
+87 -1
View File
@@ -1049,6 +1049,9 @@ static void __init dump_acpi_table_header(struct acpi_table_header *table)
}
static struct acpi_ivrs_memory __initdata user_ivmds[8];
static unsigned int __initdata nr_ivmd;
#define to_ivhd_block(hdr) \
container_of(hdr, const struct acpi_ivrs_hardware, header)
#define to_ivmd_block(hdr) \
@@ -1072,7 +1075,7 @@ static int __init parse_ivrs_table(struct acpi_table_header *table)
{
const struct acpi_ivrs_header *ivrs_block;
unsigned long length;
unsigned int apic;
unsigned int apic, i;
bool_t sb_ioapic = !iommu_intremap;
int error = 0;
@@ -1107,6 +1110,12 @@ static int __init parse_ivrs_table(struct acpi_table_header *table)
length += ivrs_block->length;
}
/* Add command line specified IVMD-equivalents. */
if ( nr_ivmd )
AMD_IOMMU_DEBUG("IVMD: %u command line provided entries\n", nr_ivmd);
for ( i = 0; !error && i < nr_ivmd; ++i )
error = parse_ivmd_block(user_ivmds + i);
/* Each IO-APIC must have been mentioned in the table. */
for ( apic = 0; !error && iommu_intremap && apic < nr_ioapics; ++apic )
{
@@ -1348,3 +1357,80 @@ int __init amd_iommu_get_supported_ivhd_type(void)
{
return acpi_table_parse(ACPI_SIG_IVRS, get_supported_ivhd_type);
}
/*
* Parse "ivmd" command line option to later add the parsed devices / regions
* into unity mapping lists, just like IVMDs parsed from ACPI.
* Format:
* ivmd=<start>[-<end>][=<bdf1>[-<bdf1>'][,<bdf2>[-<bdf2>'][,...]]][;<start>...]
*/
static int __init parse_ivmd_param(const char *s)
{
do {
unsigned long start, end;
const char *cur;
if ( nr_ivmd >= ARRAY_SIZE(user_ivmds) )
return -E2BIG;
start = simple_strtoul(cur = s, &s, 16);
if ( cur == s )
return -EINVAL;
if ( *s == '-' )
{
end = simple_strtoul(cur = s + 1, &s, 16);
if ( cur == s || end < start )
return -EINVAL;
}
else
end = start;
if ( *s != '=' )
{
user_ivmds[nr_ivmd].start_address = start << PAGE_SHIFT;
user_ivmds[nr_ivmd].memory_length = (end - start + 1) << PAGE_SHIFT;
user_ivmds[nr_ivmd].header.flags = ACPI_IVMD_UNITY |
ACPI_IVMD_READ | ACPI_IVMD_WRITE;
user_ivmds[nr_ivmd].header.length = sizeof(*user_ivmds);
user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_ALL;
++nr_ivmd;
continue;
}
do {
unsigned int seg, bus, dev, func;
if ( nr_ivmd >= ARRAY_SIZE(user_ivmds) )
return -E2BIG;
s = parse_pci(s + 1, &seg, &bus, &dev, &func);
if ( !s || seg )
return -EINVAL;
user_ivmds[nr_ivmd].start_address = start << PAGE_SHIFT;
user_ivmds[nr_ivmd].memory_length = (end - start + 1) << PAGE_SHIFT;
user_ivmds[nr_ivmd].header.flags = ACPI_IVMD_UNITY |
ACPI_IVMD_READ | ACPI_IVMD_WRITE;
user_ivmds[nr_ivmd].header.length = sizeof(*user_ivmds);
user_ivmds[nr_ivmd].header.device_id = PCI_BDF(bus, dev, func);
user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_ONE;
if ( *s == '-' )
{
s = parse_pci(s + 1, &seg, &bus, &dev, &func);
if ( !s || seg )
return -EINVAL;
user_ivmds[nr_ivmd].aux_data = PCI_BDF(bus, dev, func);
if ( user_ivmds[nr_ivmd].aux_data <
user_ivmds[nr_ivmd].header.device_id )
return -EINVAL;
user_ivmds[nr_ivmd].header.type = ACPI_IVRS_TYPE_MEMORY_RANGE;
}
} while ( ++nr_ivmd, *s == ',' );
} while ( *s++ == ';' );
return s[-1] ? -EINVAL : 0;
}
custom_param("ivmd", parse_ivmd_param);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.