CPUID
CPUID指令可用于检索有关cpu的各种信息,如供应商字符串和型号、内部缓存的大小以及(更有趣的是)支持的cpu功能列表。
如何使用CPUID
检查CPUID可用性
在使用CPUID指令之前,还应该通过测试eflags中的“ID”位(0x200000)来确保处理器支持该指令。 此位仅在支持CPUID指令时才可修改。 对于不支持CPUID的系统,更改 'ID' 位不会有任何影响。
注意:在示例C中实现此例程可能会导致问题,因为编译器可能随时更改EFLAG。
此汇编例程检查是否支持CPUID:
pushfd ;保存EFLAGS
pushfd ;存储EFLAGS
xor dword [esp],0x00200000 ;反转存储的EFLAGS中的ID位
popfd ;加载存储的电子标志 (ID位反转)
pushfd ;再次存储EFLAGS(ID位可能反转,也可能不反转)
pop eax ;eax=修改后的EFLAGS(ID位可能反转,也可能不反转)
xor eax,[esp] ;eax = 更改了哪些位
popfd ;恢复原始EFLAG
and eax,0x00200000 ;如果ID位不能更改,则eax=0,否则为非零值
ret
注1: 有一些旧的cpu支持CPUID但eblags中的ID位不支持 (NexGen)。 当且仅当必须首先启用CPUID(Cyrix M1)时,这些CPU也支持CPUID。
注2:您可以简单地尝试执行CPUID指令,并查看是否得到无效操作码异常。 这避免了支持CPUID但不支持EBLAGS中的ID位的cpu的问题; 对于支持CPUID的CPU,速度可能会更快(对于不支持CPUID的CPU,速度可能会更慢)。
基本用法
CPUID指令的想法是,您可以在EAX中使用不同的值调用它,它将返回有关处理器的不同信息。 例如,如果我们想要供应商ID字符串(见下文),我们应该这样编码:
mov eax, 0x0
cpuid
AMD和英特尔之间存在差异。 根据Intel CPUID应用说明,在取出信息 (例如处理器签名,处理器功能标志等) 之前,我们应该首先检查供应商ID字符串中的 “GenuineIntel”。
CPU供应商ID字符串
在EAX=0的情况下调用时,CPUID返回EBX、EDX和ECX格式的供应商ID字符串。 按此顺序将它们写入内存会产生12个字符的字符串。 这些可以根据已知的供应商ID字符串进行测试:
/* Vendor strings from CPUs. */
#define CPUID_VENDOR_OLDAMD "AMDisbetter!" // Early engineering samples of AMD K5 processor
#define CPUID_VENDOR_AMD "AuthenticAMD"
#define CPUID_VENDOR_INTEL "GenuineIntel"
#define CPUID_VENDOR_VIA "VIA VIA VIA "
#define CPUID_VENDOR_OLDTRANSMETA "TransmetaCPU"
#define CPUID_VENDOR_TRANSMETA "GenuineTMx86"
#define CPUID_VENDOR_CYRIX "CyrixInstead"
#define CPUID_VENDOR_CENTAUR "CentaurHauls"
#define CPUID_VENDOR_NEXGEN "NexGenDriven"
#define CPUID_VENDOR_UMC "UMC UMC UMC "
#define CPUID_VENDOR_SIS "SiS SiS SiS "
#define CPUID_VENDOR_NSC "Geode by NSC"
#define CPUID_VENDOR_RISE "RiseRiseRise"
#define CPUID_VENDOR_VORTEX "Vortex86 SoC"
#define CPUID_VENDOR_OLDAO486 "GenuineAO486"
#define CPUID_VENDOR_AO486 "MiSTer AO486"
#define CPUID_VENDOR_ZHAOXIN " Shanghai "
#define CPUID_VENDOR_HYGON "HygonGenuine"
#define CPUID_VENDOR_ELBRUS "E2K MACHINE "
/* Vendor strings from hypervisors. */
#define CPUID_VENDOR_QEMU "TCGTCGTCGTCG"
#define CPUID_VENDOR_KVM " KVMKVMKVM "
#define CPUID_VENDOR_VMWARE "VMwareVMware"
#define CPUID_VENDOR_VIRTUALBOX "VBoxVBoxVBox"
#define CPUID_VENDOR_XEN "XenVMMXenVMM"
#define CPUID_VENDOR_HYPERV "Microsoft Hv"
#define CPUID_VENDOR_PARALLELS " prl hyperv "
#define CPUID_VENDOR_PARALLELS_ALT " lrpepyh vr " //有时Parallels会因为字节顺序不匹配而将“PRL HyperV”错误地编码为“lrpepyh VR”。
#define CPUID_VENDOR_BHYVE "bhyve bhyve "
#define CPUID_VENDOR_QNX " QNXQVMBSQG "
您已经知道供应商ID字符串在EBX,ECX,EDX中返回。 让我们以英特尔处理器为例。 它应该返回“GenuineIntel”。 查看以下文本,以了解如何将字符串放置在寄存器中:
MSB LSB EBX = 'u' 'n' 'e' 'G' EDX = 'I' 'e' 'n' 'i' ECX = 'l' 'e' 't' 'n' 其中,MSB代表“最高有效字节”,LSB代表“最低有效字节”。
此外,EAX设置为CPUID调用支持的最大EAX值,因为并非所有处理器都支持所有查询。
中央处理器功能
当使用EAX=1(CPUID_GETFEATURES)调用CPUID时,CPUID会在EDX中返回一个包含以下值的位字段。 请注意,不同品牌的CPU可能会赋予这些处理器不同的含义。 最近的一些处理器还将ECX用于功能 (形成不同的集合),由于某些旧cpu在此寄存器中返回虚假信息,因此您应该非常小心。
enum {
CPUID_FEAT_ECX_SSE3 = 1 << 0,
CPUID_FEAT_ECX_PCLMUL = 1 << 1,
CPUID_FEAT_ECX_DTES64 = 1 << 2,
CPUID_FEAT_ECX_MONITOR = 1 << 3,
CPUID_FEAT_ECX_DS_CPL = 1 << 4,
CPUID_FEAT_ECX_VMX = 1 << 5,
CPUID_FEAT_ECX_SMX = 1 << 6,
CPUID_FEAT_ECX_EST = 1 << 7,
CPUID_FEAT_ECX_TM2 = 1 << 8,
CPUID_FEAT_ECX_SSSE3 = 1 << 9,
CPUID_FEAT_ECX_CID = 1 << 10,
CPUID_FEAT_ECX_SDBG = 1 << 11,
CPUID_FEAT_ECX_FMA = 1 << 12,
CPUID_FEAT_ECX_CX16 = 1 << 13,
CPUID_FEAT_ECX_XTPR = 1 << 14,
CPUID_FEAT_ECX_PDCM = 1 << 15,
CPUID_FEAT_ECX_PCID = 1 << 17,
CPUID_FEAT_ECX_DCA = 1 << 18,
CPUID_FEAT_ECX_SSE4_1 = 1 << 19,
CPUID_FEAT_ECX_SSE4_2 = 1 << 20,
CPUID_FEAT_ECX_X2APIC = 1 << 21,
CPUID_FEAT_ECX_MOVBE = 1 << 22,
CPUID_FEAT_ECX_POPCNT = 1 << 23,
CPUID_FEAT_ECX_TSC = 1 << 24,
CPUID_FEAT_ECX_AES = 1 << 25,
CPUID_FEAT_ECX_XSAVE = 1 << 26,
CPUID_FEAT_ECX_OSXSAVE = 1 << 27,
CPUID_FEAT_ECX_AVX = 1 << 28,
CPUID_FEAT_ECX_F16C = 1 << 29,
CPUID_FEAT_ECX_RDRAND = 1 << 30,
CPUID_FEAT_ECX_HYPERVISOR = 1 << 31,
CPUID_FEAT_EDX_FPU = 1 << 0,
CPUID_FEAT_EDX_VME = 1 << 1,
CPUID_FEAT_EDX_DE = 1 << 2,
CPUID_FEAT_EDX_PSE = 1 << 3,
CPUID_FEAT_EDX_TSC = 1 << 4,
CPUID_FEAT_EDX_MSR = 1 << 5,
CPUID_FEAT_EDX_PAE = 1 << 6,
CPUID_FEAT_EDX_MCE = 1 << 7,
CPUID_FEAT_EDX_CX8 = 1 << 8,
CPUID_FEAT_EDX_APIC = 1 << 9,
CPUID_FEAT_EDX_SEP = 1 << 11,
CPUID_FEAT_EDX_MTRR = 1 << 12,
CPUID_FEAT_EDX_PGE = 1 << 13,
CPUID_FEAT_EDX_MCA = 1 << 14,
CPUID_FEAT_EDX_CMOV = 1 << 15,
CPUID_FEAT_EDX_PAT = 1 << 16,
CPUID_FEAT_EDX_PSE36 = 1 << 17,
CPUID_FEAT_EDX_PSN = 1 << 18,
CPUID_FEAT_EDX_CLFLUSH = 1 << 19,
CPUID_FEAT_EDX_DS = 1 << 21,
CPUID_FEAT_EDX_ACPI = 1 << 22,
CPUID_FEAT_EDX_MMX = 1 << 23,
CPUID_FEAT_EDX_FXSR = 1 << 24,
CPUID_FEAT_EDX_SSE = 1 << 25,
CPUID_FEAT_EDX_SSE2 = 1 << 26,
CPUID_FEAT_EDX_SS = 1 << 27,
CPUID_FEAT_EDX_HTT = 1 << 28,
CPUID_FEAT_EDX_TM = 1 << 29,
CPUID_FEAT_EDX_IA64 = 1 << 30,
CPUID_FEAT_EDX_PBE = 1 << 31
};
在GCC中使用CPUID
或者,可以使用GCC附带的__get_cpuid函数。 要使用此函数,请包含 <cpuid.h>。
#include <cpuid.h>
/*示例:获取CPU的型号*/
static int get_model(void)
{
int ebx, unused;
__cpuid(0, unused, ebx, unused, unused);
return ebx;
}
/*示例:检查内置本地APIC。*/
static int check_apic(void)
{
unsigned int eax, unused, edx;
__get_cpuid(1, &eax, &unused, &unused, %edx);
return edx & CPUID_FEAT_EDX_APIC;
}
CPUID可以用各种请求代码 (在eax中) 调用,并且将在一般寄存器中返回值 (很大程度上是内置的服务中断)。 以下代码由Clicker的x86/cpu.h公开。H
/*已弃用:应该使用GCC附带的<cpuid.h>头。*/
enum cpuid_requests {
CPUID_GETVENDORSTRING,
CPUID_GETFEATURES,
CPUID_GETTLB,
CPUID_GETSERIAL,
CPUID_INTELEXTENDED=0x80000000,
CPUID_INTELFEATURES,
CPUID_INTELBRANDSTRING,
CPUID_INTELBRANDSTRINGMORE,
CPUID_INTELBRANDSTRINGEND,
};
/** 向CPUID发出单个请求。例如,适配 “intel功能特性”
* 请注意,即使只关注“eax”和“edx”,其他寄存器
* 将被操作修改,因此我们需要将其告知编译器。
*/
static inline void cpuid(int code, uint32_t *a, uint32_t *d) {
asm volatile("cpuid":"=a"(*a),"=d"(*d):"a"(code):"ecx","ebx");
}
/** 发出完整请求,将通用寄存器输出存储为字符串
*/
static inline int cpuid_string(int code, uint32_t where[4]) {
asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
"=c"(*(where+2)),"=d"(*(where+3)):"a"(code));
return (int)where[0];
}
另见
文章
- VMX 英特尔的虚拟机扩展(Virtual-Machine Extensions)
- Detecting CPU Topology (80x86)检测80x86上的CPU拓扑
论坛文章
- CPUID For OS Developers, an implementation written by Brynet-Inc.
- Forum topic about issues with CPUID
外部链接
- You can check out SandPile.org for an exhaustive list of functions available through CPUID instruction.
- Intel's CPUID Specification
- AMD's CPUID Specification