Skip to content

Commit

Permalink
acpi: Add PPTT support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Milo-D authored and LeanSheng committed Dec 2, 2023
1 parent faf2779 commit d982274
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/acpi/Kconfig
Expand Up @@ -91,3 +91,17 @@ config MAX_ACPI_TABLE_SIZE_KB
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.
1 change: 1 addition & 0 deletions src/acpi/Makefile.inc
Expand Up @@ -8,6 +8,7 @@ ramstage-y += acpi_apic.c
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
Expand Down
15 changes: 15 additions & 0 deletions src/acpi/acpi.c
Expand Up @@ -1204,6 +1204,18 @@ unsigned long acpi_create_lpi_desc_ncst(acpi_lpi_desc_ncst_t *lpi_desc, uint16_t
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 */
Expand Down Expand Up @@ -1394,6 +1406,7 @@ unsigned long write_acpi_tables(const unsigned long start)
{ 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;
Expand Down Expand Up @@ -1743,6 +1756,8 @@ int get_acpi_table_revision(enum acpi_tables table)
return 4;
case GTDT:
return 3;
case PPTT: /* ACPI 6.4 */
return 3;
default:
return -1;
}
Expand Down
186 changes: 186 additions & 0 deletions 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, &current);

/* update length field in pptt header */
pptt->header.length = current - (unsigned long)pptt;
}
109 changes: 108 additions & 1 deletion src/include/acpi/acpi.h
Expand Up @@ -93,6 +93,7 @@ enum acpi_tables {
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 */
Expand All @@ -102,7 +103,6 @@ enum acpi_tables {
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 */
Expand Down Expand Up @@ -1419,6 +1419,110 @@ typedef struct acpi_einj {
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;
Expand Down Expand Up @@ -1589,6 +1693,9 @@ int acpi_create_cedt_chbs(acpi_cedt_chbs_t *chbs, u32 uid, u32 cxl_ver, u64 base
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);

Expand Down

0 comments on commit d982274

Please sign in to comment.