Intel x86_64使用cpuid指令获取CPU信息


前言

写了一个简易版的Linux 系统下获取cpu的信息,后面会完善,目前先获取一些最基本的处理器信息,代码主要参考Linux内核源码,基本是移植过来的。。。。。。
在这篇文章我介绍过cpuid指令:
Intel x86_64 CPUID指令介绍
这里就不介绍那么详细啦。

一、cpuid指令简介

1.1 cpuid指令功能

主要用来获取处理器身份和特征信息。
在这里插入图片描述

1.2 cpuid指令代码

我在应用层使用内嵌汇编 asm volatile,调用cpuid指令,根据输入不同的值,将处理器的信息作为返回值写在eax、ebx、ecx、edx中。

static void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
{
    
    
	*eax = op;
	*ecx = 0;
	asm volatile("cpuid"			//内嵌汇编指令 cpuid
	    : "=a" (*eax),				//输出参数
	      "=b" (*ebx),
	      "=c" (*ecx),
	      "=d" (*edx)
	    : "0" (*eax), "2" (*ecx)	//输入参数
	    : "memory");
}

二、获取处理器信息

2.1 输入参数为0H

当输入参数为0H时,用来获取厂商标识字符串信息,如Intel,AMD等等。
Intel:“GenuineIntel”
AMD:“AuthenticAMD”
在这里插入图片描述

在这里插入图片描述

代码如下:

int	cpuid_level;
char x86_vendor_id[16] = {
    
    0};
cpuid(0x00000000, (unsigned int *)&cpuid_level,
	      		  (unsigned int *)&x86_vendor_id[0],
	              (unsigned int *)&x86_vendor_id[8],
	              (unsigned int *)&x86_vendor_id[4]);

这样供应商的信息就存放在x86_vendor_id数组中了。

2.2 输入参数为01H

当输入参数为01H时,在返回值EAX寄存器中就可以获得处理器的 DisplayFamily_DisplayModel,DisplayFamily_DisplayModel 信息通常用以识别特定的处理器。
在这里插入图片描述
在这里插入图片描述

代码如下(示例):

unsigned int x86_family(unsigned int sig)
{
    
    
	unsigned int x86;

	x86 = (sig >> 8) & 0xf;

	if (x86 == 0xf)
		x86 += (sig >> 20) & 0xff;

	return x86;
}

unsigned int x86_model(unsigned int sig)
{
    
    
	unsigned int fam, model;

	fam = x86_family(sig);

	model = (sig >> 4) & 0xf;

	if (fam >= 0x6)
		model += ((sig >> 16) & 0xf) << 4;

	return model;
}

unsigned int x86_stepping(unsigned int sig)
{
    
    
	return sig & 0xf;
}

2.3 输入参数为0x80000002H

当输入参数为0x80000002H,0x80000003H,0x80000004H时用来获取处理器品牌字符串,如:
在这里插入图片描述
在这里插入图片描述

static void get_model_name()
{
    
    
	char x86_model_id[64] = {
    
    0};
	unsigned int *v = (unsigned int *)x86_model_id;
	cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
	cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
	x86_model_id[48] = 0;
}

2.4 输入参数为0x80000008H

当输入参数为0x80000008H时,获取物理地址大小信息和虚拟地址信息大小。如:
在这里插入图片描述

在这里插入图片描述

void get_cpu_address_sizes()
{
    
    
	unsigned int eax, ebx, ecx, edx;
	cpuid(0x80000008, &eax, &ebx, &ecx, &edx);

	x86_virt_bits = (eax >> 8) & 0xff;
	x86_phys_bits = eax & 0xff;
}

三、完整代码演示

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

struct cpuinfo_x86 {
    
    
	unsigned char			x86;			
	unsigned char			x86_vendor;		
	unsigned char			x86_model;
	unsigned char			x86_stepping;
	int			     		cpuid_level;
	char					x86_vendor_id[16];
	char					x86_model_id[64];
	int						x86_cache_alignment;	
	unsigned short			x86_clflush_size;
	unsigned char			x86_virt_bits;
	unsigned char			x86_phys_bits;
	unsigned char			x86_cache_bits;
};


static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx,
			 				unsigned int *ecx, unsigned int *edx)
{
    
    
	*eax = op;
	*ecx = 0;

	asm volatile("cpuid"
	    : "=a" (*eax),
	      "=b" (*ebx),
	      "=c" (*ecx),
	      "=d" (*edx)
	    : "0" (*eax), "2" (*ecx)
	    : "memory");
}

