当前位置: 首页 > 嵌入式底层BIOS > 正文

linux C语言调用Intel处理器CPUID指令的实例

在之前写的文章中,仅简单讲了一下CPUID指令,通过该指令可以获取很多和处理器相关的信息,如处理器的系列、型号、内存地址是多少位,等等。本文在Linux环境下,使用C语言内嵌汇编的手段使用CPUID指令,进而在高级语言层面上看到获取的信息。
头文件cpuid.h代码如下 :

/*
 * 假设执行程序所在PC是支持CPUID指令的
*/

#ifndef _CPUID_H
#define _CPUID_H

#include <stdint.h>

void get_cpu_vendor(char* cpu_vendor, int* cpuid_level);


void get_cpu_version(unsigned int* version);

void get_cpu_fms(int* family, int* model, int* stepping);

void get_cpu_name(char *processor_name);

void get_address_bits(int* linear, int* physical);


////////////////////////////////////////////

struct cpuid_result {
	uint32_t eax;
	uint32_t ebx;
	uint32_t ecx;
	uint32_t edx;
};

/*
 * Generic CPUID function
 */
static inline struct cpuid_result cpuid(int op)
{
	struct cpuid_result result;
	asm volatile(
		"mov %%ebx, %%edi;"
		"cpuid;"
		"mov %%ebx, %%esi;"
		"mov %%edi, %%ebx;"
		: "=a" (result.eax),
		  "=S" (result.ebx),
		  "=c" (result.ecx),
		  "=d" (result.edx)
		: "0" (op)
		: "edi");
	return result;
}

实现文件cpuid.c代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "cpuid.h"

void get_cpu_vendor(char* cpu_vendor, int* cpuid_level)
{
	char vendor_name[16];

	vendor_name[0] = '\0'; /* Unset */

    int level = 0;
    struct cpuid_result result;
    result = cpuid(0x00000000); // eax为0表示读取vendor id,一共12字节,依次在ebx、edx、ecx。
    level    = result.eax;
    vendor_name[ 0] = (result.ebx >>  0) & 0xff;
    vendor_name[ 1] = (result.ebx >>  8) & 0xff;
    vendor_name[ 2] = (result.ebx >> 16) & 0xff;
    vendor_name[ 3] = (result.ebx >> 24) & 0xff;
    vendor_name[ 4] = (result.edx >>  0) & 0xff;
    vendor_name[ 5] = (result.edx >>  8) & 0xff;
    vendor_name[ 6] = (result.edx >> 16) & 0xff;
    vendor_name[ 7] = (result.edx >> 24) & 0xff;
    vendor_name[ 8] = (result.ecx >>  0) & 0xff;
    vendor_name[ 9] = (result.ecx >>  8) & 0xff;
    vendor_name[10] = (result.ecx >> 16) & 0xff;
    vendor_name[11] = (result.ecx >> 24) & 0xff;
    vendor_name[12] = '\0';
    //printf("vendor_name: %s\n", vendor_name);
    
    strcpy(cpu_vendor, vendor_name);
    *cpuid_level = level;
}

void get_cpu_version(unsigned int* version)
{
    unsigned int tmp = 0;
    tmp = cpuid_eax(0x00000001);
    
    *version = tmp;
}

struct cpuinfo_x86 {
	uint8_t	x86;		/* CPU family */
	uint8_t	x86_vendor;	/* CPU vendor */
	uint8_t	x86_model;  /* CPU model */
	uint8_t	x86_step;  /* CPU stepping */
};

// 参考IA32开发手册第2卷第3章。CPUID exa==0x01的图3-6
static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms)
{
	c->x86 = (tfms >> 8) & 0xf;
	c->x86_model = (tfms >> 4) & 0xf;
	c->x86_step = tfms & 0xf;
	if (c->x86 == 0xf)
		c->x86 += (tfms >> 20) & 0xff;
	if (c->x86 >= 0x6)
		c->x86_model += ((tfms >> 16) & 0xF) << 4;
}

