您的位置:首页 > 其它

操作系统开发系列——Bootloaders

2016-05-16 14:43 225 查看

简介

欢迎光临!本章教程相信您已期待很久。本章我们将涵盖许多话题,如:

启动过程——它是如何工作的
Bootloader理论
开发一个简单的Bootloader
用NASM来汇编这个Bootloader
使用VFD(Virtual Floppy Drive)软件;创建一个软盘镜像
使用PartCopy;将我们的Bootloader复制到软盘镜像
使用Bochs——基本的安装和使用;测试这个Bootloader

准备好了吗?

启动过程

按下电源按钮

当你按下电源按钮,实际上发生了什么?当这个按钮被按下,连接到这个按钮的线向主板发送一个电子信号。主板仅仅重定向这个信号到电源(power supply)(PSU)。

这个信号包含了单个位的数据。如果它是0,那么,当然,没有电源(所以电脑是关闭的,或者主板是失效的(the motherboard is dead))。如果它是1(意味着一个有效的信号),这意味着电源已经正在供电。

为了更好地理解它,记住计算机中的二进制逻辑的基础。8个“位”仅仅代表8根电流能够通过的“线”。0表示当前没有电流,而1表示当前电线中有电流。这个再加上逻辑门,就是数字逻辑电子学的基础,计算机就是在其基础上建立起来的。

当电源接收到活动信号,它就开始给系统其余部分供电。当给所有设备提供了正确数额的电压,电源将能够继续补充那些没有重大问题的电源。

接着电源发送一个信号,一个被称为“power_good”的信号,给主板中的基本输入输出系统(BIOS)。

BIOS自检(POST)

当BIOS接收到”power_good”信号,BIOS就会开始一个成为自检(Power On Self Test)的过程。自检会测试提供的电压以确保足够,会测试安装的设备(例如键盘,鼠标,USB,串口等等),还有保证内存可用(通过测试内存是否损坏)。

接着自检会将控制权交给BIOS。自检将BIOS加载到内存的结尾(可能是0xFFFFF0)并且将一个跳转指令放在内存的第一个字节。

处理器的指令指针(CS:IP)被设为0,接着处理器获得控制权。

这意味着什么?处理器开始执行地址0×0的指令。在这里,是被自检所放的跳转指令。这个跳转指令会跳到0xFFFFF0(或者其他BIOS被加载的位置),接着处理器开始执行BIOS。

BIOS获得控制权…

BIOS

基本输入输出系统(BIOS)会做这么几件事。它创建一个中断向量表(IVT),并提供一些基本的中断服务。然后BIOS会做一些测试以确保没有硬件问题。BIOS也提供一个设置工具。

接着BIOS需要找到一个操作系统。基于你在BIOS中所设的引导顺序,BIOS会执行中断(INT)0×19来试着找到一个可引导的设备。

如果没有发现引导设备(INT 0×19返回),BIOS接着试引导顺序中所列的下一个设备。如果没有设备了,它会打印一条错误就像“没有发现操作系统”这样的然后停止系统。

中断和中断向量表(IVT)

一个中断就是一个可以通过许多不同程序运行的子程序。这些中断被存放在地址0×0开头的一个成为中断向量表的表里面。例如,一个常用的中断是DOS中使用INT 0×21。

备注:这里没有DOS!“仅有”的可用中断是由BIOS提供的,除此之外没有别的了!使用其他的中断将会引起系统执行一个不存在的例程,导致你的程序崩溃!

备注:如果你转换处理器模式,IVT将不再有效。这意味这绝对“没有”中断——既无软件中断也无硬件中断可用,甚至没有BIOS中断。对于一个32位的操作系统,我们不得不这样做。虽然现在还没有!

BIOS中断0×19

INT 0×19 – SYSTEM:BOOTSTRAP LOADER

通过热启动重启系统而并不清除内存以及恢复中断向量表

这个中断是被BIOS执行的。它读取第一个硬盘的第一个扇区(1扇区,0扇面,0磁道)。

扇区

