From cee2648cf0cebfb5e0106a2187d4b1c33851a9ec Mon Sep 17 00:00:00 2001 From: Mykola Hohsadze Date: Thu, 18 Aug 2022 14:55:21 +0300 Subject: 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 --- src/impl_x86__base_implementation.inl | 111 ++++++++++++++++++++++++++++++++++ test/cpuinfo_x86_test.cc | 105 ++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/src/impl_x86__base_implementation.inl b/src/impl_x86__base_implementation.inl index 7d4c34f..c34d2cf 100644 --- a/src/impl_x86__base_implementation.inl +++ b/src/impl_x86__base_implementation.inl @@ -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; diff --git a/test/cpuinfo_x86_test.cc b/test/cpuinfo_x86_test.cc index 5a19f26..7049ef7 100644 --- a/test/cpuinfo_x86_test.cc +++ b/test/cpuinfo_x86_test.cc @@ -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 -- cgit v1.2.3