Add type=2 SMMU Component subtable generation to build_tlbi(). The Identifier of each SMMU subtable in the TLBI table corresponds one-to-one to the Identifier of the SMMU node in the IORT table. In IORT, the ITS Group node has Identifier=0 and SMMU nodes start at 1, so when SMMU is configured the TLBI type=2 subtable Identifier starts at 1 accordingly. When no SMMU is configured but -tlbidomain is used, a placeholder subtable with Identifier=0 is emitted. Regardless of whether SMMU is configured, each SMMU subtable's domain array is set to [0..num_vdomains-1], ensuring that any domain-scoped TLBI broadcast reaches the SMMU. Signed-off-by: Tian Zheng <zhengtian10@huawei.com> --- hw/arm/virt-acpi-build.c | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 344909afd7..5bbae18af1 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1243,8 +1243,8 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * Build the TLBI ACPI table (signature "TLBI"). * * Table layout (after standard ACPI header): - * [4 bytes] max_cpus — number of PE Component entries - * For each vCPU (PE Component subtable): + * [4 bytes] nr_components - number of PE + SMMU Component entries + * For each vCPU (PE Component subtable, type = 1): * [2] type = 1 (PE Component) * [2] reserved * [4] component_length (total bytes of this subtable, including domain IDs) @@ -1253,19 +1253,40 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * [4] domain_offset (byte offset from TLBI Header start to * the domain ID array, or 0) * [domain_count * 4] domain IDs + * For each SMMU (SMMU Component subtable, type = 2): + * [2] type = 2 (SMMU Component) + * [2] reserved + * [4] component_length + * [4] identifier (IORT SMMU node Identifier) + * [4] domain_count + * [4] domain_offset + * [domain_count * 4] domain IDs * * Guest kernel parses this via acpi_tlbi_init() to learn TLBI vdomain topology. + * The kernel requires at least one SMMU Component subtable to be present; + * a placeholder entry (identifier = 0) is emitted when no SMMU is configured. */ static void build_tlbi(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { unsigned int max_cpus = MACHINE(vms)->smp.max_cpus; int i, j; + int num_smmus = 0; + int num_smmu_entries; + + if (vms->smmu_accel_count) { + num_smmus = vms->smmu_accel_count; + } else if (virt_has_smmuv3(vms)) { + num_smmus = 1; + } + num_smmu_entries = num_smmus > 0 ? num_smmus : 1; + AcpiTable table = { .sig = "TLBI", .rev = 1, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; acpi_table_begin(&table, table_data); - build_append_int_noprefix(table_data, max_cpus, 4); /* PE count */ + /* Number of components = PE count + SMMU count */ + build_append_int_noprefix(table_data, max_cpus + num_smmu_entries, 4); for (i = 0; i < (int)max_cpus; i++) { GArray *dom_ids = g_array_new(false, false, sizeof(uint32_t)); @@ -1302,6 +1323,48 @@ build_tlbi(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) g_array_free(dom_ids, true); } + for (i = 0; i < num_smmu_entries; i++) { + GArray *dom_ids = g_array_new(false, false, sizeof(uint32_t)); + uint32_t component_len, domain_offset; + + /* + * Each SMMU belongs to all vdomains so that any domain-scoped + * TLBI broadcast reaches every SMMU. The kernel assumes + * "each domain is assumed to contain all system-wide SMMUs". + */ + for (j = 0; j < vms->num_vdomains; j++) { + uint32_t id = j; + g_array_append_val(dom_ids, id); + } + + component_len = 20 + dom_ids->len * sizeof(uint32_t); + domain_offset = dom_ids->len > 0 + ? (table_data->len - table.table_offset + 20) : 0; + + /* Type: SMMU Component */ + build_append_int_noprefix(table_data, 2, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); + /* Length */ + build_append_int_noprefix(table_data, component_len, 4); + /* + * Identifier: matches the IORT SMMU node Identifier (ITS=0, + * SMMU nodes start at 1). Use 0 for a placeholder entry when + * no SMMU is configured. + */ + build_append_int_noprefix(table_data, + num_smmus > 0 ? 1 + i : 0, 4); + /* Domain count */ + build_append_int_noprefix(table_data, dom_ids->len, 4); + /* Domain offset */ + build_append_int_noprefix(table_data, domain_offset, 4); + for (j = 0; j < (int)dom_ids->len; j++) { + build_append_int_noprefix(table_data, + g_array_index(dom_ids, uint32_t, j), 4); + } + g_array_free(dom_ids, true); + } + acpi_table_end(linker, &table); } -- 2.33.0