一个扇区仅仅代表一组512字节。因此,扇区1代表一个磁盘的第一个512字节。

扇面

一个“扇面”(或者面)代表磁盘的一个侧边,扇面0是前侧,扇面1是后侧。大多数磁盘只有1个侧边,因此只有一个扇面(“扇面0”)。

磁道

为了理解磁道,我们看下面一张图:




在这张图中,这个磁盘可以代表一个硬盘或者一个软盘。这里我们看到的是扇面1(前侧),并且扇区代表512字节。一个磁道是扇区的集合。

备注:记住一个扇区是512字节,软盘上每个磁道有18个扇区。这在我们加载文件时是非常重要的。

如果这个磁盘是可引导的,引导扇区将会被加载到0x7C00,然后INT 0×19会跳到它,从而使bootloader获得控制权。

备注:记住bootloader被加载到0x7C00,这是非常重要的!

备注:在某些系统上,你也可以通过在地址0×0040:0072存储0×1234值,然后跳转到0xFFFF:0来执行一个热启动。要想冷重启,则存储0×0。

现在,我们的1337 bootloader已在控制之中。

Bootloader理论

我们已经谈到过很多次bootloader了。让我们把重要的部分集中起来!

目前为止,Bootloader…

…与主引导记录(MBR)一起存储
…在磁盘的第一个扇区
…它的大小是单个扇区(512)的字节数
…它被BIOS INT 0×19加载到地址0x7C00

你可以想象的到,在512字节中我们并不能做太多,那我们该怎么办呢?

在汇编语言中,我们可以非常容易地越过512字节的位置。因此,代码可能看起来很好,但仅仅只有部分会出现在内存中。例如,考虑这种情况:

1
mov ax, 4ch
2
inc bx              ;512 byte
3
mov     [var], bx   ;514 byte
在汇编语言中,程序从文件的顶部向下执行。然而,记住,当将文件载入内存时,我们加载的是扇区。每一个扇区是512字节,所以它只会将文件的512字节复制到内存中去。如果上面的代码被执行,而仅仅第一个扇区被加载到内存,它只会复制到512字节的地方(inc bx指令)。因此,最后的mov指令仍然在磁盘上,它不在内存中!

那么在inc bx指令之后处理器会做什么呢?它会继续执行到514字节的地方。因为它不再内存中,它会执行超过文件的结尾!结尾意味着什么?崩溃!

然而,加载第二个扇区(或者更多)到一个给定的地址并执行也是可能的。这样文件剩余的部分就会在内存中,然后所有的东西都会正常工作了。

这种方法可以工作,但是却很难实现。最通常的方法是保持bootloader的大小在512字节以内,搜索,加载并执行一个二级bootloader。我们后面会详细看看这个方法。

硬件异常

硬件异常很像软件异常,但处理器会执行他们,而非软件。

有些时候我们需要停止所有的异常,以防止他们发生。例如,在切换计算机模式时,整个中断向量表将不再有效,因此,任何硬件或软件中断,都会导致你的系统崩溃。后面会详细讲到。

CLI和STI指令

你可以使用STI和CLI指令来启用和禁用所有的中断。大多数系统禁止在应用程序中使用这些指令,因为它可能会导致大问题(虽然系统能模拟他们)。

1
cli     ;clear interrupts
2
3
;
do
something...
4
5
sti     ;enable interrupts--we're in the clear!

双重故障硬件异常(Double Fault Hardware Exception)

如果处理器在执行过程中发现了一个问题(如一个无效的指令,除以0等等),它就会执行一个二级故障异常处理程序(双重故障),也就是中断0×8。

我们将在不久之后看到一个双重故障。如果处理器在一个双重故障之后仍不能继续,则它会执行一个三重故障

三重故障(Triple Fault)

我们在以前看到过这个术语,不是吗?一个CPU的“三重故障”仅仅意味着系统硬重启。

在开始的几级,如bootloader,无论你的代码的任何地方有bug,系统都会引起三重故障。这表示你的代码中有个问题。

开发一个简单的Bootloader

