本文主要是介绍【我所认知的BIOS】-反汇编BIOS之Bootblock(2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1 、 CPU micro code 的背景
先做个铺垫为什么要在 BIOS 刚刚开始跑的时候就来讲 CPU 的 micro code 。
以下引用自网络:
; -------------------------------------
在十多年前的 Pentium 时代 , INTEL 曾经发布过有缺陷的 CPU, 因为浮点运算表边界上有几个数据错误 , 导致在某些应用会出现错误 , 这个错误概率很小 , 出错几率小于千万分之一 , 但在还是被捅出来了 . INTEL 为此召回 CPU.
从 Pentium Pro 起 , INTEL 决定给 CPU 留出一个补丁接口 , 当 CPU 内部有缺陷的时候 , 通过加载微码 (Microcode), 可以修复 CPU 的部分缺陷 .
INTEL 说 , 他们只测试过的 CPU 都是加载了微码的 , 如果没有加载微码 , INTEL 不保证会出现什么问题 .
现在的 CPU 有一个 CPUID, 通过执行 CPUID 指令 , 可以知道当前 CPU 的版本和 Stepping. 根据这个信息 , 再给 CPU 打相应的补丁 .
下图就是 CPUID=06D2, Rev.A2
何时给 CPU 打补丁 ?
在给 CPU 初始化的时候 , 就需要把 INTEL 提供的微码写进 CPU 去 , 因此 , 加载 CPU 微码就是系统 BIOS 的任务 .
如果系统的 CPU 是可更换的 , 那么其微码也需要更换 . 因此 , 在 BIOD 里 , 一般要包进若干个 ID 的 CPU 微码 , 以便工厂安排不同的 SKU 出货 . 如果 BIOS 发布是在 CPU 发布之前 , 那么 BIOS 里很可能就没有包进最新的微码 , 这个系统要使用新 CPU 的时候 , CPU 微码是无法加载的 .
另外 , BIOS ROM 容量有限 , 一个微码补丁最小有 2K, 如果平台兼容的 CPU 很多 , 则微码数量是十分巨大的 , 台式机某些主板可能兼容 20 多个 CPU 版本 , 那么微码的体积很大 , BIOS 里根本包不下这么多东西 , 于是 , 厂商不得不缩水 , 去掉一些不常用的微码 . 这些不常用的微码一般都是早期的 CPU, 如 DOTHAN 早期的 A STEPPING 就很可能没有对应的微码包进你的本本中去 .
不打补丁会有什么问题 ?
INTEL 说他们没测过不打补丁的 CPU, 也就不知道会出什么问题 . 呵呵 , 这显然 , 他们不想说太多技术细节而已 . 以俺的经验 , 如果不打补丁 , 99.99% 的时候 , 用户是感觉不到的 , 除非问题特别突出 . 只是俺遇到过几个明显的例子 , 为此出了几身汗 . 有几个案例 :
1, 某 Prescott CPU, 在台式机上发热 量特别大 , 超出 Design Point, 后来发现没加 载微码 , 加上微 码就正常了 ;
2, 某 Pentium D CPU, 进 WINDOWS XP 会蓝屏 , 以安全模式进去后 , 安装一个 SP2 补丁 , 就正常了 . 后来查出 , 也是微码没加载 ;
3, 某 Pentium M 架构 CPU, 在使用 CPU 内部 TSC 时 , 发现测出的 CPU 内部频率高出实现的 4 倍 , 如 2GHz CPU 测出却有 8GHz, 后查 , 也是没加载微码造成的异常 .
此类案例很多 , 特别是 Core 架构 CPU, 不但微码必须加载 , 而且要求尽早加载 , 否则 , 连 BIOS 都跑不完 , 系统就挂了 . 但是 INTEL 但至今没有任何官方对每个 CPU 微码版本进行描述的文件 .
怎么检查 CPU 微码是否加载 ?
加载微码后 , 在 CPU 的 MSR( 机器特定寄存器 ) 里可以读出版本号 . INTEL IA-32 编程手册上给出标准检查方法 :
MOV ECX, 008bh
XOR EAX, EAX
XOR EDX, EDX
WRMSR ; 向 MSR 8BH 写 0, 清除 MSR 中的信息
MOV EAX, 0001
CPUID ; 读 CPUID, 让 CPU 查看微码版本 , 并把微码版本送到 MSR 8B 中
MOV ECX, 008bh
RDMSR ; 读出当前 CPU 微码版本
执行上面的代码后 , 如果 EDX 的值为 0, 则说明你的 CPU 微码是没有没加载的 , 你的 CPU 运行在有缺陷的状态 . 如果不为 0, 则显示的是当前微码版本号
以上代码可以在 DOS 环境下 , 用 DEBUG32 调试界面执行 .
; ----------------------------------------------------
2 、来来来,动手反汇编了!!!
2.1 BIOS 开始的代码
如果您做了前两篇的东西,那么您 会不难反汇编到这里来,( F000_FFF0 跳到 F000:E05B ,然后再一个 near jmp 到了 F000:E1B0 )也就是下面这段代码。这段代码也就是 Award BIOS 最开始的代码部分了。
之前我们有研究过,当 CPU 收到 reset 信号后它内部会做一系列的动作,而且当第一次从 FFFF_FFF0H 处取指令执行的时候,还会在对应的寄存器中保存一些相关的信息。在
http://blog.csdn.net/lightseed/archive/2009/10/27/4735101.aspx
中的图 1 您可以看到 DX 里面究竟存了什么值。(其实就是 CPU 的 type 。)
在加注的时候,其实我个人更喜欢 用英语注释哈。但是在之前有人说想要我用中文注释,想了又想,其实真的没有必要。如果连这点英语都看不懂,那么我想看 datasheet 也估计很困难。所以最后还是确定用 English 来注释。注解有误的话,还往各位谅解并指正,我会尽快修正。
_F000:E1B0
_F000:E1B0 loc_FE1B0: ; CODE XREF: _F000:E05BJ
_F000:E1B0 mov gs, dx ; Save CPU type to GS
_F000:E1B2 cli ; Close interrupt
_F000:E1B3 cld
_F000:E1B4 mov ax, cs
_F000:E1B6 mov ss, ax ; CS and SS use the same segment, that can use ROM_CALL
_F000:E1B8 mov sp, 0E1BEh ; First Use ROM_CALL
_F000:E1BB jmp BT_CPU_Init ; Save esp (Return Address)
这段代码也就是 BIOS 开始两个 JMP 后的代码,应该说比较好理解。在 E1B8 这里 BIOS 第一次用了上一节中提到的 ROM_CALL 这个宏。也遇到了第一个函数。 BT_CPU_Init 其实是在 CPU 开始跑 code 的最开始就针对特殊的 CPU 做 patch 。那么我就来详细和大家说说这个函数。
_F000:0010
_F000:0010 BT_CPU_Init: ; CODE XREF: _F000:E1BBj
_F000:0010 xchg esp, ebp ; Save esp (Return Address)
_F000:0013 mov al, 0B0h ; '?
_F000:0015 out 80h, al ; manufacture's diagnostic checkpoint
_F000:0017 jmp short CpuMicroCodeUpdate
_F000:0019 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪
_F000:0019
_F000:0019 Update_Micro_code_finish: ; CODE XREF: _F000:00C4j
_F000:0019 nop
_F000:001A xchg esp, ebp ; Return
_F000:001D retn
_F000:0020
_F000:0020 CpuMicroCodeUpdate: ; CODE XREF: _F000:0017j
_F000:0020 mov bx, 0FC80h ; Address of GTD
_F000:0023 mov sp, 29h ; ')'
_F000:0026 jmp Enter_Flat_Mode
_F000:0026 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
_F000:0029 dw 2Bh
_F000:002B ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
_F000:002B mov eax, 1
_F000:0031 cpuid
_F000:0033 mov edi, eax ; Save the CPU information
_F000:0036 cmp ax, 0F62h ; Is Cedar Mill B1, if it is , do not patch for it. No go on.
_F000:0039 jz Update_Micro_code_ret
_F000:003D mov ecx, 1000h
_F000:0043 mov esi, 0FFFEFFF0h
_F000:0049
_F000:0049 loc_F0049: ; CODE XREF: _F000:0057j
_F000:0049 cmp dword ptr [esi], '1SB*' ; Compare if it is CPU micro code?
_F000:0051 jz HaveFoundMicroCode
_F000:0053 sub esi, 10h ; No, go on looping
_F000:0057 loop loc_F0049
_F000:0059 jmp short Update_Micro_code_ret
_F000:005B ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
_F000:005B
_F000:005B HaveFoundMicroCode: ; CODE XREF: _F000:0051j
_F000:005B mov ebx, [esi+0Bh] ; get the Code Base
_F000:0060 or ebx, ebx ; If invalid, return
_F000:0063 jz Update_Micro_code_ret
_F000:0065 cmp ebx, 0FFFFFFFFh
_F000:0069 jz Update_Micro_code_ret
_F000:006B
_F000:006B Update_Micro_code_Start: ; CODE XREF: _F000:00C2j
_F000:006B cmp dword ptr [ebx], 1
_F000:0070 jnz Update_Micro_code_ret
_F000:0072 cmp dword ptr [ebx+14h], 1
_F000:0078 jnz Update_Micro_code_ret
_F000:007A cmp di, [ebx+0Ch]
_F000:007E jnz NotMatch
_F000:0080 mov ecx, 17h
_F000:0086 rdmsr
_F000:0088 shr edx, 12h
_F000:008C and dl, 7
_F000:008F mov cl, dl
_F000:0091 mov al, 1
_F000:0093 shl al, cl
_F000:0095 test [ebx+18h], al
_F000:0099 jz NotMatch
_F000:009B mov eax, ebx
_F000:009E add eax, 30h ; '0'
_F000:00A2 mov ecx, 79h ; 'y' ; MSR register to update micro code
_F000:00A8 xor edx, edx
_F000:00AB wrmsr
_F000:00AD jmp short Update_Micro_code_ret
_F000:00AF ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
_F000:00AF
_F000:00AF NotMatch: ; CODE XREF: _F000:007Ej
_F000:00AF ; _F000:0099j
_F000:00AF mov ecx, [ebx+20h]
_F000:00B4 or ecx, ecx ; Size is zero? Zero means size of CPU micro code is 2 KB
_F000:00B7 jnz Not2KB
_F000:00B9 mov ecx, 800h ; 2KB
_F000:00BF
_F000:00BF Not2KB: ; CODE XREF: _F000:00B7j
_F000:00BF add ebx, ecx
_F000:00C2 jmp short Update_Micro_code_Start
_F000:00C4 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
_F000:00C4
_F000:00C4 Update_Micro_code_ret: ; CODE XREF: _F000:0039j
_F000:00C4 ; _F000:0059j ...
_F000:00C4 jmp Update_Micro_code_finish
上面 micro code update 的全部函数。一些简单的动作,我也在 code 中注释了。我着重拎一些不太好懂的说说。
① _F000:0036 在这行中,主要是先对比一下 CPU 是否为 Cedar Mill B1 ,如果不是那么这个函数就不用跑了。 Patch 当然也就不用做了。
② _F000:0049 在这行中,有一个对比的指令,其实很明显可以猜到这就是 CPU micro code 的标志。让我们 用 ultraedit 打开 A552V110.BIN 来看看,如图 1
图 1
从图 1 中可以看出,“ *BS1 ”是处于 bin 文件中的 6000 段的,也就是 BIOS 在实际跑中的 E000 段。所以和代码中的段刚好符合。如果把整个 E000 段都找完了还是找不到的话,那么就说明没有这个部分的 micro code ,直接就 return 了。
③ _F000:005B 这行是为了得到 CPU micro code 的 base address 。这个地址是 CBROM 填入了,我们可以不用 care 它。只需要知道从这里可以得到 CPU micro code 就好了。见图 2 bin 文件中实际的 micro code 位置 FFFE_57E0H 。
图 2
紧接着,我们可以从对应的地址处 看到实际的 CPU micro code 。如图 3
图 3
而牵涉到 CPU 的 micro code 那么就不得不提它的结构。从 micro code 的第 0 个字节算起,会有如下一个表。
CPU micro code 开始几个 flag
HeaderVersion | ;offset 00h |
UpdateRevision | ;offset 04h |
UpdateData | ;offset 08h |
ProcessorVersion | ;offset 0ch |
Checksum | ;offset 10h |
LoaderRevision | ;offset 14h |
Platform_ID | ;offset 18h |
DataSize | ;offset 1ch |
TotalSize | ;offset 20h |
Reserved | ;offset 24h |
Update_Data | ;offset 30h |
有了这几个说明,我想从 Update_Micro_code_Start: 开始的那些 EBX+XX 之类的比较就应该没有什么问题了哦。看看图 3 中,我还给大家标了一些标志出来。您可以追追看。
④ _F000:0080 和 _F000:0086 两行操作的是 MSR 寄存器 17H 。它的说明在 3 B 里面有说,以下是截图。
图 4
⑤ _F000:00A2 这行主要是初始化 MSR 的 79H 。关于它的说明也是在 3B 里面见图 5 所示。
图 5
经过那么多的判断后,最终只确定 了一件事,那就是目前的 CPU 确实是应该 update 以下它的 micro code 了,于是在 _F000:00AB wrmsr 命令执行后, CPU 开始升级它的 micro code 。
至此关于 CPU 在 Bootblock 的时候升级 micro code 的动作就做完了。其实在后面 POST 的时候,也会有这样的动作,但是原理都是一样的。(稍微有些些差别)我们理解原理就好啦。呵 呵。。。希望大家喜欢。
2.2 CPU micro code update
这篇关于【我所认知的BIOS】-反汇编BIOS之Bootblock(2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!