hw/riscv/virt-acpi-build: Add support for RIMT

RISC-V IO Mapping Table (RIMT) is a new static ACPI table used to
communicate IOMMU information to the OS. Add support for creating this
table when the IOMMU is present. The specification is frozen and
available at [1].

[1] - https://github.com/riscv-non-isa/riscv-acpi-rimt/releases/download/v0.99/rimt-spec.pdf

Signed-off-by: Sunil V L <sunilvl@ventanamicro.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Message-ID: <20250322043139.2003479-3-sunilvl@ventanamicro.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
Sunil V L 2025-03-22 10:01:38 +05:30 committed by Alistair Francis
parent d2a88aca76
commit cd18dbbf9d

View file

@ -198,6 +198,32 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
aml_append(scope, dev);
}
/*
* Add DSDT entry for the IOMMU platform device.
* ACPI ID for IOMMU is defined in the section 6.2 of RISC-V BRS spec.
* https://github.com/riscv-non-isa/riscv-brs/releases/download/v0.8/riscv-brs-spec.pdf
*/
static void acpi_dsdt_add_iommu_sys(Aml *scope, const MemMapEntry *iommu_memmap,
uint32_t iommu_irq)
{
uint32_t i;
Aml *dev = aml_device("IMU0");
aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0004")));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
Aml *crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(iommu_memmap->base,
iommu_memmap->size, AML_READ_WRITE));
for (i = iommu_irq; i < iommu_irq + 4; i++) {
aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_LOW,
AML_EXCLUSIVE, &i, 1));
}
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
/*
* Serial Port Console Redirection Table (SPCR)
* Rev: 1.10
@ -450,6 +476,9 @@ static void build_dsdt(GArray *table_data,
}
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
if (virt_is_iommu_sys_enabled(s)) {
acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_IRQ);
}
if (socket_count == 1) {
virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
@ -602,6 +631,187 @@ static void build_madt(GArray *table_data,
acpi_table_end(linker, &table);
}
#define ID_MAPPING_ENTRY_SIZE 20
#define IOMMU_ENTRY_SIZE 40
#define RISCV_INTERRUPT_WIRE_OFFSSET 40
#define ROOT_COMPLEX_ENTRY_SIZE 20
#define RIMT_NODE_OFFSET 48
/*
* ID Mapping Structure
*/
static void build_rimt_id_mapping(GArray *table_data, uint32_t source_id_base,
uint32_t num_ids, uint32_t dest_id_base)
{
/* Source ID Base */
build_append_int_noprefix(table_data, source_id_base, 4);
/* Number of IDs */
build_append_int_noprefix(table_data, num_ids, 4);
/* Destination Device ID Base */
build_append_int_noprefix(table_data, source_id_base, 4);
/* Destination IOMMU Offset */
build_append_int_noprefix(table_data, dest_id_base, 4);
/* Flags */
build_append_int_noprefix(table_data, 0, 4);
}
struct AcpiRimtIdMapping {
uint32_t source_id_base;
uint32_t num_ids;
};
typedef struct AcpiRimtIdMapping AcpiRimtIdMapping;
/* Build the rimt ID mapping to IOMMU for a given PCI host bridge */
static int rimt_host_bridges(Object *obj, void *opaque)
{
GArray *idmap_blob = opaque;
if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
if (bus && !pci_bus_bypass_iommu(bus)) {
int min_bus, max_bus;
pci_bus_range(bus, &min_bus, &max_bus);
AcpiRimtIdMapping idmap = {
.source_id_base = min_bus << 8,
.num_ids = (max_bus - min_bus + 1) << 8,
};
g_array_append_val(idmap_blob, idmap);
}
}
return 0;
}
static int rimt_idmap_compare(gconstpointer a, gconstpointer b)
{
AcpiRimtIdMapping *idmap_a = (AcpiRimtIdMapping *)a;
AcpiRimtIdMapping *idmap_b = (AcpiRimtIdMapping *)b;
return idmap_a->source_id_base - idmap_b->source_id_base;
}
/*
* RISC-V IO Mapping Table (RIMT)
* https://github.com/riscv-non-isa/riscv-acpi-rimt/releases/download/v0.99/rimt-spec.pdf
*/
static void build_rimt(GArray *table_data, BIOSLinker *linker,
RISCVVirtState *s)
{
int i, nb_nodes, rc_mapping_count;
size_t node_size, iommu_offset = 0;
uint32_t id = 0;
g_autoptr(GArray) iommu_idmaps = g_array_new(false, true,
sizeof(AcpiRimtIdMapping));
AcpiTable table = { .sig = "RIMT", .rev = 1, .oem_id = s->oem_id,
.oem_table_id = s->oem_table_id };
acpi_table_begin(&table, table_data);
object_child_foreach_recursive(object_get_root(),
rimt_host_bridges, iommu_idmaps);
/* Sort the ID mapping by Source ID Base*/
g_array_sort(iommu_idmaps, rimt_idmap_compare);
nb_nodes = 2; /* RC, IOMMU */
rc_mapping_count = iommu_idmaps->len;
/* Number of RIMT Nodes */
build_append_int_noprefix(table_data, nb_nodes, 4);
/* Offset to Array of RIMT Nodes */
build_append_int_noprefix(table_data, RIMT_NODE_OFFSET, 4);
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
iommu_offset = table_data->len - table.table_offset;
/* IOMMU Device Structure */
build_append_int_noprefix(table_data, 0, 1); /* Type - IOMMU*/
build_append_int_noprefix(table_data, 1, 1); /* Revision */
node_size = IOMMU_ENTRY_SIZE;
build_append_int_noprefix(table_data, node_size, 2); /* Length */
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
build_append_int_noprefix(table_data, id++, 2); /* ID */
if (virt_is_iommu_sys_enabled(s)) {
/* Hardware ID */
build_append_int_noprefix(table_data, 'R', 1);
build_append_int_noprefix(table_data, 'S', 1);
build_append_int_noprefix(table_data, 'C', 1);
build_append_int_noprefix(table_data, 'V', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '4', 1);
/* Base Address */
build_append_int_noprefix(table_data,
s->memmap[VIRT_IOMMU_SYS].base, 8);
build_append_int_noprefix(table_data, 0, 4); /* Flags */
} else {
/* Hardware ID */
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '1', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '0', 1);
build_append_int_noprefix(table_data, '1', 1);
build_append_int_noprefix(table_data, '4', 1);
build_append_int_noprefix(table_data, 0, 8); /* Base Address */
build_append_int_noprefix(table_data, 1, 4); /* Flags */
}
build_append_int_noprefix(table_data, 0, 4); /* Proximity Domain */
build_append_int_noprefix(table_data, 0, 2); /* PCI Segment number */
/* PCIe B/D/F */
if (virt_is_iommu_sys_enabled(s)) {
build_append_int_noprefix(table_data, 0, 2);
} else {
build_append_int_noprefix(table_data, s->pci_iommu_bdf, 2);
}
/* Number of interrupt wires */
build_append_int_noprefix(table_data, 0, 2);
/* Interrupt wire array offset */
build_append_int_noprefix(table_data, RISCV_INTERRUPT_WIRE_OFFSSET, 2);
/* PCIe Root Complex Node */
build_append_int_noprefix(table_data, 1, 1); /* Type */
build_append_int_noprefix(table_data, 1, 1); /* Revision */
node_size = ROOT_COMPLEX_ENTRY_SIZE +
ID_MAPPING_ENTRY_SIZE * rc_mapping_count;
build_append_int_noprefix(table_data, node_size, 2); /* Length */
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
build_append_int_noprefix(table_data, id++, 2); /* ID */
build_append_int_noprefix(table_data, 0, 4); /* Flags */
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
/* PCI Segment number */
build_append_int_noprefix(table_data, 0, 2);
/* ID mapping array offset */
build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 2);
/* Number of ID mappings */
build_append_int_noprefix(table_data, rc_mapping_count, 2);
/* Output Reference */
AcpiRimtIdMapping *range;
/* ID mapping array */
for (i = 0; i < iommu_idmaps->len; i++) {
range = &g_array_index(iommu_idmaps, AcpiRimtIdMapping, i);
if (virt_is_iommu_sys_enabled(s)) {
range->source_id_base = 0;
} else {
range->source_id_base = s->pci_iommu_bdf + 1;
}
range->num_ids = 0xffff - s->pci_iommu_bdf;
build_rimt_id_mapping(table_data, range->source_id_base,
range->num_ids, iommu_offset);
}
acpi_table_end(linker, &table);
}
/*
* ACPI spec, Revision 6.5+
* 5.2.16 System Resource Affinity Table (SRAT)
@ -679,6 +889,11 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables)
acpi_add_table(table_offsets, tables_blob);
build_rhct(tables_blob, tables->linker, s);
if (virt_is_iommu_sys_enabled(s) || s->pci_iommu_bdf) {
acpi_add_table(table_offsets, tables_blob);
build_rimt(tables_blob, tables->linker, s);
}
acpi_add_table(table_offsets, tables_blob);
spcr_setup(tables_blob, tables->linker, s);