当前位置: 首页 > 工具软件 > cpuid > 使用案例 >

11.15-cpuid

甄德寿
2023-12-01

11.15

任务目标 // 进度:

  • CPUID学习,编写小工具读取CPUID相关信息

工作结果:

学习笔记:

CPU信息获取的方式有两种方式:

​ 1.使用内嵌汇编调用CPUID指令

​ 2.读取/proc/cpuinfo文件

使用内嵌汇编调用CPUID指令

首先使用内嵌汇编调用CPUID指令来获取CPUID、CPU厂商、CPU Brand、CPU Family、CPU Model、CPU Stepping ID,然后采用MD5算法计算CPU信息的Hash值。

CPUID指令的基本原理是,CPU根据传递给EAX 寄存器的值,将对应的CPU信息返回给 EAX、EBX、ECX 及 EDX 寄存器。通过对这些寄存器的值进行操作即可获取CPU各类信息。CPUID指令带代码如下:

#define cpuid(func,eax,ebx,ecx,edx)\
    __asm__ __volatile__ ("cpuid":\
    "=a" (eax),"=b" (ebx),"=c" (ecx),"=d" (edx):\
    "a" (func));
    
本文使用了宏定义的方式封装CPUID指令函数。
对于上面的__cpuid 

asm statements: “cpuid”。汇编代码为——调用cpuid指令 
outputs:”=a” (eax), “=b” (ebx), “=c” (ecx), “=d” (edx)。表示有4个输出参数——参数0为eax(绑定到变量a)、参数1为ebx(绑定到变量b)、参数2为ecx(绑定到变量c)、参数3为edx(绑定到变量d)。 
inputs:”a” (func)。表示有1个输入参数——将变量func赋给参数0(eax)。 
registers-modified:(无)。 

因此,上述cpuid函数执行过程为: 
	1. 将变量func的值赋给eax 
	2. 执行cpuid指令 
	3. 将返回的eax的值赋给a 
	4. 将返回的ebx的值赋给b 
	5. 将返回的ecx的值赋给c 
	6. 将返回的edx的值赋给d

获取CPU ID

功能号eax=0x80000004,返回CPU ID。执行cpuid指令后,返回值寄存器依次为eax,ebx,ecx,edx。具体代码如下:

void getCPUID(char *cpuId)
{
    int a,b,c,d;
    char cpuid_buf[HWI_BUFFER_SIZE];
    memset((void*)cpuid_buf,0,HWI_BUFFER_SIZE);
    cpuid(0x80000004,a,b,c,d);
    sprintf(cpuid_buf,"%#010x %#010x %#010x %#010x",a,b,c,d);  //#代表的是在字符串前面加上0x; 10表示输出10个字符; x是输出16进制
    strcpy(cpuId,cpuid_buf);
}

获取CPU 厂商

功能号eax=0,返回CPU 厂商。执行cpuid指令后,返回值寄存器依次为ebx,ecx,edx。具体代码如下:

void getCPUCompany(char *cCom)
{
    char ComName[13];
    int a,b,c,d;
    int i,k;

    memset((void*)ComName,0,sizeof(ComName));
    cpuid(0,a,b,c,d);

    for(i = 0; b > 0; i++)
    {
        k = b;
        k = (k>>8);
        k = (k<<8);
        ComName[i]=b-k;
        b=(b>>8);
    }
    for(; d > 0; i++)
    {
        k = d;
        k = (k>>8);
        k = (k<<8);
        ComName[i]=d-k;
        d=(d>>8);
    }
    for(; c > 0; i++)
    {
        k = c;
        k = (k>>8);
        k = (k<<8);
        ComName[i]=c-k;
        c=(c>>8);
    }
    ComName[12]='\0';
    strcpy(cCom,ComName);   
}

获取CPU Brand

EAX=80000002h、80000003h、80000004h:返回处理器brand字符串。

void getCPUBrand(char *cBrand)
{
    int a,b,c,d,i;
    unsigned int cpu_brand_buf[13];
    int k = 0;

    memset((void*)cpu_brand_buf,0,sizeof(cpu_brand_buf));
    /**
    * eax == 0x800000000
    * 如果CPU支持Brand String,则在EAX中返 >= 0x80000004的值。
    */
    cpuid(0x80000000,a,b,c,d);
    if(a < 0x80000004)
    {
        printf("the cpu is not support\n");
        return;
    }

    for(i=0x80000002;i<=0x80000004;i++)
    {
        cpuid(i,a,b,c,d);
        cpu_brand_buf[k++]=a;
        cpu_brand_buf[k++]=b;
        cpu_brand_buf[k++]=c;
        cpu_brand_buf[k++]=d;
    }
    strcpy(cBrand,(char*)cpu_brand_buf);
    printf("CPU Brand:%s\n", (char *)cpu_brand_buf);
}

获取CPU Family,Model,Stepping ID

