[PATCH v7 5/5] xen/acpi: Parse PPTT to initialize CPU topology
Hirokazu Takahashi <[email protected]>
| Newsgroups | org.xenproject.lists.xen-devel |
|---|---|
| Message-ID | <[email protected]> |
Parse the ACPI PPTT (Processor Properties Topology Table) to initialize the CPU topology. For ACPI 6.3 and later, the ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD flag is checked to determine the presence of threading. For ACPI 6.2 and earlier, CPUs are assumed not to support threading. Signed-off-by: Hirokazu Takahashi <[email protected]> --- Changes in v7: - PPTT Table Validation & Hardening: * find_pptt_node(): Added leaf node verification (ACPI 6.3+). * acpi_init_cpu_topology(): Added bounds checks to ensure proc->parent validly references memory strictly inside the PPTT table bounds. * Factored out shared table checking helper functions used by both find_pptt_node() and acpi_init_cpu_topology(). * Added subtable alignment checks and strengthened processor structure size validation. - Graceful Fallback & Allocation: * Treated missing topology definitions as benign conditions. * Used xvzalloc_array() for temporary memory allocations during initialization. - Code Cleanups & Style Conformance: * Removed redundant error checks and reduced unnecessary pointer casts. * Switched type-based sizeof(type) to variable-based sizeof(*var). * Added leading spaces before labels ( out:) xen/arch/arm/acpi/boot.c | 2 + xen/arch/arm/include/asm/acpi.h | 2 + xen/drivers/acpi/topology.c | 312 ++++++++++++++++++++++++++++++-- xen/include/xen/acpi.h | 2 + 4 files changed, 305 insertions(+), 13 deletions(-) diff --git a/xen/arch/arm/acpi/boot.c b/xen/arch/arm/acpi/boot.c index 4ac0fd8f51..fc7ecb5749 100644 --- a/xen/arch/arm/acpi/boot.c +++ b/xen/arch/arm/acpi/boot.c @@ -85,6 +85,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) return; } bootcpu_valid = true; + acpi_map_cpu_acpiid(0, processor->uid); return; } @@ -119,6 +120,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) /* map the logical cpu id to cpu MPIDR */ cpu_logical_map(enabled_cpus) = mpidr; + acpi_map_cpu_acpiid(enabled_cpus, processor->uid); enabled_cpus++; } diff --git a/xen/arch/arm/include/asm/acpi.h b/xen/arch/arm/include/asm/acpi.h index 13756dd341..b2e156e131 100644 --- a/xen/arch/arm/include/asm/acpi.h +++ b/xen/arch/arm/include/asm/acpi.h @@ -61,6 +61,8 @@ paddr_t acpi_get_table_offset(struct membank tbl_add[], EFI_MEM_RES index); (!(entry) || (unsigned long)(entry) + sizeof(*(entry)) > (end) || \ (entry)->header.length != ACPI_MADT_GICC_LENGTH) +#define INVALID_ACPIID (-1U) + #ifdef CONFIG_ACPI extern bool acpi_disabled; /* Basic configuration for ACPI */ diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c index 56fcfa4945..2966117fef 100644 --- a/xen/drivers/acpi/topology.c +++ b/xen/drivers/acpi/topology.c @@ -4,33 +4,319 @@ #include <xen/cpu-topology.h> #include <xen/cpumask.h> #include <xen/init.h> +#include <xen/xvmalloc.h> + +#define ACPI_PPTT_MAX_LEVELS 16 + +static uint32_t __initdata map_cpu_acpiid[NR_CPUS] = { + [0 ... NR_CPUS - 1] = INVALID_ACPIID +}; /* - * TODO: Populate the topology information by scanning the ACPI - * PPTT (Processor Properties Topology Table). + * The first argument 'cpu' is the logical CPU ID assigned by Xen, + * and the second argument 'acpi_id' is passed the 'uid' field from + * the ACPI MADT Generic Interrupt subtable. */ -int __init acpi_init_cpu_topology(void) +void __init acpi_map_cpu_acpiid(unsigned int cpu, uint32_t acpi_id) { - unsigned int cpu; + map_cpu_acpiid[cpu] = acpi_id; +} + +static unsigned int __init get_logical_id(unsigned int key, + unsigned int *map, + unsigned int *count) +{ + unsigned int id; + + for ( id = 0; id < *count; id++ ) + if ( map[id] == key ) + return id; + + map[*count] = key; + + return (*count)++; +} + +static bool __init verify_subtable(const struct acpi_subtable_header *entry, + const struct acpi_table_pptt *pptt) +{ + unsigned long table_end = (unsigned long)pptt + pptt->header.length; + + if ( entry->length < sizeof(*entry) || entry->length & 3 ) + { + printk(XENLOG_ERR "ACPI: PPTT subtable length is invalid\n"); + return false; + } + + if ( (unsigned long)entry + entry->length > table_end ) + { + printk(XENLOG_ERR "ACPI: PPTT subtable extends beyond table end.\n"); + return false; + } + + return true; +} + +static bool __init verify_proc(const struct acpi_pptt_processor *proc) +{ + unsigned long table_size; + + if ( proc->header.length < sizeof(*proc) ) + { + printk(XENLOG_ERR "ACPI: PPTT processor node length is too small.\n"); + return false; + } /* - * Generate temporary cpu topology information for now. - * It assumes that the cpu doesn't have SMT and all CPUs - * belong to the same socket. + * Each private resource is represented by a 32-bit resource ID. + * Ensure the structure length accurately accounts for the trailing array. */ + table_size = sizeof(*proc) + + (unsigned long)proc->number_of_priv_resources * sizeof(uint32_t); + + if ( proc->header.length != table_size ) + { + printk(XENLOG_ERR "ACPI: PPTT processor node length invalid.\n"); + return false; + } + + return true; +} + +static const struct acpi_pptt_processor *__init find_pptt_node( + const struct acpi_table_pptt *pptt, uint32_t acpi_id) +{ + const struct acpi_subtable_header *entry; + unsigned long table_end; + const void *ptr; + + table_end = (unsigned long)pptt + pptt->header.length; + + ptr = pptt + 1; + + while ( (unsigned long)ptr + sizeof(*entry) <= table_end ) + { + entry = ptr; + + if ( !verify_subtable(entry, pptt) ) + break; + + if ( entry->type == ACPI_PPTT_TYPE_PROCESSOR ) + { + const struct acpi_pptt_processor *proc; + + proc = container_of(entry, const struct acpi_pptt_processor, + header); + + if ( !verify_proc(proc) ) + break; + + /* + * Leaf node verification is only required for ACPI 6.3 + * (PPTT revision 2) or later. + */ + if ( (proc->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) + && proc->acpi_processor_id == acpi_id + && (pptt->header.revision < 2 + || (proc->flags & ACPI_PPTT_ACPI_LEAF_NODE)) ) + return proc; + } + + ptr += entry->length; + } + + return NULL; +} + +/* + * Populate the topology information by scanning the ACPI PPTT + * (Processor Properties Topology Table). + */ +int __init acpi_init_cpu_topology(void) +{ + acpi_status status; + struct acpi_table_header *table_header; + const struct acpi_table_pptt *pptt; + unsigned int num_sockets = 0; + unsigned int num_clusters = 0; + unsigned int num_cores = 0; + unsigned int *socket_map = xvzalloc_array(unsigned int, nr_cpu_ids); + unsigned int *cluster_map = xvzalloc_array(unsigned int, nr_cpu_ids); + unsigned int *core_map = xvzalloc_array(unsigned int, nr_cpu_ids); + unsigned int cpu; + int ret = 0; + + status = acpi_get_table(ACPI_SIG_PPTT, 0, &table_header); + if ( ACPI_FAILURE(status) ) + { + /* A missing PPTT is benign; fall back to the default topology. */ + ret = -ENODEV; + goto out; + } + + if ( !socket_map || !cluster_map || !core_map ) + { + printk(XENLOG_ERR + "ACPI: Failed to allocate memory for topology parsing.\n"); + ret = -ENOMEM; + goto out; + } + + pptt = container_of(table_header, const struct acpi_table_pptt, header); + for_each_possible_cpu(cpu) { + uint32_t acpi_id = map_cpu_acpiid[cpu]; struct cpu_topology *topo = &cpu_topology[cpu]; + const struct acpi_pptt_processor *proc; + unsigned int level; + unsigned int core_group_key = 0; + unsigned int cluster_group_key = 0; + unsigned int socket_group_key = 0; + bool threading = false; + + proc = find_pptt_node(pptt, acpi_id); + if ( !proc ) + { + printk(XENLOG_WARNING + "ACPI: No PPTT leaf node for CPU %u (ACPI ID 0x%u)\n", + cpu, acpi_id); + ret = -ENOENT; + goto out; + } + + /* + * Limit the maximum loop depth to prevent an infinite loop in case + * the PPTT is corrupted or contains cyclic references. + */ + for ( level = 0; level < ACPI_PPTT_MAX_LEVELS; level++ ) + { + const unsigned int offset = (const void *)proc - (const void *)pptt; + + if ( proc->flags & ACPI_PPTT_PHYSICAL_PACKAGE ) + { + socket_group_key = offset; - topo->phys_core_id = cpu; - topo->num_siblings = 1; + /* + * If cluster/core info is absent upon reaching the physical + * package, assume one cluster per socket and one core per + * cluster. + */ + if ( cluster_group_key == 0 ) + cluster_group_key = socket_group_key; - cpumask_set_cpu(cpu, topo->thread_sibling); - cpumask_copy(topo->core_sibling, &cpu_possible_map); - cpumask_copy(topo->cluster_sibling, &cpu_possible_map); + if ( core_group_key == 0 ) + core_group_key = cluster_group_key; + + break; + } + else if ( level == 0 ) + { + /* + * ACPI_PPTT_PROCESSOR_IS_THREAD is supported in PPTT + * revision 2 and later. Assume no threading support when + * PPTT revision is 1. + */ + if ( proc->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD ) + threading = true; + else + core_group_key = offset; + } + else if ( level == 1 ) + { + if ( threading ) + core_group_key = offset; + else + cluster_group_key = offset; + } + else if ( level == 2 && threading ) + cluster_group_key = offset; + + if ( !proc->parent ) + break; + + if ( (proc->parent & 3) + || proc->parent < sizeof(*pptt) + || proc->parent > pptt->header.length - sizeof(*proc) + || (proc->parent + sizeof(*proc) > offset + && proc->parent < offset + proc->header.length) ) + { + printk(XENLOG_WARNING + "ACPI: PPTT parent offset is invalid\n"); + break; + } + + proc = (const void *)pptt + proc->parent; + + if ( proc->header.type != ACPI_PPTT_TYPE_PROCESSOR ) + { + printk(XENLOG_WARNING + "ACPI: PPTT parent node is not a processor structure\n"); + break; + } + + if ( !verify_subtable(&proc->header, pptt) + || !verify_proc(proc) ) + break; + } + + if ( socket_group_key == 0 ) + { + printk(XENLOG_WARNING + "ACPI: Could not reach the physical package node for CPU %u (ACPI ID 0x%u)\n", + cpu, acpi_id); + ret = -ENOENT; + goto out; + } + + topo->phys_socket_id = + get_logical_id(socket_group_key, socket_map, &num_sockets); + topo->phys_cluster_id = + get_logical_id(cluster_group_key, cluster_map, &num_clusters); + topo->phys_core_id = + get_logical_id(core_group_key, core_map, &num_cores); + } + + for_each_possible_cpu(cpu) + { + struct cpu_topology *topo = &cpu_topology[cpu]; + unsigned int tcpu; + + for_each_possible_cpu(tcpu) + { + struct cpu_topology *ttopo = &cpu_topology[tcpu]; + + if ( cpu > tcpu ) + continue; + + if ( topo->phys_core_id == ttopo->phys_core_id ) + { + cpumask_set_cpu(tcpu, topo->thread_sibling); + cpumask_set_cpu(cpu, ttopo->thread_sibling); + } + + if ( topo->phys_cluster_id == ttopo->phys_cluster_id ) + { + cpumask_set_cpu(tcpu, topo->cluster_sibling); + cpumask_set_cpu(cpu, ttopo->cluster_sibling); + } + + if ( topo->phys_socket_id == ttopo->phys_socket_id ) + { + cpumask_set_cpu(tcpu, topo->core_sibling); + cpumask_set_cpu(cpu, ttopo->core_sibling); + } + } + + topo->num_siblings = cpumask_weight(topo->thread_sibling); } - return 0; + out: + xvfree(socket_map); + xvfree(cluster_map); + xvfree(core_map); + + return ret; } /* diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index cbb02e0f35..d2a1531893 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -137,10 +137,12 @@ static inline int acpi_boot_table_init(void) #ifdef CONFIG_ACPI_CPU_TOPOLOGY +void acpi_map_cpu_acpiid(unsigned int cpu, uint32_t acpi_id); int acpi_init_cpu_topology(void); #else /* CONFIG_ACPI_CPU_TOPOLOGY */ +static inline void acpi_map_cpu_acpiid(unsigned int cpu, uint32_t acpi_id) {} static inline int acpi_init_cpu_topology(void) { return -EOPNOTSUPP; -- 2.43.0