为什么BIOS入口点以WBINVD指令开头? [英] Why does the BIOS entry point start with a WBINVD instruction?

查看:98
本文介绍了为什么BIOS入口点以WBINVD指令开头?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究计算机中的BIOS代码(x86_64 Linux,IvyBridge).我使用以下过程转储BIOS代码:

I'm investigating the BIOS code in my machine (x86_64 Linux, IvyBridge). I use the following procedure to dump the BIOS code:

$ sudo cat /proc/iomem | grep ROM
  000f0000-000fffff : System ROM
$ sudo dd if=/dev/mem of=bios.dump bs=1M count=1

然后我使用radare2读取并反汇编二进制转储:

Then I use radare2 to read and disassemble the binary dump:

$ r2 -b 16 bios.dump 
[0000:0000]> s 0xffff0
[f000:fff0]> pd 3
        :   f000:fff0      0f09           wbinvd
        `=< f000:fff2      e927f5         jmp 0xff51c
            f000:fff5      0000           add byte [bx + si], al

我知道x86处理器初始化总是从16位8086环境开始,并且要执行的第一条指令在f000:fff0,即0xffff0.所以我去那个地方并反汇编代码.

I know x86 processor initialization always starts with a 16-bit 8086 environment, and the first instruction to be executed is at f000:fff0, i.e. 0xffff0. So I go to that location and disassemble the code.

令我惊讶的是,第一条指令是 WBINVD ,其功能是使高速缓存,这在处理器开机或重置时似乎无关紧要.我希望第一条指令只是对较低内存地址的jmp.

To my surprise, the first instruction is WBINVD, whose functionality is to invalidate the cache, which seems to be irrelevant when the processor is powered on or reset. I would expect the first instruction to be simply a jmp to a lower memory address.

为什么在jmp之前有WBINVD?

我已经搜索了英特尔手册第3卷第9章处理器管理和初始化"的相关部分,但未提及WBINVD.我还搜索了一些在线资源,但没有找到任何解释.

I've already searched the relevant portion of the Intel manuals, Volume 3 Chapter 9 Processor Management and Initialization, but it doesn't mention anything about WBINVD. I also searched some online resources but didn't find any explanation.

按照0xff51c指令执行0xff51c后,该代码更有趣;它正在进行自我检查:

After following the jmp instruction to 0xff51c, the code is more interesting; it's doing a self-check:

[f000:f51c]> pd
            f000:f51c      dbe3           fninit
            f000:f51e      0f6ec0         movd mm0, eax
            f000:f521      6631c0         xor eax, eax
            f000:f524      8ec0           mov es, ax
            f000:f526      8cc8           mov ax, cs
            f000:f528      8ed8           mov ds, ax
            f000:f52a      b800f0         mov ax, 0xf000
            f000:f52d      8ec0           mov es, ax
            f000:f52f      6726a0f0ff00.  mov al, byte es:[0xfff0]     ; [0xfff0:1]=0
            f000:f536      3cea           cmp al, 0xea
        ,=< f000:f538      750f           jne 0xff549
        |   f000:f53a      b91b00         mov cx, 0x1b
        |   f000:f53d      0f32           rdmsr  ; check BSP (Boot Strap Processor) flag, if set, loop back to 0xffff0; otherwise, infinite hlt
        |   f000:f53f      f6c401         test ah, 1
       ,==< f000:f542      7441           je 0xff585
      ,===< f000:f544      eaf0ff00f0     ljmp 0xf000:0xfff0
      ||`-> f000:f549      b001           mov al, 1
      ||    f000:f54b      e680           out 0x80, al
      ||    f000:f54d      66be8cfdffff   mov esi, 0xfffffd8c          ; 4294966668
      ||    f000:f553      662e0f0114     lgdt cs:[si]
      ||    f000:f558      0f20c0         mov eax, cr0
      ||    f000:f55b      6683c803       or eax, 3
      ||    f000:f55f      0f22c0         mov cr0, eax
      ||    f000:f562      0f20e0         mov eax, cr4
      ||    f000:f565      660d00060000   or eax, 0x600
      ||    f000:f56b      0f22e0         mov cr4, eax
      ||    f000:f56e      b81800         mov ax, 0x18
      ||    f000:f571      8ed8           mov ds, ax
      ||    f000:f573      8ec0           mov es, ax
      ||    f000:f575      8ee0           mov fs, ax
      ||    f000:f577      8ee8           mov gs, ax
      ||    f000:f579      8ed0           mov ss, ax
      ||    f000:f57b      66be92fdffff   mov esi, 0xfffffd92          ; 4294966674
      ||    f000:f581      662eff2c       ljmp cs:[si]
      |`.-> f000:f585      fa             cli
      | :   f000:f586      f4             hlt
      | `=< f000:f587      ebfc           jmp 0xff585

总而言之,这个BIOS代码是在0xffff0处读取自身并将字节与0xea进行比较,而这恰好是一个远跳操作码:

To conclude the weirdness, this BIOS code is reading itself at 0xffff0 and comparing the byte with 0xea, which is exactly the opcode of a far jump:

            f000:f52a      b800f0         mov ax, 0xf000
            f000:f52d      8ec0           mov es, ax
            f000:f52f      6726a0f0ff00.  mov al, byte es:[0xfff0]     ; [0xfff0:1]=0
            f000:f536      3cea           cmp al, 0xea

