本文主要记录 Linux 0.11 内核引导程序概况以及简单阐述 BIOS 与内核相关部分。
Linux 0.11 内核引导程序概况
Linux 0.11 内核源码(或在线浏览)boot
目录下总共有 3 个文件:bootsect.s
、setup.s
和head.s
。其中,前两者用 Intel 8086 汇编语法写成,需要使用 Intel 8086 汇编编译器 as86 和链接器 ld86 来进行编译和链接,工作在实模式(16 位)
;最后者用 AT&T 汇编语法写成,需要用 GNU 的 as 和 ld 进行编译和链接,工作在保护模式(32 位)
。
对于内核引导程序工作概况,《Linux 内核完全注释》有很简明清晰的描述(简单整理):
- 当 PC 的电源打开后,80x86 结构的 CPU 将自动进入实模式,并从地址
0xFFFF0
开始自动执行程序代码,这个地址通常是 ROM-BIOS 的地址(注:这个说法有问题,请参照后文关于 BIOS 解释)。PC 机的 BIOS 将执行某些系统的检测,并在物理地址 0 处开始初始化中断向量。此后,它将可启动设备的第一个扇区(磁盘引导扇区,512 字节)读入内存绝对地址 0x7C00 处,并跳转到这个地方。启动设备通常是软驱或是硬盘。
- Linux 的最前面部分是用 8086 汇编语言编写的(bootsect 模块),它将由 BIOS 读入到内存绝对地址 0x7C00 (31 KB)处,当它被执行时就会把自己“移动”(描述有误,应该是
复制
)到内存绝对地址 0x90000(576 KB)处,并把启动设备后 2 KB 字节代码(setup 模块)读入到内存 0x90200 处,而内核的其他部分(system 模块)则被读入到从内存地址 0x10000 (64 KB)开始处,因此从加电开始顺序执行的程序如下图所示:
图片来源:《Linux 内核完全注释》
因为当时 system 模块的长度不会超过 0x80000 字节大小(即 512 KB),所以 bootsect 程序把 system 模块读入物理地址 0x10000 开始位置处时并不会覆盖在 0x90000 (576 KB)处开始的 bootsect 和 setup 模块。后面 setup 程序将会把 system 模块移动到物理内存起始位置处,这样 system 模块中代码的地址也即等于实际的物理地址,便于对内核代码和数据进行操作。下图清晰地显示出 Linux 系统启动时这几个程序或模块在内存中的动态位置:
图片来源:《Linux 内核完全注释》
此时所有 32 位运行方式的设置启动已完成: IDT、 GDT 以及 LDT 被加载,处理器和协处理器也已确认,分页工作也已设置好;最终 init/main.c 中的 main 函数被调用。这些操作的源代码在 boot/head.s 中。
到这里,我们可能有这样的疑问:为什么 bootsect 模块不直接把 system 模块加载到物理内存起始位置处而要由 setup 模块来完成?这是因为在随后执行的 setup 代码开始部分还需要使用 ROM BIOS 的中断调用来获取机器的一些参数(例如显示卡模式、硬盘参数表等)。当 BIOS 初始化时会在物理内存开始处放置一个大小为 0x400 字节的中断向量表,因此需要在使用完 BIOS 的中断调用后才能将这个区域覆盖掉。
仅在内存中加载了上述内核代码模块并不能让 Linux 系统运行起来。作为完整可运行的 Linux 系统还需要有一个基本的
文件系统
支持,即根文件系统。 Linux 0.11 内核仅支持 MINIX 的 1.0 文件系统。根文件系统通常是在另一个软盘或者一个硬盘分区中。为了通知内核所需要的根文件系统在什么地方,bootsect.s 程序的第 43 行上给出了根文件系统所在的默认块设备号。
计算机启动流程及 BIOS
计算机启动流程
关于计算机启动流程,PCGUIDE 网站有一个简明的说明:
The system BIOS is what starts the computer running when you turn it on. The following are the steps that a typical boot sequence involves. Of course this will vary by the manufacturer of your hardware, BIOS, etc., and especially by what peripherals you have in the PC. Here is what generally happens when you turn on your system power:
- The internal power supply turns on and initializes. The power supply takes some time until it can generate reliable power for the rest of the computer, and having it turn on prematurely could potentially lead to damage. Therefore, the chipset will generate a reset signal to the processor (the same as if you held the reset button down for a while on your case) until it receives the Power Good signal from the power supply.
- When the reset button is released, the processor will be ready to start executing. When the processor first starts up, it is suffering from amnesia; there is nothing at all in the memory to execute. Of course processor makers know this will happen, so
they pre-program the processor to always look at the same place in the system BIOS ROM for the start of the BIOS boot program.
This is normally location FFFF0h, right at the end of the system memory. They put it there so that the size of the ROM can be changed without creating compatibility problems.Since there are only 16 bytes left from there to the end of conventional memory, this location just contains a "jump" instruction telling the processor where to go to find the real BIOS startup program.
- The BIOS performs the power-on self test (POST). If there are any fatal errors, the boot process stops. POST beep codes can be found in this area of the Troubleshooting Expert.
- The BIOS looks for the video card. In particular, it looks for the video card’s built in BIOS program and runs it. This BIOS is normally found at location C000h in memory. The system BIOS executes the video card BIOS, which initializes the video card. Most modern cards will display information on the screen about the video card. (This is why on a modern PC you usually see something on the screen about the video card before you see the messages from the system BIOS itself).
- The BIOS then looks for other devices’ ROMs to see if any of them have BIOSes. Normally, the IDE/ATA hard disk BIOS will be found at C8000h and executed. If any other device BIOSes are found, they are executed as well.
- The BIOS displays its startup screen.
- The BIOS does more tests on the system, including the memory count-up test which you see on the screen. The BIOS will generally display a text error message on the screen if it encounters an error at this point; these error messages and their explanations can be found in this part of the Troubleshooting Expert.
- The BIOS performs a “system inventory” of sorts, doing more tests to determine what sort of hardware is in the system. Modern BIOSes have many automatic settings and will determine memory timing (for example) based on what kind of memory it finds. Many BIOSes can also dynamically set hard drive parameters and access modes, and will determine these at roughly this time. Some will display a message on the screen for each drive they detect and configure this way. The BIOS will also now search for and label logical devices (COM and LPT ports).
- If the BIOS supports the Plug and Play standard, it will detect and configure Plug and Play devices at this time and display a message on the screen for each one it finds. See here for more details on how PnP detects devices and assigns resources.
- The BIOS will display a summary screen about your system’s configuration. Checking this page of data can be helpful in diagnosing setup problems, although it can be hard to see because sometimes it flashes on the screen very quickly before scrolling off the top.
- The BIOS begins the search for a drive to boot from. Most modern BIOSes contain a setting that controls if the system should first try to boot from the floppy disk (A:) or first try the hard disk (C:). Some BIOSes will even let you boot from your CD-ROM drive or other devices, depending on the boot sequence BIOS setting.
- Having identified its target boot drive, the BIOS looks for boot information to start the operating system boot process. If it is searching a hard disk, it looks for a master boot record at cylinder 0, head 0, sector 1 (the first sector on the disk); if it is searching a floppy disk, it looks at the same address on the floppy disk for a volume boot sector.
- If it finds what it is looking for, the BIOS starts the process of booting the operating system, using the information in the boot sector. At this point, the code in the boot sector takes over from the BIOS. The DOS boot process is described in detail here. If the first device that the system tries (floppy, hard disk, etc.) is not found, the BIOS will then try the next device in the boot sequence, and continue until it finds a bootable device.
- If no boot device at all can be found, the system will normally display an error message and then freeze up the system. What the error message is depends entirely on the BIOS, and can be anything from the rather clear “No boot device available” to the very cryptic “NO ROM BASIC - SYSTEM HALTED”. This will also happen if you have a bootable hard disk partition but forget to set it active.
This process is called a “cold boot” (since the machine was off, or cold, when it started). A “warm boot” is the same thing except it occurs when the machine is rebooted using {Ctrl}+{Alt}+{Delete} or similar. In this case the POST is skipped and the boot process continues roughly at step 8 above.
加载和执行 BIOS
关于 BIOS,维基百科有一个简明扼要的定义:
The BIOS (an acronym for Basic Input/Output System and also known as the System BIOS, ROM BIOS or PC BIOS) is a type of firmware used to
perform hardware initialization
during the booting process (power-on startup) on IBM PC compatible computers, and toprovide runtime services for operating systems and programs
. The BIOS firmware is built into personal computers (PCs), and it is the first software they run when powered on.
从上边的分析中我们知道,Linux 内核首先是需要由 BIOS 来加载的,但 BIOS 要由谁来加载呢?
在计算机刚开机时,内存空空如也,而 CPU 也只能运行内存中的程序,那 CPU 如何能够执行 BIOS 程序?
请看下文。
母板
Motherboard Chipsets and the Memory Map 是一篇特别赞的文章。作者在博文中展示了以 Intel Core 2 为处理器的计算机母板(motherboard):
图片来源
内存映射 I/O
我们知道计算机刚开机时是工作在实模式的,最大的寻址范围是 1MB (0~0xFFFFF)。实模式意味着线性地址等于物理地址。另外,BIOS 作为计算机执行的第一个程序,也是在实模式下被执行的。而且,BIOS 既然能够被执行,就意味着能够被寻址。所以,BIOS 能被寻址的线性地址(或物理地址)范围必定在 1MB 以内。
但是问题就来了,BIOS 被固化在 ROM (或其他如 Flash、EEPROM) 上,但 CPU 并不能够“直接”对 ROM 寻址,也就是说 CPU 执行不了 BIOS 程序。不过既然不能“直接”寻址 ROM,我们可以“间接”寻址啊,哈哈!这就是我们接下来要介绍的 内存映射 I/O (Memory-mapped I/O)。
Memory-mapped I/O 说白了就是将母板上的一些设备(如 ROM、Video cards、PCI cards)的端口映射到内存 RAM 中的某个区域,这样 CPU 通过寻址 RAM 的这些区域可以间接寻址这些设备,也就是说能够执行这些设备上的程序。注意现在计算机是工作在实模式,只要 BIOS 固件被映射到的地址范围在 1MB 以内,计算机通过直接寻址 RAM 就来间接寻址 BIOS,从而达到执行 BIOS 的目的。
Motherboard Chipsets and the Memory Map对具体的寻址过程有一个简要描述:
When the northbridge receives a physical memory request it decides where to route it: should it go to RAM? Video card maybe? This routing is decided via the memory address map.
For each region of physical memory addresses, the memory map knows the device that owns that region.
The bulk of the addresses are mapped to RAM, but when they aren’t the memory map tells the chipset which device should service requests for those addresses. This mapping of memory addresses away from RAM modules causes the classic hole in PC memory between640KB and 1MB
.
另外,作者还举了一个内存映射 I/O的例子:
图片来源
注:上图中 BIOS 还映射到 1MB 以外的内存空间。这个主要是因为随着计算机发展,BIOS 越来越复杂,所需映射的空间也越来越大,640KB~1MB 的空间已显得不足。在这里我们暂时忽略。
Reset Vector
Reset Vector 实际是一个物理地址,它跟内存末端距离 16 个字节,存放着一个 JMP 或者 CALL 指令。对于 4G RAM,Reset Vector 大小为 FFFFFFF0H。CPU 通过执行该地址处的指令就能够跳转到 BIOS 被映射到 RAM 的入口地址(0xF0000),开始执行 BIOS 程序。
博文 Motherboard Chipsets and the Memory Map对此也有一个简要描述:
he motherboard ensures that the instruction at the reset vector is a jump to the memory location mapped to the BIOS entry point. This jump implicitly clears the hidden base address present at power up. All of these memory locations have the right contents needed by the CPU thanks to the memory map kept by the chipset. They are all mapped to flash memory containing the BIOS since at this point the RAM modules have random crap in them. An example of the relevant memory regions is shown below:
当然,来自 Intel 80386 官方手册的解释就来的更为权威:
After RESET, address lines A{31-20} are automatically asserted for instruction fetches. This fact, together with the initial values of CS:IP, causes instruction execution to begin at physical address FFFFFFF0H. Near (intrasegment) forms of control transfer instructions may be used to pass control to other addresses in the upper 64K bytes of the address space.
The first far (intersegment) JMP or CALL instruction causes A{31-20} to drop low, and the 80386 continues executing instructions in the lower one megabyte of physical memory. This automatic assertion of address lines A{31-20} allows systems designers to use a ROM at the high end of the address space to initialize the system.
不过需要注意的是,在 Linux 0.11 环境,计算机内存值有 16MB,此时 Reset Vector 大小为 0xFFFF0(其中CS=0xF000、IP=0xFFF0)。
到此,也算是把前面的两个问题解决了。对这两个问题,个人其实花了很长时间才算真正解决。下边将参考过的资料列写如下:
- Motherboard Chipsets and the Memory Map
- How Computers Boot Up
- The Kernel Boot Process
- Intel 80386 官方手册
- Quora 上问题 How is BIOS program loaded into memory when PC just boots and the memory is clean (‘clean’ means nothing has been loaded into the memory)?(自己提问的,哈哈!)的一个来自 Phillip Remaker 的解答
- StackOverflow 上的一个解答
前 3 个资料的作者是 Gustavo Duarte。这个作者真的很赞,博文写的特别认真,而且还易懂透彻。以防链接失效,这 3 篇文章特备份到百度云上。
BIOS 加载中断向量表和中断向量服务程序
BIOS 除了上电自检(Power-on self test,POST),最重要的莫过于建立中断向量表以及中断向量服务程序。
按《Linux 内核设计的艺术》一书说法:
BIOS 程序在内存最开始的位置(0x00000)用 1KB 的的内存空间(0x00000
0x003FF)构建中断向量表,在紧挨着它的位置用 256 个字节的内存空间构建 BIOS 数据区(0x004000x004FF),并在大约 57KB 以后的位置(0x0E05B)加载了 8KB 左右与中断向量表相应的中断服务程序。下图标注了这些位置:
图片来源:《Linux 内核设计的艺术》
关于中断向量表,可参考之前博文 Linux 内核学习笔记:预备知识之“中断和异常”。