Microcode是什么?它为什么能修正CPU硬件错误?

Microcode是什么?它为什么能修正CPU硬件错误?

前一阵的CPU的melt down带来安全隐患被广为报道,搞得鸡飞狗跳,也让CPU的硬件设计错误或者说疏漏问题开始被普通电脑用户所了解。“什么,CPU硬件也会出bug,难道要召回吗?”看看自己大几千买的电脑似乎转眼就要变成黑客的游乐场,不少用户开电脑的手指颤抖了。尽管我们知道这种攻击需要大量实验和时间,而黑客们对一般用户是不会花费如此大的精力的,主要目标是服务器和使用他们的财大气粗的企业们。但谁知道呢,不安全就是不安全,小心使得万年船,谨慎总是没错的。电脑用户的担心也是合理的。

Intel赶工做出的新版Microcode修正了这个问题。在擦掉一头冷汗后,小白们不禁对这个“强大”到能够修正指令执行逻辑的东西感兴趣起来。我们今天就来了解一下它的前世今生。

CPU出错会被召回吗?

CPU历史上最著名的召回事件莫属1994年Intel召回奔腾处理器了[1]。在发现fdiv(浮点除)在某些情况下结果会出问题后,CPU算出的结果变得不可信了。尽管Intel的工程师证明CPU计算700年才会出错一次,但谁也说不准这个统计学上的时间是不是会发生在刚才算出的存款余额上。在很多疑神疑鬼的人宣称自己发现的错误几率高的多的情况下,Intel不得不召回了这款处理器,从而在金钱上和名誉上受到了很大损失。

这时如何在CPU出货后还可以发布硬件补丁的问题就被提上了议事日程。而在指令解码器中使用的microcode显然是最合理的地方了。X86 CPU自从80年代后就一直将复杂的变长IA32指令翻译成简单的微代码,它默默工作了这么长的时间,如果在转换时能够重新映射和修改,那么问题不就很好解决了吗?

CPU是如何认识指令的?

现代高级语言要被编译器编译成CPU认识的机器代码,才能够被CPU所识别并执行;而汇编语言只是被发明出来方便人类解读和产生机器代码的,这些基本知识我就不介绍了。一组机器代码和相应汇编语言的例子:

这些指令和数据流在被识别(decode)之前会被分别放入一级cache(L1 Cache)中,指令放在指令L1 Cache中,数据放在数据L1 Cache中。注意L1 Cache是唯一指令和数据单独分割的Cache。然后,指令Cache中的指令会进入解码器decode,那里才是神奇的开始:

现代CPU的指令解码器(Instruction Decode Unit ,IDU)大致分成两种:硬件指令解码器和微码指令解码器

硬件指令解码器是完全由硬件连线(hardwired)完成的机器代码解码。它是最原始的解码器,由有限状态机驱动,解码速度十分快。它现在还在很多精简指令CPU(RISC)中发挥作用。

我们现在普遍使用的电脑X86 CPU,采用的是复杂指令集(CISC),指令很多,而且长短不一。如果所有的指令全部采用硬件解码,那将是一个不可能完成的任务。所以一条机器指令,将被拆解成数个类似RISC的精简微操作:微码,Micro-Ops,Microcode。而这些Micro-Ops,则可以完全被硬件执行。如下面这个例子:

那么每个X86指令会被分解成多少个micro-ops呢?这和该指令的复杂程度相关,很简单的指令甚至只有一个micro-ops,一般3个左右,复杂的可以4个以上。

这么做除了能化繁为简外,它的输出Micro-Ops作为可以执行的最小单位,可以被调度器Scheduler放入Pipeline中来提高指令的并行性

然后才会进入ALU,IMUL等等逻辑运算单元。它们基本是由逻辑门搭出来的。

一条机器指令,经过重重解码,才会流到逻辑运算单元。而这个decode的过程,让曾经泾渭分明的RISC和CISC两种CPU架构的界限变得模糊了起来。RISC CPU加入了越来越多的指令,很多CPU也不再仅仅是硬件指令解码,而对部分指令采取了微码解码方式。而CISC CPU因为加入了Micro-Ops,而在decode后端显示出了RISC的特性。从这个意义上来讲,以ARM为代表的RISC CPU和以X86为代表的CISCCPU在指令集层次很大程度融合了。

Microcode如何打补丁

既然一条指令会被解码成microcode/Micro-Ops,如果转换后的microcode出了问题,打个补丁就行了呗。原始的microcode映射是从一个ROM中来的:

新设计了一块相对比较小的SRAM,它用来在该ROM上打补丁:

Patch的过程需要Microcode解码器硬件支持,基本是向量替换的方式。具体细节Intel和AMD并不同,而且是商业机密。

结论

在吸取了奔腾的教训之后,第一版可以更新microcode的CPU分别是1995年的Intel 奔腾Pro(P6)和AMD在1999年的K7。从那以后,硬件错误往往悄悄的通过更新Microcode来进行。

Microcode打补丁既然是在SRAM上进行的,掉电以后就需要重新打补丁了,也就是说每次重启都要重新打补丁。打补丁的方式也很简单,就是把Microcode放在某个内存中,写个MSR就行了。

Microcode更新可以通过BIOS来进行,也可以通过操作系统来进行。BIOS更新的好处是一更新,上面安装的操作系统(Linux,Windows)都可以收益;坏处是用户更新BIOS太麻烦,小白不敢更新。操心系统,如Windows一般通过Windows Update来更新Microcode,好处是用户一般无感,体验很好;缺点是另外的系统如Linux就不能得到好处了,而且Linux发行版众多,Microcode更新混乱,甚至有些需要手动更新微码。

其他CPU硬件文章:

欢迎大家关注我的专栏和用微信扫描下方二维码加入微信公众号"UEFIBlog",在那里有最新的文章。

用微信扫描二维码加入UEFIBlog公众号

参考

  1. ^奔腾召回 https://www.techradar.com/news/computing-components/processors/pentium-fdiv-the-processor-bug-that-shook-the-world-1270773
编辑于 2019-11-11 12:18