blob: 6c8d7fd4209517f965a9948f4c5513b1c9c911b3 [file] [log] [blame]
Arthur Heymans177e1352022-11-04 13:03:23 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <cpu/cpu.h>
4#include <device/device.h>
5#include <cpu/x86/topology.h>
6
7#define CPUID_EXTENDED_CPU_TOPOLOGY 0x0b
8#define LEVEL_TYPE_CORE 2
9#define LEVEL_TYPE_SMT 1
10
11#define CPUID_CPU_TOPOLOGY(x, val) \
12 (((val) >> CPUID_CPU_TOPOLOGY_##x##_SHIFT) & CPUID_CPU_TOPOLOGY_##x##_MASK)
13
14#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_SHIFT 0x8
15#define CPUID_CPU_TOPOLOGY_LEVEL_TYPE_MASK 0xff
16#define CPUID_CPU_TOPOLOGY_LEVEL(res) CPUID_CPU_TOPOLOGY(LEVEL_TYPE, (res).ecx)
17
18#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_SHIFT 0x0
19#define CPUID_CPU_TOPOLOGY_LEVEL_BITS_MASK 0x1f
20#define CPUID_CPU_TOPOLOGY_THREAD_BITS(res) CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)
21#define CPUID_CPU_TOPOLOGY_CORE_BITS(res, threadbits) \
22 ((CPUID_CPU_TOPOLOGY(LEVEL_BITS, (res).eax)) - threadbits)
23
24/* Get number of bits for core ID and SMT ID */
25static enum cb_err get_cpu_core_thread_bits(uint32_t *core_bits, uint32_t *thread_bits)
26{
27 struct cpuid_result cpuid_regs;
28 int level_num, cpu_id_op = 0;
29 const uint32_t cpuid_max_func = cpuid_get_max_func();
30
31 /*
32 * Not all CPUs support this, those won't get topology filled in here.
33 * CPU specific code can do this however.
34 */
35 if (cpuid_max_func < CPUID_EXTENDED_CPU_TOPOLOGY)
36 return CB_ERR;
37
38 cpu_id_op = CPUID_EXTENDED_CPU_TOPOLOGY;
39
40 *core_bits = level_num = 0;
41 cpuid_regs = cpuid_ext(cpu_id_op, level_num);
42
43 /*
44 * Sub-leaf index 0 enumerates SMT level, some AMD CPUs leave this CPUID leaf
45 * reserved so bail out. Cpu specific code can fill in the topology later.
46 */
47 if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) != LEVEL_TYPE_SMT)
48 return CB_ERR;
49
50 *thread_bits = CPUID_CPU_TOPOLOGY_THREAD_BITS(cpuid_regs);
51 do {
52 level_num++;
53 cpuid_regs = cpuid_ext(cpu_id_op, level_num);
54 if (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs) == LEVEL_TYPE_CORE) {
55 *core_bits = CPUID_CPU_TOPOLOGY_CORE_BITS(cpuid_regs, *thread_bits);
56 break;
57 }
58 /* Stop when level type is invalid i.e 0 */
59 } while (CPUID_CPU_TOPOLOGY_LEVEL(cpuid_regs));
60
61 return CB_SUCCESS;
62}
63
64static void set_cpu_topology(struct device *cpu, unsigned int node,
65 unsigned int package, unsigned int core,
66 unsigned int thread)
67{
68 cpu->path.apic.node_id = node;
69 cpu->path.apic.package_id = package;
70 cpu->path.apic.core_id = core;
71 cpu->path.apic.thread_id = thread;
72}
73
74void set_cpu_topology_from_leaf_b(struct device *cpu)
75{
76 static uint32_t core_bits, thread_bits;
77 static enum cb_err core_thread_bits_ret;
78 static bool done = false;
79 if (!done) {
80 core_thread_bits_ret = get_cpu_core_thread_bits(&core_bits, &thread_bits);
81 done = true;
82 }
83
84 const uint32_t apicid = cpu->path.apic.initial_lapicid;
85 uint32_t package_id, core_id, thread_id;
86 /*
87 * If leaf_b does not exist set the following best-guess defaults:
88 * - 1 package
89 * - no SMP
90 * - core_id = apicid
91 * CPU specific code can always update these later on.
92 */
93 if (core_thread_bits_ret != CB_SUCCESS) {
94 package_id = 0;
95 core_id = apicid;
96 thread_id = 0;
97 } else {
98 package_id = apicid >> (thread_bits + core_bits);
99 core_id = (apicid >> thread_bits) & ((1 << core_bits) - 1);
100 thread_id = apicid & ((1 << thread_bits) - 1);
101 }
102
103 set_cpu_topology(cpu, 0, package_id, core_id, thread_id);
104}