[PATCH v9 4/4] 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 v9: - Add bounds checking for the number of private resources in the PPTT processor table. - Verify the validity of entries when indexing into the map_cpu_acpiid table. - Coding style adjustments (following Xen standards): * Place logical operators ('||', '&&') at the end of lines when splitting multi-line conditions. * Place arithmetic operators ('+', '-') at the end of lines when wrapping long assignment statements. * Enclose bitwise operations in parentheses within conditional expressions that combine logical operators. * Combine variable declaration and initialization where applicable. xen/arch/arm/acpi/boot.c | 2 + xen/arch/x86/include/asm/acpi.h | 2 - xen/drivers/acpi/topology.c | 323 ++++++++++++++++++++++++++++++-- xen/include/xen/acpi.h | 4 + 4 files changed, 316 insertions(+), 15 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/x86/include/asm/acpi.h b/xen/arch/x86/include/asm/acpi.h index 217819dd61..7a89baa143 100644 --- a/xen/arch/x86/include/asm/acpi.h +++ b/xen/arch/x86/include/asm/acpi.h @@ -132,8 +132,6 @@ struct acpi_sleep_info { extern u32 x86_acpiid_to_apicid[]; #define MAX_LOCAL_APIC MAX(256, 4 * NR_CPUS) -#define INVALID_ACPIID (-1U) - extern u32 pmtmr_ioport; extern unsigned int pmtmr_width; diff --git a/xen/drivers/acpi/topology.c b/xen/drivers/acpi/topology.c index 56fcfa4945..c543ed85c9 100644 --- a/xen/drivers/acpi/topology.c +++ b/xen/drivers/acpi/topology.c @@ -4,33 +4,330 @@ #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' the 32-bit ACPI processor ID. */ -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. + * Reject if the number of private resources exceeds what can fit in + * the 8-bit limit of proc->header.length. */ + if ( proc->number_of_priv_resources > + (UINT8_MAX - sizeof(*proc)) / sizeof(uint32_t) ) + { + printk(XENLOG_ERR "ACPI: PPTT too many private resources\n"); + return false; + } + + /* + * Ensure the structure length accurately accounts for the trailing array. + */ + table_size = sizeof(*proc) + + 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 = (unsigned long)pptt + pptt->header.length; + const void *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 = + 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) +{ + 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; + acpi_status 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; + + if ( acpi_id == INVALID_ACPIID ) + { + printk(XENLOG_WARNING "ACPI: Invalid ACPI ID for CPU %u\n", cpu); + ret = -EINVAL; + goto out; + } + + proc = find_pptt_node(pptt, acpi_id); + if ( !proc ) + { + printk(XENLOG_WARNING + "ACPI: No PPTT leaf node for CPU %u (ACPI ID %#x)\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 this proc has no parent node, it is the root node. Treat it + * as equivalent to an ACPI_PPTT_PHYSICAL_PACKAGE node. + */ + if ( (proc->flags & ACPI_PPTT_PHYSICAL_PACKAGE) || !proc->parent ) + { + socket_group_key = offset; + + /* + * 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; + + 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 & 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 %#x)\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; - topo->phys_core_id = cpu; - topo->num_siblings = 1; + for_each_possible_cpu(tcpu) + { + struct cpu_topology *ttopo = &cpu_topology[tcpu]; - cpumask_set_cpu(cpu, topo->thread_sibling); - cpumask_copy(topo->core_sibling, &cpu_possible_map); - cpumask_copy(topo->cluster_sibling, &cpu_possible_map); + 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..9788586be4 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -52,6 +52,8 @@ (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) || \ (entry)->header.length < sizeof(*(entry))) +#define INVALID_ACPIID (-1U) + #ifdef CONFIG_ACPI #include <acpi/acpi.h> @@ -137,10 +139,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