MBR引导程序简单实现

本文详细介绍了Linux内核开发中BIOS的作用,MBR在启动流程中的关键位置,以及如何通过编写MBR来加载内核。涉及BIOS的启动机制、地址0x7C00的重要性、MBR的结构和内核加载过程。
摘要由CSDN通过智能技术生成

想要进行Linux内核开发,就必须要对计算机的启动流程有一个大致的了解,知道系统开机后,它的内部到底做了什么事,我们的系统是怎么跑起来的,同时还需要学习MBR的作用,开机后BIOS检测内存、显卡等外设信息,完成初始化硬件的工作后,就将接力棒交由MBR来加载我们的内核,本博客的初步工作就是编写一个简单的MBR

MBR(主引导记录)是存储在计算机硬盘的第一个扇区中的一个特殊的引导扇区。它包含了启动计算机所需的信息,例如分区表和引导程序。当计算机开机时,BIOS会读取MBR中的引导程序,并将控制权交给它。引导程序再根据分区表信息加载操作系统的引导程序,最终启动操作系统。因此,MBR是计算机启动的关键。

目录

1.BIOS

2.BIOS的启动

3.地址0x7c00

4.MBR简介

5.自己写一个内核程序

1.制作一个镜像

2.bochsrc配置文件

3.boot.asm编译

4.运行和验证


1.BIOS

当我们按下计算机的power键,然后计算机上电启动,运行BIOS(基本输入输出系统),BIOS的存放位置在哪呢?其实这和实模式下的1MB布局有关。

BIOS程序是主板自带的,出厂之前烧录到主板的ROM里面的

众所周知,Intel 8086有20条地址线,即可以访问1MB的内存空间,其中1MB=2的20次方=1048576,也就是16进制下的0x00000—0xFFFFF。

这里比较重要的是画红框的两个地址,后面会讲到。

从以上的内存分布可以看到,地址0—0x9FFFF处是动态随机访问内存DRAM(动态随机访问内存,断电即丢失),也就是我们插在主板的内存条,但是,内存条的内存不等于全部的物理内存,而0xF0000—0xFFFFF这64KB内存则是ROM(只读存储),其中存放BIOS的代码(其实这里存储的只是跳转指令)。是 BIOS 的入口地址,此处的内容是 跳转指令 jmp f000: e05b 。其中 其中 0xFFFF0—0xFFFFF 这段 16 字节,BIOS 的作用是 检测、初始化硬件 和 建立中断向量表,这里的建立操作对硬件来说就是 IO 操作。

BIOS主要用来检测和初始化硬件,建立中断向量表。BIOS程序在内存最开始的位置(即 0x00000) 用1KB的内存空间(0x00000 ~ 0x003FF)构建中断向量表, 并在紧挨着它的位置用256 B的内存构建BIOS 数据区(0x00400 ~ 0x004FF),在大约56KB以后的位置(0x0E2CE)加载 8KB 左右的与中断向量表相应的若干中断服务程序

实模式(实地址模式)

计算机刚加电时处于实模式下
程序按照8086寻址方式访问0h-FFFFFh(1MB)空间
寻址方式:物理地址(20位)=段地址:偏移地址
CPU单任务运行
保护模式

计算机启动成功后处于保护模式下
寻址方式:段(32位)和偏移量(32位),寻址4GB空间
段页式寻址机制(段,页)
虚拟地址,进程,封闭空间
应用程序和操作系统的运行环境都被保护
CPU支持多任务

2.BIOS的启动

