基于__asm实现跨Intel/AMD的L3缓存检测与关联度获取问题排查
CPUID函数问题排查:has_l3_cache返回false、get_l3_cache_associativity结果错误
问题背景
作为汇编新手,我用__asm编写了三个函数:has_cpuid()、has_l3_cache()、get_l3_cache_associativity()。其中has_cpuid()工作正常,但明明CPU有L3缓存,has_l3_cache()却返回false,get_l3_cache_associativity()返回的关联度也不正确。以下是原代码:
#include <stdio.h> #include <stdbool.h> /* Function to check CPUID support */ bool has_cpuid() { int id_bit; __asm { pushfd // Push EFLAGS on the stack pop eax // Load EFLAGS flags into EAX mov ecx, eax // Save a copy xor eax, 200000h // Toggle the ID bit in EAX push eax // Push new EFLAGS on the stack popfd // Restore EFLAGS from the stack pushfd // Save EFLAGS back on the stack pop eax // Load EFLAGS flags into EAX again xor eax, ecx // If the ID bit has changed, then the CPUID is supported shr eax, 21 // Move result to least significant bit mov id_bit,eax } return id_bit; } /* Function to check for L3 cache */ bool has_l3_cache() { int cacheLevel; __asm { mov eax, 0x80000006 cpuid mov cacheLevel, ecx } return cacheLevel & (1 << 16); } /* Function to get L3 cache associativity */ int get_l3_cache_associativity() { int level = 3; // L3 cache int result; __asm { mov eax, 4 // CPUID function for cache information mov ecx, level // Cache level to check cpuid // Call CPUID mov eax, ebx // Associativity data is in EBX[31:22] shr eax, 22 // Move Associativity Data To LSB inc eax // CPUID returns n-1, so increase by 1 mov result, eax } return result; } int main() { if (!has_cpuid()) { printf("CPUID is not supported.\n"); return 1; } if (has_l3_cache()) { printf("L3 Cache is present.\n"); printf("L3 Cache associativity: %d\n", get_l3_cache_associativity()); } else { printf("L3 Cache is not present.\n"); } return 0; }
问题分析与修正
1. has_l3_cache()函数错误
原代码存在两个核心问题:
- 未提前确认CPU支持
0x80000006扩展CPUID功能,需先调用0x80000000获取最大扩展功能号,判断其是否≥0x80000006。 - 错误读取
ECX寄存器并检查bit16,实际上0x80000006的EDX寄存器bits 15-8存储L3缓存大小(单位KB),该值非零则说明存在L3缓存。
修正后的has_l3_cache():
bool has_l3_cache() { int max_ext_func; __asm { mov eax, 0x80000000 cpuid mov max_ext_func, eax } // 确认支持0x80000006扩展功能 if (max_ext_func < 0x80000006) { return false; } int l3_size; __asm { mov eax, 0x80000006 cpuid mov l3_size, edx } // 检查L3缓存大小是否大于0 return (l3_size & 0xFF00) != 0; }
2. get_l3_cache_associativity()函数错误
原代码的问题在于:
- CPUID函数
eax=4的ecx参数不是缓存级别,而是缓存类型索引,需要从ecx=0开始遍历,直到找到缓存级别为3的条目。 - 未检查CPU是否支持
eax=4功能,需先调用eax=0获取最大基本功能号,判断其是否≥4。
修正后的get_l3_cache_associativity():
int get_l3_cache_associativity() { int max_basic_func; __asm { mov eax, 0 cpuid mov max_basic_func, eax } // 确认支持eax=4的缓存信息功能 if (max_basic_func < 4) { return -1; // 不支持该功能,返回错误值 } int ecx_idx = 0; int result = -1; while (1) { int eax_out, ebx_out; __asm { mov eax, 4 mov ecx, ecx_idx cpuid mov eax_out, eax mov ebx_out, ebx } // 提取缓存级别(EAX的bits7-5) int cache_level = (eax_out >> 5) & 0x7; if (cache_level == 3) { // 提取关联度值(EBX的bits31-22,返回值为n-1) int assoc = (ebx_out >> 22) & 0x3FF; result = assoc + 1; break; } // 缓存类型为0时,无更多条目可遍历 int cache_type = eax_out & 0x1F; if (cache_type == 0) { break; } ecx_idx++; } return result; }
完整修正代码
#include <stdio.h> #include <stdbool.h> /* Function to check CPUID support */ bool has_cpuid() { int id_bit; __asm { pushfd // Push EFLAGS on the stack pop eax // Load EFLAGS flags into EAX mov ecx, eax // Save a copy xor eax, 200000h // Toggle the ID bit in EAX push eax // Push new EFLAGS on the stack popfd // Restore EFLAGS from the stack pushfd // Save EFLAGS back on the stack pop eax // Load EFLAGS flags into EAX again xor eax, ecx // If the ID bit has changed, then the CPUID is supported shr eax, 21 // Move result to least significant bit mov id_bit,eax } return id_bit; } /* Function to check for L3 cache */ bool has_l3_cache() { int max_ext_func; __asm { mov eax, 0x80000000 cpuid mov max_ext_func, eax } if (max_ext_func < 0x80000006) { return false; } int l3_size; __asm { mov eax, 0x80000006 cpuid mov l3_size, edx } return (l3_size & 0xFF00) != 0; } /* Function to get L3 cache associativity */ int get_l3_cache_associativity() { int max_basic_func; __asm { mov eax, 0 cpuid mov max_basic_func, eax } if (max_basic_func < 4) { return -1; } int ecx_idx = 0; int result = -1; while (1) { int eax_out, ebx_out; __asm { mov eax, 4 mov ecx, ecx_idx cpuid mov eax_out, eax mov ebx_out, ebx } int cache_level = (eax_out >> 5) & 0x7; if (cache_level == 3) { int assoc = (ebx_out >> 22) & 0x3FF; result = assoc + 1; break; } int cache_type = eax_out & 0x1F; if (cache_type == 0) { break; } ecx_idx++; } return result; } int main() { if (!has_cpuid()) { printf("CPUID is not supported.\n"); return 1; } if (has_l3_cache()) { printf("L3 Cache is present.\n"); int assoc = get_l3_cache_associativity(); if (assoc != -1) { printf("L3 Cache associativity: %d\n", assoc); } else { printf("Failed to get L3 Cache associativity.\n"); } } else { printf("L3 Cache is not present.\n"); } return 0; }
内容的提问来源于stack exchange,提问作者Fenix FVE
相关产品推荐
相关产品推荐

