CPUID 指令

Posted rtoax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CPUID 指令相关的知识,希望对你有一定的参考价值。

https://www.geoffchappell.com/studies/windows/km/cpu/cpuid/index.htm

目录

CPUID 指令

检测

树叶

基本叶子

长叶

超级遮阳叶


CPUID 指令

识别现代 x86 或 x64 处理器的主要方法是 cpuid指令。这是在 1990 年代初期为当时的英特尔新奔腾处理器开发的,但它也存在于英特尔 80486 处理器的某些型号和其他制造商的 80486 类似处理器中。

检测

64 位内核知道它只能在具有 64 位指令集的相对现代的处理器上执行,因此可以将 cpuid指令的存在视为理所当然。相比之下,32 位内核是在 Pentium 发布之前开发的,必须在旧处理器上运行——包括最初几年的 80386。尽管 80486 无法运行新的 Windows 版本——但不是正式的但对于所有实际效果而言,由于 Windows XP 使cmpxchg8b指令变得必不可少,因此 32 位内核最近才停止防御在没有cpuid的处理器上运行。

直到并包括 6.2 版,32 位内核将 cpuid指令视为未实现,如果:

  • eflags 寄存器中的ID位 (21)不能更改;
  • eflags 中设置ID位 且eax设置为零的情况下执行cpuid指令 会导致无效操作码异常 (6)。

eflags 中设置ID位 可能会在理想情况下保证处理器提供 cpuid指令以供执行,但这种理想是受挫的,因为虽然早期的处理器将该位作为保留位,但不能完全依赖它们都已实现保留为清除。显然可靠的是,如果该位卡在 0 或 1 上,则它不打算用作ID位,因此处理器肯定没有 cpuid指令,大概是 80486 甚至是 80386(待整理)通过CPUID 之前CPU 识别方法)。

然而,相反的情况并非如此——或者微软的程序员在 1993 年修订 Windows NT 3.1 以供发布时并不这么认为。英特尔似乎一开始就认为它应该是真的。在看起来是第一本 奔腾处理器用户手册,第 3 卷: 1993 年的体系结构和编程手册(订单号 241430-001)中,英特尔重复了几次,略有不同,“设置和清除此位的能力表明处理器是否支持 CPUID 指令”,甚至说明它的含义是:“如果软件能够改变 ID 位的值,那么处理器支持 CPUID 指令。” 然而,虽然最早的英特尔处理器标识与 CPUID 指令我还没有发现(订单号 241618-003,日期为 1994 年 10 月)继续似乎暗示“如果软件可以更改此标志的值,则 CPUID 指令可用”,将标题更改为 Intel 处理器标识和 CPUID说明 (订单号 241618-005,日期为 1996 年 12 月)带有一个脚注,它可能很容易被阅读,而不是澄清何时可以更改标志,而是警告其含义本身成立“仅在某些 Intel486和成功的处理器。” 如果您怀疑 ID位可以更改cpuid 肯定不支持,那么除了尝试执行它并安排您可以在处理器反对时恢复之外别无他法。

可能永远不会知道微软的程序员是过于谨慎还是被一个处理器(不一定由英特尔制造)抓住了,该处理器具有可更改的ID位但没有 cpuid。可以知道的是,他们对这种可能性的防御是在发布之前对他们的cpuid检测代码的最后添加之一。互联网是黑暗的,充满了恐怖,但在它的阴影下是垃圾场,公开披露曾经是非法的(可能仍然是),但如果我们的技术的早期历史要被准确地保存下来,它们的生存是必要的。从在一个下午的搜索中很容易找到的 Windows NT 3.1 的预发布版本来看,很明显,防御在 1993 年 4 月 22 日为构建 3.10.428.1 和 1993 年 7 月 24 日为公开发布的构建 3.10.5098.1 添加了触发无效操作码异常的cpuid(尽管已确定ID位是可更改的)。