如果发现0xffff0处的代码有很大的距离,那么它将陷入无限循环.

If it finds the code at 0xffff0 is a far jump, then it will go into an infinite loop.

更准确地说,AP(应用处理器)将按hlt指令无限循环,而BSP(引导处理器)将循环回到起始0xffff0.由于0xffff0处的代码不会更改,因此可以断定BSP总是会发现该字节为0xea且永远不会跳出循环.

More precisely, the APs (Application Processors) will loop infinitely at the hlt instruction, while the BSP (Boot Strap Processor) will loop back to the beginning 0xffff0. Since the code at 0xffff0 won't be changed, we can conclude the BSP will always find the byte being 0xea and will never go out of the loop.

那么这种自我检查的目的是什么?我简直无法相信这是天真地防止修改.

So what's the purpose of this self-checking? I can hardly believe it's a naive attempt to prevent modification.

推荐答案

尽管很难推理,但请记住,即使es设置为mov al, byte es:[0xfff0]也不是从BIOS第一条指令中读取的. >.

Albeit hard to reason about, remember that the load mov al, byte es:[0xfff0] is not reading from the the BIOS first instruction, even though es is set to 0xf000.

0xfffffff0中读取第一条指令,PCH在复位时也可能会将0xf0000-0xfffff别名为0xffff0000-0xffffffff,因此,启动BSP时,它将执行您转储的代码. IIRC,除非明确唤醒,否则AP不会启动.

The first instruction is read from 0xfffffff0, the PCH will also probably alias 0xf0000-0xfffff to 0xffff0000-0xffffffff at reset, so when the BSP is booted it will execute the code you dumped.
IIRC, the APs don't boot unless explicitly waken up.

然后BSP将继续初始化硬件(从转储中判断).
在某些时候,它将设置0xf0000-0xfffff的属性映射,以引导对内存的读取和写入(或只是写入然后读取). 最终结果是,当处理器(硬件线程)启动时,它将执行闪存中的代码,直到执行远距离跳转为止.
此时,cs基数已根据实模式规则正确计算(非常类似于非实模式),并且指令将从0xf0000-0xfffff(即从RAM)获取. 所有这一切,而cs段值实际上并没有改变.

The BSP will then will proceed with initialising the HW (judging from the dump).
At some point it will set the attribute map for the 0xf0000-0xfffff to steer reads and writes (or just writes and then reads) to memory.
The end result is that when a processor (an HW thread) boots it will execute the code from the flash until it perform a far jump.
At the point the cs base is correctly computed as per real-mode rules (pretty much like the unreal mode) and the instruction will be fetched from the 0xf0000-0xfffff (i.e. from the RAM).
All of this while the cs segment value didn't actually change.

BSP将在某个时候启动其多处理器初始化例程,在该例程中,它向所有人(包括他自己)广播INIT-SIPI-SIPI,这将导致AP进入睡眠状态,并且BSP会出现ljmp 0xf000:0xfff0.
这里的技巧是,跳转目标0xf000:0xfff0wbinvd指令的总线地址不同.
那里可能还有其他东西,可能是另一个初始化例程.

The BSP at some point will start its multiprocessor initialisation routine, where it broadcasts to everyone (including himself) an INIT-SIPI-SIPI that will result in a sleep for the APs and a ljmp 0xf000:0xfff0 for the BSP.
The trick here is that the target of the jump, 0xf000:0xfff0, is not the same bus address of the wbinvd instruction.
There could be something else there, probably another initialisation routine.

在初始化结束时,BIOS可以简单地重置0xf0000-0xfffff的属性以落入闪存(因此可以进行软件重置),从而防止(无意间)转储中间代码.

At the end of the initialisation the BIOS could simply reset the attributes of the 0xf0000-0xfffff to fall through to the flash (so a software reset is possible), preventing (not intentionally) a dump of the intermediary code.

这不是很有效,但是BIOS通常不是代码的杰作.

This is not very efficient, but BIOSes are not usually masterpieces of code.

我没有足够的信息来确定正在发生的事情,我的意思是,ljmp 0xf000:0xfff0mov al, byte es:[0xfff0]不必从它们所在的同一区域读取.
考虑到这一点,所有赌注都关闭了.
只有适当的逆向工程才能证明一切.

I don't have enough element to be sure what's going on, my point is that the ljmp 0xf000:0xfff0 and the mov al, byte es:[0xfff0] doesn't have to read from the same region they reside in.
With this in mind, all bets are off.
Only a proper reverse engineering will tell.

关于wbinvd,我在评论中建议它可能与热启动功能有关,而Peter Cordes建议它可能特别与cache-as-RAM有关.
这是有道理的,但我想永远都不确定.
这也可能是对货物狂热的一种情况,在这种情况下,程序员基于传言认为该指令是必要的.

Regarding the wbinvd, I suggested in the comment it could be related to the warm boot facility and Peter Cordes suggested that it may specifically have to do with cache-as-RAM.
It makes sense, I guess will never be sure though.
It could as well be a case of cargo cult, where a programmer deemed the instruction necessary based rumors.

这篇关于为什么BIOS入口点以WBINVD指令开头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