[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v7 5/5] xen/acpi: Parse PPTT to initialize CPU topology


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Hirokazu Takahashi <taka@xxxxxxxxxxxxx>
  • Date: Tue, 21 Jul 2026 01:28:19 +0900
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=valinux.co.jp; dmarc=pass action=none header.from=valinux.co.jp; dkim=pass header.d=valinux.co.jp; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=tFICUonHJZJoac1NdNgiIOkW7B7hkFZDYI80EIK19zA=; b=JnB/bNqQuTu+sduWN61XNodAJgz72ij6EAkmLzYDMVaZuyq/pr6V69FN2Mj1/WcuukQhuzk9frrfPbM9sARnaDKh0wqaW+p1e9Lj4nHRa2gDundHnJH5G00cWmXwmfphA1d7OF4SfX6cHZIFqMf7ecNmHmjh6l3RR97jY3SkRN1HrCGD5SmA/XSvYVXKQUQUVUcdHydqVhwEYmYLzwPUc6k0kvUx0yPTY1Rl+/9wJiV2iT3JGMppHGUrW2+GjkPDjzC+aIWEYKfgNIaSgdOwKunEyfifv+jXgkUIOnDSag16QBobwuszHJ4bmZuJ6cBYRXCwB1sOI4AKppquxu/OlQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=hpPBfT2zTJBuJs4DbZMM07t7LVQyH4GgW3Q1zOQGqtC5FeISPRWYQQrRR/DgzQbJurY7sPFPGgUMUiwPj5J7nu3lTCSNirB8OqMflyDsycbf3sUuEdVWXUBdUDxXRYJkn3EAuN1A6e2hA6m8HZoMMWrEQzRB1Cq1paMvTmi0/mABVZ5lcxnuiZCllChxKme4O28tljFroayo1YGR3cvsGVjk4kaltk2Cdtf+yhxzUFMWSDnWRlXf7SDpL/bvOEKvEiHojp9q/7oW6cq9cPNs5H1/TcO5tTPoNJNqj3rhPOdOEFU/94hFOkHLpAeU5ETIaStkDB/v3uWwMF2sC37isA==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=valinux.co.jp header.i="@valinux.co.jp" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=valinux.co.jp;
  • Cc: Mykyta_Poturai@xxxxxxxx, Hirokazu Takahashi <taka@xxxxxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Roger Pau Monné <roger.pau@xxxxxxxxxx>
  • Delivery-date: Mon, 20 Jul 2026 16:30:17 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

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 <taka@xxxxxxxxxxxxx>
---
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




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.