从头开始编写操作系统(4) 第3章:引导加载器

来源:互联网 发布:新网互联域名证书生成 编辑:程序博客网 时间:2024/06/06 04:48

译自:http://www.brokenthorn.com/Resources/OSDev3.html

3章:引导加载器
by Mike, 2008, 2009

本系列文章旨在向您展示并说明如何从头开发一个操作系统。

介绍

欢迎!这正是您等待的一篇教程,在这一章包括的主题有:

  • 引导步骤——它是怎么工作的
  • 引导加载器理论
  • 开发一个简单的引导加载器
  • 使用NASM汇编器汇编引导加载器
  • 使用VFD(虚拟磁盘驱动器)软件:创建一个软盘映像
  • 使用PartCopy:将引导加载器复制到软盘映像
  • 使用Bochs——基本设置和应用:测试引导加载器

准备好了吗?

引导过程

当电源按钮按下时

当我们按下电源按钮是到底发生了什么?当这个按钮被按下后,连接到这个按钮的线缆会向主板发送一个电信号,主板简单的把这个信号转发给电源(PSU)。

这个信号只包含1比特信息。如果是0,表示没电(计算机关闭,主板不活动)。如果是1(活动信号),意味着系统已经加电。

为了更好的理解,记住计算机的基础是二值逻辑。8“比特”仅仅表示8条可以导电的“线缆”,0代表线上没有电流,而1表示线上有电流。这些与逻辑门,一起构成了数字逻辑点了的基础,而在此之上构建的整个计算机。

PSU收到这个活动信号,它开始向系统的其余部分供电。当所有设备都得到正确数量的供电时,就可以确定PSU会持续向它们供电而不发生大的问题。

PSU会发送一个“供电正常(power_good)”的信号到主板的基本输入输出系统 (BIOS)

BIOS POST

到那个BIOS接收到“power_good”信号,BIOS开始一个称为POST(Power On Self Test 加电自检)的初始化过程。POST通过测试确保供电正确,设备已安装 (如:键盘、鼠标、USB、串口等),并确保内存状态良好 (通过检测内存损伤)

POSTBIOS交出控制权。POSTBIOS加载到内存的末尾(可能是0xFFFFF0)并且在内存的第一个字节处放置一个跳转指令。

处理器指令指针 (CS:IP)被设置为0,然后处理器得到控制权。

什么意思呢?处理器会在地址0x0处开始执行指令。这里,它是一条POST程序放置的跳转指令,这条指令跳转到0xFFFFF0 (或者其他BIOS被加载到的地址),然后处理器开始执行BIOS

BIOS得到控制权……

BIOS

基本输入输出系统(BIOS)会做一些工作。它创建一个中断向量表(IVT), 并提供基本的中断服务。BIOS然后会做一些检查以确保没有硬件问题。BIOS也提供一个设置的功能。

BIOS需要找到一个操作系统。根据您在BIOS设置中指定的引导顺序,BIOS执行0x19号中断来找出一个可引导设备。

如果没有找到可引导设备 (INT 0x19 返回了)BIOS会尝试引导顺序列表中的下一个设备。如果再没有可供尝试的设备,BIOS会打印一个类似于“操作系统未找到”的信息,并停止系统的运行。

中断与中断向量表 (IVT)

一个中断是可以被许多不同的程序调用的子程序。这些中断被保存在从地址0x0开始的被称为中断向量表的空间中。比如,一个常见的中断INT 0x21被用于DOS系统。

注意:这儿没有DOS!“只有”BIOS设置的中断才有效,没有其他的!使用其他的中断会导致系统执行不存在的程序,这将导致你的系统崩溃。

注意:如果切换处理器模式,IVT会变得无效。这意味着,任何的中断(无论软硬件,包括BIOS)全都无效。对于32位操作系统,我们不得不这样做。

BIOS 0x19中断

INT 0x19——引导程序加载器

通过“热重启”重启系统,不会清空内存,也不会恢复中断向量表。