甚至更早的 3.10 版预发布版本也强烈暗示cpuid指令的早期设计(如果不是它的实现)与每个人自 1993 年以来编码的内容大不相同。它没有将eax寄存器作为隐含操作数和除了将处理器标识签名 CPU ID,如果您愿意)加载到eax 中, 它似乎没有其他存在的理由。但是这段历史中的几个故事我将分别放在eax from cpuid Leaf 0和 eax from cpuid Leaf 1的单独页面中. 在这里,我仅继续介绍针对已发布的 Windows 版本进行测试和使用的指令。

树叶

尝试将eax 设置为零的指令的要点是该指令是为可扩展功能而设计的。eax 中的输入选择英特尔对函数 甚至叶函数的不同称呼。一些叶子在ecx 中接受额外的输入。All 可以使用eaxebx、 ecxedx 中的任何一个或全部 用于输出。叶子自然从零开始。可扩展性是叶子 0 告诉支持哪些其他叶子。尽管该指令将使用不受支持的eax执行 作为输入,程序员最好将这种执行视为未定义。了解输入定义的方法是执行cpuid Leaf 0:它在eax中产生的 是输入的最大有效叶子。

至少在 Windows 早在 1993 年之前就知道,意图就是如此。随着时间的推移,eax的可能输入已被划分为多个范围。这不是从英特尔开始,而是从 AMD 开始。也许是为了 AMD 可以独立于英特尔(未来)描述来描述自己的特性,AMD 让它的cpuid标准叶子与一组从 0x80000000 开始的单独 扩展叶子区分开来 。“功能分为两种类型”至少可以追溯到 AMD 处理器识别(出版号 20734 修订版 D,日期为 1997 年 1 月)。也许不可避免地,AMD 为“访问所有 x86 处理器通用信息的软件”提供了数量较少的叶子。同样不可避免的是,英特尔不会在意这个建议,即 x86 指令是任何类型的通用或模仿标准,因此当它自己的处理器实现扩展功能时,它谈到了低编号的叶子而不是标准但作为基本。

英特尔 x86 指令集的其他模仿者已经定义了自己的范围,并且管理程序也加入了游戏。Windows 只知道三个范围,从零开始,0x40000000 和 0x80000000。每个人的共同点是执行范围的第一个叶子会在eax 中产生范围的最大叶子数 。

基本叶子

直到版本 5.0,Windows 内核才使用0 和 1 以外的任何 cpuid叶。内核很少使用 0 和 1 以外的任何基本叶而不检查它是否在叶 0 报告的范围内。版本 3.50 到 6.2 32 位内核甚至会检查叶子 1(有一个例外,仅在 4.0 和 5.0 版本中,它异常值得注意,因为这是大多数版本的 Windows NT 4.0 在安装在现代处理器上时崩溃的主要原因)。

CPUID 叶内核版本
0x00000000全部
0x00000001全部
0x000000025.0 及更高版本(仅限 x86)
0x000000046.0 及更高版本 (x86);
全部 (x64) 
0x000000066.1 及更高版本
0x000000076.2 及更高版本
0x0000000B10.0 及更高版本 (x86);
6.1 及更高版本 (x64)
0x0000000D6.1 及更高版本
0x000000101803 及更高版本(仅限 x64)
0x000000121511 及更高版本
0x000000181803 及更高版本

请记住,“全部”以适用于 32 位 Windows 的 3.10 版开始,但以适用于 64 位 Windows 的 Windows Server 2003 SP1 中的 5.2 版开始。看到两个基本叶子比 32 位 Windows 更早地用于 64 位 Windows,一个更早。

另请注意,这些列表仅供内核使用。cpuid 的其他使用 ,包括 HAL 的使用,超出了本说明的当前范围。

长叶

