diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9032a5df1c..4ae4cbc6cd 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -257,6 +257,29 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) #define ROOT_COMPLEX_ENTRY_SIZE 36 #define IORT_NODE_OFFSET 48 +#define IORT_RMR_NUM_ID_MAPPINGS 1 +#define IORT_RMR_NUM_MEM_RANGE_DESC 1 +#define IORT_RMR_COMMON_HEADER_SIZE 28 +#define IORT_RMR_MEM_RANGE_DESC_SIZE 20 + +/* + * IORT RMR flags: + * Bit[0] = 0 Disallow remapping of reserved ranges + * Bit[1] = 0 Unprivileged access + * Bits[9:2] = 0x00 Device nGnRnE memory + */ +#define IORT_RMR_FLAGS 0 + +/* + * MSI doorbell IOVA window used by the host kernel SMMUv3 driver. + * Described in IORT RMR nodes to reserve the IOVA range where the host + * kernel maps physical MSI doorbells for devices. This ensures guests + * preserve a flat mapping for MSI doorbell in nested SMMUv3(accel=on) + * configurations. + */ +#define MSI_IOVA_BASE 0x8000000 +#define MSI_IOVA_LENGTH 0x100000 + /* * Append an ID mapping entry as described by "Table 4 ID mapping format" in * "IO Remapping Table System Software on ARM Platforms", Chapter 3. @@ -265,7 +288,8 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) * Note that @id_count gets internally subtracted by one, following the spec. */ static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, - uint32_t id_count, uint32_t out_ref) + uint32_t id_count, uint32_t out_ref, + uint32_t flags) { build_append_int_noprefix(table_data, input_base, 4); /* Input base */ /* Number of IDs - The number of IDs in the range minus one */ @@ -273,7 +297,7 @@ static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, build_append_int_noprefix(table_data, input_base, 4); /* Output base */ build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ /* Flags */ - build_append_int_noprefix(table_data, 0 /* Single mapping (disabled) */, 4); + build_append_int_noprefix(table_data, flags, 4); } struct AcpiIortIdMapping { @@ -321,6 +345,7 @@ typedef struct AcpiIortSMMUv3Dev { GArray *rc_smmu_idmaps; /* Offset of the SMMUv3 IORT Node relative to the start of the IORT */ size_t offset; + bool accel; } AcpiIortSMMUv3Dev; /* @@ -330,7 +355,7 @@ typedef struct AcpiIortSMMUv3Dev { static int populate_smmuv3_legacy_dev(GArray *sdev_blob) { VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); - AcpiIortSMMUv3Dev sdev; + AcpiIortSMMUv3Dev sdev = {0}; sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); object_child_foreach_recursive(object_get_root(), iort_host_bridges, @@ -362,10 +387,10 @@ static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b) static int iort_smmuv3_devices(Object *obj, void *opaque) { VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + AcpiIortSMMUv3Dev sdev = {0}; GArray *sdev_blob = opaque; AcpiIortIdMapping idmap; PlatformBusDevice *pbus; - AcpiIortSMMUv3Dev sdev; int min_bus, max_bus; SysBusDevice *sbdev; PCIBus *bus; @@ -375,6 +400,9 @@ static int iort_smmuv3_devices(Object *obj, void *opaque) } bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort)); + if (object_property_find(obj, "accel")) { + sdev.accel = object_property_get_bool(obj, "accel", &error_abort); + } pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); sbdev = SYS_BUS_DEVICE(obj); sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); @@ -448,10 +476,69 @@ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs) } } +static void +build_iort_rmr_nodes(GArray *table_data, GArray *smmuv3_devices, uint32_t *id) +{ + AcpiIortSMMUv3Dev *sdev; + AcpiIortIdMapping *idmap; + int i; + + for (i = 0; i < smmuv3_devices->len; i++) { + uint16_t rmr_len; + int bdf; + + sdev = &g_array_index(smmuv3_devices, AcpiIortSMMUv3Dev, i); + if (!sdev->accel) { + continue; + } + + /* + * Spec reference:Arm IO Remapping Table(IORT), ARM DEN 0049E.d, + * Section 3.1.1.5 "Reserved Memory Range node" + */ + idmap = &g_array_index(sdev->rc_smmu_idmaps, AcpiIortIdMapping, 0); + bdf = idmap->input_base; + rmr_len = IORT_RMR_COMMON_HEADER_SIZE + + (IORT_RMR_NUM_ID_MAPPINGS * ID_MAPPING_ENTRY_SIZE) + + (IORT_RMR_NUM_MEM_RANGE_DESC * IORT_RMR_MEM_RANGE_DESC_SIZE); + + /* Table 18 Reserved Memory Range Node */ + build_append_int_noprefix(table_data, 6 /* RMR */, 1); /* Type */ + /* Length */ + build_append_int_noprefix(table_data, rmr_len, 2); + build_append_int_noprefix(table_data, 3, 1); /* Revision */ + build_append_int_noprefix(table_data, (*id)++, 4); /* Identifier */ + /* Number of ID mappings */ + build_append_int_noprefix(table_data, IORT_RMR_NUM_ID_MAPPINGS, 4); + /* Reference to ID Array */ + build_append_int_noprefix(table_data, IORT_RMR_COMMON_HEADER_SIZE, 4); + + /* RMR specific data */ + + /* Flags */ + build_append_int_noprefix(table_data, IORT_RMR_FLAGS, 4); + /* Number of Memory Range Descriptors */ + build_append_int_noprefix(table_data, IORT_RMR_NUM_MEM_RANGE_DESC, 4); + /* Reference to Memory Range Descriptors */ + build_append_int_noprefix(table_data, IORT_RMR_COMMON_HEADER_SIZE + + (IORT_RMR_NUM_ID_MAPPINGS * ID_MAPPING_ENTRY_SIZE), 4); + build_iort_id_mapping(table_data, bdf, idmap->id_count, sdev->offset, + 1); + + /* Table 19 Memory Range Descriptor */ + + /* Physical Range offset */ + build_append_int_noprefix(table_data, MSI_IOVA_BASE, 8); + /* Physical Range length */ + build_append_int_noprefix(table_data, MSI_IOVA_LENGTH, 8); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + } +} + /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", - * Document number: ARM DEN 0049E.b, Feb 2021 + * Document number: ARM DEN 0049E.d, Feb 2022 */ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) @@ -465,7 +552,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) GArray *smmuv3_devs = g_array_new(false, true, sizeof(AcpiIortSMMUv3Dev)); GArray *rc_its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, + AcpiTable table = { .sig = "IORT", .rev = 5, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; /* Table 2 The IORT */ acpi_table_begin(&table, table_data); @@ -491,6 +578,13 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) nb_nodes++; /* ITS */ rc_mapping_count += rc_its_idmaps->len; } + /* Calculate RMR nodes required. One per SMMUv3 with accelerated mode */ + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + if (sdev->accel) { + nb_nodes++; + } + } } else { if (vms->its) { nb_nodes = 2; /* RC and ITS */ @@ -563,7 +657,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Array of ID mappings */ if (smmu_mapping_count) { /* Output IORT node is the ITS Group node (the first node). */ - build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET, 0); } } @@ -615,7 +709,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) AcpiIortIdMapping, j); /* Output IORT node is the SMMUv3 node. */ build_iort_id_mapping(table_data, range->input_base, - range->id_count, sdev->offset); + range->id_count, sdev->offset, 0); } } @@ -628,7 +722,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) range = &g_array_index(rc_its_idmaps, AcpiIortIdMapping, i); /* Output IORT node is the ITS Group node (the first node). */ build_iort_id_mapping(table_data, range->input_base, - range->id_count, IORT_NODE_OFFSET); + range->id_count, IORT_NODE_OFFSET, 0); } } } else { @@ -637,9 +731,10 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * SMMU: RC -> ITS. * Output IORT node is the ITS Group node (the first node). */ - build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET, 0); } + build_iort_rmr_nodes(table_data, smmuv3_devs, &id); acpi_table_end(linker, &table); g_array_free(rc_its_idmaps, true); for (i = 0; i < num_smmus; i++) {