unsigned int x86_family(unsigned int sig)
{
    
    
	unsigned int x86;

	x86 = (sig >> 8) & 0xf;

	if (x86 == 0xf)
		x86 += (sig >> 20) & 0xff;

	return x86;
}

unsigned int x86_model(unsigned int sig)
{
    
    
	unsigned int fam, model;

	fam = x86_family(sig);

	model = (sig >> 4) & 0xf;

	if (fam >= 0x6)
		model += ((sig >> 16) & 0xf) << 4;

	return model;
}

unsigned int x86_stepping(unsigned int sig)
{
    
    
	return sig & 0xf;
}


static void get_model_name(struct cpuinfo_x86 *c)
{
    
    
	unsigned int *v = (unsigned int *)c->x86_model_id;
	cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
	cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
	cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
	c->x86_model_id[48] = 0;
}

void cpu_detect(struct cpuinfo_x86 *c)
{
    
    
	/* Get vendor name */
	memset(c->x86_vendor_id, 0, sizeof(c->x86_vendor_id));
	cpuid(0x00000000, (unsigned int *)&c->cpuid_level,
	      (unsigned int *)&c->x86_vendor_id[0],
	      (unsigned int *)&c->x86_vendor_id[8],
	      (unsigned int *)&c->x86_vendor_id[4]);

	c->x86 = 4;
	/* Intel-defined flags: level 0x00000001 */
	if (c->cpuid_level >= 0x00000001) {
    
    
		unsigned int junk, tfms, cap0, misc;

		cpuid(0x00000001, &tfms, &misc, &junk, &cap0);
		c->x86		= x86_family(tfms);
		c->x86_model	= x86_model(tfms);
		c->x86_stepping	= x86_stepping(tfms);

		if (cap0 & (1<<19)) {
    
    
			c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
			c->x86_cache_alignment = c->x86_clflush_size;
		}
	}
}

void get_cpu_address_sizes(struct cpuinfo_x86 *c)
{
    
    
	unsigned int eax, ebx, ecx, edx;

	cpuid(0x80000008, &eax, &ebx, &ecx, &edx);

	c->x86_virt_bits = (eax >> 8) & 0xff;
	c->x86_phys_bits = eax & 0xff;

	c->x86_cache_bits = c->x86_phys_bits;
}

int main()
{
    
    
    unsigned int eax = 0;
	unsigned int ebx = 0;
	unsigned int ecx = 0;
	unsigned int edx = 0;

	struct cpuinfo_x86 _cpuinfo_x86;

    cpuid(0, &eax, &ebx, &ecx, &edx);
    printf("EBX ← %x (“Genu”)EDX ← %x (“ineI”) ECX ← %x (“ntel”)\n", ebx, edx ,ecx);

	get_cpu_address_sizes(&_cpuinfo_x86);
	cpu_detect(&_cpuinfo_x86);
	get_model_name(&_cpuinfo_x86);

	printf("Address sizes:   phys_bits = %d  virt_bits = %d\n", _cpuinfo_x86.x86_phys_bits,  _cpuinfo_x86.x86_virt_bits);
	printf("Vendor Id= %s\n", _cpuinfo_x86.x86_vendor_id);
	printf("cpuid level = %d\n", _cpuinfo_x86.cpuid_level);
	printf("CPU family = %d\n", _cpuinfo_x86.x86);
	printf("Model = %d\n", _cpuinfo_x86.x86_model);
	printf("Stepping = %d\n", _cpuinfo_x86.x86_stepping);
	printf("Model name = %s\n", _cpuinfo_x86.x86_model_id);
	printf("clflush_size = %d\n", _cpuinfo_x86.x86_clflush_size);
	printf("cache_alignment = %d\n", _cpuinfo_x86.x86_cache_alignment);

     return 0;

}

看看结果:
在这里插入图片描述
与lscpu显示的消息一致(也可以用cat /proc/cpuinfo查看):
在这里插入图片描述

总结

主要是参考了 Linux内核源码 5.13.0中获取CPU信息的代码:

 arch/x86/kernel/cpu/common.c
 arch/x86/include/asm/processor.h

实验环境:
vmware + ubuntu 20.04

参考资料

Linux内核源码 5.13.0
Intel官方手册 vol2

猜你喜欢

转载自blog.csdn.net/weixin_45030965/article/details/124461693
今日推荐