该中断由BIOS执行。它读入磁盘的第一个扇区(扇区(Sector 1,磁头(Head 0, 磁道(Track 0)

扇区Sectors

扇区即一个512字节的组,扇区1表示磁盘最前面的512字节数据。

磁头Heads

磁头(或“面”)表示磁盘的一面。磁头0是正面,磁头1是背面. 多数磁盘值由一个面,因此只有一个磁头。

磁道Tracks

为了理解磁道,看下面的图:

http://www.brokenthorn.com/Resources/images/platter%5b1%5d.gif

图中的磁盘代表硬盘或软盘,我们看到的是磁头0(正面),并且每扇区512字节。磁道是扇区的集合。

注意:记住1扇区是512字节,软盘的1磁道又18扇区,这一点在加载文件时很重要。

如果磁盘可引导,则引导扇会被加载到0x7C00 INT 0x19 会跳转到哪里,将控制权交给一点加载器。

注意:引导加载器会被加载扫0x7C00,这很重要!

注意:有些系统在按下“热重启”按钮,会在地址0x0040:0072处放置一个0x1234 再跳转到0xFFFF:0。冷重启则会用0x0代替。

现在,我们的1337 引导加载器得到控制权!

引导加载器理论

关于引导加载器我们已经看了不少。现在,把其中重要的部分放在一起。

引导加载器是……

  • ……与主引导记录(Master Boot Record (MBR))在一起。
  • ……在磁盘的第一个扇区。
  • ……大小是一个扇区 (512字节)。
  • ……已经被BIOSINT 0x19加载到地址0x7C00处。

如你所想,我们不能在512字节里做很多事情。我们要做什么呢?

在汇编语言中我们很容易超过512字节。尽管代码看起来不错,可是只有一部分在内存中,比如,想想下面的例子:

mov     ax, 4ch

inc     bx         ; 512 byte

mov     [var],bx  ; 514 byte

在汇编语言中,从上往下执行。但,要记得当向内存加载文件时,以扇区为单位。 每扇区512字节,因此只能复制几个512字节到内存中。

如果只有第一个扇区被加载到内存,我们仅仅复制到第512字节 (inc bx 那一行)。这样最后的mov指令仍在磁盘上,不在内存中!

那当处理器执行完inc bx 之后会做什么呢?处理器会继续执行第514字节。但是我们的代码不在内存中,它会跨过文件的末尾!结果呢?崩溃。

但是,再加载第二(或更多)扇区到指定地址并执行是可能的。这样文件中剩余的数据就会加载到内存中,会工作的很好。

这种方法是可行的,但难以使用。常见的方法是将引导加载器控制在512字节,用来搜索、加载和执行第二段引导加载器。这个我们会在后面看到。

硬件异常Hardware Exceptions

硬件异常与软件异常相似不过它由处理器而不是软件执行。

有时,我们必须避免所有异常的产生。比如,在我们切换处理器状态后,整个中断向量表失效,此时,任何的软硬件中断都会使我们的系统崩溃。我们在后面详细说明。

CLI STI 指令

您可以使用STICLI指令允许或禁止所有的中断,大部分系统不允许应用程序执行这两条指令,因为这可能会带来大问题(尽管系统可以模拟它们)。

cli            ; 禁止中断

 

; do something...

 

sti            ; 允许中断——我们前面禁止了!

双重错误(Double Fault)的硬件异常

如果处理器在处理异常的时候发现了问题(如,非法指令,除0,等),处理器会执行双重错误处理程序,即0x8中断。

我们在后面会看到一个双重错误。如果处理器在双重错误之后还不能恢复,它会执行一个三重错误(Triple Fault)。

三重错误Triple Fault

前面我们见到过这个条目吗?CPU的三重错误意味着系统重启。

在早期阶段,比如在引导加载过程中,你代码中的错误,会产生一个三重错误。这表明你的代码有问题。

开发一个简单的引导加载器

总算到了我们等的了!

让我们在看看我们的列表:

  • 与主引导记录(Master Boot Record (MBR))在一起。
  • 在磁盘的第一个扇区。
  • 大小是一个扇区 (512字节)。
  • 已经被BIOSINT 0x19加载到地址0x7C00处。

打开任意的文本编辑器(我使用Visual Studio 2005,Notepad(记事本)就足够了。

这里是引导加载器 (Boot1.asm)...

;*********************************************

;       Boot1.asm

;              - ASimple Bootloader

;

;       OperatingSystems Development Tutorial

;*********************************************

 

org            0x7c00                         ; 我们已经被BIOS加载到 0x7C00

 

bits    16                                    ; 我们在16位实模式

Start:

 

        cli                                   ; 禁止中断

        hlt                                   ; 系统停机

       

times 510 - ($-$$) db 0                              ;我们得有512字节,将剩余的部分清零

dw 0xAA55                                     ;引导标志

这些并没有什么令人兴奋的,下面我们一行行分析:

org            0x7c00

记住:BIOS把我们加载到0x7C00,上面的代码告诉NASM 确保相对地址为0x7C00。这表示,第一条指令在0x7C00

bits    16

还记得第2章吗?在那一章里,我解释了x86系列向后兼容老DOS系统。因为老DOS系统是16位的,所有的x86兼容机引导时为16位模式,也就是:

  • 我们受限在1 MB内存。
  • 我们受限在16为寄存器。

我们会在后面将计算机切换到32位模式。

times 510 - ($-$$) db 0

我希望这里有文档可以参考。在NASM中,美元符($)表示当前行的地址。$$表示第一条指令的地址(0x7C00)。所以,$-$$ 返回当前行到起点共有多少字节 (这里就是程序的大小)

dw 0xAA55

这需要一些解释。

BIOS INT 0x19会搜索可引导磁盘。那它怎么知道一个磁盘是否可以引导呢?因为引导标志。如果511字节是0xAA512字节是0x55INT 0x19会加载它,并执行引导加载器。

因为引导表示必须是引导扇的最后两个字节。我们使用times 关键字填充到第510个字节,而不是第512个字节。

使用NASM汇编

NASM是命令行汇编器,因此必须通过命令行或批处理脚本执行。汇编Boot1.asm 这么做:

nasm -f bin Boot1.asm -o Boot1.bin

-f选项用于告诉NASM生成哪种类型的文件,这里是二进制文件。

-o 选项由于给出输出文件名,这里是Boot1.bin

汇编之后你会得到一个名为"Boot1.bin"512字节的文件。

注意:因为一些原因Windows文件浏览器会显示文件的大小为1 KB,但查看文件属性时你会发现它确实是512字节。

使用VFD (虚拟软盘驱动器)

我们使用VFD来创建我们的操作系统要保存的虚拟软盘。下面解释如何使用它。

  1. 打开 vfdwin.exe.
  2. Driver标签下,点击Start按钮,以启动驱动器。
  3. 点击Drive0Drive1标签。
  4. 点击Open

你会看到这个:

http://www.brokenthorn.com/Resources/images/VFD.gif

确保Media Type(媒体类型)是stard3.5" 1.44 MB floppy(标准的3.5" 1.44 MB 软盘),并且类型是RAM。同样的,确保Write Protect(写保护)打开(不选中)。点击 "Create".

到“我的电脑”(在“您”的计算机上)您会见到一个新的软盘驱动器。

在驱动器图标上右击->属性,格式化软盘。在VFD 标签处会有一个格式化选项。

PartCopy——复制引导加载器

好,现在我们有了自己的引导加载器,怎么把它复制到硬盘中呢?你可能知道,Windows不允许我们将文件直接复制到磁盘的第一个扇区上,所以我们用一个命令来完成。

在第1章中我们见到了debug命令,如果你决定使用那个命令,请跳过这一节。

PartCopy是一个命令行出现,它使用下面的语法:

partcopy file first_byte last_byte drive

PartCopy不仅仅用于复制文件,它可以将制定的字节复制到扇区或从扇区复制出来,感谢它的语法(见上面),这是一个安全的方法。

因为你可以模拟软驱,你可以使用驱动器号(如A:)来代表驱动器。

要复制引导驱动器,这么做:

partcopy Boot1.bin 0 200 -f0

f0代表0号软驱。你可以使用f0f1等,这要根据你的软盘在那个驱动器里。Boot1.bin是我们要复制的文件。从第一个字节(0x0)复制到最后一个字节 (0x200,十进制的512)。注意partcopy只接受16进制的数据。

警告:如果使用不小心,可能会导致磁盘数据损坏。上面的命令值适用于软盘,不要在硬盘上尝试。

Bochs: 测试引导加载器

Bochs是一个32PC仿真器,我们使用它来调试和测试。

Bochs使用配置文件来描述要仿真的硬件。如下例,是我使用的配置文件:

# ROM VGA BIOSimages ---------------------------------------------

 

romimage:   file=BIOS-bochs-latest, address=0xf0000

vgaromimage: VGABIOS-lgpl-latest

 

# boot from floppy using our disk image-------------------------------

 

floppya: 1_44=a:, status=inserted  # Boot from drive A

 

# logging reporting -----------------------------------------------

 

log:        OSDev.log             # All errors and info logs will output to OSDev.log

error:      action=report

info:       action=report

配置文件使用#注释。它试图从一个在驱动器A中的软盘引导。

ROM BIOSVGA BIOS映像文件是和Bochs一起的,所以别为它担心。

定位BIOS ROM

配置文件中的大部分都很简单。有一行需要再看看:

romimage:   file=BIOS-bochs-latest, address=0xf0000

这行告诉BochsBIOS放到内存的什么位置。要知道BIOS的大小是可变的,BIOS必须放在1MB的末尾,即BIOS的最后一个字节必须在0xFFFFF

因此你可能需要改变BIOS的位置。这可以通过获得BIOS映像的大小(它在Bochs文件夹下的BIOS-bochs-latest)。这个大小以字节为单位。

这样从0xFFFFF减去BIOS文件的大小(以字节为单位)。这就是新的BIOS地址,更新这一行的address,把BIOS移到一个新位置。

你可能不需要这一步。如果你被告知“BIOS必须在0xFFFFF结束”时,你就要这么做了。

如何使用Bochs

使用Bochs:

  1. 执行bochs.exe
  2. 选择option 2 (Read options form);按回车。
  3. 输入配置文件名 (我们上面创建的那个); 回车。
  4. 你会返回到主菜单。选option 5:Begin Simulation(开始仿真),回车。

一个新的窗口会打开,你会看到:

http://www.brokenthorn.com/Resources/images/Bochs.gif

如果Bochs退出并重启了

……你有了一个三重错误的经历。返回到代码,找找哪里出了错。如果你需要帮助,联系我吧。

如果窗口出现,但什么也没发生

恭喜!这是我们的clihlt指令使系统停止了,我们的引导加载器在执行了。

构建步骤——总结

和我们在前一章里提到的构建步骤相比较,一旦你跟着做了,你会发现这很简单。

从此往后,我们将步骤详细重复这个构建步骤。

下次见

原创粉丝点击