@samitouri / QOSamiQemu / commits / c08830df14

hw/acpi: add cache hierarchy to pptt table

Add cache topology to PPTT table. Signed-off-by: Alireza Sanaee <alireza.sanaee@huawei.com> Reviewed-by: Gustavo Romero <gustavo.romero@linaro.org> Message-id: 20260311160609.358-7-alireza.sanaee@huawei.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Alireza Sanaee committed Apr 23, 2026 at 10:24 UTC c08830df14551c8722687af817da231116ed0a88
3 files changed +209 -9
hw/acpi/aml-build.c
+193 -7
@@ -34,6 +34,7 @@
34 #include "hw/pci/pci_bridge.h"
35 #include "hw/acpi/acpi_aml_interface.h"
36 #include "qemu/cutils.h"
37 +#include "hw/core/cpu.h"
38
39 static GArray *build_alloc_array(void)
40 {
@@ -2150,6 +2151,108 @@ void build_spcr(GArray *table_data, BIOSLinker *linker,
2151 }
2152 acpi_table_end(linker, &table);
2153 }
2154 +
2155 +/*
2156 + * ACPI spec, Revision 6.3
2157 + * 5.2.29.2 Cache Type Structure (Type 1)
2158 + */
2159 +static void build_cache_nodes(GArray *tbl, CPUCoreCaches *cache,
2160 + uint32_t next_offset)
2161 +{
2162 + const uint8_t node_length = 24;
2163 + int start_len = tbl->len;
2164 + int val;
2165 +
2166 + build_append_byte(tbl, 1); /* Type 1 - cache */
2167 + build_append_byte(tbl, node_length); /* Length */
2168 + build_append_int_noprefix(tbl, 0, 2); /* Reserved */
2169 + build_append_int_noprefix(tbl, 0x7f, 4); /* Flags */
2170 + build_append_int_noprefix(tbl, next_offset, 4); /* Next Level of Cache */
2171 + build_append_int_noprefix(tbl, cache->size, 4); /* Size */
2172 + build_append_int_noprefix(tbl, cache->sets, 4); /* Number of sets */
2173 + build_append_byte(tbl, cache->associativity); /* Associativity */
2174 + val = 0x3;
2175 + switch (cache->type) {
2176 + case INSTRUCTION_CACHE:
2177 + val |= (1 << 2); /* Instruction Cache */
2178 + break;
2179 + case DATA_CACHE:
2180 + val |= (0 << 2); /* Data Cache */
2181 + break;
2182 + case UNIFIED_CACHE:
2183 + val |= (3 << 2); /* Unified */
2184 + break;
2185 + }
2186 + build_append_byte(tbl, val); /* Attributes */
2187 + build_append_int_noprefix(tbl, cache->linesize, 2); /* Line size */
2188 + g_assert(tbl->len == start_len + node_length);
2189 +}
2190 +
2191 +/*
2192 + * Build PPTT Cache Type structures (Type 1) from cache level `level_high`
2193 + * down to `level_low` (both inclusive), appending them to the PPTT table.
2194 + *
2195 + * On output, `data_offset` and `instr_offset` hold the PPTT offsets of the
2196 + * lowest-level data and instruction cache nodes respectively. These offsets
2197 + * are referenced as private resources in the Processor Hierarchy Node (Type 0)
2198 + * that owns the caches.
2199 + */
2200 +static bool build_caches(GArray *table_data, uint32_t pptt_start,
2201 + int num_caches, CPUCoreCaches *caches,
2202 + uint8_t level_high, /* Inclusive */
2203 + uint8_t level_low, /* Inclusive */
2204 + uint32_t *data_offset,
2205 + uint32_t *instr_offset)
2206 +{
2207 + uint32_t next_level_offset_data = 0, next_level_offset_instruction = 0;
2208 + uint32_t this_offset, next_offset = 0;
2209 + int c, level;
2210 + bool found_cache = false;
2211 +
2212 + /* Walk caches from top to bottom */
2213 + for (level = level_high; level >= level_low; level--) {
2214 + for (c = 0; c < num_caches; c++) {
2215 + if (caches[c].level != level) {
2216 + continue;
2217 + }
2218 +
2219 + /* Assume only unified above l1 for now */
2220 + this_offset = table_data->len - pptt_start;
2221 + switch (caches[c].type) {
2222 + case INSTRUCTION_CACHE:
2223 + next_offset = next_level_offset_instruction;
2224 + break;
2225 + case DATA_CACHE:
2226 + next_offset = next_level_offset_data;
2227 + break;
2228 + case UNIFIED_CACHE:
2229 + /* Either is fine here */
2230 + next_offset = next_level_offset_instruction;
2231 + break;
2232 + }
2233 + build_cache_nodes(table_data, &caches[c], next_offset);
2234 + switch (caches[c].type) {
2235 + case INSTRUCTION_CACHE:
2236 + next_level_offset_instruction = this_offset;
2237 + break;
2238 + case DATA_CACHE:
2239 + next_level_offset_data = this_offset;
2240 + break;
2241 + case UNIFIED_CACHE:
2242 + next_level_offset_instruction = this_offset;
2243 + next_level_offset_data = this_offset;
2244 + break;
2245 + }
2246 + *data_offset = next_level_offset_data;
2247 + *instr_offset = next_level_offset_instruction;
2248 +
2249 + found_cache = true;
2250 + }
2251 + }
2252 +
2253 + return found_cache;
2254 +}
2255 +
2256 /*
2257 * ACPI spec, Revision 6.3
2258 * 5.2.29 Processor Properties Topology Table (PPTT)
@@ -2160,11 +2263,31 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
2263 {
2264 MachineClass *mc = MACHINE_GET_CLASS(ms);
2265 CPUArchIdList *cpus = ms->possible_cpus;
2163 - int64_t socket_id = -1, cluster_id = -1, core_id = -1;
2164 - uint32_t socket_offset = 0, cluster_offset = 0, core_offset = 0;
2266 + uint32_t core_data_offset = 0;
2267 + uint32_t core_instr_offset = 0;
2268 + uint32_t cluster_instr_offset = 0;
2269 + uint32_t cluster_data_offset = 0;
2270 + uint32_t node_data_offset = 0;
2271 + uint32_t node_instr_offset = 0;
2272 + int top_node = 3;
2273 + int top_cluster = 3;
2274 + int top_core = 3;
2275 + int bottom_node = 3;
2276 + int bottom_cluster = 3;
2277 + int bottom_core = 3;
2278 + int64_t socket_id = -1;
2279 + int64_t cluster_id = -1;
2280 + int64_t core_id = -1;
2281 + uint32_t socket_offset = 0;
2282 + uint32_t cluster_offset = 0;
2283 + uint32_t core_offset = 0;
2284 uint32_t pptt_start = table_data->len;
2285 uint32_t root_offset;
2286 int n;
2287 + uint32_t priv_rsrc[2];
2288 + uint32_t num_priv = 0;
2289 + bool cache_at_topo_level;
2290 +
2291 AcpiTable table = { .sig = "PPTT", .rev = 2,
2292 .oem_id = oem_id, .oem_table_id = oem_table_id };
2293
@@ -2194,11 +2317,29 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
2317 socket_id = cpus->cpus[n].props.socket_id;
2318 cluster_id = -1;
2319 core_id = -1;
2320 + bottom_node = top_node;
2321 + num_priv = 0;
2322 + cache_at_topo_level =
2323 + machine_find_lowest_level_cache_at_topo_level(
2324 + ms, &bottom_node, CPU_TOPOLOGY_LEVEL_SOCKET);
2325 + if (cache_at_topo_level) {
2326 + build_caches(table_data, pptt_start, num_caches, caches,
2327 + top_node, bottom_node, &node_data_offset,
2328 + &node_instr_offset);
2329 + priv_rsrc[0] = node_instr_offset;
2330 + priv_rsrc[1] = node_data_offset;
2331 + if (node_instr_offset || node_data_offset) {
2332 + num_priv = node_instr_offset == node_data_offset ? 1 : 2;
2333 + }
2334 +
2335 + top_cluster = bottom_node - 1;
2336 + }
2337 +
2338 socket_offset = table_data->len - pptt_start;
2339 build_processor_hierarchy_node(table_data,
2340 (1 << 0) | /* Physical package */
2341 (1 << 4), /* Identical Implementation */
2201 - root_offset, socket_id, NULL, 0);
2342 + root_offset, socket_id, priv_rsrc, num_priv);
2343 }
2344
2345 if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) {
@@ -2206,21 +2347,66 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
2347 assert(cpus->cpus[n].props.cluster_id > cluster_id);
2348 cluster_id = cpus->cpus[n].props.cluster_id;
2349 core_id = -1;
2350 + bottom_cluster = top_cluster;
2351 + num_priv = 0;
2352 + cache_at_topo_level =
2353 + machine_find_lowest_level_cache_at_topo_level(
2354 + ms, &bottom_cluster, CPU_TOPOLOGY_LEVEL_CLUSTER);
2355 +
2356 + if (cache_at_topo_level) {
2357 + build_caches(table_data, pptt_start, num_caches, caches,
2358 + top_cluster, bottom_cluster,
2359 + &cluster_data_offset, &cluster_instr_offset);
2360 + priv_rsrc[0] = cluster_instr_offset;
2361 + priv_rsrc[1] = cluster_data_offset;
2362 + if (cluster_instr_offset || cluster_data_offset) {
2363 + num_priv =
2364 + cluster_instr_offset == cluster_data_offset ? 1 : 2;
2365 + }
2366 + top_core = bottom_cluster - 1;
2367 + } else if (top_cluster == bottom_node - 1) {
2368 + /* socket cache but no cluster cache */
2369 + top_core = bottom_node - 1;
2370 + }
2371 +
2372 cluster_offset = table_data->len - pptt_start;
2373 build_processor_hierarchy_node(table_data,
2374 (0 << 0) | /* Not a physical package */
2375 (1 << 4), /* Identical Implementation */
2213 - socket_offset, cluster_id, NULL, 0);
2376 + socket_offset, cluster_id, priv_rsrc, num_priv);
2377 }
2378 } else {
2379 + if (machine_defines_cache_at_topo_level(
2380 + ms, CPU_TOPOLOGY_LEVEL_CLUSTER)) {
2381 + error_setg(&error_fatal, "Not clusters found for the cache");
2382 + return;
2383 + }
2384 +
2385 cluster_offset = socket_offset;
2386 + top_core = bottom_node - 1; /* there is no cluster */
2387 + }
2388 +
2389 + if (cpus->cpus[n].props.core_id != core_id) {
2390 + bottom_core = top_core;
2391 + num_priv = 0;
2392 + cache_at_topo_level =
2393 + machine_find_lowest_level_cache_at_topo_level(
2394 + ms, &bottom_core, CPU_TOPOLOGY_LEVEL_CORE);
2395 + if (cache_at_topo_level) {
2396 + build_caches(table_data, pptt_start, num_caches, caches,
2397 + top_core, bottom_core, &core_data_offset,
2398 + &core_instr_offset);
2399 + priv_rsrc[0] = core_instr_offset;
2400 + priv_rsrc[1] = core_data_offset;
2401 + num_priv = core_instr_offset == core_data_offset ? 1 : 2;
2402 + }
2403 }
2404
2405 if (ms->smp.threads == 1) {
2406 build_processor_hierarchy_node(table_data,
2407 (1 << 1) | /* ACPI Processor ID valid */
2222 - (1 << 3), /* Node is a Leaf */
2223 - cluster_offset, n, NULL, 0);
2408 + (1 << 3), /* Node is a Leaf */
2409 + cluster_offset, n, priv_rsrc, num_priv);
2410 } else {
2411 if (cpus->cpus[n].props.core_id != core_id) {
2412 assert(cpus->cpus[n].props.core_id > core_id);
@@ -2229,7 +2415,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
2415 build_processor_hierarchy_node(table_data,
2416 (0 << 0) | /* Not a physical package */
2417 (1 << 4), /* Identical Implementation */
2232 - cluster_offset, core_id, NULL, 0);
2418 + cluster_offset, core_id, priv_rsrc, num_priv);
2419 }
2420
2421 build_processor_hierarchy_node(table_data,
hw/arm/virt-acpi-build.c
+6 -2
@@ -1255,6 +1255,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
1255 unsigned dsdt, xsdt;
1256 GArray *tables_blob = tables->table_data;
1257 MachineState *ms = MACHINE(vms);
1258 + CPUCoreCaches caches[CPU_MAX_CACHES];
1259 + unsigned int num_caches;
1260 +
1261 + num_caches = virt_get_caches(vms, caches);
1262
1263 table_offsets = g_array_new(false, true /* clear */,
1264 sizeof(uint32_t));
@@ -1276,8 +1280,8 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
1280
1281 if (!vmc->no_cpu_topology) {
1282 acpi_add_table(table_offsets, tables_blob);
1279 - build_pptt(tables_blob, tables->linker, ms,
1280 - vms->oem_id, vms->oem_table_id, 0, NULL);
1283 + build_pptt(tables_blob, tables->linker, ms, vms->oem_id,
1284 + vms->oem_table_id, num_caches, caches);
1285 }
1286
1287 acpi_add_table(table_offsets, tables_blob);
include/hw/acpi/cpu.h
+10
@@ -69,6 +69,16 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
69
70 void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list);
71
72 +struct CPUPPTTCaches {
73 + enum CacheType type;
74 + uint32_t sets;
75 + uint32_t size;
76 + uint32_t level;
77 + uint16_t linesize;
78 + uint8_t attributes; /* write policy: 0x0 write back, 0x1 write through */
79 + uint8_t associativity;
80 +};
81 +
82 extern const VMStateDescription vmstate_cpu_hotplug;
83 #define VMSTATE_CPU_HOTPLUG(cpuhp, state) \
84 VMSTATE_STRUCT(cpuhp, state, 1, \