gpt4 book ai didi

assembly - 为什么 BIOS 入口点以 WBINVD 指令开头?

转载 作者:行者123 更新时间:2023-12-02 18:48:27 26 4
gpt4 key购买 nike

我正在研究我的机器(x86_64 Linux、IvyBridge)中的 BIOS 代码。我使用以下过程转储 BIOS 代码:

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

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

$ 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。所以我去那个位置并反汇编代码。

令我惊讶的是,第一条指令是 WBINVD ,其功能是使缓存失效,这在处理器上电或复位时似乎无关紧要。我希望第一条指令只是指向较低内存地址的 jmp

为什么jmp之前有一个WBINVD

我已经搜索过英特尔手册第 3 卷第 9 章处理器管理和初始化的相关部分,但它没有提及任何有关 WBINVD 的内容。我也查了一些网上资源,但没有找到任何解释。

编辑更多信息:

跟随jmp指令到0xff51c之后,代码就更有趣了;它正在进行 self 检查:

[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 进行比较,这正是远跳转的操作码:

            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处的代码是远跳转,那么就会进入无限循环。

更准确地说,AP(应用程序处理器)将在 hlt 指令处无限循环,而 BSP(引导带处理器)将循环回到开头 0xffff0。由于 0xffff0 处的代码不会改变,因此我们可以得出结论,BSP 总是会找到 0xea 的字节,并且永远不会跳出循环。

那么这个自检的目的是什么呢?我简直不敢相信这是阻止修改的天真的尝试。

最佳答案

尽管很难推理,但请记住负载 mov al, byte es:[0xfff0]不读取 BIOS 第一条指令,即使 es设置为0xf000

第一条指令读取自 0xfffffff0 ,PCH 也可能会别名 0xf0000-0xfffff0xffff0000-0xffffffff重置时,因此当 BSP 启动时,它将执行您转储的代码。
IIRC,除非明确唤醒,否则 AP 不会启动。

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

BSP 在某个时刻将启动其多处理器初始化例程,向每个人(包括他自己)广播一个 INIT-SIPI-SIPI,这将导致 AP 进入休眠状态并发出 ljmp 0xf000:0xfff0 消息。对于 BSP。
这里的技巧是跳转的目标 0xf000:0xfff0 ,与wbinvd的总线地址不同。说明。
可能还有其他东西,可能是另一个初始化例程。

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

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

我没有足够的元素来确定发生了什么,我的观点是 ljmp 0xf000:0xfff0mov al, byte es:[0xfff0]不必从他们所在的同一区域进行读取。
考虑到这一点,一切皆有可能。
只有适当的逆向工程才能证明这一点。

关于wbinvd ,我在评论中建议它可能与热启动设施有关,而 Peter Cordes 则建议它可能与缓存即 RAM 有关。
这是有道理的,但我想永远不会确定。
这也可能是 cargo 崇拜的一个例子,程序员根据谣言认为该指令是必要的。

关于assembly - 为什么 BIOS 入口点以 WBINVD 指令开头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54274221/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com