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

译自: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 、串口等) ,并确保内存状态良好 ( 通过检测内存损伤)

POST BIOS 交出控制权。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

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

图中的磁盘代表硬盘或软盘,我们看到的是磁头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

;              - A Simple Bootloader

;

;       Operating Systems 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

你会看到这个:

确保Media Type (媒体类型)是stard 3.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 BIOS images ---------------------------------------------

 

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 BIOS VGA 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 (开始仿真),回车。

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

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

如果Bochs 退出并重启了

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

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

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

构建步骤——总结

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

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

下次见

你可能感兴趣的:(汇编,report,dos,测试,byte,磁盘)