当前位置: 首页 > 知识库问答 >
问题:

为什么BIOS入口点以WBINVD指令开始?

夏景胜
2023-03-14

我正在研究我的机器(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处,即0xfff0。所以我去那个位置,反汇编代码。

令我惊讶的是,第一条指令是wbinvd,其功能是使缓存无效,这在处理器上电或重置时似乎不相关。我希望第一条指令只是一个JMP到较低的内存地址。

为什么在JMP之前有WBINVD

我已经搜索了Intel手册第3卷第9章处理器管理和初始化的相关部分,但其中没有提到任何关于WBINVD的内容。我也搜索了一些在线资源,但没有找到任何解释。

按照JMP指令执行0xFF51C后,代码更有趣;它在做自我检查:

[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
            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

那么这种自检的目的是什么呢?我很难相信这是一个天真的企图阻止修改。

共有1个答案

璩正志
2023-03-14

尽管很难推断,但请记住,即使es被设置为0xf000,加载MOV al,byte es:[0xfff0]并不是从BIOS第一条指令读取的。

第一条指令是从0xFFFFFF0读取的,PCH还可能在重置时将0xFFF0000-0xFFFF别名为0xFFFF0000-0xFFFFF,因此当BSP启动时,它将执行您转储的代码。
IIRC,APs不会启动,除非显式唤醒。

然后,BSP将继续初始化HW(根据转储判断)。
在某个时候,它将为0xF0000-0xFFFFF设置属性映射,以引导读写(或只写然后读)到内存。
最终结果是,当处理器(一个HW线程)启动时,它将从闪存中执行代码,直到执行远跳转。
在这一点上,CS基是按照实模式规则正确计算的(非常类似于非实模式),指令将从0xF0000-0xFFFF中提取(即。
所有这些,而cs段值实际上并没有改变。

BSP会在某个时刻启动它的多处理器初始化例程,其中它向每个人(包括他自己)广播一个INIT-SIPI-SIPI,这将导致APs的Hibernate,并为BSP广播一个LJMP0xF000:0xFFF0
这里的诀窍是跳转的目标0xF000:0xFFF0不是WBINVD指令的相同总线地址。
可能有其他东西,可能是另一个初始化例程。

在初始化结束时,BIOS可以简单地将0xF0000-0xFFFFF的属性重置为flash(因此可以进行软件重置),防止中间代码的转储(不是故意的)。

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

我没有足够的元素来确定发生了什么,我的观点是LJMP0xF000:0xFFF0MOV al,byte ES:[0xFFF0]不必从它们所在的同一区域读取。
记住这一点,所有的赌注都取消了。
只有适当的反向工程才能知道。

关于wbinvd,我在评论中建议它可能与warm boot功能有关,而Peter Cordes建议它可能与cache-as-ram有关。
这是有道理的,但我想永远不会确定。
这也可能是一种货邪教,程序员认为指令是必要的,基于谣言。

 类似资料:
  • BIOS的主要作用有三点 1.自检及初始化:开机后BIOS最先被启动,然后它会对电脑的硬件设备进行完全彻底的检验和测试。如果发现问题,分两种情况处理:严重故障停机,不给出任何提示或信号;非严重故障则给出屏幕提示或声音报警信号,等待用户处理。如果未发现问题,则将硬件设置为备用状态,然后启动操作系统,把对电脑的控制权交给用户。 2.程序服务:BIOS直接与计算机的I/O(Input/Output,即输

  • 计算机用户在使用计算机的过程中,都会接触到BIOS,它在计算机系统中起着非常重要的作用。 BIOS,完整地说应该是ROM-BIOS,是只读存储器基本输入/输出系统的简写,它实际上是被固化到计算机中的一组程序,为计算机提供最低级的、最直接的硬件控制。准确地说,BIOS是硬件与软件程序之间的一个“转换器”或者说是接口(虽然它本身也只是一个程序),负责解决硬件的即时需求,并按软件对硬件的操作要求具体执行

  • 免费获得新功能 升级BIOS最直接的好处就是不用花钱就能获得许多新功能,比如原来你用的是PⅡ的CPU,升级BIOS后也许就能直接使用PⅢ的CPU,不用换主板了;看着别人能用光驱来启动的计算机,自己的不行,升级BIOS后,成了;另外还能增加PnP即插即用功能、新硬盘的LBA和DMA功能、识别其它新硬件等等,简直就是免费升级电脑! 解决莫名其妙的故障 另外,升级BIOS还可以解决一些特殊的电脑故障,这

  • 问题内容: ..如果您使用ng-model,则必须在某处有一个点。如果没有点,那么您就在做错误..” 但是,Angular.JS网站中的第一个示例(基础知识)似乎与此矛盾。是什么赋予了?自从MTV聚会以来,Angular.JS是否已更改,因为它现在对ng- model更宽容了? 问题答案: 在处理范围继承的复杂性时,这个小点非常重要。 该egghead.io视频“点”有一个很好的概述,做到这一点非

  • 本文向大家介绍什么是JSP指令?,包括了什么是JSP指令?的使用技巧和注意事项,需要的朋友参考一下 JSP指令影响Servlet类的整体结构。它通常具有以下形式- 指令标记有三种类型- 序号 指令与说明 1 <%@ page ...%> 定义与页面相关的属性,例如脚本语言,错误页面和缓冲要求。 2 <%@ include ...%> 在翻译阶段包含文件。 3 <%@ taglib ...%> 声明

  • 问题内容: 我花了很多时间阅读AngularJS文档和一些教程,对于文档的难懂性,我感到很惊讶。 我有一个简单的,可以回答的问题,对于其他希望使用AngularJS的人也可能有用: 什么是AngularJS指令? 某个地方应该有一个简单,精确的指令定义,但是AngularJS网站提供了这些令人惊讶的无用定义: 在主页上: 指令是AngularJS中可用的独特而强大的功能。指令可让您发明特定于您的应