EAX=0x01,返回Model、Family、Stepping信息.执行CPUID后,返回的EAX寄存器包含Model、Family、Stepping信息。

void getCPUBaseParam(char *baseParam)
{
    char baseParamBuf[HWI_BUFFER_SIZE];
    unsigned long CPUBaseInfo;
    int a,b,c,d;

    memset((void*)baseParamBuf,0,HWI_BUFFER_SIZE);
    cpuid(1,a,b,c,d);

    CPUBaseInfo = a;
    sprintf(baseParamBuf,"Family:%d Model:%d Stepping ID:%d",
        (CPUBaseInfo & 0x0F00)>>8,(CPUBaseInfo & 0xF0)>>4,CPUBaseInfo & 0xF);
    strcpy(baseParam,baseParamBuf);
}

CPUID是Intel Pentium以上级CPU内置的一个指令(486级及以下的CPU不支持),它用于识别某一类型的CPU,它能返回CPU的级别(family),型号(model),CPU步进(Stepping ID)及CPU字串等信息,从此命令也可以得到CPU的缓存与TLB信息.

CPUID返回数据类型是在EAX寄存器里面定义的,而指令返回的数值则在存储在EAX,EBX,ECX和EDX寄存器里面.
返回的信息分两部分:基本信息与扩展信息.在EAX输入0-3参数时,它返回的CPU的基本信息;而在EAX输入0x8000000至0x800000x时,它返回的是CPU的扩展信息(extended function information).扩展信息只包含在Pentium 4及以后的CPU上,Pentium 4以前的CPU无法取得它的扩展信息.


sprintf , printf:由于 sprintf 跟 printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。

debug32基本命令

[xxxx] 可选内存地址

-a[xxxx] 开始汇编

-u[xxxx] 指定位置反汇编

-d[xxxx] 显示指定位置内存内容

-r[Reg] 无参数显示 -r32 -r16 -r8 显示位数

-g[xxxx] 无参数从当前CS:IP开始执行指令

-g=[xxxx] 从此地址开始执行指令

-t 单步执行

-e[xxxx] 更改指定位置内容

-rip 返回

-q 退出程序

cpuid


------------------------------------------------------------------
0x0
EAX CPU基本参数的输入值
EBX “Genu”
ECX “Intel”
EDX “inel”
------------------------------------------------------------------
0x1
EAX CPU的级别,型号及步进
EBX 第 0 - 7位: CPU字串索引 (Brand Index)
第 8 - 15位: CLFLUSH线大小(CLFLUSH line size) (返回值*8 = cache line size)
第16 - 23位: 保留
第24 - 31位: 处理器APIC物理标号 (Processor local APIC physical ID)
ECX 保留
EDX 特征信息(Feature Information)
​ 位 标号 解释
​ 0 FPU Floating Point Unit On-Chip. CPU是否内置浮点计算单元
​ 1 VME Virtual 8086 Mode Enhancements. 是否支持虚拟8086模式
​ 2 DE Debugging Extensions. 是否支持调试功能.
​ 3 PSE Page Size Extension. 是否支持大于4MB的分页.
​ 4 TSC Time Stamp Counter. 是否支持RDTSC指令.(注:RDTSC指令可以计算出CPU的频率)
​ 5 MSR Module Specific Registers RDMSR and WRMSR Instructions. 是否支持RDMSR与WRMSR
------------------------------------------------------------------
0x2
​ EAX到EDX返回的都是缓存和TLB的信息
------------------------------------------------------------------
0x3
​ EAX 保留
EBX 保留
ECX CPU序列号(0 - 31bit) (只是在Pentium 3中才有效)
EDX CPU序列号(32 - 63bit) (只是在Pentium 3 中才有效)
------------------------------------------------------------------
0x80000000
​ EAX 扩展信息输入数最大值(具有扩展信息的CPU才能返回)
EBX 保留
ECX 保留
EDX 保留
------------------------------------------------------------------
0x80000001
​ EAX CPU特征(Signature)和扩展特征位(Extended Feature Bits)
EBX 到 ECX 保留
------------------------------------------------------------------
0x80000002
​ EAX 处理器字串(Processor Brand String)
EBX 处理器字串(续)
ECX 处理器字串(续)
EDX 处理器字串(续)
------------------------------------------------------------------
0x80000003
​ EAX 处理器字串(续)
EBX 处理器字串(续)
ECX 处理器字串(续)
EDX 处理器字串(续)
------------------------------------------------------------------
0x80000004
​ EAX 处理器字串(续)
EBX 处理器字串(续)
ECX 处理器字串(续)
EDX 处理器字串(续)
------------------------------------------------------------------


通过CPUID指令获取CPU信息

CPUID详解

C 字符串函数 sprintf()、snprintf() 详解

心情感悟:

 类似资料:

相关阅读

相关文章

相关问答