mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2025-11-15 19:14:02 +00:00
- Limit the hardcoded topology quirk for Hygon CPUs to those which have a
model ID less than 4. The newer models have the topology CPUID leaf 0xB
correctly implemented and are not affected.
- Make SMT control more robust against enumeration failures
SMT control was added to allow controlling SMT at boottime or
runtime. The primary purpose was to provide a simple mechanism to
disable SMT in the light of speculation attack vectors.
It turned out that the code is sensible to enumeration failures and
worked only by chance for XEN/PV. XEN/PV has no real APIC enumeration
which means the primary thread mask is not set up correctly. By chance
a XEN/PV boot ends up with smp_num_siblings == 2, which makes the
hotplug control stay at its default value "enabled". So the mask is
never evaluated.
The ongoing rework of the topology evaluation caused XEN/PV to end up
with smp_num_siblings == 1, which sets the SMT control to "not
supported" and the empty primary thread mask causes the hotplug core to
deny the bringup of the APS.
Make the decision logic more robust and take 'not supported' and 'not
implemented' into account for the decision whether a CPU should be
booted or not.
- Fake primary thread mask for XEN/PV
Pretend that all XEN/PV vCPUs are primary threads, which makes the
usage of the primary thread mask valid on XEN/PV. That is consistent
with because all of the topology information on XEN/PV is fake or even
non-existent.
- Encapsulate topology information in cpuinfo_x86
Move the randomly scattered topology data into a separate data
structure for readability and as a preparatory step for the topology
evaluation overhaul.
- Consolidate APIC ID data type to u32
It's fixed width hardware data and not randomly u16, int, unsigned long
or whatever developers decided to use.
- Cure the abuse of cpuinfo for persisting logical IDs.
Per CPU cpuinfo is used to persist the logical package and die
IDs. That's really not the right place simply because cpuinfo is
subject to be reinitialized when a CPU goes through an offline/online
cycle.
Use separate per CPU data for the persisting to enable the further
topology management rework. It will be removed once the new topology
management is in place.
- Provide a debug interface for inspecting topology information
Useful in general and extremly helpful for validating the topology
management rework in terms of correctness or "bug" compatibility.
-----BEGIN PGP SIGNATURE-----
iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmU+yX0THHRnbHhAbGlu
dXRyb25peC5kZQAKCRCmGPVMDXSYoROUD/4vlvKEcpm9rbI5DzLcaq4DFHKbyEZF
cQtzuOSM/9vTc9DHnuoNNLl9TWSYxiVYnejf3E21evfsqspYlzbTH8bId9XBCUid
6B68AJW842M2erNuwj0b0HwF1z++zpDmBDyhGOty/KQhoM8pYOHMvntAmbzJbuso
Dgx6BLVFcboTy6RwlfRa0EE8f9W5V+JbmG/VBDpdyCInal7VrudoVFZmWQnPIft7
zwOJpAoehkp8OKq7geKDf79yWxu9a1sNPd62HtaVEvfHwehHqE6OaMLss1us+0vT
SJ/D6gmRQBOwcXaZL0wL1dG7Km9Et4AisOvzhXGvTa5b2D5oljVoqJ7V7FTf5g3u
y3aqWbeUJzERUbeJt1HoGVAKyA4GtZOvg+TNIysf6F1Z4khl9alfa9jiqjj4g1au
zgItq/ZMBEBmJ7X4FxQUEUVBG2CDsEidyNBDRcimWQUDfBakV/iCs0suD8uu8ZOD
K5jMx8Hi2+xFx7r1YqsfsyMBYOf/zUZw65RbNe+kI992JbJ9nhcODbnbo5MlAsyv
vcqlK5FwXgZ4YAC8dZHU/tyTiqAW7oaOSkqKwTP5gcyNEqsjQHV//q6v+uqtjfYn
1C4oUsRHT2vJiV9ktNJTA4GQHIYF4geGgpG8Ih2SjXsSzdGtUd3DtX1iq0YiLEOk
eHhYsnniqsYB5g==
=xrz8
-----END PGP SIGNATURE-----
Merge tag 'x86-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 core updates from Thomas Gleixner:
- Limit the hardcoded topology quirk for Hygon CPUs to those which have
a model ID less than 4.
The newer models have the topology CPUID leaf 0xB correctly
implemented and are not affected.
- Make SMT control more robust against enumeration failures
SMT control was added to allow controlling SMT at boottime or
runtime. The primary purpose was to provide a simple mechanism to
disable SMT in the light of speculation attack vectors.
It turned out that the code is sensible to enumeration failures and
worked only by chance for XEN/PV. XEN/PV has no real APIC enumeration
which means the primary thread mask is not set up correctly. By
chance a XEN/PV boot ends up with smp_num_siblings == 2, which makes
the hotplug control stay at its default value "enabled". So the mask
is never evaluated.
The ongoing rework of the topology evaluation caused XEN/PV to end up
with smp_num_siblings == 1, which sets the SMT control to "not
supported" and the empty primary thread mask causes the hotplug core
to deny the bringup of the APS.
Make the decision logic more robust and take 'not supported' and 'not
implemented' into account for the decision whether a CPU should be
booted or not.
- Fake primary thread mask for XEN/PV
Pretend that all XEN/PV vCPUs are primary threads, which makes the
usage of the primary thread mask valid on XEN/PV. That is consistent
with because all of the topology information on XEN/PV is fake or
even non-existent.
- Encapsulate topology information in cpuinfo_x86
Move the randomly scattered topology data into a separate data
structure for readability and as a preparatory step for the topology
evaluation overhaul.
- Consolidate APIC ID data type to u32
It's fixed width hardware data and not randomly u16, int, unsigned
long or whatever developers decided to use.
- Cure the abuse of cpuinfo for persisting logical IDs.
Per CPU cpuinfo is used to persist the logical package and die IDs.
That's really not the right place simply because cpuinfo is subject
to be reinitialized when a CPU goes through an offline/online cycle.
Use separate per CPU data for the persisting to enable the further
topology management rework. It will be removed once the new topology
management is in place.
- Provide a debug interface for inspecting topology information
Useful in general and extremly helpful for validating the topology
management rework in terms of correctness or "bug" compatibility.
* tag 'x86-core-2023-10-29-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits)
x86/apic, x86/hyperv: Use u32 in hv_snp_boot_ap() too
x86/cpu: Provide debug interface
x86/cpu/topology: Cure the abuse of cpuinfo for persisting logical ids
x86/apic: Use u32 for wakeup_secondary_cpu[_64]()
x86/apic: Use u32 for [gs]et_apic_id()
x86/apic: Use u32 for phys_pkg_id()
x86/apic: Use u32 for cpu_present_to_apicid()
x86/apic: Use u32 for check_apicid_used()
x86/apic: Use u32 for APIC IDs in global data
x86/apic: Use BAD_APICID consistently
x86/cpu: Move cpu_l[l2]c_id into topology info
x86/cpu: Move logical package and die IDs into topology info
x86/cpu: Remove pointless evaluation of x86_coreid_bits
x86/cpu: Move cu_id into topology info
x86/cpu: Move cpu_core_id into topology info
hwmon: (fam15h_power) Use topology_core_id()
scsi: lpfc: Use topology_core_id()
x86/cpu: Move cpu_die_id into topology info
x86/cpu: Move phys_proc_id into topology info
x86/cpu: Encapsulate topology information in cpuinfo_x86
...
402 lines
9.9 KiB
C
402 lines
9.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Hygon Processor Support for Linux
|
|
*
|
|
* Copyright (C) 2018 Chengdu Haiguang IC Design Co., Ltd.
|
|
*
|
|
* Author: Pu Wen <puwen@hygon.cn>
|
|
*/
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/apic.h>
|
|
#include <asm/cpu.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/numa.h>
|
|
#include <asm/cacheinfo.h>
|
|
#include <asm/spec-ctrl.h>
|
|
#include <asm/delay.h>
|
|
|
|
#include "cpu.h"
|
|
|
|
#define APICID_SOCKET_ID_BIT 6
|
|
|
|
/*
|
|
* nodes_per_socket: Stores the number of nodes per socket.
|
|
* Refer to CPUID Fn8000_001E_ECX Node Identifiers[10:8]
|
|
*/
|
|
static u32 nodes_per_socket = 1;
|
|
|
|
#ifdef CONFIG_NUMA
|
|
/*
|
|
* To workaround broken NUMA config. Read the comment in
|
|
* srat_detect_node().
|
|
*/
|
|
static int nearby_node(int apicid)
|
|
{
|
|
int i, node;
|
|
|
|
for (i = apicid - 1; i >= 0; i--) {
|
|
node = __apicid_to_node[i];
|
|
if (node != NUMA_NO_NODE && node_online(node))
|
|
return node;
|
|
}
|
|
for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) {
|
|
node = __apicid_to_node[i];
|
|
if (node != NUMA_NO_NODE && node_online(node))
|
|
return node;
|
|
}
|
|
return first_node(node_online_map); /* Shouldn't happen */
|
|
}
|
|
#endif
|
|
|
|
static void hygon_get_topology_early(struct cpuinfo_x86 *c)
|
|
{
|
|
if (cpu_has(c, X86_FEATURE_TOPOEXT))
|
|
smp_num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
|
|
}
|
|
|
|
/*
|
|
* Fixup core topology information for
|
|
* (1) Hygon multi-node processors
|
|
* Assumption: Number of cores in each internal node is the same.
|
|
* (2) Hygon processors supporting compute units
|
|
*/
|
|
static void hygon_get_topology(struct cpuinfo_x86 *c)
|
|
{
|
|
/* get information required for multi-node processors */
|
|
if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
|
|
int err;
|
|
u32 eax, ebx, ecx, edx;
|
|
|
|
cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
|
|
|
|
c->topo.die_id = ecx & 0xff;
|
|
|
|
c->topo.core_id = ebx & 0xff;
|
|
|
|
if (smp_num_siblings > 1)
|
|
c->x86_max_cores /= smp_num_siblings;
|
|
|
|
/*
|
|
* In case leaf B is available, use it to derive
|
|
* topology information.
|
|
*/
|
|
err = detect_extended_topology(c);
|
|
if (!err)
|
|
c->x86_coreid_bits = get_count_order(c->x86_max_cores);
|
|
|
|
/*
|
|
* Socket ID is ApicId[6] for the processors with model <= 0x3
|
|
* when running on host.
|
|
*/
|
|
if (!boot_cpu_has(X86_FEATURE_HYPERVISOR) && c->x86_model <= 0x3)
|
|
c->topo.pkg_id = c->topo.apicid >> APICID_SOCKET_ID_BIT;
|
|
|
|
cacheinfo_hygon_init_llc_id(c);
|
|
} else if (cpu_has(c, X86_FEATURE_NODEID_MSR)) {
|
|
u64 value;
|
|
|
|
rdmsrl(MSR_FAM10H_NODE_ID, value);
|
|
c->topo.die_id = value & 7;
|
|
c->topo.llc_id = c->topo.die_id;
|
|
} else
|
|
return;
|
|
|
|
if (nodes_per_socket > 1)
|
|
set_cpu_cap(c, X86_FEATURE_AMD_DCM);
|
|
}
|
|
|
|
/*
|
|
* On Hygon setup the lower bits of the APIC id distinguish the cores.
|
|
* Assumes number of cores is a power of two.
|
|
*/
|
|
static void hygon_detect_cmp(struct cpuinfo_x86 *c)
|
|
{
|
|
unsigned int bits;
|
|
|
|
bits = c->x86_coreid_bits;
|
|
/* Low order bits define the core id (index of core in socket) */
|
|
c->topo.core_id = c->topo.initial_apicid & ((1 << bits)-1);
|
|
/* Convert the initial APIC ID into the socket ID */
|
|
c->topo.pkg_id = c->topo.initial_apicid >> bits;
|
|
/* Use package ID also for last level cache */
|
|
c->topo.llc_id = c->topo.die_id = c->topo.pkg_id;
|
|
}
|
|
|
|
static void srat_detect_node(struct cpuinfo_x86 *c)
|
|
{
|
|
#ifdef CONFIG_NUMA
|
|
int cpu = smp_processor_id();
|
|
int node;
|
|
unsigned int apicid = c->topo.apicid;
|
|
|
|
node = numa_cpu_node(cpu);
|
|
if (node == NUMA_NO_NODE)
|
|
node = c->topo.llc_id;
|
|
|
|
/*
|
|
* On multi-fabric platform (e.g. Numascale NumaChip) a
|
|
* platform-specific handler needs to be called to fixup some
|
|
* IDs of the CPU.
|
|
*/
|
|
if (x86_cpuinit.fixup_cpu_id)
|
|
x86_cpuinit.fixup_cpu_id(c, node);
|
|
|
|
if (!node_online(node)) {
|
|
/*
|
|
* Two possibilities here:
|
|
*
|
|
* - The CPU is missing memory and no node was created. In
|
|
* that case try picking one from a nearby CPU.
|
|
*
|
|
* - The APIC IDs differ from the HyperTransport node IDs.
|
|
* Assume they are all increased by a constant offset, but
|
|
* in the same order as the HT nodeids. If that doesn't
|
|
* result in a usable node fall back to the path for the
|
|
* previous case.
|
|
*
|
|
* This workaround operates directly on the mapping between
|
|
* APIC ID and NUMA node, assuming certain relationship
|
|
* between APIC ID, HT node ID and NUMA topology. As going
|
|
* through CPU mapping may alter the outcome, directly
|
|
* access __apicid_to_node[].
|
|
*/
|
|
int ht_nodeid = c->topo.initial_apicid;
|
|
|
|
if (__apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
|
|
node = __apicid_to_node[ht_nodeid];
|
|
/* Pick a nearby node */
|
|
if (!node_online(node))
|
|
node = nearby_node(apicid);
|
|
}
|
|
numa_set_node(cpu, node);
|
|
#endif
|
|
}
|
|
|
|
static void early_init_hygon_mc(struct cpuinfo_x86 *c)
|
|
{
|
|
#ifdef CONFIG_SMP
|
|
unsigned int bits, ecx;
|
|
|
|
/* Multi core CPU? */
|
|
if (c->extended_cpuid_level < 0x80000008)
|
|
return;
|
|
|
|
ecx = cpuid_ecx(0x80000008);
|
|
|
|
c->x86_max_cores = (ecx & 0xff) + 1;
|
|
|
|
/* CPU telling us the core id bits shift? */
|
|
bits = (ecx >> 12) & 0xF;
|
|
|
|
/* Otherwise recompute */
|
|
if (bits == 0) {
|
|
while ((1 << bits) < c->x86_max_cores)
|
|
bits++;
|
|
}
|
|
|
|
c->x86_coreid_bits = bits;
|
|
#endif
|
|
}
|
|
|
|
static void bsp_init_hygon(struct cpuinfo_x86 *c)
|
|
{
|
|
if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) {
|
|
u64 val;
|
|
|
|
rdmsrl(MSR_K7_HWCR, val);
|
|
if (!(val & BIT(24)))
|
|
pr_warn(FW_BUG "TSC doesn't count with P0 frequency!\n");
|
|
}
|
|
|
|
if (cpu_has(c, X86_FEATURE_MWAITX))
|
|
use_mwaitx_delay();
|
|
|
|
if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
|
|
u32 ecx;
|
|
|
|
ecx = cpuid_ecx(0x8000001e);
|
|
__max_die_per_package = nodes_per_socket = ((ecx >> 8) & 7) + 1;
|
|
} else if (boot_cpu_has(X86_FEATURE_NODEID_MSR)) {
|
|
u64 value;
|
|
|
|
rdmsrl(MSR_FAM10H_NODE_ID, value);
|
|
__max_die_per_package = nodes_per_socket = ((value >> 3) & 7) + 1;
|
|
}
|
|
|
|
if (!boot_cpu_has(X86_FEATURE_AMD_SSBD) &&
|
|
!boot_cpu_has(X86_FEATURE_VIRT_SSBD)) {
|
|
/*
|
|
* Try to cache the base value so further operations can
|
|
* avoid RMW. If that faults, do not enable SSBD.
|
|
*/
|
|
if (!rdmsrl_safe(MSR_AMD64_LS_CFG, &x86_amd_ls_cfg_base)) {
|
|
setup_force_cpu_cap(X86_FEATURE_LS_CFG_SSBD);
|
|
setup_force_cpu_cap(X86_FEATURE_SSBD);
|
|
x86_amd_ls_cfg_ssbd_mask = 1ULL << 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void early_init_hygon(struct cpuinfo_x86 *c)
|
|
{
|
|
u32 dummy;
|
|
|
|
early_init_hygon_mc(c);
|
|
|
|
set_cpu_cap(c, X86_FEATURE_K8);
|
|
|
|
rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy);
|
|
|
|
/*
|
|
* c->x86_power is 8000_0007 edx. Bit 8 is TSC runs at constant rate
|
|
* with P/T states and does not stop in deep C-states
|
|
*/
|
|
if (c->x86_power & (1 << 8)) {
|
|
set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
|
|
set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC);
|
|
}
|
|
|
|
/* Bit 12 of 8000_0007 edx is accumulated power mechanism. */
|
|
if (c->x86_power & BIT(12))
|
|
set_cpu_cap(c, X86_FEATURE_ACC_POWER);
|
|
|
|
/* Bit 14 indicates the Runtime Average Power Limit interface. */
|
|
if (c->x86_power & BIT(14))
|
|
set_cpu_cap(c, X86_FEATURE_RAPL);
|
|
|
|
#ifdef CONFIG_X86_64
|
|
set_cpu_cap(c, X86_FEATURE_SYSCALL32);
|
|
#endif
|
|
|
|
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PCI)
|
|
/*
|
|
* ApicID can always be treated as an 8-bit value for Hygon APIC So, we
|
|
* can safely set X86_FEATURE_EXTD_APICID unconditionally.
|
|
*/
|
|
if (boot_cpu_has(X86_FEATURE_APIC))
|
|
set_cpu_cap(c, X86_FEATURE_EXTD_APICID);
|
|
#endif
|
|
|
|
/*
|
|
* This is only needed to tell the kernel whether to use VMCALL
|
|
* and VMMCALL. VMMCALL is never executed except under virt, so
|
|
* we can set it unconditionally.
|
|
*/
|
|
set_cpu_cap(c, X86_FEATURE_VMMCALL);
|
|
|
|
hygon_get_topology_early(c);
|
|
}
|
|
|
|
static void init_hygon(struct cpuinfo_x86 *c)
|
|
{
|
|
u64 vm_cr;
|
|
|
|
early_init_hygon(c);
|
|
|
|
/*
|
|
* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
|
|
* 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
|
|
*/
|
|
clear_cpu_cap(c, 0*32+31);
|
|
|
|
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
|
|
|
|
/* get apicid instead of initial apic id from cpuid */
|
|
c->topo.apicid = read_apic_id();
|
|
|
|
/*
|
|
* XXX someone from Hygon needs to confirm this DTRT
|
|
*
|
|
init_spectral_chicken(c);
|
|
*/
|
|
|
|
set_cpu_cap(c, X86_FEATURE_ZEN);
|
|
set_cpu_cap(c, X86_FEATURE_CPB);
|
|
|
|
cpu_detect_cache_sizes(c);
|
|
|
|
hygon_detect_cmp(c);
|
|
hygon_get_topology(c);
|
|
srat_detect_node(c);
|
|
|
|
init_hygon_cacheinfo(c);
|
|
|
|
if (cpu_has(c, X86_FEATURE_SVM)) {
|
|
rdmsrl(MSR_VM_CR, vm_cr);
|
|
if (vm_cr & SVM_VM_CR_SVM_DIS_MASK) {
|
|
pr_notice_once("SVM disabled (by BIOS) in MSR_VM_CR\n");
|
|
clear_cpu_cap(c, X86_FEATURE_SVM);
|
|
}
|
|
}
|
|
|
|
if (cpu_has(c, X86_FEATURE_XMM2)) {
|
|
/*
|
|
* Use LFENCE for execution serialization. On families which
|
|
* don't have that MSR, LFENCE is already serializing.
|
|
* msr_set_bit() uses the safe accessors, too, even if the MSR
|
|
* is not present.
|
|
*/
|
|
msr_set_bit(MSR_AMD64_DE_CFG,
|
|
MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT);
|
|
|
|
/* A serializing LFENCE stops RDTSC speculation */
|
|
set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
|
|
}
|
|
|
|
/*
|
|
* Hygon processors have APIC timer running in deep C states.
|
|
*/
|
|
set_cpu_cap(c, X86_FEATURE_ARAT);
|
|
|
|
/* Hygon CPUs don't reset SS attributes on SYSRET, Xen does. */
|
|
if (!cpu_feature_enabled(X86_FEATURE_XENPV))
|
|
set_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS);
|
|
|
|
check_null_seg_clears_base(c);
|
|
}
|
|
|
|
static void cpu_detect_tlb_hygon(struct cpuinfo_x86 *c)
|
|
{
|
|
u32 ebx, eax, ecx, edx;
|
|
u16 mask = 0xfff;
|
|
|
|
if (c->extended_cpuid_level < 0x80000006)
|
|
return;
|
|
|
|
cpuid(0x80000006, &eax, &ebx, &ecx, &edx);
|
|
|
|
tlb_lld_4k[ENTRIES] = (ebx >> 16) & mask;
|
|
tlb_lli_4k[ENTRIES] = ebx & mask;
|
|
|
|
/* Handle DTLB 2M and 4M sizes, fall back to L1 if L2 is disabled */
|
|
if (!((eax >> 16) & mask))
|
|
tlb_lld_2m[ENTRIES] = (cpuid_eax(0x80000005) >> 16) & 0xff;
|
|
else
|
|
tlb_lld_2m[ENTRIES] = (eax >> 16) & mask;
|
|
|
|
/* a 4M entry uses two 2M entries */
|
|
tlb_lld_4m[ENTRIES] = tlb_lld_2m[ENTRIES] >> 1;
|
|
|
|
/* Handle ITLB 2M and 4M sizes, fall back to L1 if L2 is disabled */
|
|
if (!(eax & mask)) {
|
|
cpuid(0x80000005, &eax, &ebx, &ecx, &edx);
|
|
tlb_lli_2m[ENTRIES] = eax & 0xff;
|
|
} else
|
|
tlb_lli_2m[ENTRIES] = eax & mask;
|
|
|
|
tlb_lli_4m[ENTRIES] = tlb_lli_2m[ENTRIES] >> 1;
|
|
}
|
|
|
|
static const struct cpu_dev hygon_cpu_dev = {
|
|
.c_vendor = "Hygon",
|
|
.c_ident = { "HygonGenuine" },
|
|
.c_early_init = early_init_hygon,
|
|
.c_detect_tlb = cpu_detect_tlb_hygon,
|
|
.c_bsp_init = bsp_init_hygon,
|
|
.c_init = init_hygon,
|
|
.c_x86_vendor = X86_VENDOR_HYGON,
|
|
};
|
|
|
|
cpu_dev_register(hygon_cpu_dev);
|