BIOS作为计算机上第一个启动的软件,是由只读存储器ROM加载的,其入口地址就是0xFFFF0;当CPU去执行BIOS时,其cs:ip值会组合成0xFFFF0。CPU访问内存采用的是分段访问机制,即段地址+偏移地址;由于在实模式下,段地址需要乘以16再和偏移地址相加,从而得到物理地址。而在开机的一瞬间,CPU的cs:ip寄存器就会被强制初始化为0xF000:0xFFF0,就此得到BIOS的入口地址0xFFFF0。由于BIOS是在实模式下运行的,而实模式只能访问1MB的空间,而0xFFFF0离1MB只有16字节了,并不足够存放BIOS的代码,因此物理地址0xFFFF0处存放的内容实际上是跳转指令(jmp f000:e05b),让CPU的执行流跳转到BIOS代码真正开始的地方。紧接着BIOS便会开始检测内存、显卡等外设信息,然后初始化硬件,在0x000—0x3FF处建立数据结构、中断向量表IVT并填写中断例程。到此,BIOS便完成了自己的使命,接下来进行接力,将执行权交给MBR

3.地址0x7c00

BIOS的最后一项工作是校验启动盘中位于0盘0道1扇区(也就是主引导记录MBR)的内容,如果此扇区末尾两个字节是0x55和0xaa,BIOS便会认为此扇区中确实存在可执行程序(包括内核程序),然后将可执行程序加载到地址0x7c00,也就是说,这些可执行程序(包括内核程序)就放在内存物理地址为0x7c00的位置上。

MBR主引导扇区位于磁盘的第一个扇区,即0号扇区,主要由引导代码、分区表、结束标志(0x55 0xaa)三部分构成,总共占512字节。

4.MBR简介

首先明确一点:主引导记录(MBR,Master Boot Record)是装有Linux系统的硬盘的第一个扇区,即C/H/S地址的0柱面0磁头1扇区,也叫做MBR扇区(来源百度百科)。所以MBR应该是一个硬件。

在这里我们简单介绍一下MBR。

计算机接电后运行的是BIOS,它完成检测和初始化工作后就会处理器使用权交给MBR。MBR位于整个硬盘最开始的扇区,称为MBR主引导扇区,其内容是:

446字节的引导程序和参数;

64字节的分区表;

2字节结束标级0x55和0xaa。

在得到控制权后,MBR开始寻找“次引导程序”(内核加载程序),然后再移交控制权。

MBR找到第一个活动分区后,会加载活动分区的代码。通常这段代码所在位置也是活动分区的第一个扇区,即512KB,这段代码通常被称为boot代码(boot.bin),这个扇区的最后两个字节也是0x55aa。最后MBR会将权限交给boot.bin,实际上boot.bin可以直接就是内核代码本身。

可以用下面这张简图,描述一下内核程序的加载运行流程。

5.自己写一个内核程序

本部分将做一个实验,验证上面的一系列说法。

MBR的大小必须是512字节,我们使用bochs模拟器,它模拟的是x86平台。

1.制作一个镜像

Linux命令行下输入bximage指令:输入1,回车——>输入fd,回车——>我选择的默认大小1.44M,直接回车——>输入镜像名:boot.img。

创建成功后,目录下就会生成boot.img文件,同时Linux会输出以上信息,注意上面画红框的内容,意思是说:“floppya:image="boot.img", status=inserted”这句话要出现在bochsrc配置文件中。

2.bochsrc配置文件

下面创建bochsrc配置文件,文件内容如下:

# 下面两个配置可以打开寄存器监视窗口
# magic_break: enabled=1
# display_library: x, options="gui_debug"
megs: 32
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
floppya: 1_44=boot.img, status=inserted
boot: a
floppya:image="boot.img", status=inserted
log: run.log
mouse: enabled=0
keyboard: keymap=$BXSHARE/keymaps/x11-pc-us.map

下一步,就开始些内核文件,命名为boot.asm。内容简单写一下,大概作用就是让内核程序打印出一句话——“hello,Linux!”。

ORG 0x7c00 ;程序开始的地址

    mov ax,cs ;使用cs初始化其他寄存器
    mov ds,ax ;通过jmp 0:0x7c00到的MBR开始地址
    mov es,ax ;此时ax为0,也就是用0初始化其他寄存器
    
    call MyPrint  ;打印函数
    jmp $   ;让程序死循环

MyPrint:
    mov ax,msg
    mov bp,ax
    mov cx,16
    mov ax,0x01301
    mov bx,0x000c
    mov dl,0
    int 10h    ;中断
    ret

