如何通过CPUID命令用C/C++获取物理和虚拟地址位



我在windows中使用CPUID命令用C获取物理和虚拟地址位大小。我可以通过这种方式获得处理器信息,但我对获得地址位感到困惑。看起来我应该给你80000008指令,但我这样做,只显示7-8位连续变化的数字。我想了解这个命令是如何工作的,并解决这个问题

#include <stdio.h>
void getcpuid(int T, int* val) {
int reg_ax;
int reg_bx;
int reg_cx;
int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
int val[5]; val[4] = 0;
getcpuid(0x80000002, val);
printf("%srn", &val[0]);
getcpuid(0x80000003, val);
printf("%srn", &val[0]);
getcpuid(0x80000004, val);
printf("%srn", &val[0]);
return 0;
}

当用EAX=800000002、80000003、80000004操作此代码时,显示Intel处理器品牌字符串。我把80000008放在获取物理和虚拟地址位上,但显示的是不断变化的随机数。我想知道如何使用这个带有80000008的cpuid来获得那些地址位

我是编程和操作系统的初学者。请让我知道我必须做什么。

您使用的内联程序集可能是正确的;但这取决于它是哪一个编译器。我认为它适合微软的MSVC(但我从未使用过,也不能确定(。对于GCC(和CLANG(,您必须通知编译器您正在修改寄存器和内存的内容(通过.a clobber列表(,并且告诉编译器您正在4个寄存器中输出4个值会更有效。

主要问题是您试图将输出视为(以null结尾的(字符串;CPUID返回的数据从来都不是以零结尾的字符串(即使对于"获取供应商字符串"one_answers"获取品牌名称字符串",它也是一个没有零结尾的空白填充字符串(。

要解决这个问题,您可以:

void getcpuid(int T, int* val) {
unsigned int reg_ax;
unsigned int reg_bx;
unsigned int reg_cx;
unsigned int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
uint32_t val[5]; val[4] = 0;
getcpuid(0x80000002U, val);
printf("0x%08Xrn", val[0]);
getcpuid(0x80000003U, val);
printf("0x%08Xrn", val[1]);
getcpuid(0x80000004U, val);
printf("0x%08Xrn", val[2]);
return 0;
}

下一个问题是提取虚拟地址大小和物理地址大小值。这些是被打包到CCD_ 1的第一和第二字节中的8比特值;所以:

int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
printf("Virtual %d, physical %drn", virtualAddressSize, physicalAddressSize);
return 0;
}

这应该适用于最新的CPU;这意味着它在旧的CPU上仍然很糟糕和坏。

要开始修复您想要检查CPU是否支持";CPUID叶0x80000008";在你假设它存在之前:

int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
physicalAddressSize = -1;
virtualAddressSize = -1;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %drn", virtualAddressSize, physicalAddressSize);
return 0;
}

当";CPUID叶0x80000008";不存在。对于所有不支持";CPUID叶0x80000008";;虚拟地址大小是32位,而物理地址大小是36位(如果支持PAE(或32位(如果不支持PAE的话(。您可以使用CPUID来确定CPU是否支持PAE,因此它最终有点像这样:

int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
getcpuid(0x00000000U, val);
if(val[0] == 0) {
physicalAddressSize = 32;          // "CPUID leaf 0x00000001" not supported
} else {
getcpuid(0x00000001U, val);
if( val[3] & (1 << 6) != 0) {
physicalAddressSize = 36;      // PAE is supported
} else {
physicalAddressSize = 32;      // PAE not supported
}
}
virtualAddressSize = 32;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %drn", virtualAddressSize, physicalAddressSize);
return 0;
}

另一个问题是有时CPUID有漏洞;这意味着你必须搜索每个CPU(来自英特尔、AMD、VIA等(的每个勘误表,以确保CPUID的结果实际上是正确的。例如;"英特尔奔腾4处理器在90纳米工艺上";其中";CPUID叶0x800000008";是错误的并且说";物理地址是40位";当它们实际上是36比特时。

对于所有这些情况,您都需要实现解决方案(例如,从CPUID获取CPU供应商/系列/型号/步进信息,如果它与Pentium 4的3个有缺陷的型号之一匹配,请执行"if(physicalAddressSize == 40) physicalAddressSize = 36;"来修复CPU的错误(。

相关内容

  • 没有找到相关文章

最新更新