|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +#include <linux/cpu.h> |
| 3 | + |
| 4 | +#include <asm/apic.h> |
| 5 | +#include <asm/memtype.h> |
| 6 | +#include <asm/processor.h> |
| 7 | + |
| 8 | +#include "cpu.h" |
| 9 | + |
| 10 | +static bool parse_8000_0008(struct topo_scan *tscan) |
| 11 | +{ |
| 12 | + struct { |
| 13 | + // ecx |
| 14 | + u32 cpu_nthreads : 8, // Number of physical threads - 1 |
| 15 | + : 4, // Reserved |
| 16 | + apicid_coreid_len : 4, // Number of thread core ID bits (shift) in APIC ID |
| 17 | + perf_tsc_len : 2, // Performance time-stamp counter size |
| 18 | + : 14; // Reserved |
| 19 | + } ecx; |
| 20 | + unsigned int sft; |
| 21 | + |
| 22 | + if (tscan->c->extended_cpuid_level < 0x80000008) |
| 23 | + return false; |
| 24 | + |
| 25 | + cpuid_leaf_reg(0x80000008, CPUID_ECX, &ecx); |
| 26 | + |
| 27 | + /* If the thread bits are 0, then get the shift value from ecx.cpu_nthreads */ |
| 28 | + sft = ecx.apicid_coreid_len; |
| 29 | + if (!sft) |
| 30 | + sft = get_count_order(ecx.cpu_nthreads + 1); |
| 31 | + |
| 32 | + topology_set_dom(tscan, TOPO_SMT_DOMAIN, sft, ecx.cpu_nthreads + 1); |
| 33 | + return true; |
| 34 | +} |
| 35 | + |
| 36 | +static void store_node(struct topo_scan *tscan, unsigned int nr_nodes, u16 node_id) |
| 37 | +{ |
| 38 | + /* |
| 39 | + * Starting with Fam 17h the DIE domain could probably be used to |
| 40 | + * retrieve the node info on AMD/HYGON. Analysis of CPUID dumps |
| 41 | + * suggests it's the topmost bit(s) of the CPU cores area, but |
| 42 | + * that's guess work and neither enumerated nor documented. |
| 43 | + * |
| 44 | + * Up to Fam 16h this does not work at all and the legacy node ID |
| 45 | + * has to be used. |
| 46 | + */ |
| 47 | + tscan->amd_nodes_per_pkg = nr_nodes; |
| 48 | + tscan->amd_node_id = node_id; |
| 49 | +} |
| 50 | + |
| 51 | +static bool parse_8000_001e(struct topo_scan *tscan, bool has_0xb) |
| 52 | +{ |
| 53 | + struct { |
| 54 | + // eax |
| 55 | + u32 ext_apic_id : 32; // Extended APIC ID |
| 56 | + // ebx |
| 57 | + u32 core_id : 8, // Unique per-socket logical core unit ID |
| 58 | + core_nthreads : 8, // #Threads per core (zero-based) |
| 59 | + : 16; // Reserved |
| 60 | + // ecx |
| 61 | + u32 node_id : 8, // Node (die) ID of invoking logical CPU |
| 62 | + nnodes_per_socket : 3, // #nodes in invoking logical CPU's package/socket |
| 63 | + : 21; // Reserved |
| 64 | + // edx |
| 65 | + u32 : 32; // Reserved |
| 66 | + } leaf; |
| 67 | + |
| 68 | + if (!boot_cpu_has(X86_FEATURE_TOPOEXT)) |
| 69 | + return false; |
| 70 | + |
| 71 | + cpuid_leaf(0x8000001e, &leaf); |
| 72 | + |
| 73 | + tscan->c->topo.initial_apicid = leaf.ext_apic_id; |
| 74 | + |
| 75 | + /* |
| 76 | + * If leaf 0xb is available, then SMT shift is set already. If not |
| 77 | + * take it from ecx.threads_per_core and use topo_update_dom() - |
| 78 | + * topology_set_dom() would propagate and overwrite the already |
| 79 | + * propagated CORE level. |
| 80 | + */ |
| 81 | + if (!has_0xb) { |
| 82 | + unsigned int nthreads = leaf.core_nthreads + 1; |
| 83 | + |
| 84 | + topology_update_dom(tscan, TOPO_SMT_DOMAIN, get_count_order(nthreads), nthreads); |
| 85 | + } |
| 86 | + |
| 87 | + store_node(tscan, leaf.nnodes_per_socket + 1, leaf.node_id); |
| 88 | + |
| 89 | + if (tscan->c->x86_vendor == X86_VENDOR_AMD) { |
| 90 | + if (tscan->c->x86 == 0x15) |
| 91 | + tscan->c->topo.cu_id = leaf.core_id; |
| 92 | + |
| 93 | + cacheinfo_amd_init_llc_id(tscan->c, leaf.node_id); |
| 94 | + } else { |
| 95 | + /* |
| 96 | + * Package ID is ApicId[6..] on certain Hygon CPUs. See |
| 97 | + * commit e0ceeae708ce for explanation. The topology info |
| 98 | + * is screwed up: The package shift is always 6 and the |
| 99 | + * node ID is bit [4:5]. |
| 100 | + */ |
| 101 | + if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && tscan->c->x86_model <= 0x3) { |
| 102 | + topology_set_dom(tscan, TOPO_CORE_DOMAIN, 6, |
| 103 | + tscan->dom_ncpus[TOPO_CORE_DOMAIN]); |
| 104 | + } |
| 105 | + cacheinfo_hygon_init_llc_id(tscan->c); |
| 106 | + } |
| 107 | + return true; |
| 108 | +} |
| 109 | + |
| 110 | +static bool parse_fam10h_node_id(struct topo_scan *tscan) |
| 111 | +{ |
| 112 | + struct { |
| 113 | + union { |
| 114 | + u64 node_id : 3, |
| 115 | + nodes_per_pkg : 3, |
| 116 | + unused : 58; |
| 117 | + u64 msr; |
| 118 | + }; |
| 119 | + } nid; |
| 120 | + |
| 121 | + if (!boot_cpu_has(X86_FEATURE_NODEID_MSR)) |
| 122 | + return false; |
| 123 | + |
| 124 | + rdmsrl(MSR_FAM10H_NODE_ID, nid.msr); |
| 125 | + store_node(tscan, nid.nodes_per_pkg + 1, nid.node_id); |
| 126 | + tscan->c->topo.llc_id = nid.node_id; |
| 127 | + return true; |
| 128 | +} |
| 129 | + |
| 130 | +static void legacy_set_llc(struct topo_scan *tscan) |
| 131 | +{ |
| 132 | + unsigned int apicid = tscan->c->topo.initial_apicid; |
| 133 | + |
| 134 | + /* parse_8000_0008() set everything up except llc_id */ |
| 135 | + tscan->c->topo.llc_id = apicid >> tscan->dom_shifts[TOPO_CORE_DOMAIN]; |
| 136 | +} |
| 137 | + |
| 138 | +static void parse_topology_amd(struct topo_scan *tscan) |
| 139 | +{ |
| 140 | + bool has_0xb = false; |
| 141 | + |
| 142 | + /* |
| 143 | + * If the extended topology leaf 0x8000_001e is available |
| 144 | + * try to get SMT and CORE shift from leaf 0xb first, then |
| 145 | + * try to get the CORE shift from leaf 0x8000_0008. |
| 146 | + */ |
| 147 | + if (cpu_feature_enabled(X86_FEATURE_TOPOEXT)) |
| 148 | + has_0xb = cpu_parse_topology_ext(tscan); |
| 149 | + |
| 150 | + if (!has_0xb && !parse_8000_0008(tscan)) |
| 151 | + return; |
| 152 | + |
| 153 | + /* Prefer leaf 0x8000001e if available */ |
| 154 | + if (parse_8000_001e(tscan, has_0xb)) |
| 155 | + return; |
| 156 | + |
| 157 | + /* Try the NODEID MSR */ |
| 158 | + if (parse_fam10h_node_id(tscan)) |
| 159 | + return; |
| 160 | + |
| 161 | + legacy_set_llc(tscan); |
| 162 | +} |
| 163 | + |
| 164 | +void cpu_parse_topology_amd(struct topo_scan *tscan) |
| 165 | +{ |
| 166 | + tscan->amd_nodes_per_pkg = 1; |
| 167 | + parse_topology_amd(tscan); |
| 168 | + |
| 169 | + if (tscan->amd_nodes_per_pkg > 1) |
| 170 | + set_cpu_cap(tscan->c, X86_FEATURE_AMD_DCM); |
| 171 | +} |
| 172 | + |
| 173 | +void cpu_topology_fixup_amd(struct topo_scan *tscan) |
| 174 | +{ |
| 175 | + struct cpuinfo_x86 *c = tscan->c; |
| 176 | + |
| 177 | + /* |
| 178 | + * Adjust the core_id relative to the node when there is more than |
| 179 | + * one node. |
| 180 | + */ |
| 181 | + if (tscan->c->x86 < 0x17 && tscan->amd_nodes_per_pkg > 1) |
| 182 | + c->topo.core_id %= tscan->dom_ncpus[TOPO_CORE_DOMAIN] / tscan->amd_nodes_per_pkg; |
| 183 | +} |
0 commit comments