我们等待的时刻终于到了!:)

让我们再一次看看我们的列表:

与主引导记录一起存储
在磁盘的第一个扇区
它的大小是单个扇区(512)的字节数
它被BIOS INT 0×19加载到地址0x7C00

打开任何一个普通的文本编辑器(我使用的Visual Studio 2005),记事本已经足够了。

下面是bootloader(Boot1.asm)…

01
;*********************************************
02
;  Boot1.asm
03
;      - A Simple Bootloader
04
;
05
;  Operating Systems Development Tutorial
06
;*********************************************
07
08
org     0x7c00              ;We are loaded by BIOS at 0x7C00
09
10
bits    16                  ;We are still in 16 bit Real Mode
11
12
Start:
13
14
cli                         ;Clear all Interrupts
15
hlt                         ;halt the 
system
16
17
times 510 - ($-$$) db 0     ;We have to be 512 bytes. Clear the rest of the bytes with 0
18
19
dw 0xAA55                   ;Boot Signiture
这些本不会带给我们太多惊喜。让我们一行一行分析:

1
org     0x7c00              ;We are loaded by BIOS at 0x7C00
记住:BIOS将我们加载到0x7C00。上面的代码告诉NASM以保证所有的地址都是相对于0x7C00。这意味着,第一条指令将会在0x7C00处。

1
bits    16                  ;We are still in 16 bit Real Mode
还记得第二章的内容吗?在那章中,我解释过x86家族是如何向后兼容那些老的DOS系统的。因为老的DOS系统都是16位的,因此所有x86兼容的计算机启动后进入16位模式。这意味着:

我们被限制在1MB的内存里
我们被限制只能使用16位的寄存器

我们需要将计算机转换为32位模式,我们将后面做这件事。

1
times 510 - ($-$$) db 0             ;We have to be 512 bytes. Clear the rest of the bytes with 0
我希望有更多关于这个的文档。在NASM中,美元符号($)代表当前行的地址,$$代表第一条指令的地址(应该是0x7C00)。因此,$-$$返回的是当前行到开始处的字节数(在本例中,就是程序的大小)。

1
dw 0xAA55                   ;Boot Signiture
这个需要一些解释。

记住,BIOS INT 0×19搜索可引导的磁盘。它怎么知道磁盘是可引导的呢?答案是引导标签。如果511字节是0xAA,并且512字节是0×55,那么INT 0×19将会加载并执行bootloader。

因为引导标签必须在bootloader的最后两个字节,所以我们使用times关键字来计算可以填充到第510字节的可变大小,而不是第512字节。

使用NASM汇编

NASM是一个命令行汇编器,因此必须通过命令行或者批处理脚本来执行。为了汇编Boot1.asm需要执行下面的命令:

1
nasm -f bin Boot1.asm -o Boot1.bin
-f选项用来告知NASM所生成的输出文件的类型。在本例中是二进制程序。

-o选项用来给出一个生成的文件的文件名。在本例中是Boot1.bin

汇编之后,你会得到一个精确的512字节名叫“Boot1.bin”的文件。

备注:由于某些原因,windows资源管理器显示的大小限制为1kb。查看文件的属性,它会告知为512字节。

如何使用VFD(Virtual Floppy Drive)

我们将会使用VFD来创建一个虚拟软盘镜像用于将我们的操作系统复制进去。现在解释如何使用它。

打开 vfdwin.exe
点击Driver选项卡下的开始按钮,它用于打开驱动器
点击Driver0或者Driver1选项卡。
点击打开

你将会看到如下所示的界面:





确保media类型为标准的3.5英寸1.44MB软盘,磁盘类型为RAM。同时,保证写保护为禁用状态。点击“创建”按钮。

打开我的电脑,(在你的电脑上)你将会看到一个新的软盘驱动器。

右键点击驱动器选择属性来格式化磁盘。在VFD选项卡下会有一个格式化选项。

PartCopy-复制到引导扇区

