11d200c07SBorislav Petkov // SPDX-License-Identifier: GPL-2.0
21d200c07SBorislav Petkov /*
31d200c07SBorislav Petkov * Routines to identify caches on Intel CPU.
41d200c07SBorislav Petkov *
51d200c07SBorislav Petkov * Changes:
61d200c07SBorislav Petkov * Venkatesh Pallipadi : Adding cache identification through cpuid(4)
71d200c07SBorislav Petkov * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
81d200c07SBorislav Petkov * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD.
91d200c07SBorislav Petkov */
101d200c07SBorislav Petkov
111d200c07SBorislav Petkov #include <linux/slab.h>
121d200c07SBorislav Petkov #include <linux/cacheinfo.h>
131d200c07SBorislav Petkov #include <linux/cpu.h>
1430f89e52SJuergen Gross #include <linux/cpuhotplug.h>
151d200c07SBorislav Petkov #include <linux/sched.h>
161d200c07SBorislav Petkov #include <linux/capability.h>
171d200c07SBorislav Petkov #include <linux/sysfs.h>
181d200c07SBorislav Petkov #include <linux/pci.h>
190b9a6a8bSJuergen Gross #include <linux/stop_machine.h>
201d200c07SBorislav Petkov
211d200c07SBorislav Petkov #include <asm/cpufeature.h>
22ad3bc25aSBorislav Petkov #include <asm/cacheinfo.h>
231d200c07SBorislav Petkov #include <asm/amd_nb.h>
241d200c07SBorislav Petkov #include <asm/smp.h>
2523a63e36SJuergen Gross #include <asm/mtrr.h>
2623a63e36SJuergen Gross #include <asm/tlbflush.h>
271d200c07SBorislav Petkov
28b5cf8707SThomas Gleixner #include "cpu.h"
29b5cf8707SThomas Gleixner
301d200c07SBorislav Petkov #define LVL_1_INST 1
311d200c07SBorislav Petkov #define LVL_1_DATA 2
321d200c07SBorislav Petkov #define LVL_2 3
331d200c07SBorislav Petkov #define LVL_3 4
341d200c07SBorislav Petkov #define LVL_TRACE 5
351d200c07SBorislav Petkov
36adbcaef8SSander Vanheule /* Shared last level cache maps */
37adbcaef8SSander Vanheule DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_llc_shared_map);
38adbcaef8SSander Vanheule
39adbcaef8SSander Vanheule /* Shared L2 cache maps */
40adbcaef8SSander Vanheule DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_l2c_shared_map);
41adbcaef8SSander Vanheule
42a32226faSThomas Gleixner static cpumask_var_t cpu_cacheinfo_mask;
43a32226faSThomas Gleixner
4445fa71f1SJuergen Gross /* Kernel controls MTRR and/or PAT MSRs. */
4545fa71f1SJuergen Gross unsigned int memory_caching_control __ro_after_init;
4645fa71f1SJuergen Gross
471d200c07SBorislav Petkov struct _cache_table {
481d200c07SBorislav Petkov unsigned char descriptor;
491d200c07SBorislav Petkov char cache_type;
501d200c07SBorislav Petkov short size;
511d200c07SBorislav Petkov };
521d200c07SBorislav Petkov
531d200c07SBorislav Petkov #define MB(x) ((x) * 1024)
541d200c07SBorislav Petkov
551d200c07SBorislav Petkov /* All the cache descriptor types we care about (no TLB or
561d200c07SBorislav Petkov trace cache entries) */
571d200c07SBorislav Petkov
581d200c07SBorislav Petkov static const struct _cache_table cache_table[] =
591d200c07SBorislav Petkov {
601d200c07SBorislav Petkov { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */
611d200c07SBorislav Petkov { 0x08, LVL_1_INST, 16 }, /* 4-way set assoc, 32 byte line size */
621d200c07SBorislav Petkov { 0x09, LVL_1_INST, 32 }, /* 4-way set assoc, 64 byte line size */
631d200c07SBorislav Petkov { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */
641d200c07SBorislav Petkov { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */
651d200c07SBorislav Petkov { 0x0d, LVL_1_DATA, 16 }, /* 4-way set assoc, 64 byte line size */
661d200c07SBorislav Petkov { 0x0e, LVL_1_DATA, 24 }, /* 6-way set assoc, 64 byte line size */
671d200c07SBorislav Petkov { 0x21, LVL_2, 256 }, /* 8-way set assoc, 64 byte line size */
681d200c07SBorislav Petkov { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
691d200c07SBorislav Petkov { 0x23, LVL_3, MB(1) }, /* 8-way set assoc, sectored cache, 64 byte line size */
701d200c07SBorislav Petkov { 0x25, LVL_3, MB(2) }, /* 8-way set assoc, sectored cache, 64 byte line size */
711d200c07SBorislav Petkov { 0x29, LVL_3, MB(4) }, /* 8-way set assoc, sectored cache, 64 byte line size */
721d200c07SBorislav Petkov { 0x2c, LVL_1_DATA, 32 }, /* 8-way set assoc, 64 byte line size */
731d200c07SBorislav Petkov { 0x30, LVL_1_INST, 32 }, /* 8-way set assoc, 64 byte line size */
741d200c07SBorislav Petkov { 0x39, LVL_2, 128 }, /* 4-way set assoc, sectored cache, 64 byte line size */
751d200c07SBorislav Petkov { 0x3a, LVL_2, 192 }, /* 6-way set assoc, sectored cache, 64 byte line size */
761d200c07SBorislav Petkov { 0x3b, LVL_2, 128 }, /* 2-way set assoc, sectored cache, 64 byte line size */
771d200c07SBorislav Petkov { 0x3c, LVL_2, 256 }, /* 4-way set assoc, sectored cache, 64 byte line size */
781d200c07SBorislav Petkov { 0x3d, LVL_2, 384 }, /* 6-way set assoc, sectored cache, 64 byte line size */
791d200c07SBorislav Petkov { 0x3e, LVL_2, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */
801d200c07SBorislav Petkov { 0x3f, LVL_2, 256 }, /* 2-way set assoc, 64 byte line size */
811d200c07SBorislav Petkov { 0x41, LVL_2, 128 }, /* 4-way set assoc, 32 byte line size */
821d200c07SBorislav Petkov { 0x42, LVL_2, 256 }, /* 4-way set assoc, 32 byte line size */
831d200c07SBorislav Petkov { 0x43, LVL_2, 512 }, /* 4-way set assoc, 32 byte line size */
841d200c07SBorislav Petkov { 0x44, LVL_2, MB(1) }, /* 4-way set assoc, 32 byte line size */
851d200c07SBorislav Petkov { 0x45, LVL_2, MB(2) }, /* 4-way set assoc, 32 byte line size */
861d200c07SBorislav Petkov { 0x46, LVL_3, MB(4) }, /* 4-way set assoc, 64 byte line size */
871d200c07SBorislav Petkov { 0x47, LVL_3, MB(8) }, /* 8-way set assoc, 64 byte line size */
881d200c07SBorislav Petkov { 0x48, LVL_2, MB(3) }, /* 12-way set assoc, 64 byte line size */
891d200c07SBorislav Petkov { 0x49, LVL_3, MB(4) }, /* 16-way set assoc, 64 byte line size */
901d200c07SBorislav Petkov { 0x4a, LVL_3, MB(6) }, /* 12-way set assoc, 64 byte line size */
911d200c07SBorislav Petkov { 0x4b, LVL_3, MB(8) }, /* 16-way set assoc, 64 byte line size */
921d200c07SBorislav Petkov { 0x4c, LVL_3, MB(12) }, /* 12-way set assoc, 64 byte line size */
931d200c07SBorislav Petkov { 0x4d, LVL_3, MB(16) }, /* 16-way set assoc, 64 byte line size */
941d200c07SBorislav Petkov { 0x4e, LVL_2, MB(6) }, /* 24-way set assoc, 64 byte line size */
951d200c07SBorislav Petkov { 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */
961d200c07SBorislav Petkov { 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */
971d200c07SBorislav Petkov { 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */
981d200c07SBorislav Petkov { 0x68, LVL_1_DATA, 32 }, /* 4-way set assoc, sectored cache, 64 byte line size */
991d200c07SBorislav Petkov { 0x70, LVL_TRACE, 12 }, /* 8-way set assoc */
1001d200c07SBorislav Petkov { 0x71, LVL_TRACE, 16 }, /* 8-way set assoc */
1011d200c07SBorislav Petkov { 0x72, LVL_TRACE, 32 }, /* 8-way set assoc */
1021d200c07SBorislav Petkov { 0x73, LVL_TRACE, 64 }, /* 8-way set assoc */
1031d200c07SBorislav Petkov { 0x78, LVL_2, MB(1) }, /* 4-way set assoc, 64 byte line size */
1041d200c07SBorislav Petkov { 0x79, LVL_2, 128 }, /* 8-way set assoc, sectored cache, 64 byte line size */
1051d200c07SBorislav Petkov { 0x7a, LVL_2, 256 }, /* 8-way set assoc, sectored cache, 64 byte line size */
1061d200c07SBorislav Petkov { 0x7b, LVL_2, 512 }, /* 8-way set assoc, sectored cache, 64 byte line size */
1071d200c07SBorislav Petkov { 0x7c, LVL_2, MB(1) }, /* 8-way set assoc, sectored cache, 64 byte line size */
1081d200c07SBorislav Petkov { 0x7d, LVL_2, MB(2) }, /* 8-way set assoc, 64 byte line size */
1091d200c07SBorislav Petkov { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */
1101d200c07SBorislav Petkov { 0x80, LVL_2, 512 }, /* 8-way set assoc, 64 byte line size */
1111d200c07SBorislav Petkov { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */
1121d200c07SBorislav Petkov { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */
1131d200c07SBorislav Petkov { 0x84, LVL_2, MB(1) }, /* 8-way set assoc, 32 byte line size */
1141d200c07SBorislav Petkov { 0x85, LVL_2, MB(2) }, /* 8-way set assoc, 32 byte line size */
1151d200c07SBorislav Petkov { 0x86, LVL_2, 512 }, /* 4-way set assoc, 64 byte line size */
1161d200c07SBorislav Petkov { 0x87, LVL_2, MB(1) }, /* 8-way set assoc, 64 byte line size */
1171d200c07SBorislav Petkov { 0xd0, LVL_3, 512 }, /* 4-way set assoc, 64 byte line size */
1181d200c07SBorislav Petkov { 0xd1, LVL_3, MB(1) }, /* 4-way set assoc, 64 byte line size */
1191d200c07SBorislav Petkov { 0xd2, LVL_3, MB(2) }, /* 4-way set assoc, 64 byte line size */
1201d200c07SBorislav Petkov { 0xd6, LVL_3, MB(1) }, /* 8-way set assoc, 64 byte line size */
1211d200c07SBorislav Petkov { 0xd7, LVL_3, MB(2) }, /* 8-way set assoc, 64 byte line size */
1221d200c07SBorislav Petkov { 0xd8, LVL_3, MB(4) }, /* 12-way set assoc, 64 byte line size */
1231d200c07SBorislav Petkov { 0xdc, LVL_3, MB(2) }, /* 12-way set assoc, 64 byte line size */
1241d200c07SBorislav Petkov { 0xdd, LVL_3, MB(4) }, /* 12-way set assoc, 64 byte line size */
1251d200c07SBorislav Petkov { 0xde, LVL_3, MB(8) }, /* 12-way set assoc, 64 byte line size */
1261d200c07SBorislav Petkov { 0xe2, LVL_3, MB(2) }, /* 16-way set assoc, 64 byte line size */
1271d200c07SBorislav Petkov { 0xe3, LVL_3, MB(4) }, /* 16-way set assoc, 64 byte line size */
1281d200c07SBorislav Petkov { 0xe4, LVL_3, MB(8) }, /* 16-way set assoc, 64 byte line size */
1291d200c07SBorislav Petkov { 0xea, LVL_3, MB(12) }, /* 24-way set assoc, 64 byte line size */
1301d200c07SBorislav Petkov { 0xeb, LVL_3, MB(18) }, /* 24-way set assoc, 64 byte line size */
1311d200c07SBorislav Petkov { 0xec, LVL_3, MB(24) }, /* 24-way set assoc, 64 byte line size */
1321d200c07SBorislav Petkov { 0x00, 0, 0}
1331d200c07SBorislav Petkov };
1341d200c07SBorislav Petkov
1351d200c07SBorislav Petkov
1361d200c07SBorislav Petkov enum _cache_type {
1371d200c07SBorislav Petkov CTYPE_NULL = 0,
1381d200c07SBorislav Petkov CTYPE_DATA = 1,
1391d200c07SBorislav Petkov CTYPE_INST = 2,
1401d200c07SBorislav Petkov CTYPE_UNIFIED = 3
1411d200c07SBorislav Petkov };
1421d200c07SBorislav Petkov
1431d200c07SBorislav Petkov union _cpuid4_leaf_eax {
1441d200c07SBorislav Petkov struct {
1451d200c07SBorislav Petkov enum _cache_type type:5;
1461d200c07SBorislav Petkov unsigned int level:3;
1471d200c07SBorislav Petkov unsigned int is_self_initializing:1;
1481d200c07SBorislav Petkov unsigned int is_fully_associative:1;
1491d200c07SBorislav Petkov unsigned int reserved:4;
1501d200c07SBorislav Petkov unsigned int num_threads_sharing:12;
1511d200c07SBorislav Petkov unsigned int num_cores_on_die:6;
1521d200c07SBorislav Petkov } split;
1531d200c07SBorislav Petkov u32 full;
1541d200c07SBorislav Petkov };
1551d200c07SBorislav Petkov
1561d200c07SBorislav Petkov union _cpuid4_leaf_ebx {
1571d200c07SBorislav Petkov struct {
1581d200c07SBorislav Petkov unsigned int coherency_line_size:12;
1591d200c07SBorislav Petkov unsigned int physical_line_partition:10;
1601d200c07SBorislav Petkov unsigned int ways_of_associativity:10;
1611d200c07SBorislav Petkov } split;
1621d200c07SBorislav Petkov u32 full;
1631d200c07SBorislav Petkov };
1641d200c07SBorislav Petkov
1651d200c07SBorislav Petkov union _cpuid4_leaf_ecx {
1661d200c07SBorislav Petkov struct {
1671d200c07SBorislav Petkov unsigned int number_of_sets:32;
1681d200c07SBorislav Petkov } split;
1691d200c07SBorislav Petkov u32 full;
1701d200c07SBorislav Petkov };
1711d200c07SBorislav Petkov
1721d200c07SBorislav Petkov struct _cpuid4_info_regs {
1731d200c07SBorislav Petkov union _cpuid4_leaf_eax eax;
1741d200c07SBorislav Petkov union _cpuid4_leaf_ebx ebx;
1751d200c07SBorislav Petkov union _cpuid4_leaf_ecx ecx;
1761d200c07SBorislav Petkov unsigned int id;
1771d200c07SBorislav Petkov unsigned long size;
1781d200c07SBorislav Petkov struct amd_northbridge *nb;
1791d200c07SBorislav Petkov };
1801d200c07SBorislav Petkov
1811d200c07SBorislav Petkov static unsigned short num_cache_leaves;
1821d200c07SBorislav Petkov
1831d200c07SBorislav Petkov /* AMD doesn't have CPUID4. Emulate it here to report the same
1841d200c07SBorislav Petkov information to the user. This makes some assumptions about the machine:
1851d200c07SBorislav Petkov L2 not shared, no SMT etc. that is currently true on AMD CPUs.
1861d200c07SBorislav Petkov
1871d200c07SBorislav Petkov In theory the TLBs could be reported as fake type (they are in "dummy").
1881d200c07SBorislav Petkov Maybe later */
1891d200c07SBorislav Petkov union l1_cache {
1901d200c07SBorislav Petkov struct {
1911d200c07SBorislav Petkov unsigned line_size:8;
1921d200c07SBorislav Petkov unsigned lines_per_tag:8;
1931d200c07SBorislav Petkov unsigned assoc:8;
1941d200c07SBorislav Petkov unsigned size_in_kb:8;
1951d200c07SBorislav Petkov };
1961d200c07SBorislav Petkov unsigned val;
1971d200c07SBorislav Petkov };
1981d200c07SBorislav Petkov
1991d200c07SBorislav Petkov union l2_cache {
2001d200c07SBorislav Petkov struct {
2011d200c07SBorislav Petkov unsigned line_size:8;
2021d200c07SBorislav Petkov unsigned lines_per_tag:4;
2031d200c07SBorislav Petkov unsigned assoc:4;
2041d200c07SBorislav Petkov unsigned size_in_kb:16;
2051d200c07SBorislav Petkov };
2061d200c07SBorislav Petkov unsigned val;
2071d200c07SBorislav Petkov };
2081d200c07SBorislav Petkov
2091d200c07SBorislav Petkov union l3_cache {
2101d200c07SBorislav Petkov struct {
2111d200c07SBorislav Petkov unsigned line_size:8;
2121d200c07SBorislav Petkov unsigned lines_per_tag:4;
2131d200c07SBorislav Petkov unsigned assoc:4;
2141d200c07SBorislav Petkov unsigned res:2;
2151d200c07SBorislav Petkov unsigned size_encoded:14;
2161d200c07SBorislav Petkov };
2171d200c07SBorislav Petkov unsigned val;
2181d200c07SBorislav Petkov };
2191d200c07SBorislav Petkov
2201d200c07SBorislav Petkov static const unsigned short assocs[] = {
2211d200c07SBorislav Petkov [1] = 1,
2221d200c07SBorislav Petkov [2] = 2,
2231d200c07SBorislav Petkov [4] = 4,
2241d200c07SBorislav Petkov [6] = 8,
2251d200c07SBorislav Petkov [8] = 16,
2261d200c07SBorislav Petkov [0xa] = 32,
2271d200c07SBorislav Petkov [0xb] = 48,
2281d200c07SBorislav Petkov [0xc] = 64,
2291d200c07SBorislav Petkov [0xd] = 96,
2301d200c07SBorislav Petkov [0xe] = 128,
2311d200c07SBorislav Petkov [0xf] = 0xffff /* fully associative - no way to show this currently */
2321d200c07SBorislav Petkov };
2331d200c07SBorislav Petkov
2341d200c07SBorislav Petkov static const unsigned char levels[] = { 1, 1, 2, 3 };
2351d200c07SBorislav Petkov static const unsigned char types[] = { 1, 2, 3, 3 };
2361d200c07SBorislav Petkov
2371d200c07SBorislav Petkov static const enum cache_type cache_type_map[] = {
2381d200c07SBorislav Petkov [CTYPE_NULL] = CACHE_TYPE_NOCACHE,
2391d200c07SBorislav Petkov [CTYPE_DATA] = CACHE_TYPE_DATA,
2401d200c07SBorislav Petkov [CTYPE_INST] = CACHE_TYPE_INST,
2411d200c07SBorislav Petkov [CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED,
2421d200c07SBorislav Petkov };
2431d200c07SBorislav Petkov
2441d200c07SBorislav Petkov static void
amd_cpuid4(int leaf,union _cpuid4_leaf_eax * eax,union _cpuid4_leaf_ebx * ebx,union _cpuid4_leaf_ecx * ecx)2451d200c07SBorislav Petkov amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
2461d200c07SBorislav Petkov union _cpuid4_leaf_ebx *ebx,
2471d200c07SBorislav Petkov union _cpuid4_leaf_ecx *ecx)
2481d200c07SBorislav Petkov {
2491d200c07SBorislav Petkov unsigned dummy;
2501d200c07SBorislav Petkov unsigned line_size, lines_per_tag, assoc, size_in_kb;
2511d200c07SBorislav Petkov union l1_cache l1i, l1d;
2521d200c07SBorislav Petkov union l2_cache l2;
2531d200c07SBorislav Petkov union l3_cache l3;
2541d200c07SBorislav Petkov union l1_cache *l1 = &l1d;
2551d200c07SBorislav Petkov
2561d200c07SBorislav Petkov eax->full = 0;
2571d200c07SBorislav Petkov ebx->full = 0;
2581d200c07SBorislav Petkov ecx->full = 0;
2591d200c07SBorislav Petkov
2601d200c07SBorislav Petkov cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val);
2611d200c07SBorislav Petkov cpuid(0x80000006, &dummy, &dummy, &l2.val, &l3.val);
2621d200c07SBorislav Petkov
2631d200c07SBorislav Petkov switch (leaf) {
2641d200c07SBorislav Petkov case 1:
2651d200c07SBorislav Petkov l1 = &l1i;
266df561f66SGustavo A. R. Silva fallthrough;
2671d200c07SBorislav Petkov case 0:
2681d200c07SBorislav Petkov if (!l1->val)
2691d200c07SBorislav Petkov return;
2701d200c07SBorislav Petkov assoc = assocs[l1->assoc];
2711d200c07SBorislav Petkov line_size = l1->line_size;
2721d200c07SBorislav Petkov lines_per_tag = l1->lines_per_tag;
2731d200c07SBorislav Petkov size_in_kb = l1->size_in_kb;
2741d200c07SBorislav Petkov break;
2751d200c07SBorislav Petkov case 2:
2761d200c07SBorislav Petkov if (!l2.val)
2771d200c07SBorislav Petkov return;
2781d200c07SBorislav Petkov assoc = assocs[l2.assoc];
2791d200c07SBorislav Petkov line_size = l2.line_size;
2801d200c07SBorislav Petkov lines_per_tag = l2.lines_per_tag;
2811d200c07SBorislav Petkov /* cpu_data has errata corrections for K7 applied */
2821d200c07SBorislav Petkov size_in_kb = __this_cpu_read(cpu_info.x86_cache_size);
2831d200c07SBorislav Petkov break;
2841d200c07SBorislav Petkov case 3:
2851d200c07SBorislav Petkov if (!l3.val)
2861d200c07SBorislav Petkov return;
2871d200c07SBorislav Petkov assoc = assocs[l3.assoc];
2881d200c07SBorislav Petkov line_size = l3.line_size;
2891d200c07SBorislav Petkov lines_per_tag = l3.lines_per_tag;
2901d200c07SBorislav Petkov size_in_kb = l3.size_encoded * 512;
2911d200c07SBorislav Petkov if (boot_cpu_has(X86_FEATURE_AMD_DCM)) {
2921d200c07SBorislav Petkov size_in_kb = size_in_kb >> 1;
2931d200c07SBorislav Petkov assoc = assoc >> 1;
2941d200c07SBorislav Petkov }
2951d200c07SBorislav Petkov break;
2961d200c07SBorislav Petkov default:
2971d200c07SBorislav Petkov return;
2981d200c07SBorislav Petkov }
2991d200c07SBorislav Petkov
3001d200c07SBorislav Petkov eax->split.is_self_initializing = 1;
3011d200c07SBorislav Petkov eax->split.type = types[leaf];
3021d200c07SBorislav Petkov eax->split.level = levels[leaf];
3031d200c07SBorislav Petkov eax->split.num_threads_sharing = 0;
3041d200c07SBorislav Petkov eax->split.num_cores_on_die = __this_cpu_read(cpu_info.x86_max_cores) - 1;
3051d200c07SBorislav Petkov
3061d200c07SBorislav Petkov
3071d200c07SBorislav Petkov if (assoc == 0xffff)
3081d200c07SBorislav Petkov eax->split.is_fully_associative = 1;
3091d200c07SBorislav Petkov ebx->split.coherency_line_size = line_size - 1;
3101d200c07SBorislav Petkov ebx->split.ways_of_associativity = assoc - 1;
3111d200c07SBorislav Petkov ebx->split.physical_line_partition = lines_per_tag - 1;
3121d200c07SBorislav Petkov ecx->split.number_of_sets = (size_in_kb * 1024) / line_size /
3131d200c07SBorislav Petkov (ebx->split.ways_of_associativity + 1) - 1;
3141d200c07SBorislav Petkov }
3151d200c07SBorislav Petkov
3161d200c07SBorislav Petkov #if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS)
3171d200c07SBorislav Petkov
3181d200c07SBorislav Petkov /*
3191d200c07SBorislav Petkov * L3 cache descriptors
3201d200c07SBorislav Petkov */
amd_calc_l3_indices(struct amd_northbridge * nb)3211d200c07SBorislav Petkov static void amd_calc_l3_indices(struct amd_northbridge *nb)
3221d200c07SBorislav Petkov {
3231d200c07SBorislav Petkov struct amd_l3_cache *l3 = &nb->l3_cache;
3241d200c07SBorislav Petkov unsigned int sc0, sc1, sc2, sc3;
3251d200c07SBorislav Petkov u32 val = 0;
3261d200c07SBorislav Petkov
3271d200c07SBorislav Petkov pci_read_config_dword(nb->misc, 0x1C4, &val);
3281d200c07SBorislav Petkov
3291d200c07SBorislav Petkov /* calculate subcache sizes */
3301d200c07SBorislav Petkov l3->subcaches[0] = sc0 = !(val & BIT(0));
3311d200c07SBorislav Petkov l3->subcaches[1] = sc1 = !(val & BIT(4));
3321d200c07SBorislav Petkov
3331d200c07SBorislav Petkov if (boot_cpu_data.x86 == 0x15) {
3341d200c07SBorislav Petkov l3->subcaches[0] = sc0 += !(val & BIT(1));
3351d200c07SBorislav Petkov l3->subcaches[1] = sc1 += !(val & BIT(5));
3361d200c07SBorislav Petkov }
3371d200c07SBorislav Petkov
3381d200c07SBorislav Petkov l3->subcaches[2] = sc2 = !(val & BIT(8)) + !(val & BIT(9));
3391d200c07SBorislav Petkov l3->subcaches[3] = sc3 = !(val & BIT(12)) + !(val & BIT(13));
3401d200c07SBorislav Petkov
3411d200c07SBorislav Petkov l3->indices = (max(max3(sc0, sc1, sc2), sc3) << 10) - 1;
3421d200c07SBorislav Petkov }
3431d200c07SBorislav Petkov
3441d200c07SBorislav Petkov /*
3451d200c07SBorislav Petkov * check whether a slot used for disabling an L3 index is occupied.
3461d200c07SBorislav Petkov * @l3: L3 cache descriptor
3471d200c07SBorislav Petkov * @slot: slot number (0..1)
3481d200c07SBorislav Petkov *
3491d200c07SBorislav Petkov * @returns: the disabled index if used or negative value if slot free.
3501d200c07SBorislav Petkov */
amd_get_l3_disable_slot(struct amd_northbridge * nb,unsigned slot)3511d200c07SBorislav Petkov static int amd_get_l3_disable_slot(struct amd_northbridge *nb, unsigned slot)
3521d200c07SBorislav Petkov {
3531d200c07SBorislav Petkov unsigned int reg = 0;
3541d200c07SBorislav Petkov
3551d200c07SBorislav Petkov pci_read_config_dword(nb->misc, 0x1BC + slot * 4, ®);
3561d200c07SBorislav Petkov
3571d200c07SBorislav Petkov /* check whether this slot is activated already */
3581d200c07SBorislav Petkov if (reg & (3UL << 30))
3591d200c07SBorislav Petkov return reg & 0xfff;
3601d200c07SBorislav Petkov
3611d200c07SBorislav Petkov return -1;
3621d200c07SBorislav Petkov }
3631d200c07SBorislav Petkov
show_cache_disable(struct cacheinfo * this_leaf,char * buf,unsigned int slot)3641d200c07SBorislav Petkov static ssize_t show_cache_disable(struct cacheinfo *this_leaf, char *buf,
3651d200c07SBorislav Petkov unsigned int slot)
3661d200c07SBorislav Petkov {
3671d200c07SBorislav Petkov int index;
3681d200c07SBorislav Petkov struct amd_northbridge *nb = this_leaf->priv;
3691d200c07SBorislav Petkov
3701d200c07SBorislav Petkov index = amd_get_l3_disable_slot(nb, slot);
3711d200c07SBorislav Petkov if (index >= 0)
3721d200c07SBorislav Petkov return sprintf(buf, "%d\n", index);
3731d200c07SBorislav Petkov
3741d200c07SBorislav Petkov return sprintf(buf, "FREE\n");
3751d200c07SBorislav Petkov }
3761d200c07SBorislav Petkov
3771d200c07SBorislav Petkov #define SHOW_CACHE_DISABLE(slot) \
3781d200c07SBorislav Petkov static ssize_t \
3791d200c07SBorislav Petkov cache_disable_##slot##_show(struct device *dev, \
3801d200c07SBorislav Petkov struct device_attribute *attr, char *buf) \
3811d200c07SBorislav Petkov { \
3821d200c07SBorislav Petkov struct cacheinfo *this_leaf = dev_get_drvdata(dev); \
3831d200c07SBorislav Petkov return show_cache_disable(this_leaf, buf, slot); \
3841d200c07SBorislav Petkov }
3851d200c07SBorislav Petkov SHOW_CACHE_DISABLE(0)
3861d200c07SBorislav Petkov SHOW_CACHE_DISABLE(1)
3871d200c07SBorislav Petkov
amd_l3_disable_index(struct amd_northbridge * nb,int cpu,unsigned slot,unsigned long idx)3881d200c07SBorislav Petkov static void amd_l3_disable_index(struct amd_northbridge *nb, int cpu,
3891d200c07SBorislav Petkov unsigned slot, unsigned long idx)
3901d200c07SBorislav Petkov {
3911d200c07SBorislav Petkov int i;
3921d200c07SBorislav Petkov
3931d200c07SBorislav Petkov idx |= BIT(30);
3941d200c07SBorislav Petkov
3951d200c07SBorislav Petkov /*
3961d200c07SBorislav Petkov * disable index in all 4 subcaches
3971d200c07SBorislav Petkov */
3981d200c07SBorislav Petkov for (i = 0; i < 4; i++) {
3991d200c07SBorislav Petkov u32 reg = idx | (i << 20);
4001d200c07SBorislav Petkov
4011d200c07SBorislav Petkov if (!nb->l3_cache.subcaches[i])
4021d200c07SBorislav Petkov continue;
4031d200c07SBorislav Petkov
4041d200c07SBorislav Petkov pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg);
4051d200c07SBorislav Petkov
4061d200c07SBorislav Petkov /*
4071d200c07SBorislav Petkov * We need to WBINVD on a core on the node containing the L3
4081d200c07SBorislav Petkov * cache which indices we disable therefore a simple wbinvd()
4091d200c07SBorislav Petkov * is not sufficient.
4101d200c07SBorislav Petkov */
4111d200c07SBorislav Petkov wbinvd_on_cpu(cpu);
4121d200c07SBorislav Petkov
4131d200c07SBorislav Petkov reg |= BIT(31);
4141d200c07SBorislav Petkov pci_write_config_dword(nb->misc, 0x1BC + slot * 4, reg);
4151d200c07SBorislav Petkov }
4161d200c07SBorislav Petkov }
4171d200c07SBorislav Petkov
4181d200c07SBorislav Petkov /*
4191d200c07SBorislav Petkov * disable a L3 cache index by using a disable-slot
4201d200c07SBorislav Petkov *
4211d200c07SBorislav Petkov * @l3: L3 cache descriptor
4221d200c07SBorislav Petkov * @cpu: A CPU on the node containing the L3 cache
4231d200c07SBorislav Petkov * @slot: slot number (0..1)
4241d200c07SBorislav Petkov * @index: index to disable
4251d200c07SBorislav Petkov *
4261d200c07SBorislav Petkov * @return: 0 on success, error status on failure
4271d200c07SBorislav Petkov */
amd_set_l3_disable_slot(struct amd_northbridge * nb,int cpu,unsigned slot,unsigned long index)4281d200c07SBorislav Petkov static int amd_set_l3_disable_slot(struct amd_northbridge *nb, int cpu,
4291d200c07SBorislav Petkov unsigned slot, unsigned long index)
4301d200c07SBorislav Petkov {
4311d200c07SBorislav Petkov int ret = 0;
4321d200c07SBorislav Petkov
4331d200c07SBorislav Petkov /* check if @slot is already used or the index is already disabled */
4341d200c07SBorislav Petkov ret = amd_get_l3_disable_slot(nb, slot);
4351d200c07SBorislav Petkov if (ret >= 0)
4361d200c07SBorislav Petkov return -EEXIST;
4371d200c07SBorislav Petkov
4381d200c07SBorislav Petkov if (index > nb->l3_cache.indices)
4391d200c07SBorislav Petkov return -EINVAL;
4401d200c07SBorislav Petkov
4411d200c07SBorislav Petkov /* check whether the other slot has disabled the same index already */
4421d200c07SBorislav Petkov if (index == amd_get_l3_disable_slot(nb, !slot))
4431d200c07SBorislav Petkov return -EEXIST;
4441d200c07SBorislav Petkov
4451d200c07SBorislav Petkov amd_l3_disable_index(nb, cpu, slot, index);
4461d200c07SBorislav Petkov
4471d200c07SBorislav Petkov return 0;
4481d200c07SBorislav Petkov }
4491d200c07SBorislav Petkov
store_cache_disable(struct cacheinfo * this_leaf,const char * buf,size_t count,unsigned int slot)4501d200c07SBorislav Petkov static ssize_t store_cache_disable(struct cacheinfo *this_leaf,
4511d200c07SBorislav Petkov const char *buf, size_t count,
4521d200c07SBorislav Petkov unsigned int slot)
4531d200c07SBorislav Petkov {
4541d200c07SBorislav Petkov unsigned long val = 0;
4551d200c07SBorislav Petkov int cpu, err = 0;
4561d200c07SBorislav Petkov struct amd_northbridge *nb = this_leaf->priv;
4571d200c07SBorislav Petkov
4581d200c07SBorislav Petkov if (!capable(CAP_SYS_ADMIN))
4591d200c07SBorislav Petkov return -EPERM;
4601d200c07SBorislav Petkov
4611d200c07SBorislav Petkov cpu = cpumask_first(&this_leaf->shared_cpu_map);
4621d200c07SBorislav Petkov
4631d200c07SBorislav Petkov if (kstrtoul(buf, 10, &val) < 0)
4641d200c07SBorislav Petkov return -EINVAL;
4651d200c07SBorislav Petkov
4661d200c07SBorislav Petkov err = amd_set_l3_disable_slot(nb, cpu, slot, val);
4671d200c07SBorislav Petkov if (err) {
4681d200c07SBorislav Petkov if (err == -EEXIST)
4691d200c07SBorislav Petkov pr_warn("L3 slot %d in use/index already disabled!\n",
4701d200c07SBorislav Petkov slot);
4711d200c07SBorislav Petkov return err;
4721d200c07SBorislav Petkov }
4731d200c07SBorislav Petkov return count;
4741d200c07SBorislav Petkov }
4751d200c07SBorislav Petkov
4761d200c07SBorislav Petkov #define STORE_CACHE_DISABLE(slot) \
4771d200c07SBorislav Petkov static ssize_t \
4781d200c07SBorislav Petkov cache_disable_##slot##_store(struct device *dev, \
4791d200c07SBorislav Petkov struct device_attribute *attr, \
4801d200c07SBorislav Petkov const char *buf, size_t count) \
4811d200c07SBorislav Petkov { \
4821d200c07SBorislav Petkov struct cacheinfo *this_leaf = dev_get_drvdata(dev); \
4831d200c07SBorislav Petkov return store_cache_disable(this_leaf, buf, count, slot); \
4841d200c07SBorislav Petkov }
4851d200c07SBorislav Petkov STORE_CACHE_DISABLE(0)
4861d200c07SBorislav Petkov STORE_CACHE_DISABLE(1)
4871d200c07SBorislav Petkov
subcaches_show(struct device * dev,struct device_attribute * attr,char * buf)4881d200c07SBorislav Petkov static ssize_t subcaches_show(struct device *dev,
4891d200c07SBorislav Petkov struct device_attribute *attr, char *buf)
4901d200c07SBorislav Petkov {
4911d200c07SBorislav Petkov struct cacheinfo *this_leaf = dev_get_drvdata(dev);
4921d200c07SBorislav Petkov int cpu = cpumask_first(&this_leaf->shared_cpu_map);
4931d200c07SBorislav Petkov
4941d200c07SBorislav Petkov return sprintf(buf, "%x\n", amd_get_subcaches(cpu));
4951d200c07SBorislav Petkov }
4961d200c07SBorislav Petkov
subcaches_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4971d200c07SBorislav Petkov static ssize_t subcaches_store(struct device *dev,
4981d200c07SBorislav Petkov struct device_attribute *attr,
4991d200c07SBorislav Petkov const char *buf, size_t count)
5001d200c07SBorislav Petkov {
5011d200c07SBorislav Petkov struct cacheinfo *this_leaf = dev_get_drvdata(dev);
5021d200c07SBorislav Petkov int cpu = cpumask_first(&this_leaf->shared_cpu_map);
5031d200c07SBorislav Petkov unsigned long val;
5041d200c07SBorislav Petkov
5051d200c07SBorislav Petkov if (!capable(CAP_SYS_ADMIN))
5061d200c07SBorislav Petkov return -EPERM;
5071d200c07SBorislav Petkov
5081d200c07SBorislav Petkov if (kstrtoul(buf, 16, &val) < 0)
5091d200c07SBorislav Petkov return -EINVAL;
5101d200c07SBorislav Petkov
5111d200c07SBorislav Petkov if (amd_set_subcaches(cpu, val))
5121d200c07SBorislav Petkov return -EINVAL;
5131d200c07SBorislav Petkov
5141d200c07SBorislav Petkov return count;
5151d200c07SBorislav Petkov }
5161d200c07SBorislav Petkov
5171d200c07SBorislav Petkov static DEVICE_ATTR_RW(cache_disable_0);
5181d200c07SBorislav Petkov static DEVICE_ATTR_RW(cache_disable_1);
5191d200c07SBorislav Petkov static DEVICE_ATTR_RW(subcaches);
5201d200c07SBorislav Petkov
5211d200c07SBorislav Petkov static umode_t
cache_private_attrs_is_visible(struct kobject * kobj,struct attribute * attr,int unused)5221d200c07SBorislav Petkov cache_private_attrs_is_visible(struct kobject *kobj,
5231d200c07SBorislav Petkov struct attribute *attr, int unused)
5241d200c07SBorislav Petkov {
5251d200c07SBorislav Petkov struct device *dev = kobj_to_dev(kobj);
5261d200c07SBorislav Petkov struct cacheinfo *this_leaf = dev_get_drvdata(dev);
5271d200c07SBorislav Petkov umode_t mode = attr->mode;
5281d200c07SBorislav Petkov
5291d200c07SBorislav Petkov if (!this_leaf->priv)
5301d200c07SBorislav Petkov return 0;
5311d200c07SBorislav Petkov
5321d200c07SBorislav Petkov if ((attr == &dev_attr_subcaches.attr) &&
5331d200c07SBorislav Petkov amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
5341d200c07SBorislav Petkov return mode;
5351d200c07SBorislav Petkov
5361d200c07SBorislav Petkov if ((attr == &dev_attr_cache_disable_0.attr ||
5371d200c07SBorislav Petkov attr == &dev_attr_cache_disable_1.attr) &&
5381d200c07SBorislav Petkov amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
5391d200c07SBorislav Petkov return mode;
5401d200c07SBorislav Petkov
5411d200c07SBorislav Petkov return 0;
5421d200c07SBorislav Petkov }
5431d200c07SBorislav Petkov
5441d200c07SBorislav Petkov static struct attribute_group cache_private_group = {
5451d200c07SBorislav Petkov .is_visible = cache_private_attrs_is_visible,
5461d200c07SBorislav Petkov };
5471d200c07SBorislav Petkov
init_amd_l3_attrs(void)5481d200c07SBorislav Petkov static void init_amd_l3_attrs(void)
5491d200c07SBorislav Petkov {
5501d200c07SBorislav Petkov int n = 1;
5511d200c07SBorislav Petkov static struct attribute **amd_l3_attrs;
5521d200c07SBorislav Petkov
5531d200c07SBorislav Petkov if (amd_l3_attrs) /* already initialized */
5541d200c07SBorislav Petkov return;
5551d200c07SBorislav Petkov
5561d200c07SBorislav Petkov if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE))
5571d200c07SBorislav Petkov n += 2;
5581d200c07SBorislav Petkov if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
5591d200c07SBorislav Petkov n += 1;
5601d200c07SBorislav Petkov
5611d200c07SBorislav Petkov amd_l3_attrs = kcalloc(n, sizeof(*amd_l3_attrs), GFP_KERNEL);
5621d200c07SBorislav Petkov if (!amd_l3_attrs)
5631d200c07SBorislav Petkov return;
5641d200c07SBorislav Petkov
5651d200c07SBorislav Petkov n = 0;
5661d200c07SBorislav Petkov if (amd_nb_has_feature(AMD_NB_L3_INDEX_DISABLE)) {
5671d200c07SBorislav Petkov amd_l3_attrs[n++] = &dev_attr_cache_disable_0.attr;
5681d200c07SBorislav Petkov amd_l3_attrs[n++] = &dev_attr_cache_disable_1.attr;
5691d200c07SBorislav Petkov }
5701d200c07SBorislav Petkov if (amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
5711d200c07SBorislav Petkov amd_l3_attrs[n++] = &dev_attr_subcaches.attr;
5721d200c07SBorislav Petkov
5731d200c07SBorislav Petkov cache_private_group.attrs = amd_l3_attrs;
5741d200c07SBorislav Petkov }
5751d200c07SBorislav Petkov
5761d200c07SBorislav Petkov const struct attribute_group *
cache_get_priv_group(struct cacheinfo * this_leaf)5771d200c07SBorislav Petkov cache_get_priv_group(struct cacheinfo *this_leaf)
5781d200c07SBorislav Petkov {
5791d200c07SBorislav Petkov struct amd_northbridge *nb = this_leaf->priv;
5801d200c07SBorislav Petkov
5811d200c07SBorislav Petkov if (this_leaf->level < 3 || !nb)
5821d200c07SBorislav Petkov return NULL;
5831d200c07SBorislav Petkov
5841d200c07SBorislav Petkov if (nb && nb->l3_cache.indices)
5851d200c07SBorislav Petkov init_amd_l3_attrs();
5861d200c07SBorislav Petkov
5871d200c07SBorislav Petkov return &cache_private_group;
5881d200c07SBorislav Petkov }
5891d200c07SBorislav Petkov
amd_init_l3_cache(struct _cpuid4_info_regs * this_leaf,int index)5901d200c07SBorislav Petkov static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index)
5911d200c07SBorislav Petkov {
5921d200c07SBorislav Petkov int node;
5931d200c07SBorislav Petkov
5941d200c07SBorislav Petkov /* only for L3, and not in virtualized environments */
5951d200c07SBorislav Petkov if (index < 3)
5961d200c07SBorislav Petkov return;
5971d200c07SBorislav Petkov
598db970bd2SYazen Ghannam node = topology_die_id(smp_processor_id());
5991d200c07SBorislav Petkov this_leaf->nb = node_to_amd_nb(node);
6001d200c07SBorislav Petkov if (this_leaf->nb && !this_leaf->nb->l3_cache.indices)
6011d200c07SBorislav Petkov amd_calc_l3_indices(this_leaf->nb);
6021d200c07SBorislav Petkov }
6031d200c07SBorislav Petkov #else
6041d200c07SBorislav Petkov #define amd_init_l3_cache(x, y)
6051d200c07SBorislav Petkov #endif /* CONFIG_AMD_NB && CONFIG_SYSFS */
6061d200c07SBorislav Petkov
6071d200c07SBorislav Petkov static int
cpuid4_cache_lookup_regs(int index,struct _cpuid4_info_regs * this_leaf)6081d200c07SBorislav Petkov cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf)
6091d200c07SBorislav Petkov {
6101d200c07SBorislav Petkov union _cpuid4_leaf_eax eax;
6111d200c07SBorislav Petkov union _cpuid4_leaf_ebx ebx;
6121d200c07SBorislav Petkov union _cpuid4_leaf_ecx ecx;
6131d200c07SBorislav Petkov unsigned edx;
6141d200c07SBorislav Petkov
6151d200c07SBorislav Petkov if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
6161d200c07SBorislav Petkov if (boot_cpu_has(X86_FEATURE_TOPOEXT))
6171d200c07SBorislav Petkov cpuid_count(0x8000001d, index, &eax.full,
6181d200c07SBorislav Petkov &ebx.full, &ecx.full, &edx);
6191d200c07SBorislav Petkov else
6201d200c07SBorislav Petkov amd_cpuid4(index, &eax, &ebx, &ecx);
6211d200c07SBorislav Petkov amd_init_l3_cache(this_leaf, index);
622d4f7423eSPu Wen } else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) {
623d4f7423eSPu Wen cpuid_count(0x8000001d, index, &eax.full,
624d4f7423eSPu Wen &ebx.full, &ecx.full, &edx);
625d4f7423eSPu Wen amd_init_l3_cache(this_leaf, index);
6261d200c07SBorislav Petkov } else {
6271d200c07SBorislav Petkov cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
6281d200c07SBorislav Petkov }
6291d200c07SBorislav Petkov
6301d200c07SBorislav Petkov if (eax.split.type == CTYPE_NULL)
6311d200c07SBorislav Petkov return -EIO; /* better error ? */
6321d200c07SBorislav Petkov
6331d200c07SBorislav Petkov this_leaf->eax = eax;
6341d200c07SBorislav Petkov this_leaf->ebx = ebx;
6351d200c07SBorislav Petkov this_leaf->ecx = ecx;
6361d200c07SBorislav Petkov this_leaf->size = (ecx.split.number_of_sets + 1) *
6371d200c07SBorislav Petkov (ebx.split.coherency_line_size + 1) *
6381d200c07SBorislav Petkov (ebx.split.physical_line_partition + 1) *
6391d200c07SBorislav Petkov (ebx.split.ways_of_associativity + 1);
6401d200c07SBorislav Petkov return 0;
6411d200c07SBorislav Petkov }
6421d200c07SBorislav Petkov
find_num_cache_leaves(struct cpuinfo_x86 * c)6431d200c07SBorislav Petkov static int find_num_cache_leaves(struct cpuinfo_x86 *c)
6441d200c07SBorislav Petkov {
6451d200c07SBorislav Petkov unsigned int eax, ebx, ecx, edx, op;
6461d200c07SBorislav Petkov union _cpuid4_leaf_eax cache_eax;
6471d200c07SBorislav Petkov int i = -1;
6481d200c07SBorislav Petkov
649d4f7423eSPu Wen if (c->x86_vendor == X86_VENDOR_AMD ||
650d4f7423eSPu Wen c->x86_vendor == X86_VENDOR_HYGON)
6511d200c07SBorislav Petkov op = 0x8000001d;
6521d200c07SBorislav Petkov else
6531d200c07SBorislav Petkov op = 4;
6541d200c07SBorislav Petkov
6551d200c07SBorislav Petkov do {
6561d200c07SBorislav Petkov ++i;
6571d200c07SBorislav Petkov /* Do cpuid(op) loop to find out num_cache_leaves */
6581d200c07SBorislav Petkov cpuid_count(op, i, &eax, &ebx, &ecx, &edx);
6591d200c07SBorislav Petkov cache_eax.full = eax;
6601d200c07SBorislav Petkov } while (cache_eax.split.type != CTYPE_NULL);
6611d200c07SBorislav Petkov return i;
6621d200c07SBorislav Petkov }
6631d200c07SBorislav Petkov
cacheinfo_amd_init_llc_id(struct cpuinfo_x86 * c,int cpu)664028c221eSYazen Ghannam void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu)
66568091ee7SSuravee Suthikulpanit {
66668091ee7SSuravee Suthikulpanit /*
66768091ee7SSuravee Suthikulpanit * We may have multiple LLCs if L3 caches exist, so check if we
66868091ee7SSuravee Suthikulpanit * have an L3 cache by looking at the L3 cache CPUID leaf.
66968091ee7SSuravee Suthikulpanit */
67068091ee7SSuravee Suthikulpanit if (!cpuid_edx(0x80000006))
67168091ee7SSuravee Suthikulpanit return;
67268091ee7SSuravee Suthikulpanit
67368091ee7SSuravee Suthikulpanit if (c->x86 < 0x17) {
67468091ee7SSuravee Suthikulpanit /* LLC is at the node level. */
675028c221eSYazen Ghannam per_cpu(cpu_llc_id, cpu) = c->cpu_die_id;
6761b7aebf0SQian Cai } else if (c->x86 == 0x17 && c->x86_model <= 0x1F) {
67768091ee7SSuravee Suthikulpanit /*
67868091ee7SSuravee Suthikulpanit * LLC is at the core complex level.
67968091ee7SSuravee Suthikulpanit * Core complex ID is ApicId[3] for these processors.
68068091ee7SSuravee Suthikulpanit */
68168091ee7SSuravee Suthikulpanit per_cpu(cpu_llc_id, cpu) = c->apicid >> 3;
68268091ee7SSuravee Suthikulpanit } else {
68368091ee7SSuravee Suthikulpanit /*
68468091ee7SSuravee Suthikulpanit * LLC ID is calculated from the number of threads sharing the
68568091ee7SSuravee Suthikulpanit * cache.
68668091ee7SSuravee Suthikulpanit * */
68768091ee7SSuravee Suthikulpanit u32 eax, ebx, ecx, edx, num_sharing_cache = 0;
68868091ee7SSuravee Suthikulpanit u32 llc_index = find_num_cache_leaves(c) - 1;
68968091ee7SSuravee Suthikulpanit
69068091ee7SSuravee Suthikulpanit cpuid_count(0x8000001d, llc_index, &eax, &ebx, &ecx, &edx);
69168091ee7SSuravee Suthikulpanit if (eax)
69268091ee7SSuravee Suthikulpanit num_sharing_cache = ((eax >> 14) & 0xfff) + 1;
69368091ee7SSuravee Suthikulpanit
69468091ee7SSuravee Suthikulpanit if (num_sharing_cache) {
695964d9784SSuravee Suthikulpanit int bits = get_count_order(num_sharing_cache);
69668091ee7SSuravee Suthikulpanit
69768091ee7SSuravee Suthikulpanit per_cpu(cpu_llc_id, cpu) = c->apicid >> bits;
69868091ee7SSuravee Suthikulpanit }
69968091ee7SSuravee Suthikulpanit }
70068091ee7SSuravee Suthikulpanit }
70168091ee7SSuravee Suthikulpanit
cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 * c,int cpu)702028c221eSYazen Ghannam void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c, int cpu)
703d4f7423eSPu Wen {
704d4f7423eSPu Wen /*
705d4f7423eSPu Wen * We may have multiple LLCs if L3 caches exist, so check if we
706d4f7423eSPu Wen * have an L3 cache by looking at the L3 cache CPUID leaf.
707d4f7423eSPu Wen */
708d4f7423eSPu Wen if (!cpuid_edx(0x80000006))
709d4f7423eSPu Wen return;
710d4f7423eSPu Wen
711d4f7423eSPu Wen /*
712d4f7423eSPu Wen * LLC is at the core complex level.
713d4f7423eSPu Wen * Core complex ID is ApicId[3] for these processors.
714d4f7423eSPu Wen */
715d4f7423eSPu Wen per_cpu(cpu_llc_id, cpu) = c->apicid >> 3;
716d4f7423eSPu Wen }
717d4f7423eSPu Wen
init_amd_cacheinfo(struct cpuinfo_x86 * c)7181d200c07SBorislav Petkov void init_amd_cacheinfo(struct cpuinfo_x86 *c)
7191d200c07SBorislav Petkov {
7201d200c07SBorislav Petkov
7211d200c07SBorislav Petkov if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
7221d200c07SBorislav Petkov num_cache_leaves = find_num_cache_leaves(c);
7231d200c07SBorislav Petkov } else if (c->extended_cpuid_level >= 0x80000006) {
7241d200c07SBorislav Petkov if (cpuid_edx(0x80000006) & 0xf000)
7251d200c07SBorislav Petkov num_cache_leaves = 4;
7261d200c07SBorislav Petkov else
7271d200c07SBorislav Petkov num_cache_leaves = 3;
7281d200c07SBorislav Petkov }
7291d200c07SBorislav Petkov }
7301d200c07SBorislav Petkov
init_hygon_cacheinfo(struct cpuinfo_x86 * c)731d4f7423eSPu Wen void init_hygon_cacheinfo(struct cpuinfo_x86 *c)
732d4f7423eSPu Wen {
733d4f7423eSPu Wen num_cache_leaves = find_num_cache_leaves(c);
734d4f7423eSPu Wen }
735d4f7423eSPu Wen
init_intel_cacheinfo(struct cpuinfo_x86 * c)736807e9bc8SDavid Wang void init_intel_cacheinfo(struct cpuinfo_x86 *c)
7371d200c07SBorislav Petkov {
7381d200c07SBorislav Petkov /* Cache sizes */
739851026a2SBorislav Petkov (AMD) unsigned int l1i = 0, l1d = 0, l2 = 0, l3 = 0;
7401d200c07SBorislav Petkov unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
7411d200c07SBorislav Petkov unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
7421d200c07SBorislav Petkov unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
7431d200c07SBorislav Petkov #ifdef CONFIG_SMP
7441d200c07SBorislav Petkov unsigned int cpu = c->cpu_index;
7451d200c07SBorislav Petkov #endif
7461d200c07SBorislav Petkov
7471d200c07SBorislav Petkov if (c->cpuid_level > 3) {
7481d200c07SBorislav Petkov static int is_initialized;
7491d200c07SBorislav Petkov
7501d200c07SBorislav Petkov if (is_initialized == 0) {
7511d200c07SBorislav Petkov /* Init num_cache_leaves from boot CPU */
7521d200c07SBorislav Petkov num_cache_leaves = find_num_cache_leaves(c);
7531d200c07SBorislav Petkov is_initialized++;
7541d200c07SBorislav Petkov }
7551d200c07SBorislav Petkov
7561d200c07SBorislav Petkov /*
7571d200c07SBorislav Petkov * Whenever possible use cpuid(4), deterministic cache
7581d200c07SBorislav Petkov * parameters cpuid leaf to find the cache details
7591d200c07SBorislav Petkov */
7601d200c07SBorislav Petkov for (i = 0; i < num_cache_leaves; i++) {
7611d200c07SBorislav Petkov struct _cpuid4_info_regs this_leaf = {};
7621d200c07SBorislav Petkov int retval;
7631d200c07SBorislav Petkov
7641d200c07SBorislav Petkov retval = cpuid4_cache_lookup_regs(i, &this_leaf);
7651d200c07SBorislav Petkov if (retval < 0)
7661d200c07SBorislav Petkov continue;
7671d200c07SBorislav Petkov
7681d200c07SBorislav Petkov switch (this_leaf.eax.split.level) {
7691d200c07SBorislav Petkov case 1:
7701d200c07SBorislav Petkov if (this_leaf.eax.split.type == CTYPE_DATA)
7711d200c07SBorislav Petkov new_l1d = this_leaf.size/1024;
7721d200c07SBorislav Petkov else if (this_leaf.eax.split.type == CTYPE_INST)
7731d200c07SBorislav Petkov new_l1i = this_leaf.size/1024;
7741d200c07SBorislav Petkov break;
7751d200c07SBorislav Petkov case 2:
7761d200c07SBorislav Petkov new_l2 = this_leaf.size/1024;
7771d200c07SBorislav Petkov num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
7781d200c07SBorislav Petkov index_msb = get_count_order(num_threads_sharing);
7791d200c07SBorislav Petkov l2_id = c->apicid & ~((1 << index_msb) - 1);
7801d200c07SBorislav Petkov break;
7811d200c07SBorislav Petkov case 3:
7821d200c07SBorislav Petkov new_l3 = this_leaf.size/1024;
7831d200c07SBorislav Petkov num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
7841d200c07SBorislav Petkov index_msb = get_count_order(num_threads_sharing);
7851d200c07SBorislav Petkov l3_id = c->apicid & ~((1 << index_msb) - 1);
7861d200c07SBorislav Petkov break;
7871d200c07SBorislav Petkov default:
7881d200c07SBorislav Petkov break;
7891d200c07SBorislav Petkov }
7901d200c07SBorislav Petkov }
7911d200c07SBorislav Petkov }
7921d200c07SBorislav Petkov /*
7931d200c07SBorislav Petkov * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
7941d200c07SBorislav Petkov * trace cache
7951d200c07SBorislav Petkov */
7961d200c07SBorislav Petkov if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
7971d200c07SBorislav Petkov /* supports eax=2 call */
7981d200c07SBorislav Petkov int j, n;
7991d200c07SBorislav Petkov unsigned int regs[4];
8001d200c07SBorislav Petkov unsigned char *dp = (unsigned char *)regs;
8011d200c07SBorislav Petkov int only_trace = 0;
8021d200c07SBorislav Petkov
8031d200c07SBorislav Petkov if (num_cache_leaves != 0 && c->x86 == 15)
8041d200c07SBorislav Petkov only_trace = 1;
8051d200c07SBorislav Petkov
8061d200c07SBorislav Petkov /* Number of times to iterate */
8071d200c07SBorislav Petkov n = cpuid_eax(2) & 0xFF;
8081d200c07SBorislav Petkov
8091d200c07SBorislav Petkov for (i = 0 ; i < n ; i++) {
8101d200c07SBorislav Petkov cpuid(2, ®s[0], ®s[1], ®s[2], ®s[3]);
8111d200c07SBorislav Petkov
8121d200c07SBorislav Petkov /* If bit 31 is set, this is an unknown format */
813*db79249bSAhmed S. Darwish for (j = 0 ; j < 4 ; j++)
8141d200c07SBorislav Petkov if (regs[j] & (1 << 31))
8151d200c07SBorislav Petkov regs[j] = 0;
8161d200c07SBorislav Petkov
8171d200c07SBorislav Petkov /* Byte 0 is level count, not a descriptor */
8181d200c07SBorislav Petkov for (j = 1 ; j < 16 ; j++) {
8191d200c07SBorislav Petkov unsigned char des = dp[j];
8201d200c07SBorislav Petkov unsigned char k = 0;
8211d200c07SBorislav Petkov
8221d200c07SBorislav Petkov /* look up this descriptor in the table */
8231d200c07SBorislav Petkov while (cache_table[k].descriptor != 0) {
8241d200c07SBorislav Petkov if (cache_table[k].descriptor == des) {
8251d200c07SBorislav Petkov if (only_trace && cache_table[k].cache_type != LVL_TRACE)
8261d200c07SBorislav Petkov break;
8271d200c07SBorislav Petkov switch (cache_table[k].cache_type) {
8281d200c07SBorislav Petkov case LVL_1_INST:
8291d200c07SBorislav Petkov l1i += cache_table[k].size;
8301d200c07SBorislav Petkov break;
8311d200c07SBorislav Petkov case LVL_1_DATA:
8321d200c07SBorislav Petkov l1d += cache_table[k].size;
8331d200c07SBorislav Petkov break;
8341d200c07SBorislav Petkov case LVL_2:
8351d200c07SBorislav Petkov l2 += cache_table[k].size;
8361d200c07SBorislav Petkov break;
8371d200c07SBorislav Petkov case LVL_3:
8381d200c07SBorislav Petkov l3 += cache_table[k].size;
8391d200c07SBorislav Petkov break;
8401d200c07SBorislav Petkov }
8411d200c07SBorislav Petkov
8421d200c07SBorislav Petkov break;
8431d200c07SBorislav Petkov }
8441d200c07SBorislav Petkov
8451d200c07SBorislav Petkov k++;
8461d200c07SBorislav Petkov }
8471d200c07SBorislav Petkov }
8481d200c07SBorislav Petkov }
8491d200c07SBorislav Petkov }
8501d200c07SBorislav Petkov
8511d200c07SBorislav Petkov if (new_l1d)
8521d200c07SBorislav Petkov l1d = new_l1d;
8531d200c07SBorislav Petkov
8541d200c07SBorislav Petkov if (new_l1i)
8551d200c07SBorislav Petkov l1i = new_l1i;
8561d200c07SBorislav Petkov
8571d200c07SBorislav Petkov if (new_l2) {
8581d200c07SBorislav Petkov l2 = new_l2;
8591d200c07SBorislav Petkov #ifdef CONFIG_SMP
8601d200c07SBorislav Petkov per_cpu(cpu_llc_id, cpu) = l2_id;
86166558b73STim Chen per_cpu(cpu_l2c_id, cpu) = l2_id;
8621d200c07SBorislav Petkov #endif
8631d200c07SBorislav Petkov }
8641d200c07SBorislav Petkov
8651d200c07SBorislav Petkov if (new_l3) {
8661d200c07SBorislav Petkov l3 = new_l3;
8671d200c07SBorislav Petkov #ifdef CONFIG_SMP
8681d200c07SBorislav Petkov per_cpu(cpu_llc_id, cpu) = l3_id;
8691d200c07SBorislav Petkov #endif
8701d200c07SBorislav Petkov }
8711d200c07SBorislav Petkov
8721d200c07SBorislav Petkov #ifdef CONFIG_SMP
8731d200c07SBorislav Petkov /*
8741d200c07SBorislav Petkov * If cpu_llc_id is not yet set, this means cpuid_level < 4 which in
8751d200c07SBorislav Petkov * turns means that the only possibility is SMT (as indicated in
8761d200c07SBorislav Petkov * cpuid1). Since cpuid2 doesn't specify shared caches, and we know
8771d200c07SBorislav Petkov * that SMT shares all caches, we can unconditionally set cpu_llc_id to
8781d200c07SBorislav Petkov * c->phys_proc_id.
8791d200c07SBorislav Petkov */
8801d200c07SBorislav Petkov if (per_cpu(cpu_llc_id, cpu) == BAD_APICID)
8811d200c07SBorislav Petkov per_cpu(cpu_llc_id, cpu) = c->phys_proc_id;
8821d200c07SBorislav Petkov #endif
8831d200c07SBorislav Petkov
8841d200c07SBorislav Petkov c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
8851d200c07SBorislav Petkov
886807e9bc8SDavid Wang if (!l2)
887807e9bc8SDavid Wang cpu_detect_cache_sizes(c);
8881d200c07SBorislav Petkov }
8891d200c07SBorislav Petkov
__cache_amd_cpumap_setup(unsigned int cpu,int index,struct _cpuid4_info_regs * base)8901d200c07SBorislav Petkov static int __cache_amd_cpumap_setup(unsigned int cpu, int index,
8911d200c07SBorislav Petkov struct _cpuid4_info_regs *base)
8921d200c07SBorislav Petkov {
893dda451f3SYang Li struct cpu_cacheinfo *this_cpu_ci;
8941d200c07SBorislav Petkov struct cacheinfo *this_leaf;
8951d200c07SBorislav Petkov int i, sibling;
8961d200c07SBorislav Petkov
8971d200c07SBorislav Petkov /*
8981d200c07SBorislav Petkov * For L3, always use the pre-calculated cpu_llc_shared_mask
8991d200c07SBorislav Petkov * to derive shared_cpu_map.
9001d200c07SBorislav Petkov */
9011d200c07SBorislav Petkov if (index == 3) {
9021d200c07SBorislav Petkov for_each_cpu(i, cpu_llc_shared_mask(cpu)) {
9031d200c07SBorislav Petkov this_cpu_ci = get_cpu_cacheinfo(i);
9041d200c07SBorislav Petkov if (!this_cpu_ci->info_list)
9051d200c07SBorislav Petkov continue;
9061d200c07SBorislav Petkov this_leaf = this_cpu_ci->info_list + index;
9071d200c07SBorislav Petkov for_each_cpu(sibling, cpu_llc_shared_mask(cpu)) {
9081d200c07SBorislav Petkov if (!cpu_online(sibling))
9091d200c07SBorislav Petkov continue;
9101d200c07SBorislav Petkov cpumask_set_cpu(sibling,
9111d200c07SBorislav Petkov &this_leaf->shared_cpu_map);
9121d200c07SBorislav Petkov }
9131d200c07SBorislav Petkov }
9141d200c07SBorislav Petkov } else if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
9151d200c07SBorislav Petkov unsigned int apicid, nshared, first, last;
9161d200c07SBorislav Petkov
9171d200c07SBorislav Petkov nshared = base->eax.split.num_threads_sharing + 1;
9181d200c07SBorislav Petkov apicid = cpu_data(cpu).apicid;
9191d200c07SBorislav Petkov first = apicid - (apicid % nshared);
9201d200c07SBorislav Petkov last = first + nshared - 1;
9211d200c07SBorislav Petkov
9221d200c07SBorislav Petkov for_each_online_cpu(i) {
9231d200c07SBorislav Petkov this_cpu_ci = get_cpu_cacheinfo(i);
9241d200c07SBorislav Petkov if (!this_cpu_ci->info_list)
9251d200c07SBorislav Petkov continue;
9261d200c07SBorislav Petkov
9271d200c07SBorislav Petkov apicid = cpu_data(i).apicid;
9281d200c07SBorislav Petkov if ((apicid < first) || (apicid > last))
9291d200c07SBorislav Petkov continue;
9301d200c07SBorislav Petkov
9311d200c07SBorislav Petkov this_leaf = this_cpu_ci->info_list + index;
9321d200c07SBorislav Petkov
9331d200c07SBorislav Petkov for_each_online_cpu(sibling) {
9341d200c07SBorislav Petkov apicid = cpu_data(sibling).apicid;
9351d200c07SBorislav Petkov if ((apicid < first) || (apicid > last))
9361d200c07SBorislav Petkov continue;
9371d200c07SBorislav Petkov cpumask_set_cpu(sibling,
9381d200c07SBorislav Petkov &this_leaf->shared_cpu_map);
9391d200c07SBorislav Petkov }
9401d200c07SBorislav Petkov }
9411d200c07SBorislav Petkov } else
9421d200c07SBorislav Petkov return 0;
9431d200c07SBorislav Petkov
9441d200c07SBorislav Petkov return 1;
9451d200c07SBorislav Petkov }
9461d200c07SBorislav Petkov
__cache_cpumap_setup(unsigned int cpu,int index,struct _cpuid4_info_regs * base)9471d200c07SBorislav Petkov static void __cache_cpumap_setup(unsigned int cpu, int index,
9481d200c07SBorislav Petkov struct _cpuid4_info_regs *base)
9491d200c07SBorislav Petkov {
9501d200c07SBorislav Petkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
9511d200c07SBorislav Petkov struct cacheinfo *this_leaf, *sibling_leaf;
9521d200c07SBorislav Petkov unsigned long num_threads_sharing;
9531d200c07SBorislav Petkov int index_msb, i;
9541d200c07SBorislav Petkov struct cpuinfo_x86 *c = &cpu_data(cpu);
9551d200c07SBorislav Petkov
956d4f7423eSPu Wen if (c->x86_vendor == X86_VENDOR_AMD ||
957d4f7423eSPu Wen c->x86_vendor == X86_VENDOR_HYGON) {
9581d200c07SBorislav Petkov if (__cache_amd_cpumap_setup(cpu, index, base))
9591d200c07SBorislav Petkov return;
9601d200c07SBorislav Petkov }
9611d200c07SBorislav Petkov
9621d200c07SBorislav Petkov this_leaf = this_cpu_ci->info_list + index;
9631d200c07SBorislav Petkov num_threads_sharing = 1 + base->eax.split.num_threads_sharing;
9641d200c07SBorislav Petkov
9651d200c07SBorislav Petkov cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
9661d200c07SBorislav Petkov if (num_threads_sharing == 1)
9671d200c07SBorislav Petkov return;
9681d200c07SBorislav Petkov
9691d200c07SBorislav Petkov index_msb = get_count_order(num_threads_sharing);
9701d200c07SBorislav Petkov
9711d200c07SBorislav Petkov for_each_online_cpu(i)
9721d200c07SBorislav Petkov if (cpu_data(i).apicid >> index_msb == c->apicid >> index_msb) {
9731d200c07SBorislav Petkov struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
9741d200c07SBorislav Petkov
9751d200c07SBorislav Petkov if (i == cpu || !sib_cpu_ci->info_list)
9761d200c07SBorislav Petkov continue;/* skip if itself or no cacheinfo */
9771d200c07SBorislav Petkov sibling_leaf = sib_cpu_ci->info_list + index;
9781d200c07SBorislav Petkov cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
9791d200c07SBorislav Petkov cpumask_set_cpu(cpu, &sibling_leaf->shared_cpu_map);
9801d200c07SBorislav Petkov }
9811d200c07SBorislav Petkov }
9821d200c07SBorislav Petkov
ci_leaf_init(struct cacheinfo * this_leaf,struct _cpuid4_info_regs * base)9831d200c07SBorislav Petkov static void ci_leaf_init(struct cacheinfo *this_leaf,
9841d200c07SBorislav Petkov struct _cpuid4_info_regs *base)
9851d200c07SBorislav Petkov {
9861d200c07SBorislav Petkov this_leaf->id = base->id;
9871d200c07SBorislav Petkov this_leaf->attributes = CACHE_ID;
9881d200c07SBorislav Petkov this_leaf->level = base->eax.split.level;
9891d200c07SBorislav Petkov this_leaf->type = cache_type_map[base->eax.split.type];
9901d200c07SBorislav Petkov this_leaf->coherency_line_size =
9911d200c07SBorislav Petkov base->ebx.split.coherency_line_size + 1;
9921d200c07SBorislav Petkov this_leaf->ways_of_associativity =
9931d200c07SBorislav Petkov base->ebx.split.ways_of_associativity + 1;
9941d200c07SBorislav Petkov this_leaf->size = base->size;
9951d200c07SBorislav Petkov this_leaf->number_of_sets = base->ecx.split.number_of_sets + 1;
9961d200c07SBorislav Petkov this_leaf->physical_line_partition =
9971d200c07SBorislav Petkov base->ebx.split.physical_line_partition + 1;
9981d200c07SBorislav Petkov this_leaf->priv = base->nb;
9991d200c07SBorislav Petkov }
10001d200c07SBorislav Petkov
init_cache_level(unsigned int cpu)10014b92d4adSThomas Gleixner int init_cache_level(unsigned int cpu)
10021d200c07SBorislav Petkov {
10031d200c07SBorislav Petkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
10041d200c07SBorislav Petkov
10051d200c07SBorislav Petkov if (!num_cache_leaves)
10061d200c07SBorislav Petkov return -ENOENT;
10071d200c07SBorislav Petkov if (!this_cpu_ci)
10081d200c07SBorislav Petkov return -EINVAL;
10091d200c07SBorislav Petkov this_cpu_ci->num_levels = 3;
10101d200c07SBorislav Petkov this_cpu_ci->num_leaves = num_cache_leaves;
10111d200c07SBorislav Petkov return 0;
10121d200c07SBorislav Petkov }
10131d200c07SBorislav Petkov
10141d200c07SBorislav Petkov /*
10151d200c07SBorislav Petkov * The max shared threads number comes from CPUID.4:EAX[25-14] with input
10161d200c07SBorislav Petkov * ECX as cache index. Then right shift apicid by the number's order to get
10171d200c07SBorislav Petkov * cache id for this cache node.
10181d200c07SBorislav Petkov */
get_cache_id(int cpu,struct _cpuid4_info_regs * id4_regs)10191d200c07SBorislav Petkov static void get_cache_id(int cpu, struct _cpuid4_info_regs *id4_regs)
10201d200c07SBorislav Petkov {
10211d200c07SBorislav Petkov struct cpuinfo_x86 *c = &cpu_data(cpu);
10221d200c07SBorislav Petkov unsigned long num_threads_sharing;
10231d200c07SBorislav Petkov int index_msb;
10241d200c07SBorislav Petkov
10251d200c07SBorislav Petkov num_threads_sharing = 1 + id4_regs->eax.split.num_threads_sharing;
10261d200c07SBorislav Petkov index_msb = get_count_order(num_threads_sharing);
10271d200c07SBorislav Petkov id4_regs->id = c->apicid >> index_msb;
10281d200c07SBorislav Petkov }
10291d200c07SBorislav Petkov
populate_cache_leaves(unsigned int cpu)10304b92d4adSThomas Gleixner int populate_cache_leaves(unsigned int cpu)
10311d200c07SBorislav Petkov {
10321d200c07SBorislav Petkov unsigned int idx, ret;
10331d200c07SBorislav Petkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
10341d200c07SBorislav Petkov struct cacheinfo *this_leaf = this_cpu_ci->info_list;
10351d200c07SBorislav Petkov struct _cpuid4_info_regs id4_regs = {};
10361d200c07SBorislav Petkov
10371d200c07SBorislav Petkov for (idx = 0; idx < this_cpu_ci->num_leaves; idx++) {
10381d200c07SBorislav Petkov ret = cpuid4_cache_lookup_regs(idx, &id4_regs);
10391d200c07SBorislav Petkov if (ret)
10401d200c07SBorislav Petkov return ret;
10411d200c07SBorislav Petkov get_cache_id(cpu, &id4_regs);
10421d200c07SBorislav Petkov ci_leaf_init(this_leaf++, &id4_regs);
10431d200c07SBorislav Petkov __cache_cpumap_setup(cpu, idx, &id4_regs);
10441d200c07SBorislav Petkov }
10451d200c07SBorislav Petkov this_cpu_ci->cpu_map_populated = true;
10461d200c07SBorislav Petkov
10471d200c07SBorislav Petkov return 0;
10481d200c07SBorislav Petkov }
104923a63e36SJuergen Gross
105023a63e36SJuergen Gross /*
105123a63e36SJuergen Gross * Disable and enable caches. Needed for changing MTRRs and the PAT MSR.
105223a63e36SJuergen Gross *
105323a63e36SJuergen Gross * Since we are disabling the cache don't allow any interrupts,
105423a63e36SJuergen Gross * they would run extremely slow and would only increase the pain.
105523a63e36SJuergen Gross *
105623a63e36SJuergen Gross * The caller must ensure that local interrupts are disabled and
105723a63e36SJuergen Gross * are reenabled after cache_enable() has been called.
105823a63e36SJuergen Gross */
105923a63e36SJuergen Gross static unsigned long saved_cr4;
106023a63e36SJuergen Gross static DEFINE_RAW_SPINLOCK(cache_disable_lock);
106123a63e36SJuergen Gross
cache_disable(void)106223a63e36SJuergen Gross void cache_disable(void) __acquires(cache_disable_lock)
106323a63e36SJuergen Gross {
106423a63e36SJuergen Gross unsigned long cr0;
106523a63e36SJuergen Gross
106623a63e36SJuergen Gross /*
106723a63e36SJuergen Gross * Note that this is not ideal
106823a63e36SJuergen Gross * since the cache is only flushed/disabled for this CPU while the
106923a63e36SJuergen Gross * MTRRs are changed, but changing this requires more invasive
107023a63e36SJuergen Gross * changes to the way the kernel boots
107123a63e36SJuergen Gross */
107223a63e36SJuergen Gross
107323a63e36SJuergen Gross raw_spin_lock(&cache_disable_lock);
107423a63e36SJuergen Gross
107523a63e36SJuergen Gross /* Enter the no-fill (CD=1, NW=0) cache mode and flush caches. */
107623a63e36SJuergen Gross cr0 = read_cr0() | X86_CR0_CD;
107723a63e36SJuergen Gross write_cr0(cr0);
107823a63e36SJuergen Gross
107923a63e36SJuergen Gross /*
108023a63e36SJuergen Gross * Cache flushing is the most time-consuming step when programming
108123a63e36SJuergen Gross * the MTRRs. Fortunately, as per the Intel Software Development
108223a63e36SJuergen Gross * Manual, we can skip it if the processor supports cache self-
108323a63e36SJuergen Gross * snooping.
108423a63e36SJuergen Gross */
108523a63e36SJuergen Gross if (!static_cpu_has(X86_FEATURE_SELFSNOOP))
108623a63e36SJuergen Gross wbinvd();
108723a63e36SJuergen Gross
108823a63e36SJuergen Gross /* Save value of CR4 and clear Page Global Enable (bit 7) */
108923a63e36SJuergen Gross if (cpu_feature_enabled(X86_FEATURE_PGE)) {
109023a63e36SJuergen Gross saved_cr4 = __read_cr4();
109123a63e36SJuergen Gross __write_cr4(saved_cr4 & ~X86_CR4_PGE);
109223a63e36SJuergen Gross }
109323a63e36SJuergen Gross
109423a63e36SJuergen Gross /* Flush all TLBs via a mov %cr3, %reg; mov %reg, %cr3 */
109523a63e36SJuergen Gross count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
109623a63e36SJuergen Gross flush_tlb_local();
109723a63e36SJuergen Gross
109823a63e36SJuergen Gross if (cpu_feature_enabled(X86_FEATURE_MTRR))
109923a63e36SJuergen Gross mtrr_disable();
110023a63e36SJuergen Gross
110123a63e36SJuergen Gross /* Again, only flush caches if we have to. */
110223a63e36SJuergen Gross if (!static_cpu_has(X86_FEATURE_SELFSNOOP))
110323a63e36SJuergen Gross wbinvd();
110423a63e36SJuergen Gross }
110523a63e36SJuergen Gross
cache_enable(void)110623a63e36SJuergen Gross void cache_enable(void) __releases(cache_disable_lock)
110723a63e36SJuergen Gross {
110823a63e36SJuergen Gross /* Flush TLBs (no need to flush caches - they are disabled) */
110923a63e36SJuergen Gross count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
111023a63e36SJuergen Gross flush_tlb_local();
111123a63e36SJuergen Gross
111223a63e36SJuergen Gross if (cpu_feature_enabled(X86_FEATURE_MTRR))
111323a63e36SJuergen Gross mtrr_enable();
111423a63e36SJuergen Gross
111523a63e36SJuergen Gross /* Enable caches */
111623a63e36SJuergen Gross write_cr0(read_cr0() & ~X86_CR0_CD);
111723a63e36SJuergen Gross
111823a63e36SJuergen Gross /* Restore value of CR4 */
111923a63e36SJuergen Gross if (cpu_feature_enabled(X86_FEATURE_PGE))
112023a63e36SJuergen Gross __write_cr4(saved_cr4);
112123a63e36SJuergen Gross
112223a63e36SJuergen Gross raw_spin_unlock(&cache_disable_lock);
112323a63e36SJuergen Gross }
11247d71db53SJuergen Gross
cache_cpu_init(void)11250b9a6a8bSJuergen Gross static void cache_cpu_init(void)
11267d71db53SJuergen Gross {
11277d71db53SJuergen Gross unsigned long flags;
11287d71db53SJuergen Gross
11297d71db53SJuergen Gross local_irq_save(flags);
11307d71db53SJuergen Gross cache_disable();
11317d71db53SJuergen Gross
11327d71db53SJuergen Gross if (memory_caching_control & CACHE_MTRR)
11337d71db53SJuergen Gross mtrr_generic_set_state();
11347d71db53SJuergen Gross
11357d71db53SJuergen Gross if (memory_caching_control & CACHE_PAT)
1136adfe7512SJuergen Gross pat_cpu_init();
11377d71db53SJuergen Gross
11387d71db53SJuergen Gross cache_enable();
11397d71db53SJuergen Gross local_irq_restore(flags);
11407d71db53SJuergen Gross }
1141955d0e08SJuergen Gross
114230f89e52SJuergen Gross static bool cache_aps_delayed_init = true;
1143955d0e08SJuergen Gross
set_cache_aps_delayed_init(bool val)1144955d0e08SJuergen Gross void set_cache_aps_delayed_init(bool val)
1145955d0e08SJuergen Gross {
1146955d0e08SJuergen Gross cache_aps_delayed_init = val;
1147955d0e08SJuergen Gross }
1148955d0e08SJuergen Gross
get_cache_aps_delayed_init(void)1149955d0e08SJuergen Gross bool get_cache_aps_delayed_init(void)
1150955d0e08SJuergen Gross {
1151955d0e08SJuergen Gross return cache_aps_delayed_init;
1152955d0e08SJuergen Gross }
11530b9a6a8bSJuergen Gross
cache_rendezvous_handler(void * unused)11540b9a6a8bSJuergen Gross static int cache_rendezvous_handler(void *unused)
11550b9a6a8bSJuergen Gross {
11560b9a6a8bSJuergen Gross if (get_cache_aps_delayed_init() || !cpu_online(smp_processor_id()))
11570b9a6a8bSJuergen Gross cache_cpu_init();
11580b9a6a8bSJuergen Gross
11590b9a6a8bSJuergen Gross return 0;
11600b9a6a8bSJuergen Gross }
11610b9a6a8bSJuergen Gross
cache_bp_init(void)11620b9a6a8bSJuergen Gross void __init cache_bp_init(void)
11630b9a6a8bSJuergen Gross {
11640b9a6a8bSJuergen Gross mtrr_bp_init();
1165adfe7512SJuergen Gross pat_bp_init();
11660b9a6a8bSJuergen Gross
11670b9a6a8bSJuergen Gross if (memory_caching_control)
11680b9a6a8bSJuergen Gross cache_cpu_init();
11690b9a6a8bSJuergen Gross }
11700b9a6a8bSJuergen Gross
cache_bp_restore(void)11710b9a6a8bSJuergen Gross void cache_bp_restore(void)
11720b9a6a8bSJuergen Gross {
11730b9a6a8bSJuergen Gross if (memory_caching_control)
11740b9a6a8bSJuergen Gross cache_cpu_init();
11750b9a6a8bSJuergen Gross }
11760b9a6a8bSJuergen Gross
cache_ap_online(unsigned int cpu)1177a32226faSThomas Gleixner static int cache_ap_online(unsigned int cpu)
11780b9a6a8bSJuergen Gross {
1179a32226faSThomas Gleixner cpumask_set_cpu(cpu, cpu_cacheinfo_mask);
1180a32226faSThomas Gleixner
11810b9a6a8bSJuergen Gross if (!memory_caching_control || get_cache_aps_delayed_init())
118230f89e52SJuergen Gross return 0;
11830b9a6a8bSJuergen Gross
11840b9a6a8bSJuergen Gross /*
11850b9a6a8bSJuergen Gross * Ideally we should hold mtrr_mutex here to avoid MTRR entries
11860b9a6a8bSJuergen Gross * changed, but this routine will be called in CPU boot time,
11870b9a6a8bSJuergen Gross * holding the lock breaks it.
11880b9a6a8bSJuergen Gross *
11890b9a6a8bSJuergen Gross * This routine is called in two cases:
11900b9a6a8bSJuergen Gross *
11910b9a6a8bSJuergen Gross * 1. very early time of software resume, when there absolutely
11920b9a6a8bSJuergen Gross * isn't MTRR entry changes;
11930b9a6a8bSJuergen Gross *
11940b9a6a8bSJuergen Gross * 2. CPU hotadd time. We let mtrr_add/del_page hold cpuhotplug
11950b9a6a8bSJuergen Gross * lock to prevent MTRR entry changes
11960b9a6a8bSJuergen Gross */
11970b9a6a8bSJuergen Gross stop_machine_from_inactive_cpu(cache_rendezvous_handler, NULL,
1198a32226faSThomas Gleixner cpu_cacheinfo_mask);
119930f89e52SJuergen Gross
120030f89e52SJuergen Gross return 0;
12010b9a6a8bSJuergen Gross }
12020b9a6a8bSJuergen Gross
cache_ap_offline(unsigned int cpu)1203a32226faSThomas Gleixner static int cache_ap_offline(unsigned int cpu)
1204a32226faSThomas Gleixner {
1205a32226faSThomas Gleixner cpumask_clear_cpu(cpu, cpu_cacheinfo_mask);
1206a32226faSThomas Gleixner return 0;
1207a32226faSThomas Gleixner }
1208a32226faSThomas Gleixner
12090b9a6a8bSJuergen Gross /*
12100b9a6a8bSJuergen Gross * Delayed cache initialization for all AP's
12110b9a6a8bSJuergen Gross */
cache_aps_init(void)12120b9a6a8bSJuergen Gross void cache_aps_init(void)
12130b9a6a8bSJuergen Gross {
12140b9a6a8bSJuergen Gross if (!memory_caching_control || !get_cache_aps_delayed_init())
12150b9a6a8bSJuergen Gross return;
12160b9a6a8bSJuergen Gross
12170b9a6a8bSJuergen Gross stop_machine(cache_rendezvous_handler, NULL, cpu_online_mask);
12180b9a6a8bSJuergen Gross set_cache_aps_delayed_init(false);
12190b9a6a8bSJuergen Gross }
122030f89e52SJuergen Gross
cache_ap_register(void)122130f89e52SJuergen Gross static int __init cache_ap_register(void)
122230f89e52SJuergen Gross {
1223a32226faSThomas Gleixner zalloc_cpumask_var(&cpu_cacheinfo_mask, GFP_KERNEL);
1224a32226faSThomas Gleixner cpumask_set_cpu(smp_processor_id(), cpu_cacheinfo_mask);
1225a32226faSThomas Gleixner
122630f89e52SJuergen Gross cpuhp_setup_state_nocalls(CPUHP_AP_CACHECTRL_STARTING,
122730f89e52SJuergen Gross "x86/cachectrl:starting",
1228a32226faSThomas Gleixner cache_ap_online, cache_ap_offline);
122930f89e52SJuergen Gross return 0;
123030f89e52SJuergen Gross }
1231a32226faSThomas Gleixner early_initcall(cache_ap_register);
1232