cpu/x86/topology: Simplify CPU topology initialization

This commit rewrites the CPU topology initialization code to simplify
it and make it more maintainable.

The previous code used a complex set of if-else statements to
initialize the CPU topology based on the CPUID leaves that were
supported. This has been replaced with a simpler and more readable
function that follows the Intel Software Developer Manual
recommendation by prioritizing CPUID EAX=0x1f over CPUID EAX=0xb if
available.

The new code removes the need for separate functions to handle the
topology initialization for different CPUID leaves. It uses a static
array of bitfield descriptors to store the APIC ID descriptor
information for each level of the CPU topology. This simplifies the
code and makes it easier to add new levels of topology in the future.

The code populates the node ID based on the package ID, eliminating
the need for an extra function call.

Change-Id: Ie9424559f895af69e79c36b919e80af803861148
Signed-off-by: Jeremy Compostella <jeremy.compostella@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/85576
Reviewed-by: Jincheng Li <jincheng.li@intel.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Shuo Liu <shuo.liu@intel.com>
This commit is contained in:
Jeremy Compostella 2024-12-12 12:27:47 -08:00 committed by Jérémy Compostella
commit 70bdd2e1fa
4 changed files with 110 additions and 125 deletions

View file

@ -204,7 +204,7 @@ static asmlinkage void ap_init(unsigned int index)
dev->path.apic.initial_lapicid = initial_lapicid();
dev->enabled = 1;
set_cpu_topology_from_leaf_b(dev);
set_cpu_topology(dev);
if (cpu_is_intel())
printk(BIOS_INFO, "AP: slot %u apic_id %x, MCU rev: 0x%08x\n", index,
@ -562,7 +562,7 @@ static enum cb_err init_bsp(struct bus *cpu_bus)
return CB_ERR;
}
bsp->path.apic.initial_lapicid = initial_lapicid();
set_cpu_topology_from_leaf_b(bsp);
set_cpu_topology(bsp);
/* Find the device structure for the boot CPU. */
set_cpu_info(0, bsp);

View file