很好…现在我们的bootloader已经准备好了,我们如何把它复制到磁盘呢?你或许知道,Windows不允许我们直接将它复制到磁盘的第一个扇区。因此,我们需要使用一个命令来实现它。

在第一章我们已经看了一个这样的命令:debug。如果你已经决定使用这个命令,你可以略过关于partcopy的这一节。

PartCopy是一个命令行程序。它的使用形式如下:

1
partcopy file first_byte last_byte drive
PartCopy的功能不仅限于复制文件。它可以向或者从扇区复制特定的数量的字节。使用它的格式(上面显示的)是一种安全的方法。

因为你已经模拟了一个软盘驱动器,所以你可以通过一个字母名字来引用这个驱动器(就像A:)。

下面的命令可以复制我们的bootloader:

1
partcopy Boot1.bin 0 200 -f0
f0代表软盘0。你可以在f0和f1之间改变等等,根据你的软盘在哪个驱动器来决定。Boot1.bin是我们要复制的文件。它复制文件的第一个字节(0×0)到最后的一个字节(0×200,就是十进制的512)。注意partcopy仅仅接收16进制的数。

警告:记住如果你使用这个程序不小心可能会导致磁盘的永久性损坏。上面的命令行命令只适用于软盘,不要试图将其用于硬盘上。

[b]Bochs:测试bootloader[/b]

Bochs是一个32位的PC模拟器。我们将使用Bochs来调试和测试。

Bochs使用一个配置文件来描述所模拟的硬件。例如,下面是我所使用的配置文件:

01
# ROM and VGA BIOS images ---------------------------------------------
02
03
romimage:    file=BIOS-bochs-latest, address=0xf0000
04
vgaromimage: VGABIOS-lgpl-latest
05
06
# boot from floppy using our disk image -------------------------------
07
08
floppya: 1_44=a:, status=inserted  # Boot from drive A
09
10
# logging and reporting -----------------------------------------------
11
12
log
:         OSDev.
log
# All errors and info logs will output to OSDev.
log
13
error:       action=report
14
info:        action=report
配置文件使用#号来表示注释。它会试着从A盘的任何软盘镜像(比如说我们用VFD创建的那个)启动。ROM BIOS和VGA BIOS镜像来自Bochs,所以你不需要担心它。

定位BIOS ROM

配置文件中的很多行都非常简单,然而有一行我们需要在这里看看:

1
romimage:    file=BIOS-bochs-latest, address=0xf0000
这一行告诉Bochs将BIOS放在内存(虚拟RAM)的什么地方。还记得BIOS的大小有可能不同吗?同样还记得BIOS必须在内存中的第一个兆字节的结尾处(0xFFFFF)结束吗?

因此,你需要改变这一行来重定位Bios。可以通过获取Bios镜像的大小来实现它(在你的Bochs目录中它应该被命名为BIOS-bochs-latest)。获取大小的字节数。

之后,简单的减法0xFFFFF – bochs文件的大小(字节)。这就是新的Bios地址,然后在那行上更新这个地址就可以将Bios移到它新的地址。

你可以用或者不用做这一步。如果你在Bochs测试中获得一个关于Bios必须在0XFFFFF结尾的错误,这是你需要完成这一步它才会工作。

如何使用Bochs:

执行Bochs.exe
选择选项2(阅读选项列表);按回车键
输入配置文件名(我们上面创建的那个);按回车键
你将会回到主菜单。选择选项5:开始模拟,然后按回车键

一个新的窗口将会打开,下面就是你将会看到的:




如果Bochs仅仅退出或者重启

…那么你刚刚经历了一个三重故障(Triple Fault),回到代码中试着找找问题在哪里。如果你需要任何帮助,尽管联系我。

如果窗口出现了,但什么都没有

恭喜!那是我们的clihlt指令暂停了系统,所以我们知道我们的bootloader被执行了。

创建过程——总结

将我们已经做的与前面章节我们看到的创建过程相比较,你习惯它之后,你会觉得非常简单。

从现在开始,我不会再描述创建过程中的步骤的具体细节。

未完待续!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: