| 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 | enum topo_types { | 
|---|
| 11 | INVALID_TYPE		= 0, | 
|---|
| 12 | SMT_TYPE		= 1, | 
|---|
| 13 | CORE_TYPE		= 2, | 
|---|
| 14 | MAX_TYPE_0B		= 3, | 
|---|
| 15 | MODULE_TYPE		= 3, | 
|---|
| 16 | AMD_CCD_TYPE		= 3, | 
|---|
| 17 | TILE_TYPE		= 4, | 
|---|
| 18 | AMD_SOCKET_TYPE		= 4, | 
|---|
| 19 | MAX_TYPE_80000026	= 5, | 
|---|
| 20 | DIE_TYPE		= 5, | 
|---|
| 21 | DIEGRP_TYPE		= 6, | 
|---|
| 22 | MAX_TYPE_1F		= 7, | 
|---|
| 23 | }; | 
|---|
| 24 |  | 
|---|
| 25 | /* | 
|---|
| 26 | * Use a lookup table for the case that there are future types > 6 which | 
|---|
| 27 | * describe an intermediate domain level which does not exist today. | 
|---|
| 28 | */ | 
|---|
| 29 | static const unsigned int topo_domain_map_0b_1f[MAX_TYPE_1F] = { | 
|---|
| 30 | [SMT_TYPE]	= TOPO_SMT_DOMAIN, | 
|---|
| 31 | [CORE_TYPE]	= TOPO_CORE_DOMAIN, | 
|---|
| 32 | [MODULE_TYPE]	= TOPO_MODULE_DOMAIN, | 
|---|
| 33 | [TILE_TYPE]	= TOPO_TILE_DOMAIN, | 
|---|
| 34 | [DIE_TYPE]	= TOPO_DIE_DOMAIN, | 
|---|
| 35 | [DIEGRP_TYPE]	= TOPO_DIEGRP_DOMAIN, | 
|---|
| 36 | }; | 
|---|
| 37 |  | 
|---|
| 38 | static const unsigned int topo_domain_map_80000026[MAX_TYPE_80000026] = { | 
|---|
| 39 | [SMT_TYPE]		= TOPO_SMT_DOMAIN, | 
|---|
| 40 | [CORE_TYPE]		= TOPO_CORE_DOMAIN, | 
|---|
| 41 | [AMD_CCD_TYPE]		= TOPO_TILE_DOMAIN, | 
|---|
| 42 | [AMD_SOCKET_TYPE]	= TOPO_DIE_DOMAIN, | 
|---|
| 43 | }; | 
|---|
| 44 |  | 
|---|
| 45 | static inline bool topo_subleaf(struct topo_scan *tscan, u32 leaf, u32 subleaf, | 
|---|
| 46 | unsigned int *last_dom) | 
|---|
| 47 | { | 
|---|
| 48 | unsigned int dom, maxtype; | 
|---|
| 49 | const unsigned int *map; | 
|---|
| 50 | struct { | 
|---|
| 51 | // eax | 
|---|
| 52 | u32	x2apic_shift	:  5, // Number of bits to shift APIC ID right | 
|---|
| 53 | // for the topology ID at the next level | 
|---|
| 54 | : 27; // Reserved | 
|---|
| 55 | // ebx | 
|---|
| 56 | u32	num_processors	: 16, // Number of processors at current level | 
|---|
| 57 | : 16; // Reserved | 
|---|
| 58 | // ecx | 
|---|
| 59 | u32	level		:  8, // Current topology level. Same as sub leaf number | 
|---|
| 60 | type		:  8, // Level type. If 0, invalid | 
|---|
| 61 | : 16; // Reserved | 
|---|
| 62 | // edx | 
|---|
| 63 | u32	x2apic_id	: 32; // X2APIC ID of the current logical processor | 
|---|
| 64 | } sl; | 
|---|
| 65 |  | 
|---|
| 66 | switch (leaf) { | 
|---|
| 67 | case 0x0b: maxtype = MAX_TYPE_0B; map = topo_domain_map_0b_1f; break; | 
|---|
| 68 | case 0x1f: maxtype = MAX_TYPE_1F; map = topo_domain_map_0b_1f; break; | 
|---|
| 69 | case 0x80000026: maxtype = MAX_TYPE_80000026; map = topo_domain_map_80000026; break; | 
|---|
| 70 | default: return false; | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | cpuid_subleaf(leaf, subleaf, &sl); | 
|---|
| 74 |  | 
|---|
| 75 | if (!sl.num_processors || sl.type == INVALID_TYPE) | 
|---|
| 76 | return false; | 
|---|
| 77 |  | 
|---|
| 78 | if (sl.type >= maxtype) { | 
|---|
| 79 | pr_err_once( "Topology: leaf 0x%x:%d Unknown domain type %u\n", | 
|---|
| 80 | leaf, subleaf, sl.type); | 
|---|
| 81 | /* | 
|---|
| 82 | * It really would have been too obvious to make the domain | 
|---|
| 83 | * type space sparse and leave a few reserved types between | 
|---|
| 84 | * the points which might change instead of following the | 
|---|
| 85 | * usual "this can be fixed in software" principle. | 
|---|
| 86 | */ | 
|---|
| 87 | dom = *last_dom + 1; | 
|---|
| 88 | } else { | 
|---|
| 89 | dom = map[sl.type]; | 
|---|
| 90 | *last_dom = dom; | 
|---|
| 91 | } | 
|---|
| 92 |  | 
|---|
| 93 | if (!dom) { | 
|---|
| 94 | tscan->c->topo.initial_apicid = sl.x2apic_id; | 
|---|
| 95 | } else if (tscan->c->topo.initial_apicid != sl.x2apic_id) { | 
|---|
| 96 | pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf %d APIC ID mismatch %x != %x\n", | 
|---|
| 97 | leaf, subleaf, tscan->c->topo.initial_apicid, sl.x2apic_id); | 
|---|
| 98 | } | 
|---|
| 99 |  | 
|---|
| 100 | topology_set_dom(tscan, dom, shift: sl.x2apic_shift, ncpus: sl.num_processors); | 
|---|
| 101 | return true; | 
|---|
| 102 | } | 
|---|
| 103 |  | 
|---|
| 104 | static bool parse_topology_leaf(struct topo_scan *tscan, u32 leaf) | 
|---|
| 105 | { | 
|---|
| 106 | unsigned int last_dom; | 
|---|
| 107 | u32 subleaf; | 
|---|
| 108 |  | 
|---|
| 109 | /* Read all available subleafs and populate the levels */ | 
|---|
| 110 | for (subleaf = 0, last_dom = 0; topo_subleaf(tscan, leaf, subleaf, last_dom: &last_dom); subleaf++); | 
|---|
| 111 |  | 
|---|
| 112 | /* If subleaf 0 failed to parse, give up */ | 
|---|
| 113 | if (!subleaf) | 
|---|
| 114 | return false; | 
|---|
| 115 |  | 
|---|
| 116 | /* | 
|---|
| 117 | * There are machines in the wild which have shift 0 in the subleaf | 
|---|
| 118 | * 0, but advertise 2 logical processors at that level. They are | 
|---|
| 119 | * truly SMT. | 
|---|
| 120 | */ | 
|---|
| 121 | if (!tscan->dom_shifts[TOPO_SMT_DOMAIN] && tscan->dom_ncpus[TOPO_SMT_DOMAIN] > 1) { | 
|---|
| 122 | unsigned int sft = get_count_order(count: tscan->dom_ncpus[TOPO_SMT_DOMAIN]); | 
|---|
| 123 |  | 
|---|
| 124 | pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf 0 has shift level 0 but %u CPUs. Fixing it up.\n", | 
|---|
| 125 | leaf, tscan->dom_ncpus[TOPO_SMT_DOMAIN]); | 
|---|
| 126 | topology_update_dom(tscan, dom: TOPO_SMT_DOMAIN, shift: sft, ncpus: tscan->dom_ncpus[TOPO_SMT_DOMAIN]); | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | set_cpu_cap(tscan->c, X86_FEATURE_XTOPOLOGY); | 
|---|
| 130 | return true; | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | bool cpu_parse_topology_ext(struct topo_scan *tscan) | 
|---|
| 134 | { | 
|---|
| 135 | /* Intel: Try leaf 0x1F first. */ | 
|---|
| 136 | if (tscan->c->cpuid_level >= 0x1f && parse_topology_leaf(tscan, leaf: 0x1f)) | 
|---|
| 137 | return true; | 
|---|
| 138 |  | 
|---|
| 139 | /* AMD: Try leaf 0x80000026 first. */ | 
|---|
| 140 | if (tscan->c->extended_cpuid_level >= 0x80000026 && parse_topology_leaf(tscan, leaf: 0x80000026)) | 
|---|
| 141 | return true; | 
|---|
| 142 |  | 
|---|
| 143 | /* Intel/AMD: Fall back to leaf 0xB if available */ | 
|---|
| 144 | return tscan->c->cpuid_level >= 0x0b && parse_topology_leaf(tscan, leaf: 0x0b); | 
|---|
| 145 | } | 
|---|
| 146 |  | 
|---|