Learning MBR

PC启动过程

pc的启动过程是由BIOS和OS配合完成的
  1. 当按下电源键之后CPU初始化,初始化时CPU处于16位实模式的,从0xffff0开始执行第一行代码,这个地址被映射到BIOS,因为从0xffff0开始的地址只有16byte,所以BIOS在这里放了一段跳转指令,跳转到其他位置去执行自检POST功能。
  2. BIOS的POST是Power On Self Test的简称,就是加电自检。这个过程主要是对硬件进行检查,POST之后,BIOS调用int $0x19加载MBR。
  3. 加载并之行MBR,MBR是Master Boot Recode的简称,就是主引导记录。BIOS调用int $0x19,按照CMOS中设置的启动顺序,挨个设备查找MBR,如果没有找到MBR,BIOS会报错,一般的错误是"Operation System not found"。如果找到了MBR就将MBR加载到0x7c00地址,然后跳转到这个地址开始之行,至此BIOS就将控制权交给了MBR。

MBR

PC的启动过程中控制权会由BIOS移交到MBR,然后MBR在进一步的引导OS,但是MBR长什么样子呢?BIOS如何在众多的引导设备中识别MBR?
MBR位于引导设备的第一个扇区(0道0柱面1扇区),它必须是512字节并且最后两个字节必须是0xaa55,这两个字节是MBR的Magic Number,只有看到一个扇区的最后两个字节是0xaa55时,BIOS才认为这个扇区是MBR,否则不是。

BIOS中断和内存布局

BIOS的功能不仅仅是加载MBR这么简单,它还提供了很多中断供MBR以及后续的系统使用,同时对于最初的1M内存BIOS的布局是固定的。BIOS提供了很多实用的中断,int $0x13用于读写磁盘,int $0x10用于显式,加载MBR都是通过中断int $0x19实现的。

实例

这样看来引导一个OS的第一步就是MBR,因为MBR之前都是BIOS的工作,BIOS在固件里(当然你也可以修改BIOS让它以其他的方式引导OS那就另当别论了)。根据上面对于PC启动过程和MBR的描述就能够很简单的写一个MBR了。《自己动手写操作系统》这本书的作者在第一章就写了一个MBR的例子,但是用的nasm的语法,这里我想把它修改成AT&T格式的并且用gas编译。
.code16
.section .text
#.org 0x7c00
.globl _start
_start:
    movw %cs, %ax
    movw %ax, %ds
    movw %ax, %es

    movw $boot_message, %ax
    movw %ax, %bp
    movw $0x1301, %ax
    movw $16, %cx
    movw $0x0c, %bx
    movw $0x00, %dx

    int $0x10

    jmp .

boot_message:
    .ascii "Hello, OS world!"

dummy:
    .space 510-(.-_start), 0

magic_number:
    .byte 0x55, 0xaa

gas/nasm对比

这段代码要使用gas编译,gas使用AT&T语法,与nasm的语法有很大区别,下面的参考文章中详细的介绍了这些区别。

制作启动镜像

要将gas编译之后的二进制文件制作成启动镜像,写入软盘中,然后能够引导虚拟机或者真是的机器启动才算成功,必须采用下面的步骤来制作启动镜像:
as -o boot.o boot.s
ld -Ttext 0x7c00 -o boot boot.o
objcopy -O binary boot boot.bin
dd if=boot.bin of=boot.img count=1
最后得到的boot.img就是镜像文件,如果使用虚拟机验证最后的dd命令根本用不上,boot.bin直接就可以作为镜像使用了,但是如果你想写入真实的软盘或者U盘,dd命令就十分有用了。
《自己动手写操作系统》的例子使用nasm编译之后直接就可以当作镜像文件使用了,但是使用gas编译我们的例子却做不到这点,nasm命令nasm boot.asm -o boot.bin生成的就是无格式的二进制文件,但是gas编译之后的boot.o确实elf格式的。我们要从这里提取出无格式的二进制文件,首先用ld将.text段定位到0x7c00地址,这样代码内部的地址都是根据这个地址计算的了。然后使用objcopy提取无格式的二进制文件boot.bin。

将boot.img写入U盘来制作U盘启动盘,首先使用fdisk -l查看一下U盘设备,我的U盘被识别为/dev/sdb这样使用dd命令将boot.img写入U盘:
dd if=boot.img of=/dev/sdb count=1
写入的时候要小心,如果不慎将sda破坏就废废了。还有就是我在写入的时候犯了一个错误,但是我的U盘是格式化的被Linux识别为/dev/sdb1,我写入的时候就是用了of=/dev/sdb1这样的参数,结果在真是的机器上怎么也启动不了,后来才知道/dev/sdb和/dev/sdb1其实不是一个概念,使用of=/dev/sdb1并没有将boot.img写入到第一个扇区中。

测试

镜像制作好了,我们可以测试一下我们的代码是否能够工作,可以使用qemu+vnc终端来显式结果
/usr/libexec/qemu-kvm -fda boot.img -boot a -vnc :1
-vnc :1 表示vnc协议的地址是127.0.0.1:5901
结果如下图,证明我们的代码成功的启动了虚拟机:

调试

没有十全十美的东西,也没有十全十美的代码,任何代码都会出现问题,有了问题就需要解决,对于调试kernel来说qemu简直是神器。我们可以通过qemu+gdb来调试刚才的代码。
在一个终端中启动qemu虚拟机
qemu-kvm -fda boot.img -boot a -s -S
在另一个终端中启动gdb,并且连接qemu启动的gdbserver
(gdb) target remote localhost:1234
(gdb) set architecture i8086
(gdb) stepi
(gdb) b *0x7c00
(gdb) c

安装最新版本的qemu之后会有qemu-system-i386命令,这样就不存在断点挺不下的问题了。

参考

PC启动过程

BIOS

gas/nasm

手写x86操作系统,U盘启动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值