mirror of
https://github.com/google/cpu_features.git
synced 2025-07-01 05:11:15 +02:00
Add cache detection for old AMD processors (#199)
* Add cache detection for of old AMD processors update links * Add documentation link for cache_size * 512 * Update legacy amd cache detection
This commit is contained in:
@ -1689,6 +1689,115 @@ static void ParseCacheInfo(const int max_cpuid_leaf, uint32_t leaf_id,
|
||||
if (info.size > 0) *old_info = info;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int level;
|
||||
int cache_id;
|
||||
CacheType cache_type;
|
||||
} CacheLevelInfoLegacyAMD;
|
||||
|
||||
static int GetWaysLegacyAMD(int cache_level, const uint32_t cache_id) {
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf page 23
|
||||
// CPUID.8000_0005_ECX[23:16] L1 data cache associativity.
|
||||
// CPUID.8000_0005_EDX[23:16] L1 instruction cache associativity.
|
||||
if (cache_level == 1) {
|
||||
return ExtractBitRange(cache_id, 23, 16);
|
||||
}
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf page 24
|
||||
// See Table 4: L2/L3 Cache and TLB Associativity Field Definition.
|
||||
// CPUID.8000_0006_ECX[15:12] L2 cache associativity.
|
||||
// CPUID.8000_0006_EDX[15:12] L3 cache associativity.
|
||||
const int ways = ExtractBitRange(cache_id, 15, 12);
|
||||
switch (ways) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x4:
|
||||
return ways;
|
||||
case 0x6:
|
||||
return 8;
|
||||
case 0x8:
|
||||
return 16;
|
||||
case 0xA:
|
||||
return 32;
|
||||
case 0xB:
|
||||
return 48;
|
||||
case 0xC:
|
||||
return 64;
|
||||
case 0xD:
|
||||
return 96;
|
||||
case 0xE:
|
||||
return 128;
|
||||
case 0xF:
|
||||
return 255;
|
||||
default:
|
||||
return -1; // Reserved
|
||||
}
|
||||
}
|
||||
|
||||
static int GetCacheSizeLegacyAMD(int cache_level, const uint32_t cache_id) {
|
||||
switch (cache_level) {
|
||||
case 1:
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf page 23
|
||||
// CPUID.8000_0005_ECX[31:24] L1 data cache size in KB.
|
||||
// CPUID.8000_0005_EDX[31:24] L1 instruction cache size KB.
|
||||
return ExtractBitRange(cache_id, 31, 24);
|
||||
case 2:
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf page 25
|
||||
// CPUID.8000_0006_ECX[31:16] L2 cache size in KB.
|
||||
return ExtractBitRange(cache_id, 31, 16);
|
||||
case 3:
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf page 25
|
||||
// CPUID.8000_0006_EDX[31:18] L3 cache size.
|
||||
// Specifies the L3 cache size is within the following range:
|
||||
// (L3Size[31:18] * 512KB) <= L3 cache size < ((L3Size[31:18]+1) * 512KB).
|
||||
return ExtractBitRange(cache_id, 31, 18) * 512;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define LEGACY_AMD_MAX_CACHE_LEVEL 4
|
||||
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf
|
||||
// CPUID Fn8000_0005_E[A,B,C,D]X, Fn8000_0006_E[A,B,C,D]X - TLB and Cache info
|
||||
static void ParseCacheInfoLegacyAMD(const uint32_t max_ext, CacheInfo* info) {
|
||||
const Leaf cache_tlb_leaf1 = SafeCpuIdEx(max_ext, 0x80000005, 0);
|
||||
const Leaf cache_tlb_leaf2 = SafeCpuIdEx(max_ext, 0x80000006, 0);
|
||||
|
||||
const CacheLevelInfoLegacyAMD legacy_cache_info[LEGACY_AMD_MAX_CACHE_LEVEL] =
|
||||
{(CacheLevelInfoLegacyAMD){.cache_id = cache_tlb_leaf1.ecx,
|
||||
.cache_type = CPU_FEATURE_CACHE_DATA,
|
||||
.level = 1},
|
||||
(CacheLevelInfoLegacyAMD){.cache_id = cache_tlb_leaf1.edx,
|
||||
.cache_type = CPU_FEATURE_CACHE_INSTRUCTION,
|
||||
.level = 1},
|
||||
(CacheLevelInfoLegacyAMD){.cache_id = cache_tlb_leaf2.ecx,
|
||||
.cache_type = CPU_FEATURE_CACHE_UNIFIED,
|
||||
.level = 2},
|
||||
(CacheLevelInfoLegacyAMD){.cache_id = cache_tlb_leaf2.edx,
|
||||
.cache_type = CPU_FEATURE_CACHE_UNIFIED,
|
||||
.level = 3}};
|
||||
|
||||
const int KiB = 1024;
|
||||
const int UNDEF = -1;
|
||||
for (int i = 0; i < LEGACY_AMD_MAX_CACHE_LEVEL; ++i) {
|
||||
const int level = legacy_cache_info[i].level;
|
||||
const int cache_id = legacy_cache_info[i].cache_id;
|
||||
const CacheType cache_type = legacy_cache_info[i].cache_type;
|
||||
const int cache_size = GetCacheSizeLegacyAMD(level, cache_id);
|
||||
if (cache_size == 0) break;
|
||||
info->levels[i] =
|
||||
(CacheLevelInfo){.level = level,
|
||||
.cache_type = cache_type,
|
||||
.cache_size = cache_size * KiB,
|
||||
.ways = GetWaysLegacyAMD(level, cache_id),
|
||||
.line_size = ExtractBitRange(cache_id, 7, 0),
|
||||
.tlb_entries = UNDEF,
|
||||
.partitioning = UNDEF};
|
||||
++info->size;
|
||||
}
|
||||
}
|
||||
|
||||
CacheInfo GetX86CacheInfo(void) {
|
||||
CacheInfo info = kEmptyCacheInfo;
|
||||
const Leaves leaves = ReadLeaves();
|
||||
@ -1704,6 +1813,8 @@ CacheInfo GetX86CacheInfo(void) {
|
||||
// https://www.amd.com/system/files/TechDocs/25481.pdf
|
||||
if (IsBitSet(leaves.leaf_80000001.ecx, 22)) {
|
||||
ParseCacheInfo(leaves.max_cpuid_leaf_ext, 0x8000001D, &info);
|
||||
} else {
|
||||
ParseCacheInfoLegacyAMD(leaves.max_cpuid_leaf_ext, &info);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
|
@ -891,6 +891,111 @@ TEST_F(CpuidX86Test, INTEL_ALDER_LAKE_AVX_VNNI) {
|
||||
EXPECT_EQ(GetX86Microarchitecture(&info), X86Microarchitecture::INTEL_ADL);
|
||||
}
|
||||
|
||||
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0100FA0_K10_Thuban_CPUID.txt
|
||||
TEST_F(CpuidX86Test, AMD_THUBAN_CACHE_INFO) {
|
||||
cpu().SetLeaves({
|
||||
{{0x00000000, 0}, Leaf{0x00000006, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000000, 0}, Leaf{0x8000001B, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000001, 0}, Leaf{0x00100FA0, 0x10000050, 0x000037FF, 0xEFD3FBFF}},
|
||||
{{0x80000005, 0}, Leaf{0xFF30FF10, 0xFF30FF20, 0x40020140, 0x40020140}},
|
||||
{{0x80000006, 0}, Leaf{0x20800000, 0x42004200, 0x02008140, 0x0030B140}},
|
||||
});
|
||||
const auto info = GetX86CacheInfo();
|
||||
|
||||
EXPECT_EQ(info.size, 4);
|
||||
EXPECT_EQ(info.levels[0].level, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_type, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[0].ways, 2);
|
||||
EXPECT_EQ(info.levels[0].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[1].level, 1);
|
||||
EXPECT_EQ(info.levels[1].cache_type, 2);
|
||||
EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[1].ways, 2);
|
||||
EXPECT_EQ(info.levels[1].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[2].level, 2);
|
||||
EXPECT_EQ(info.levels[2].cache_type, 3);
|
||||
EXPECT_EQ(info.levels[2].cache_size, 512 * KiB);
|
||||
EXPECT_EQ(info.levels[2].ways, 16);
|
||||
EXPECT_EQ(info.levels[2].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[3].level, 3);
|
||||
EXPECT_EQ(info.levels[3].cache_type, 3);
|
||||
EXPECT_EQ(info.levels[3].cache_size, 6 * MiB);
|
||||
EXPECT_EQ(info.levels[3].ways, 48);
|
||||
EXPECT_EQ(info.levels[3].line_size, 64);
|
||||
}
|
||||
|
||||
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0020FB1_K8_Manchester_CPUID.txt
|
||||
TEST_F(CpuidX86Test, AMD_MANCHESTER_CACHE_INFO) {
|
||||
cpu().SetLeaves({
|
||||
{{0x00000000, 0}, Leaf{0x00000001, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000000, 0}, Leaf{0x80000018, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000001, 0}, Leaf{0x00020FB1, 0x00000150, 0x00000003, 0xE3D3FBFF}},
|
||||
{{0x80000005, 0}, Leaf{0xFF08FF08, 0xFF20FF20, 0x40020140, 0x40020140}},
|
||||
{{0x80000006, 0}, Leaf{0x00000000, 0x42004200, 0x02008140, 0x00000000}},
|
||||
});
|
||||
const auto info = GetX86CacheInfo();
|
||||
|
||||
EXPECT_EQ(info.size, 3);
|
||||
EXPECT_EQ(info.levels[0].level, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_type, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[0].ways, 2);
|
||||
EXPECT_EQ(info.levels[0].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[1].level, 1);
|
||||
EXPECT_EQ(info.levels[1].cache_type, 2);
|
||||
EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[1].ways, 2);
|
||||
EXPECT_EQ(info.levels[1].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[2].level, 2);
|
||||
EXPECT_EQ(info.levels[2].cache_type, 3);
|
||||
EXPECT_EQ(info.levels[2].cache_size, 512 * KiB);
|
||||
EXPECT_EQ(info.levels[2].ways, 16);
|
||||
EXPECT_EQ(info.levels[2].line_size, 64);
|
||||
}
|
||||
|
||||
// http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0100F22_K10_Agena_CPUID.txt
|
||||
TEST_F(CpuidX86Test, AMD_AGENA_CACHE_INFO) {
|
||||
cpu().SetLeaves({
|
||||
{{0x00000000, 0}, Leaf{0x00000005, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000000, 0}, Leaf{0x8000001A, 0x68747541, 0x444D4163, 0x69746E65}},
|
||||
{{0x80000001, 0}, Leaf{0x00100F22, 0x10000000, 0x000007FF, 0xEFD3FBFF}},
|
||||
{{0x80000005, 0}, Leaf{0xFF30FF10, 0xFF30FF20, 0x40020140, 0x40020140}},
|
||||
{{0x80000006, 0}, Leaf{0x20800000, 0x42004200, 0x02008140, 0x0010A140}},
|
||||
});
|
||||
const auto info = GetX86CacheInfo();
|
||||
|
||||
EXPECT_EQ(info.size, 4);
|
||||
EXPECT_EQ(info.levels[0].level, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_type, 1);
|
||||
EXPECT_EQ(info.levels[0].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[0].ways, 2);
|
||||
EXPECT_EQ(info.levels[0].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[1].level, 1);
|
||||
EXPECT_EQ(info.levels[1].cache_type, 2);
|
||||
EXPECT_EQ(info.levels[1].cache_size, 64 * KiB);
|
||||
EXPECT_EQ(info.levels[1].ways, 2);
|
||||
EXPECT_EQ(info.levels[1].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[2].level, 2);
|
||||
EXPECT_EQ(info.levels[2].cache_type, 3);
|
||||
EXPECT_EQ(info.levels[2].cache_size, 512 * KiB);
|
||||
EXPECT_EQ(info.levels[2].ways, 16);
|
||||
EXPECT_EQ(info.levels[2].line_size, 64);
|
||||
|
||||
EXPECT_EQ(info.levels[3].level, 3);
|
||||
EXPECT_EQ(info.levels[3].cache_type, 3);
|
||||
EXPECT_EQ(info.levels[3].cache_size, 2 * MiB);
|
||||
EXPECT_EQ(info.levels[3].ways, 32);
|
||||
EXPECT_EQ(info.levels[3].line_size, 64);
|
||||
}
|
||||
|
||||
// https://github.com/InstLatx64/InstLatx64/blob/master/GenuineIntel/GenuineIntel00106A1_Nehalem_CPUID.txt
|
||||
TEST_F(CpuidX86Test, Nehalem) {
|
||||
// Pre AVX cpus don't have xsave
|
||||
|
Reference in New Issue
Block a user