void get_cpu_fms(int* family, int* model, int* stepping)
{
    struct cpuinfo_x86 c;
    unsigned int ver = 0;
    
    ver = cpuid_eax(0x00000001);
    get_fms(&c, ver);
    
    *family = c.x86;
    *model = c.x86_model;
    *stepping = c.x86_step;
}

void get_cpu_name(char* processor_name)
{
	struct cpuid_result regs;
	char temp_processor_name[49];
	char* processor_name_start;
	unsigned int *name_as_ints = (unsigned int *)temp_processor_name;
	int i;

	/* 
	用cpuid指令,eax传入0x80000002/0x80000003/0x80000004,
	共3个,每个4个寄存器,每个寄存器4字节,故一共48字节。
	参考IA32开发手册第2卷第3章。
	*/
	for (i = 0; i < 3; i++) {
		regs = cpuid(0x80000002 + i);
		name_as_ints[i * 4 + 0] = regs.eax;
		name_as_ints[i * 4 + 1] = regs.ebx;
		name_as_ints[i * 4 + 2] = regs.ecx;
		name_as_ints[i * 4 + 3] = regs.edx;
	}

	temp_processor_name[49] = '\0'; // 最后的字节为0,结束

	/* Skip leading spaces. */
	processor_name_start = temp_processor_name;
	while (*processor_name_start == ' ')
		processor_name_start++;

	memset(processor_name, 0, 49);
	strcpy(processor_name, processor_name_start);
}

void get_address_bits(int* linear, int* physical)
{
    unsigned int tmp = 0;
    tmp = cpuid_eax(0x80000008);
    *linear = (tmp>>8) & 0xff;
    *physical = (tmp>>0) & 0xff;
    
}

主函数实现代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "cpuid.h"
#include "msr.h"

int main(void)
{
    char buffer[49] = {0};
    unsigned int version = 0;
    int cpuid_level;
    
    get_cpu_vendor(buffer, &cpuid_level);
    printf("vendor_id\t: %s\n", buffer);
    printf("cpuid level\t: %d\n", cpuid_level);
    
    get_cpu_name(buffer);
    printf("model name\t: %s\n", buffer);
    
    get_cpu_version(&version);
    
    int f,m,s;
    get_cpu_fms(&f, &m, &s);
    printf("cpu family\t: %d(0x%X)\n", f, f);
    printf("model\t\t: %d(0x%X)\n", m, m);
    printf("stepping\t: %d(0x%X)\n", s, s);
    
    int phy_bits = 0;
    int vir_bits = 0;
    get_address_bits(&vir_bits, &phy_bits);
    printf("address sizes\t: %d bits physical, %d bits virtual\n", phy_bits, vir_bits);
    
    return 0;
}

在Linux系统中,有现成的方法获取CPU信息,比如使用lscpu命令。另外还可以直接查看/proc/cpuinfo文件。使用“cat /proc/cpuinfo”命令查看CPU信息如下(有精简):

# cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 58
model name      : Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
stepping        : 9
physical id     : 0
siblings        : 8
core id         : 3
cpu cores       : 4
apicid          : 7
initial apicid  : 7
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
bogomips        : 6784.28
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

上述程序运行结果如下:

vendor_id       : GenuineIntel
level           : 13
model name      : Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
cpu family      : 6(0x6)
model           : 58(0x3A)
stepping        : 9(0x9)
address sizes   : 36 bits physical, 48 bits virtual

可以看到,两者信息高度一致。
Intel处理器的CPUID远不止上文所述,详情请参考Intel IA32软件开发手册。
李迟 2016.3.7 周一 晚

本文固定链接: http://www.latelee.org/firmware-bios/linux-c-cpuid-example.html

目前暂无评论

发表评论

*

快捷键:Ctrl+Enter