问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

MBR引导程序简单实现

创作时间:
作者:
@小白创作中心

MBR引导程序简单实现

引用
CSDN
1.
https://blog.csdn.net/qq_31841383/article/details/137728140

想要进行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程序就写完了。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号