msg: db "hello,Linux!"

times 510 - ($ - $$) db 0 ;填充剩下的空间,使生成的二进制代码为512字节
db 0x55, 0xaa  ;MBR结束标志

$和$$是nasm编译器中的关键字,用来表示当前行本section的地址,而该地址是由编译器确定的,默认情况下它们的值是相对于本文件开头的偏移量。

org是一个伪指令,这些伪指令并不是真正的汇编代码,而是给编译器使用的。

section定义的段

(PS:2.8段的概念_10000h到100ff单元组成的一个段,该段的所有有关信息有哪些-CSDN博客

$:代码当前位置

$$: 段当前的起始位置。

3.boot.asm编译

下面开始对boot.asm进行编译

nasm  boot.asm  -o  boot.o
dd  if=boot.o  of=boot.img  bs=512  count=1  conv=notrunc

dd是Linux的一个命令,用于磁盘操作,键入man dd可以看到该语句的帮助文件,简单来说该语句主要内容是:

if 指定要读取的文件

of 指定把数据输出到哪个文件

bs 指定块的大小

count 指定拷贝的块数

conv 指定如何转换文件

4.运行和验证

然后我们就可以运行这个MBR程序了!输入bochs  -q  -f  bochsrc,可以看到:

继续输入c,则会看到内核会打印出“hello,Linux!”。

 按ctrl+c,退出输入行,再输入exit,退出程序。

如果在bochsrc配置文件中开启下面这两个配置,就会开启寄存器监控窗口:

magic_break: enabled=1
display_library: x, options="gui_debug"

继续运行bochs  -q  -f   bochsrc,就能看到如下窗口,每个寄存器的数据信息就显示出来了,这对于后期继续调试寄存器很重要。

 CPU在运行程序时,一般是从内存中将数据读入到合适的寄存器,在寄存器中计算好结果,再将结果写会内存。

点击左上角的Continue,依然会显示:

 同时我们也寄存器窗口看到有这样一段信息: jmpf 0xf000:e05b。

jmp f000:e05b

它的意思是跳转到了(f000 << 4) + e05b = fe05b处,这里的段基址左移四位的原因是,在实模式下段基址寄存器只有16位,想一下,16位的寄存器最多访问2^16=64KB的空间,我们想访问实模式下1MB的空间的话就需要将段基址左移4位,自然就可以访问到1MB的空间了,这么做的原因也是出于兼容性而采取的曲线救国方式,虽然我们现在的OS都已经到了64位,它还得向下兼容。

当我们的电脑加电的一瞬间cs:ip就会被强制置位f000:e05b了,接下来就对内存,显卡等外设进行检查,做好它的初始化工作之后就完成它的任务了,在最后的时候,BIOS会通过绝对远跳,将控制权交给MBR,让它来加载内核。

jmp 0:0x7c00

 现在来验证一下:我们启动bochs模拟器,然后最开是左侧模拟器窗口完全是黑的,我们在右侧输入行不停输入:n,会看到随着输入的n个数变多,左侧模拟器窗口的设备图标变多了,这个过程应该就是系统在进行BIOS自检的过程。

 (输入4个n)

 (输入68个n)

随着输入n的次数变多,可以看到地址也在发生变化,这就是BIOS的自检过程,我们省略了中间的步骤,直接在输入行输入b 0x7c00,让程序运行地址直接来到0x7c00,然后再按c,继续连续按n,我们看到模拟器窗口打印出了“hello,Linux!”,而且也输出了我们编写的boot.asm代码。

最后我们来验证一下,boot.o文件的确为512字节:

 然后我们查看一下boot.o的内容,由于它是一个二进制文件,所以我通过clion中的BinEd插件查看(没有的话可以在插件商店下载一个)。

打开boot.o文件后,我们看到了,最后两个字节的确是55AA,文件前半部分是我们编写的代码,中间填充的都是0:


以上,基本的MBR程序就写完了。

  • 35
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值