英特尔® 处理器标识和 CPUID 指令中的修订历史记录(应用说明 485,显然不再可以从英特尔在线获得任何修订版本),2001 年 6 月首次记录了英特尔处理器的扩展叶。由英特尔实施,当然不是在 AMD 首次实施时。Windows 2000 内核尝试叶子 0x80000000,不管供应商是什么,除了家族 5 之前的 AMD 处理器。

当然,在这些早期,无法假设对延长的叶子的支持。在eax 中使用 0x80000000 执行cpuid可能会将几乎所有内容加载到 eax 中,很可能是处理器最高基本叶的通常输出。那个eax 会带着扩展叶子的最大叶子数回来,充其量是不确定的。时至今日,32 位内核仍强制执行健全性检查。如果扩展叶子的最大值不在 0x80000000 和 0x800000FF 之间(含),则不可信。

CPUID 叶内核版本
0x800000005.0 及更高版本
0x800000015.0 及更高版本
0x80000002
0x80000003
0x80000004
5.0 及更高版本
0x800000055.1 及更高版本
0x800000065.1 及更高版本
0x800000086.0 及更高版本 (x86);
全部 (x64)
0x8000000A6.2 及更高版本
0x8000001D6.2 及更高版本
0x8000001E6.2 及更高版本

再次看到 64 位 Windows 运行在 32 位 Windows 之前,尽管在这种情况下只是一个版本的一片叶子。

超级遮阳叶

从 Windows Vista 开始,32 位和 64 位内核都识别从 0x40000000 开始的第三个cpuid叶范围。如果只是开始,它尚未核实到现在还除非第31位被设置为true,内核不尝试连第一,这些叶子 ECX从 CPUID叶1。英特尔和 AMD 长期以来都将此功能标志记录为保留。英特尔仍然如此。通常应该是清楚的。微软的 hypervisor 重新实现了cpuid 以便设置此功能标志。其他虚拟机管理程序也这样做(也许首先这样做,但这目前不在本说明的范围内)。如果内核发现设置了此功能标志,则执行叶 0x40000000 以确定此范围内的最大叶并识别管理程序。

CPUID 叶内核版本
0x400000006.0 及更高版本
0x400000016.0 及更高版本
0x400000026.2 及更高版本
0x400000036.1 及更高版本
0x400000046.0 及更高版本
0x400000056.2 及更高版本
0x4000000610.0 及更高版本
0x4000000710.0 及更高版本
0x4000000810.0 及更高版本(仅限 x64)
0x400000826.1 至 6.3

上面显示的版本是内核对每个叶子的已知用途。本网站的其他页面,特别是HV_CPUID_RESULT 结构和HV_CPUID_FUNCTION枚举显示了 Microsoft 为程序员定义这些叶子的版本。这样的定义主要是针对微软自己的程序员的。无论微软的开放规范承诺是什么,实际的现实是微软的 管理程序顶级功能规范和其他管理程序的cpuid接口文档,如果只是在线发布,通常已经过时多年,并留下了不小的数量进一步研究。 

当然,叶 0x40000082 超出了 Microsoft对其虚拟机管理程序cpuid叶的已知定义的连续范围。然而它是微软的。32 位和 64 位内核仅在两者都执行时才执行:第 31 位在叶 1 的ecx 中设置;叶 0x40000000 报告 Microsoft 是管理程序供应商。叶 0x40000000 报告为管理程序范围内的最大叶对于内核执行叶 0x40000082 无关紧要。

此页面创建于2020 年 1 月,部分内容源自 2008 年 1 月 22 日首次发布的材料。最后修改时间为 2021 年 1 月 25 日。

版权所有 © 2020-2021。杰夫·查普尔。版权所有。 条件适用

以上是关于CPUID 指令的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 CPUID 指令正确获取 x86 CPU 功能? [复制]

是否可以在 Intel SDE 中结合 CPUID 和指令集仿真参数

CPUID讲解

如何通过 cpuid 指令识别 c/assembler 中的 cpu 品牌名称

CPU和CPUID是啥关系?

C语言怎么取CPU的各项信息?