@ -1,38 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <cpu/cpu.h>
#include <device/device.h>
#include <cpu/x86/topology.h>
#include <device/device.h>
#define CPUID_EXTENDED_CPU_TOPOLOGY2 0x1f
/*
* The following code defines functions for populating the topology information of a given CPU
* device (struct device *cpu).
*
* Reference: Intel Software Developer Manual Extended Topology Enumeration Leaves (CPUID EAX 0xb
* and 0x1f).
*/
#define CPUID_EXTENDED_CPU_TOPOLOGY 0x0b
#define LEVEL_TYPE_CORE 2
#define LEVEL_TYPE_SMT 1
#define CPUID_EXTENDED_CPU_TOPOLOGY 0x0b
#define CPUID_EXTENDED_CPU_TOPOLOGY2 0x1f
enum {
LEVEL_TYPE_PACKAGE, /* Use unused level zero for package. */
LEVEL_TYPE_SMT,
LEVEL_TYPE_CORE,
LEVEL_TYPE_MODULE,
LEVEL_TYPE_TILE,
LEVEL_TYPE_DIE,
LEVEL_TYPE_DIEGRP,
LEVEL_TYPE_MAX
};
#define CPUID_CPU_TOPOLOGY(x, val) \
(((val) >> CPUID_CPU_TOPOLOGY_##x##_SHIFT) & CPUID_CPU_TOPOLOGY_##x##_MASK)
#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_SHIFT 0x8
#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_MASK 0xff
#define CPUID_CPU_TOPOLOGY_LEVEL(res) CPUID_CPU_TOPOLOGY(LEVEL_TYPE, (res).ecx)
#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_SHIFT 0x8
#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_MASK 0xff
#define CPUID_CPU_TOPOLOGY_LEVEL(res) CPUID_CPU_TOPOLOGY(LEVEL_TYPE, (res).ecx)
#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_SHIFT 0x0
#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_MASK 0x1f
#define CPUID_CPU_TOPOLOGY_THREAD_BITS(res) CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)
#define CPUID_CPU_TOPOLOGY_CORE_BITS(res, threadbits) \
((CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)) - threadbits)
#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_SHIFT 0x0
#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_MASK 0x1f
/* Return the level shift for the highest supported level (the package) */
static enum cb_err get_cpu_package_bits(uint32_t *package_bits)
struct bitfield_descriptor {
uint8_t first_bit;
uint8_t num_bits;
};
/*
* Fills in the supplied bit field APIC ID descriptor structure array.
*
* Per Intel Software Developer Manual recommendation, it prioritizes CPUID EAX=0x1f over CPUID
* EAX=0xb if available. The function iterates over the hierarchy levels to extract per-domain
* (level) APIC bit field information and populates the provided topology array with the
* bitfield descriptors for each topology level.
*
* Returns CB_ERR if:
* - CPUID EAX=0x1f is not supported.
* - CPUID EAX=0xb is not supported.
* - The first topology level is 0.
*
* Returns CB_SUCCESS otherwise.
*/
static enum cb_err read_topology_structure(struct bitfield_descriptor *topology)
{
struct cpuid_result cpuid_regs;
int level_num, cpu_id_op = 0;
const uint32_t cpuid_max_func = cpuid_get_max_func();
struct cpuid_result cpuid_regs;
int level_num = 0, cpu_id_op;
unsigned int level;
uint8_t next_level_first_bit, first_bit = 0;
/*
* Not all CPUs support this, those won't get topology filled in here.
* CPU specific code can do this however.
* Not all CPUs support this, those won't get topology filled in here. CPU specific
* code can do this however.
*/
if (cpuid_max_func >= CPUID_EXTENDED_CPU_TOPOLOGY2)
cpu_id_op = CPUID_EXTENDED_CPU_TOPOLOGY2;
@ -41,124 +76,78 @@ static enum cb_err get_cpu_package_bits(uint32_t *package_bits)
else
return CB_ERR;
*package_bits = level_num = 0;
cpuid_regs = cpuid_ext(cpu_id_op, level_num);
/*
* Sub-leaf index 0 enumerates SMT level, some AMD CPUs leave this CPUID leaf
* reserved so bail out. Cpu specific code can fill in the topology later.
* Sub-leaf index 0 enumerates SMT level, some AMD CPUs leave this CPUID leaf reserved
* so bail out. Cpu specific code can fill in the topology later.
*/
if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) != LEVEL_TYPE_SMT)
return CB_ERR;
do {
*package_bits = (CPUID_CPU_TOPOLOGY(LEVEL_BITS, (cpuid_regs).eax));
level = CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs);
next_level_first_bit = CPUID_CPU_TOPOLOGY(LEVEL_BITS, cpuid_regs.eax);
if (level < LEVEL_TYPE_MAX) {
topology[level].num_bits = next_level_first_bit - first_bit;
topology[level].first_bit = first_bit;
} else {
printk(BIOS_ERR, "Unknown processor topology level %d\n", level);
}
first_bit = next_level_first_bit;
level_num++;
cpuid_regs = cpuid_ext(cpu_id_op, level_num);
/* Stop when level type is invalid i.e 0. */
} while (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs));
topology[LEVEL_TYPE_PACKAGE].first_bit = next_level_first_bit;
return CB_SUCCESS;
}
void set_cpu_node_id_leaf_1f_b(struct device *cpu)
void set_cpu_topology(struct device *cpu)
{
static uint32_t package_bits;
static enum cb_err package_bits_ret;
static bool done = false;
const uint32_t apicid = cpu->path.apic.initial_lapicid;
static struct bitfield_descriptor topology[LEVEL_TYPE_MAX];
static enum cb_err ret;
static bool done;
struct {
unsigned int level;
unsigned int *field;
} apic_fields[] = {
{ LEVEL_TYPE_SMT, &cpu->path.apic.thread_id },
{ LEVEL_TYPE_CORE, &cpu->path.apic.core_id },
{ LEVEL_TYPE_PACKAGE, &cpu->path.apic.package_id },
{ LEVEL_TYPE_PACKAGE, &cpu->path.apic.node_id }
};
if (!done) {
package_bits_ret = get_cpu_package_bits(&package_bits);
ret = read_topology_structure(topology);
done = true;
}
const uint32_t apicid = cpu->path.apic.initial_lapicid;
/*
* If leaf_1f or leaf_b does not exist don't update the node_id.
*/
if (package_bits_ret == CB_SUCCESS)
cpu->path.apic.node_id = (apicid >> package_bits);
}
/* Get number of bits for core ID and SMT ID */
static enum cb_err get_cpu_core_thread_bits(uint32_t *core_bits, uint32_t *thread_bits)
{
struct cpuid_result cpuid_regs;
int level_num, cpu_id_op = 0;
const uint32_t cpuid_max_func = cpuid_get_max_func();
/*
* Not all CPUs support this, those won't get topology filled in here.
* CPU specific code can do this however.
*/
if (cpuid_max_func < CPUID_EXTENDED_CPU_TOPOLOGY)
return CB_ERR;
cpu_id_op = CPUID_EXTENDED_CPU_TOPOLOGY;
*core_bits = level_num = 0;
cpuid_regs = cpuid_ext(cpu_id_op, level_num);
/*
* Sub-leaf index 0 enumerates SMT level, some AMD CPUs leave this CPUID leaf
* reserved so bail out. Cpu specific code can fill in the topology later.
*/
if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) != LEVEL_TYPE_SMT)
return CB_ERR;
*thread_bits = CPUID_CPU_TOPOLOGY_THREAD_BITS(cpuid_regs);
do {
level_num++;
cpuid_regs = cpuid_ext(cpu_id_op, level_num);
if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) == LEVEL_TYPE_CORE) {
*core_bits = CPUID_CPU_TOPOLOGY_CORE_BITS(cpuid_regs, *thread_bits);
break;
}
/* Stop when level type is invalid i.e 0 */
} while (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs));
return CB_SUCCESS;
}
static void set_cpu_topology(struct device *cpu, unsigned int node,
unsigned int package, unsigned int core,
unsigned int thread)
{
cpu->path.apic.node_id = node;
cpu->path.apic.package_id = package;
cpu->path.apic.core_id = core;
cpu->path.apic.thread_id = thread;
}
void set_cpu_topology_from_leaf_b(struct device *cpu)
{
static uint32_t core_bits, thread_bits;
static enum cb_err core_thread_bits_ret;
static bool done = false;
if (!done) {
core_thread_bits_ret = get_cpu_core_thread_bits(&core_bits, &thread_bits);
done = true;
}
const uint32_t apicid = cpu->path.apic.initial_lapicid;
uint32_t package_id, core_id, thread_id;
/*
* If leaf_b does not exist set the following best-guess defaults:
* If no APIC descriptor is found, set the following best-guess defaults:
* - 1 package
* - no SMP
* - core_id = apicid
* - no SMP
* - 1 node
* CPU specific code can always update these later on.
*/
if (core_thread_bits_ret != CB_SUCCESS) {
package_id = 0;
core_id = apicid;
thread_id = 0;
} else {
package_id = apicid >> (thread_bits + core_bits);
core_id = (apicid >> thread_bits) & ((1 << core_bits) - 1);
thread_id = apicid & ((1 << thread_bits) - 1);
if (ret != CB_SUCCESS) {
cpu->path.apic.package_id = 0;
cpu->path.apic.core_id = apicid;
cpu->path.apic.thread_id = 0;
cpu->path.apic.node_id = 0;
return;
}
set_cpu_topology(cpu, 0, package_id, core_id, thread_id);
for (size_t i = 0; i < ARRAY_SIZE(apic_fields); i++) {
struct bitfield_descriptor *desc = &topology[apic_fields[i].level];
if (desc->first_bit || desc->num_bits) {
unsigned int value = apicid >> desc->first_bit;
if (desc->num_bits)
value &= (1 << (desc->num_bits)) - 1;
*apic_fields[i].field = value;
}
}
}

View file

@ -5,15 +5,13 @@
#include <device/device.h>
/* Fill in the topology in struct path APIC based CPUID EAX=0xb.
* If leaf 0xb is not supported or is not implemented then no topology
* will be filled in.
/*
* Sets the topology information for the given CPU device using the bitfield descriptors
* obtained from the CPUID leaves. Per Intel Software Developer Manual recommendation, it
* prioritizes CPUID EAX=0x1f over CPUID EAX=0xb if available.
*
* If the topology information cannot be obtained from CPUID, it sets default values.
*/
void set_cpu_topology_from_leaf_b(struct device *cpu);
void set_cpu_topology(struct device *cpu);
/* Fill in the topology node ID in struct path APIC based CPUID EAX=0x1f
* or CPUID EAX=0xb. If those leaves aren't supported then the node ID
* won't be updated.
*/
void set_cpu_node_id_leaf_1f_b(struct device *cpu);
#endif

View file

@ -72,8 +72,6 @@ static void each_cpu_init(struct device *cpu)
__func__, dev_path(cpu), cpu_index(), cpu->path.apic.apic_id,
cpu->path.apic.package_id);
/* Populate the node ID. It will be used as proximity ID. */
set_cpu_node_id_leaf_1f_b(cpu);
assert (cpu->path.apic.node_id < CONFIG_MAX_SOCKET);
/*