Re: [PATCH v8 4/4] xen/acpi: Parse PPTT to initialize CPU topology

Jan Beulich <[email protected]>
Newsgroups org.xenproject.lists.xen-devel
Message-ID <[email protected]>
On 26.07.2026 02:55, Hirokazu Takahashi wrote:
> 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 v8:
>   * Relaxed processor node length check to allow
>     proc->header.length >= table_size.
>   * Treated root nodes (nodes with parent == 0) as equivalent to
>     ACPI_PPTT_PHYSICAL_PACKAGE nodes.
>   * Moved INVALID_ACPIID definition to xen/acpi.h to make it available
>     cross-architecture.
>   * Removed ARM-specific references from comments in
>     architecture-agnostic code.
>   * Added explicit parentheses around bitwise operations in logical
>     expressions.
>   * Placed binary logical operators (||, &&) at the end of lines when
>     splitting conditional statements.

This rule, however, ...

> --- a/xen/drivers/acpi/topology.c
> +++ b/xen/drivers/acpi/topology.c
> @@ -4,33 +4,315 @@
>  #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.
> +     * Ensure the structure length accurately accounts for the trailing array.
>       */
> +    table_size = sizeof(*proc)
> +        + (unsigned long)proc->number_of_priv_resources * sizeof(uint32_t);

... holds for all binary operators, i.e. also the + here. Indentation of the
continuing line would also better be

    table_size = sizeof(*proc) +
                 (unsigned long)proc->number_of_priv_resources * sizeof(uint32_t);

Where I'd then still question the need for the cast: The operation is, afaict,
the same with it dropped, both in ILP32 and LP64 models. With one exception -
there is an overflow potentially getting in the way, but the cast helps with
that only in LP64. Question is whether too large a ->number_of_priv_resources
wouldn't better be rejected uniformly as presumably bogus.

> +
> +    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;
> +
> +            proc = container_of(entry, const struct acpi_pptt_processor,
> +                                header);

Have this be the initializer of the variable:

            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)
> +{
> +    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];

You may end up reading INVALID_ACPIID here. Then ...

> +        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);

... a bogus table entry with that ID would be found here, potentially for
multiple values of "cpu".

> +        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 )

You say in the revlog that you added parentheses, but at least here you
didn't.

Jan
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.