Lean Sheng Tan has submitted this change. ( https://review.coreboot.org/c/coreboot/+/78071?usp=email )
Change subject: acpi: Add PPTT support ......................................................................
acpi: Add PPTT support
This patch adds code to generate Processor Properties Topology Tables (PPTT) compliant to the ACPI 6.4 specification.
- The 'acpi_get_pptt_topology' hook is mandatory once ACPI_PPTT is selected. Its purpose is to return a pointer to a topology tree, which describes the relationship between CPUs and caches. The hook can be provided by, for example, mainboard code.
Background: We are currently working on mainboard code for qemu-sbsa and Neoverse N2. Both require a valid PPTT table. Patch was tested against the qemu-sbsa board.
Change-Id: Ia119e1ba15756704668116bdbc655190ec94ff10 Signed-off-by: David Milosevic David.Milosevic@9elements.com Reviewed-on: https://review.coreboot.org/c/coreboot/+/78071 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Arthur Heymans arthur@aheymans.xyz Reviewed-by: Lean Sheng Tan sheng.tan@9elements.com --- M src/acpi/Kconfig M src/acpi/Makefile.inc M src/acpi/acpi.c A src/acpi/acpi_pptt.c M src/include/acpi/acpi.h 5 files changed, 324 insertions(+), 1 deletion(-)
Approvals: Lean Sheng Tan: Looks good to me, approved build bot (Jenkins): Verified Arthur Heymans: Looks good to me, approved
diff --git a/src/acpi/Kconfig b/src/acpi/Kconfig index cb2befb..cf51969 100644 --- a/src/acpi/Kconfig +++ b/src/acpi/Kconfig @@ -91,3 +91,17 @@ default 144 help Set the maximum size of all ACPI tables in KiB. + +config ACPI_PPTT + bool + depends on HAVE_ACPI_TABLES + help + Selected to build an ACPI Processor Properties Topology Table. + +config ACPI_PPTT_MAX_CACHES + int + depends on ACPI_PPTT + default 4 + help + This variable sets the maximum number of distinct caches per + topology level. Increasing this option also increases stack usage. diff --git a/src/acpi/Makefile.inc b/src/acpi/Makefile.inc index cb0bbb4..6bb34cb 100644 --- a/src/acpi/Makefile.inc +++ b/src/acpi/Makefile.inc @@ -8,6 +8,7 @@ ramstage-y += acpi_dmar.c ramstage-y += acpi_hpet.c endif +ramstage-$(CONFIG_ACPI_PPTT) += acpi_pptt.c ramstage-y += acpigen.c ramstage-y += acpigen_dptf.c ramstage-y += acpigen_dsm.c diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c index 942d464..e40d4ac 100644 --- a/src/acpi/acpi.c +++ b/src/acpi/acpi.c @@ -1204,6 +1204,18 @@ return lpi_desc->header.length; }
+static void acpi_create_pptt(acpi_header_t *header, void *unused) +{ + if (!CONFIG(ACPI_PPTT)) + return; + + if (acpi_fill_header(header, "PPTT", PPTT, sizeof(acpi_pptt_t)) != CB_SUCCESS) + return; + + acpi_pptt_t *pptt = (acpi_pptt_t *)header; + acpi_create_pptt_body(pptt); +} + static uint8_t acpi_spcr_type(void) { /* 16550-compatible with parameters defined in Generic Address Structure */ @@ -1394,6 +1406,7 @@ { acpi_create_bert, NULL, sizeof(acpi_bert_t) }, { acpi_create_spcr, NULL, sizeof(acpi_spcr_t) }, { acpi_create_gtdt, NULL, sizeof(acpi_gtdt_t) }, + { acpi_create_pptt, NULL, sizeof(acpi_pptt_t) }, };
current = start; @@ -1743,6 +1756,8 @@ return 4; case GTDT: return 3; + case PPTT: /* ACPI 6.4 */ + return 3; default: return -1; } diff --git a/src/acpi/acpi_pptt.c b/src/acpi/acpi_pptt.c new file mode 100644 index 0000000..3822bf7 --- /dev/null +++ b/src/acpi/acpi_pptt.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> +#include <include/assert.h> +#include <console/console.h> + +/* + * A structure to hold a cache pointer + * and its corresponding reference within + * the PPTT table. + */ +struct cache_reference { + struct pptt_cache *cache; // cache pointer + u32 ref; // and its reference within PPTT +}; + +/* + * A list of 'struct cache_reference', used + * to keep track of generated caches per topology level. + */ +struct cache_list { + u32 n_caches; // number of caches in list + struct cache_reference cache_refs[CONFIG_ACPI_PPTT_MAX_CACHES]; // cache reference list +}; + +/* + * Start of the PPTT table. Constant + * value as soon as we enter acpi_create_pptt_body. + */ +static uintptr_t pptt_start; + +/* --- Helper Functions (non exposed) --- */ + +static inline u32 node_to_reference(const void *node) +{ + /* + * References are the offset from the start + * of the PPTT table. + * + * PPTT + * +---------+ <- pptt_start (acpi_pptt_t) <---+ + * | | | node - pptt_start + * | | | + * +---------+ <- node (cpu or cache) <--------+ + * | | + * | | + * | | + * +---------+ + */ + return ((uintptr_t)node - pptt_start); +} + +static u32 count_resources(struct pptt_cpu_resources *res) +{ + u32 n_resources = 0; + + while (res != NULL) { + n_resources += 1; + res = res->next; + } + + return n_resources; +} + +static u32 cache_list_ref_of(struct cache_list *cache_list, const struct pptt_cache *cache) +{ + /* + * Lookup the PPTT reference of 'cache'. + * Return 0, if no PPTT structure exists for 'cache'. + */ + + for (int i = 0; i < cache_list->n_caches; i++) { + if (cache_list->cache_refs[i].cache == cache) + return cache_list->cache_refs[i].ref; + } + + /* no cache reference found */ + return 0; +} + +static inline void cache_list_append(struct cache_list *cache_list, struct pptt_cache *cache, const u32 ref) +{ + printk(BIOS_DEBUG, "acpi: pptt: cache=%p ref=%u\n", cache, ref); + + cache_list->cache_refs[cache_list->n_caches].cache = cache; + cache_list->cache_refs[cache_list->n_caches].ref = ref; + + cache_list->n_caches += 1; +} + +static u32 new_pptt_cache(unsigned long *current, struct pptt_cache *cache, struct cache_list *cache_list) +{ + static u32 unique_cache_id = 1; + u32 current_reference = 0; + + if ((current_reference = cache_list_ref_of(cache_list, cache)) != 0) + return current_reference; + + if (cache_list->n_caches >= CONFIG_ACPI_PPTT_MAX_CACHES) { + printk(BIOS_WARNING, "acpi: pptt: Too many distinct caches! PPTT incomplete.\n"); + return 0; + } + + acpi_pptt_cache_node_t *cache_node = (acpi_pptt_cache_node_t *)*current; + memset(cache_node, 0x0, sizeof(acpi_pptt_cache_node_t)); + + cache_node->type = PPTT_NODE_TYPE_CACHE; + cache_node->length = sizeof(acpi_pptt_cache_node_t); + + cache_node->flags = cache->flags.raw; + cache_node->size = cache->size; + cache_node->n_sets = cache->numsets; + cache_node->associativity = cache->associativity; + + cache_node->attributes = cache->attributes; + cache_node->line_size = cache->line_size; + cache_node->cache_id = unique_cache_id++; + + *current += cache_node->length; + + current_reference = node_to_reference(cache_node); + cache_list_append(cache_list, cache, current_reference); + + if (cache->next_level != NULL) + cache_node->next_level = new_pptt_cache(current, cache->next_level, cache_list); + + return current_reference; +} + +static u32 new_pptt_cpu(unsigned long *current, const struct pptt_topology *cpu, const u32 parent_ref, struct cache_list *cache_list) +{ + acpi_pptt_cpu_node_t *cpu_node = (acpi_pptt_cpu_node_t *)*current; + + const u32 n_resources = count_resources(cpu->resources); + const u32 structure_length = sizeof(acpi_pptt_cpu_node_t) + (n_resources * sizeof(u32)); + + memset(cpu_node, 0x0, structure_length); + + cpu_node->type = PPTT_NODE_TYPE_CPU; + cpu_node->length = structure_length; + cpu_node->flags = cpu->flags.raw; + cpu_node->processor_id = cpu->processor_id; + cpu_node->parent = parent_ref; + + *current += cpu_node->length; + + for (struct pptt_cpu_resources *it = cpu->resources; it != NULL; it = it->next) + cpu_node->resources[cpu_node->n_resources++] = new_pptt_cache(current, it->cache, cache_list); + + return node_to_reference(cpu_node); +} + +static void setup_topology(const struct pptt_topology *node, const u32 parent_ref, unsigned long *current) +{ + struct cache_list cache_list = { + .cache_refs = { }, + .n_caches = 0 + }; + + while (node != NULL) { + const u32 cpu_ref = new_pptt_cpu(current, node, parent_ref, &cache_list); + setup_topology(node->child, cpu_ref, current); + + node = node->sibling; + } +} + +/* --- PPTT generation helper functions (exposed) --- */ + +void acpi_create_pptt_body(acpi_pptt_t *pptt) +{ + /* set start of pptt table */ + pptt_start = (uintptr_t)pptt; + + /* locate start of pptt body */ + unsigned long current = (unsigned long)(pptt->body); + + /* retrieve processor topology */ + const struct pptt_topology *topology_tree = acpi_get_pptt_topology(); + + /* write processor properties topology table to memory */ + setup_topology(topology_tree, 0, ¤t); + + /* update length field in pptt header */ + pptt->header.length = current - (unsigned long)pptt; +} diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h index 0941dee..a298e39 100644 --- a/src/include/acpi/acpi.h +++ b/src/include/acpi/acpi.h @@ -93,6 +93,7 @@ LPIT, /* Low Power Idle Table */ MADT, /* Multiple APIC Description Table */ MCFG, /* PCI Express Memory Mapped Configuration */ + PPTT, /* Processor Properties Topology Table */ RSDP, /* Root System Description Pointer */ RSDT, /* Root System Description Table */ SLIT, /* System Locality Distance Information Table */ @@ -102,7 +103,6 @@ TCPA, /* Trusted Computing Platform Alliance Table */ TPM2, /* Trusted Platform Module 2.0 Table */ XSDT, /* Extended System Description Table */ - /* Additional proprietary tables used by coreboot */ CRAT, /* Component Resource Attribute Table */ NHLT, /* Non HD audio Link Table */ @@ -1419,6 +1419,110 @@ acpi_einj_action_table_t action_table[ACTION_COUNT]; } __packed acpi_einj_t;
+/* PPTT definitions */ + +#define PPTT_NODE_TYPE_CPU 0 +#define PPTT_NODE_TYPE_CACHE 1 + +/* PPTT structures for ACPI generation */ + +typedef struct acpi_pptt_cpu_node { + u8 type; // type = 0 (processor structure specification) + u8 length; // in bytes + u8 reserved[2]; // reserved, must be zero + u32 flags; // processor hierarchy node structure flags + u32 parent; // reference (delta of pptt-start and node) to parent node, must be zero if no parent + u32 processor_id; // must match id in MADT, if actual processor + u32 n_resources; // number of resource structure references + u32 resources[]; // resource structure references +} acpi_pptt_cpu_node_t; + +typedef struct acpi_pptt_cache_node { + u8 type; // type = 1 (cache type structure) + u8 length; // length = 28 + u8 reserved[2]; // reserved, must be zero + u32 flags; // cache structure flags + u32 next_level; // reference to next level cache, null if last cache level + u32 size; // cache size in bytes + u32 n_sets; // number of sets in the cache + u8 associativity; // integer number of ways + u8 attributes; // bits[7:5] reserved, must be zero + u16 line_size; // in bytes + u32 cache_id; // unique, non-zero +} acpi_pptt_cache_node_t; + +union acpi_pptt_body { + acpi_pptt_cpu_node_t cpu; + acpi_pptt_cache_node_t cache; +}; + +typedef struct acpi_pptt { + acpi_header_t header; + + /* + * followed by a variable length body + * consisting of processor topology structures. + * + * see acpi_pptt_cpu_node and + * acpi_pptt_cache_node. + */ + union acpi_pptt_body body[]; +} __packed acpi_pptt_t; + +/* PPTT structures for topology description */ + +union pptt_cache_flags { + struct { + u32 size_valid : 1; + u32 n_sets_valid : 1; + u32 associativity_valid : 1; + u32 alloc_type_valid : 1; + u32 cache_type_valid : 1; + u32 write_policy_valid : 1; + u32 line_size_valid : 1; + u32 cache_id_valid : 1; + u32 reserved : 24; + }; + + u32 raw; +}; + +union pptt_cpu_flags { + struct { + u32 is_physical_package : 1; + u32 processor_id_valid : 1; + u32 is_thread : 1; + u32 is_leaf : 1; + u32 is_identical_impl : 1; + u32 reserved : 27; + }; + + u32 raw; +}; + +struct pptt_cache { + u32 size; + u32 numsets; + u8 associativity; + u8 attributes; + u16 line_size; + union pptt_cache_flags flags; + struct pptt_cache *next_level; +}; + +struct pptt_cpu_resources { + struct pptt_cache *cache; + struct pptt_cpu_resources *next; +}; + +struct pptt_topology { + u32 processor_id; + union pptt_cpu_flags flags; + struct pptt_cpu_resources *resources; + struct pptt_topology *sibling; + struct pptt_topology *child; +}; + /* SPCR (Serial Port Console Redirection Table) */ typedef struct acpi_spcr { acpi_header_t header; @@ -1589,6 +1693,9 @@ int acpi_create_cedt_cfmws(acpi_cedt_cfmws_t *cfmws, u64 base_hpa, u64 window_size, u8 eniw, u32 hbig, u16 restriction, u16 qtg_id, const u32 *interleave_target);
+/* PPTT related functions */ +void acpi_create_pptt_body(acpi_pptt_t *pptt); +struct pptt_topology *acpi_get_pptt_topology(void);
int acpi_create_madt_ioapic_from_hw(acpi_madt_ioapic_t *ioapic, u32 addr);