我一直在寻找一个解释BIOS是如何工作的,我已经设计了一个 Bootstrap ,并成功地初始化IDT和GDT时跳转到32位模式,但在这样做的过程中,我发现“操作系统”似乎很简单,感觉好像“BIOS”是每台计算机的实际操作系统。
因此,现在我已经承担了一个新的挑战,试图发现BIOS实际上如何初始化自己,发现多少RAM是可用的,以及如何/在哪里插件卡ROM被导入RAM。据我所知,处理器,而不是通过跳转,但自动开始执行代码内的RAM在16位段:偏移地址0xFFFF:0x 0000。这意味着,所有的计算机在技术上必须有至少1 MB的RAM价值,最初为了让他们能够 Boot ,由于处理器的起始位置,并由于这一知识,我一直假设,所有的BIOS的自动写入RAM之前,处理器得到它的RST信号。我觉得这是不正确的,因为这正是“影子BIOS”是什么,可以通过BIOS禁用我相信。我一直在寻找“BIOS设计师指南”,但是,我不断空手而归的每一个规范,我似乎读。
作为一名程序员,我知道可能有许多方法可以实际完成我的实际要求,而且可能没有办法给予一个像样的直截了当的答案,如果我必须更具体地说,假设我使用的是戴尔Inspiron 518,或者至少是包含G33芯片组的计算机(G33北桥和ICH 9南桥),我想编写初始预开机自检程序,然后构建我自己的16位IDT,其中包含所有标准中断和可能成功 Boot 另一个操作系统(如Windows 10)所需的一切。BIOS实际上是如何知道有多少RAM的?它只是执行位写入位吗?读取测试在最高的内存区域,并从那里下降?以及如何附加卡ROM甚至得到加载到RAM?据我所知,BIOS建立了一个非常基本的中断和/或“入口点”的列表,附加卡ROM可以利用,并给予他们的能力,“锁存”到其他BIOS的中断,如“$PMM”?BIOS制造商如何知道BIOS中需要什么样的锚字符串才能 Boot Windows这样的操作系统?
任何答案都会很有帮助,以及任何推荐的规范和/或任何能够引导我进入我一直在寻找的知识的指南。例如,可能是一个指南,说“在移交给IPL之前,BIOS需要完成的最低要求的过程?”或甚至是一个C或汇编源代码的示例,其中包含一些可以告诉我什么是附加卡“的ROM映像实际上是或看起来像是非常有帮助的。
2条答案
按热度按时间8ftvxx2r1#
我将这个答案限制在英特尔体系结构上,因为我对它们非常熟悉。
您(以及我)正在查找的文档名为 BIOS Writer Guide,不幸的是,该文档是机密文档,目前尚未泄露(AFAIK)。
为了在开源社区推广他们的产品,英特尔发布了Firmware Support Package,这被认为类似于固件编写器的库,包含初始化内存控制器、PCH(外设控制器集线器,非正式地称为“芯片组”)和CPU 1的(二进制)代码。
开放源代码开发人员,或一般而言,任何无法与英特尔签署保密协议的开发人员,都可以使用FSP编写自己的固件。
可以反转FSP(我的许多TODO之一),但使用它作为参考会更快。
当电源打开时,在CPU从复位矢量2开始执行之前会发生很多事情,但需要记住的重要一点是,芯片组(即PCH)已经允许CPU访问闪存ROM。
事实上,这就是第一条指令的执行方式,因为CPU只能从内存地址空间获取指令。
因此,只要固件将执行流程保持在Map到闪存ROM的存储器区域内(该区域由闪存ROM本身中存在的闪存描述确定,PCH在其复位期间读取该区域,并相应地配置存储器请求的路由),其代码就可以执行。
由于存储器尚未初始化且闪存ROM为只读(存储器写入周期),因此无法使用这些功能:
这两点都很烦人,在汇编语言中你可以使用跳转和寄存器来解决它们,但是在C语言中你不能。
所以固件做的第一件事通常是设置一个“临时RAM”。
这是FSP的
TempRamInit()
例程(顺便说一下,必须使用跳转来调用它),实际上,它设置了Cache-as-RAM(CAR)。缓存即RAM
其思想是该高速缓存用作临时RAM。
最基本的一点是,缓存线不会过期,只有当内存中没有更多空间容纳新请求的缓存线时,才会收回缓存线。
因此,只要您足够小心,避免访问更多可以放入该高速缓存的变量,CPU将只从缓存中读取和写入(当然,这需要回写缓存模式)。
然而,这需要仔细定位变量,而且它确实非常脆弱。
更好的方法是使该高速缓存(通过清零
CR0
寄存器中的CD
(缓存禁用)位),然后对L13大小的存储器区域执行伪读取(甚至写入)。然后再次禁用缓存,此模式实际上称为 * 无填充模式 *,在此模式下,不会将新缓存线放入缓存(因此不会“丢失”现有缓存线),但读取和写入仍然可以命中缓存。
这允许几KiB的“RAM”。
存在用于CAR环境的C编译器。
初始化RAM
现在固件可以初始化RAM,为此必须完成三件事:
1.告知内存控制器有关DIMM计时的信息(CAS、RAS)。
1.告知内存控制器有关DIMM的大小和排列。
1.设置路由。
内存控制器是通过PCI配置空间和MMIO配置的,您可以在处理器数据手册第2卷中找到详细信息(假设MC位于CPU芯片中)。
例如,8th and 9th generation core datasheet vol 2包含内存控制器寄存器的描述。
类似地,您将找到DIMM大小和类型、通道大小等的寄存器:
第一节第一节第一节第二节第一节
这些寄存器涵盖了点1和点2(以及点3的一部分,具体取决于定义),但固件如何知道要使用哪些值?
毕竟,DIMM是可更换的。
如前所述,解决方案是Serial Presence Detect (SPD),这是一种集成在DIMM本身上的小型EEPROM,用于描述内存时序、拓扑结构和大小。
EEPROM通过I2C兼容总线访问。
在Intel体系结构中,实际使用的总线是SMBus(System Management Bus,系统管理总线),它与I2C兼容,并且是相应产生的。
SMBus主机位于PCH中,并记录在相关系列的数据手册第2卷中。
例如PCH series 200 datasheet vol 2。
SMBus主机在使用前必须进行配置,但这非常简单。一旦配置完毕,就可以用来读取SPD数据。
其工作原理与访问任何其它I2C器件完全相同。
SPD EEPROM(当然可以有多个,每个DIMM一个)保留0x 50到0x 57的地址(在200系列PCH上)。
可以写入SPD,SMBus主机中存在禁用此类行为的位:
读取SPD数据后,即可配置MC,然后即可使用RAM。
这是FSP的 FspMemoryInit() 例程。
最后一步是配置路由。
这包括在存储器地址空间中设置RAM区域的末端(有关完整信息,请参阅PCH数据手册),以及在NUMA系统中设置源地址和目标地址解码器,以便通过QPI/UPI链路跨套接字路由存储器请求。
所有这些都是通过PCH中集成器件的PCI配置空间完成的。
在NUMA系统中,还需要启动其他应用程序处理器(每个插槽一个)来配置它们的内存控制器。
这是通过LAPIC发出的处理器间中断(IPI)完成的,LAPIC是每个CPU中的MMIO组件。
总结
固件执行的大致步骤为:
1.执行任何基本环境初始化(例如切换到32位模式)。
1.初始化缓存为RAM。
1.使用PCI枚举初始化PCH中的SMBus主机。
1.读取每个DIMM的SPD EEPROM。
1.使用SPD数据配置每个插槽的内存控制器。
1.配置PCH内存Map。
1.配置NUMA路由。
1 CPU不需要初始化,事实上,在调用FSP初始化例程时,许多代码已经被执行了。它们可能意味着对一些或多或少有文档记录的特性进行"微调"。
2这里不讨论它们,但简要介绍嵌入式控制器(用于笔记本电脑,用于台式机的硬连线逻辑)将在启动后打开(使用其集成ROM)其固件将使用GPIO来打开板的必要电源门。一个此门为PCH供电,一旦EC固件Assert正确的引脚,将启动其自己的固件(称为管理引擎固件,因为它与其余ME代码捆绑在一起,位于同一闪存ROM的ME区域内,该区域还包含BIOS代码,但从技术上讲,它是启动、BUP、模块)并重置芯片组。一旦芯片组准备就绪,它将AssertCPU的电源良好引脚,然后Assert重置/初始化引脚这将使CPU开始执行POST,然后,假设是具有TXT能力的CPU,微码从闪速ROM中取出固件接口表,并从其中取出SINIT ACM(系统初始化验证控制模块,它将设置 * 测量启动 * 所需的安全性)和可选的BIOS ACM(其将执行厂商特定的任务,可能包括引导、跳过遗留重置向量)。最终,BIOS ACM(如果在FIT中未找到BIOS ACM,则为微码)将跳转到重置向量。这是传统引导流程。请注意,ACM是在采用高速缓存作为RAM(见上文)的特制环境中执行的,遵循任何其他TXT启动的语义(请参阅Intel TXT规范)。
3根据Intel的说法,当
CD
被设置时,不会进行行替换。我认为这也不会在更高的缓存中来回移动行。xiozqbni2#
简短的回答...
BIOS目前是一个错误误用的术语。但是当你从AMI等购买BIOS时,它引导芯片/系统,并通过软件中断提供传统的基本输入/输出服务。
它是用一种高级语言编写的,所以编译时需要堆栈和RAM,所以芯片上有一些SRAM,用于完成引导过程。代码本身存储在主板上的闪存中。我不知道它是像MCU一样直接从闪存运行,还是以某种方式复制到RAM。
DRAM模块包含一个带有SPD数据的EEPROM(JEDEC标准),它告诉引导加载程序有多少DRAM,引导加载程序(BIOS)就是这样知道有多少RAM的。
对于UEFI,甚至BIOS,BIOS供应商、主板供应商、操作系统供应商,推动这一独特的PC兼容性标准符合他们的最佳利益,无论是正式还是非正式。UEFI改变了最初由英特尔推动的事情(EFI),但后来进入了一个社区。传统BIOS模式,我会争辩说,可能是由微软和英特尔在BIOS/主板上维护/执行的,因为如果他们激怒了这些公司,他们就不会在业务中。
靶区; DR
要知道,一个特定的主板和它上面的BIOS之间有着密切的关系。当你开发一个主板时,在pc世界里肯定有很多共性,或者在特定的英特尔芯片/插槽世界里或者AMD。但是仍然有一个原因,为什么你要制造另一个主板。由于历史和各种原因,BIOS供应商的数量非常非常少,如果你想有一个成功的好机会,你只需打电话给一个,支付你需要支付的费用,并得到一个BIOS。这不像我买了一台电脑,没有与任何人讨论它,我选择运行Windows,BSD,Linux或其他东西。处理器/芯片供应商(Intel或AMD)、BIOS供应商(AMI、Insyde Software、Phoenix Technologies等)和您(主板创建者)之间存在三方关系,如果有成功的希望,这也是主板供应商相对较少的原因。
BIOS所做的和提供的也有一个历史,我不一定要在这里介绍。
处理器需要从某种形式的非易失性介质启动。主板上的闪存。因此,从启动的Angular 来看,可以考虑微控制器,其中的代码从闪存运行。这并不意味着您必须完全从闪存运行,您可以运行一个小循环,将其复制到某个固定的SRAM。也可能是硬件为你将闪存读入RAM。我不是最新的关于目前的英特尔和AMD处理器如何工作,关于从复位和片上资源启动(只要付钱给BIOS的人,并按照参考设计和编程的闪存与字节从BIOS的人和它启动)。
DRAM/DDR是一个烂摊子,它可能需要几周到几个月才能让它正常运行,这不一定是一个微不足道的任务(新的IP等,现有/已知的IP和匹配的电路板布局设计,可能几个小时或几天)。无论哪种方式,由于成本和历史,我们习惯了我们的DRAM被插入模块,因此,你怎么知道外面有什么,也许这是你的问题。如果你在维基百科上搜索串行存在检测,你会看到JEDEC规范中模块向控制器/主机提供的信息。已知总线上的模块上有EEPROM或等效装置(我相信是I ² C),它包含了该模块的SPD信息,从这些信息中,你不仅可以发现内存的数量,还可以发现让DRAM为该模块上的特定DRAM芯片工作所需的几个定时设置(和DRAM技术/代DDR2,DDR3,DDR3L等)。秩/库,宽度等。它将包括一个到几个可能的速度以及。
主机端的软件,我们将其归为术语BIOS(想想bootloader),它对主板和处理器或将工作的有效处理器有着深入的了解。以及知道DRAM控制器的能力,并与DRAM模块所宣传的可能组合之一相匹配。所以例如模块可能支持2133,但是如果该速度被作为它将尝试的速度的模块支持,则主机控制器可以仅支持高达1666。
当然,BIOS不是一些手工编写的汇编,不需要RAM,只依赖于通用寄存器。所以,是的,必须有一些SRAM的地方,我不知道这些英特尔芯片在这个水平(同样,非常非常非常少的人以这种方式使用这些芯片),如果你看看支持Linux的ARM芯片,这些芯片上有一些SRAM。(非x86)芯片,DRAM缓存可以与其他一些片内SRAM一起用作此类工作的直接访问,因此可以使用这些片内SRAM引导芯片(用于堆栈和数据,从闪存运行代码,或从依赖项运行代码),则当DRAM启动时,高速缓存被重新配置为高速缓存,并且DRAM现在可用。然后引导装载程序继续完成它的工作,然后寻找包含操作系统的媒体,并加载和启动它。
这是在我看来越来越容易,例如,使AMD主板,有一段时间以来,看到英特尔亲自上来。他们都拉更多的以前的多芯片解决方案到一个芯片/多芯片模块。有很多黑魔法,如果你会根据历史正在主板业务。人们会期望英特尔或AMD有详细的信息需要了解这一点,但这是公开的(有多少人在做主板,有多少人在写BIOS,有多少人愿意支付支持合同,有多少人愿意购买开发板/参考设计)。无论哪种方式,虽然如果有公开可用的信息,你需要从英特尔或AMD开始。预计一定比例的答案在这些产品的几代产品中是通用的,而一些答案可能特定于某个特定产品产品。
因此,
有一个闪存,持有引导加载程序和董事会的设计是这样的,它符合处理器的引导要求,把闪存的内容在前面的处理器引导。确实需要一些芯片上的SRAM,以协助引导过程,是的。你如何确定多少DRAM是基于知识的DRAM芯片和控制器规格/参数。对于插件模块,我们习惯了,存在包含用于模块上的模块/芯片的SPD数据的小EEPROM或类似物,使得引导加载程序不仅知道存在多少总RAM,而且知道与该DRAM正确对话所需的许多定时参数。
应该有一些开源的BIOS在那里,如果我没记错的话,他们是有点过时,可能只限于什么主板,如果有的话,他们支持,他们可能只是一个实际的BIOS实现BIOS调用,而不是一个完整的吹x86引导加载程序旨在提出了主板.通常BIOS/引导加载程序是你刚刚购买的AMI或Insyde等,您选择设计的处理器可能会决定从哪个或哪些bios供应商中选择。AMI bios或其他的源代码价格高昂,而且有很长的法律协议。可能包括某种形式的"如果您丢失/泄漏代码,您同意让我们耗尽您的银行帐户来清理泄漏"。我怀疑闪存是可读的,你可以尝试反向工程,但我也怀疑它是编译代码而不是手工编写的asm,所以它不会那么容易阅读。最好只是理解的过程,并离开它。
如果你真的想体验从x86转移到ARM的过程,因为有开源的引导加载程序,在一定程度上有关于逻辑的文档(DRAM控制器、PCIe等都是从某个第三方购买的IP,有NDA,所以外围设备的细节只有一定比例是在TI、Broadcom或Allwinner等芯片中)。但至少Linux的开源init代码和驱动程序,这比拆卸一些东西要好。你可以拿起一个BeagleBone Black或者树莓派(糟糕的例子; DRAM是在GPU中完成的,虽然我认为现在是开放的,但在某种程度上是非零的)或无数的基于全赢者的板。
在高层次上,体验和过程是相同的,引导、初始化、加载操作系统、启动操作系统。DRAM初始化、PCIe初始化、USB初始化、以太网在正确的时间发生,以便引导和启动。技术是相同的(DDR2、DDR3、DDR4、PCIe Gen 1、2、3、USB 1、2、3等),在某些情况下,购买的IP是相同或相似的,等等。
是的,bios意味着基本的i/o服务,历史上是一堆基于软件中断的处理程序,以通用的方式做一些事情,如与视频或与硬盘等,以便当你买了一个视频卡,它的物理上有一个rom上包含视频bios的视频卡,有一个程序,通过这个程序,BIOS被连接到系统中,这样当你调用一个int系统调用程序时,它会使用闪存上的代码,硬盘控制器也是如此,读取一个扇区的想法并不要求您必须知道软盘驱动器控制器或硬盘控制器的详细信息,您有这些高级系统调用。
术语BIOS和CMOS已经有点超载,不仅包括什么是BIOS(其可能还包括引导加载程序,原来的源代码是/可在原来的英特尔PC手册,我仍然有一个奠定了从我原来的PC(我没有任何更多的可悲)).今天的操作系统依赖于BIOS或EFI得到这个通用的我不'我不需要知道方法,但是一旦操作系统运行,它就会加载控制器特定的驱动程序,不再需要深入到BIOS系统调用中。这是一个非常基于PC历史的PC问题,非PC则采取更传统的方法。
因此,BIOS是/曾经是一些处理软件中断(系统调用)的代码,但我们也错误地或正确地应用该术语来包括引导加载程序。当你从AMI等购买“BIOS”时,它确实引导芯片。