Featured image of post 《操作系统真象还原》第二章——编写MBR主引导记录

《操作系统真象还原》第二章——编写MBR主引导记录

本文详细介绍了8086处理器的通用寄存器,包括AX、BX、CX、DX的8位和16位结构,以及它们在指令中的使用。同时讲述了BIOS启动过程中的内存初始化和寄存器设置,以及主引导扇区的作用。

本章节所有代码托管在

miniOS_32

本章节任务

上一节,我们完成了汇编调试器nasm和虚拟机bochs安装,本节我们将正式开始手写操作系统

任务简介

  1. 完成mbr主引导记录的代码编写,并完成编译,本节完成的代码编译并非真正的MBR主引导程序,而是为了测试mbr程序是否会被加载到0x07c00处被正确执行
  2. 将编译生成的主引导记录内容刻录到我们的创建的启动硬盘中

mbr主引导程序说明

  • mbr主引导记录程序必须存放在磁盘的0盘0道1扇区,这样BIOS程序才能进行加载

  • mbr主引导记录程序的文件大小必须是512字节

  • 程序的第510字节和第511字节处的内容必须是0x55和0xaa,BIOS程序会检查这两个字节标志来判断是否是mbr程序,否则BIOS不会加载该程序,即便它位于磁盘的0盘0道1扇区

前置知识

通用寄存器介绍

如图所示,8086处理器内部有8个16位的通用寄存器,都是由16比特组成的,并分别被命名为AX、BX、CX、DX、SI、DI、BP、SP。

这8个寄存器中的前4个,即AX、BX、CX和DX,又各自可以拆分成两个8位的寄存器来使用,总共可以提供8个8位的寄存器AH、AL、BH、BL、CH、CL、DH和DL。此外,将一个16位的寄存器当成两个8位的寄存器来用时,对其中一个8位寄存器的操作不会影响到另一个8位寄存器。

以寄存器AX为例,它可以分成两个独立的寄存器AH和AL。寄存器AX有16比特,但是,位0到位7这8比特属于寄存器AL;位8到位15这8比特属于寄存器AH。因此,我们说,寄存器AH是寄存器AX的高字节部分;寄存器AL是寄存器AX的低字节部分。同时,寄存器AX的内容也是由寄存器AH的内容和寄存器AL的内容组合而成的。

image-20241209233242700

虽说是通用寄存器,但是各个寄存器其实都用一些约定俗成的习惯用法,比如cx寄存器常用于循环指令中的循环计数,bx常用于基址寄存器等,以下是各寄存器的常用场景

image-20241209233918444

实模式下的内存布局

Intel 8086有 20条地址线,故其可以访问 1MB的内存空间,即2的20次方=1048576=1MB,地址范围若按十六进制来表示,是0x00000到0xFFFFF。

  • 地址0~0x9FFFF处是DRAM,也就是理论上的内存

  • 顶部的 0xF0000~0xFFFFF,这64KB的内存是ROM,其中存的是BIOS的代码

    • BIOS 的主要工作是检测、初始化硬件,同时建立中断向量表,因此可以通过“int 中断号”来实现相关的硬件调用,当然 BIOS 建立的这些功能就是对硬件的 IO 操作

地址总线宽度决定了可以访问的内存空间大小,如16位机的地址总线为20位,其地址范围是1MB,32位地址总线宽度是32位,其地址范围是4GB。

但在计算机中,并不是只有插在主板上的内存条需要通过地址总线访问,还有一些外设同样是需要通过地址总线来访问的。

若把全部的地址总线都指向物理内存,那其他设备该如何访问呢?由于这个原因,只好在地址总线上提前预留出来一些地址空间给这些外设用,这片连续的地址给显存,这片连续的地址给硬盘控制器等。留够了以后,地址总线上其余的可用地址再指向 DRAM,也就是指插在主板上的内存条、我们眼中的物理内存。示意如图所示。

image-20241209234239572

完整内存布局如下

image-20241209234259367

计算机的启动过程

计算机的加电和复位

计算机开始启动时,就会给处理器进行加电,同时处理器此时就会执行硬件初始化,然后将内部寄存器的内容初始化到预置的状态。

在INTEL8086处理器中,初始化操作将使代码段寄存器(CS)的内容置为0xFFFF,其他所有寄存器中的内容全部为0x0000,包括指令指针寄存器(IP)

这是有原因的,因为此时CS=0xFFFF,IP=0x0000,因此处理器要取的第一条指令就位于0xFFFF0,这正是BIOS的入口地址。如图所示,正好位于ROM中,而ROM中固化了开机时需要的指令。

image-20241209234527776

另一方面,处理器取指令执行的自然顺序是从内存的低地址往高地址推进。如果从0xFFFF0开始执行,这个位置离1MB内存的顶端(物理地址0xFFFFF)只有16字节的长度,一旦IP寄存器的值超过0x000F,比如IP=0x0011,那么,它与CS一起形成的物理地址将因为溢出而变成0x00001,这将回绕到1MB内存的最底端。所以,ROM中位于物理地址0xFFFF0的地方,通常是一个跳转指令,它通过改变CS和IP的内容,使处理器从ROM中的较低地址处开始取指令执行。如:

1
jmp 0xf000:0xe05b

在这里,“jmp”是跳转(jump)的简化形式;0xf000是要跳转到的段地址,用来改变CS寄存器的内容;0xe05b是目标代码段内的偏移地址,用来改变IP寄存器的内容。因此,目标位置的物理地址是0xfe05b。一旦执行这条指令,处理器将开始从指定的“段:偏移”处开始重新取指令执行。 ROM-BIOS的容量是有限的,当它完成自己的使命后,最后所要做的,就是从辅助存储设备(如硬盘)读取指令数据,然后转到那里开始执行。

**硬盘的第一个扇区是0面0道1扇区,或者说是0头0柱1扇区,这个扇区称为主引导扇区。**如果计算机的设置是从硬盘启动的,那么,ROM-BIOS将读取硬盘主引导扇区的内容,将它加载到内存地址0x0000:0x7c00(也就是物理地址0x07C00),然后用一个jmp指令跳到那里接着执行。

1
jmp 0x0000,0x7c00

上述过程的图解如下

image-20241209235010997

mbr初步实现

创建mbr.S文件

  • 文件功能:在屏幕上打印字符串“1 MBR”,背景色为黑色,前景色(字体颜色)为绿色

  • 功能实现方式:借助BIOS建立好的例程0x10号中断,可将0x10号中断看做一个函数,函数可根据不同的输入可实现不同的功能,因此要有一个参数表示要实现的功能,也就是接下来要说的功能号参数

    • 0x10号中断调用方式:
      • 将功能号送入ah寄存器,不同的功能号代表不同的功能
      • 其余输入参数可依据具体的功能将参数送入不同的寄存器即可

内容如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
;主引导程序MBR,由BIOS通过jmp 0:0x7c00跳转
 
;------------------------------------------
;vstart=0x7c00表示本程序在编译时,起始地址编译为0x7c00
SECTION MBR vstart=0x7c00
;由于BIOS是通过jmp 0:0x7c00跳转到MBR,故此时cs为0
;对于 ds、es、fs、gs 这类 sreg,CPU 中不能直接给它们赋值,没有从立即数到段寄存器的电路实现,只有通过其他寄存器来中转,这里我们用的是通用寄存器ax来中转。
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov fs,ax
;初始化栈指针
    mov sp,0x7c00
;------------------------------------------
 
 
;------------------------------------------
;利用0x06号功能进行清屏
;INT 0x10 功能号:0x06 功能描述:上卷窗口清屏
;输入:
    ;AH 功能号:0x06
    ;AL=上卷的行数(如果为0,则表示全部)
    ;BH=上卷的行属性
    ;(CL,CH)=窗口左上角(X,Y)位置
    ;(DL,DH)=窗口右下角(X,Y)位置
;返回值
    ;无返回值
    mov ax,0x0600
    mov bx,0x0700
    mov cx,0        ;左上角(0,0)
    mov dx,0x184f   ;右下角(80,25),VGA模式下,一行只能容纳80个字符,共25行
                    ;下标从0开始,所以0x18=24,0x4f=79
    int 0x10        ;调用BIOS中断函数
;------------------------------------------
 
;------------------------------------------
;获取光标位置
;功能号:3
;输入:
    ;ah:3
    ;bh:存储待获取光标的页号
;输出:
    ;ch=光标开始行,cl=光标结束行
    ;dh=光标所在行号,cl=光标所在列号
    mov ah,3
    mov bh,0
 
    int 0x10
;------------------------------------------
 
;------------------------------------------
;打印字符串,使用13号子功能
    mov ax,message
;es:bp为串首地址,es与cs的值一样,开始已经被初始化
    mov bp,ax
;光标位置使用dx中的内容,cx中的内容可忽略
;cx为串的长度,不包括结束符0的字符个数
    mov cx,5
;ah=13表示使用13号子功能
;al=01表示设置写字符的方式,其中01表示显示字符串并跟随光标移动
    mov ax,0x1301
;bh存储要显示的页号,此处是第0页(这里的页就是屏,因此bh指定的第0页就是第0屏的意思)
;bl是字符属性,02h表示黑底绿字
    mov bx,0x2
    int 0x10
 
    jmp $
;定义打印的字符串
    message db "1 MBR"
 
;$表示该行指令地址,$$表示该段(section)的开始地址,因此($-$$)表示程序总共占用的字节大小
;由于mbr程序的最后两个字节的内容(标志位0x55,0xaa)是固定的,而一个扇区总共是512字节,
;因此要把前510(512-2)个字节的内容给填满。
;故times 510-($-$$) db 0这段代码就表示用0将本扇区剩余空间进行填充
    times 510-($-$$) db 0
    db 0x55,0xaa

编译mrb.S文件

1
nasm -o mbr.bin mbr.S

将文件内容写入0盘0道1扇区,注意一定要用绝对路径

1
dd if=/home/minios/osCode/miniOS_32/ch2/mbr.bin  of=/home/minios/bochs/hd60M.img conv=notrunc
1
2
3
1+0 records in
1+0 records out
512 bytes copied, 0.00255388 s, 200 kB/s

dd命令可用于将一个文件写入到磁盘中,其中

  • 选项if表示输入文件位置,即被写的文件,of表示要将输入文件写入到哪里,~/bochs是笔者的bochs安装目录

  • notrunc 表示 “no truncate”,即不截断文件。这意味着如果输出文件(/home/minios/bochs/hd60M.img)的大小小于输入文件(/home/minios/osCode/mbr.bin)的大小,那么输出文件将不会被截断,而是保留原有的大小,并且只覆盖输出文件的前面部分。

启动bochs查看结果

进入bochs安装目录,输入以下指令,注意运行时一定要在bochs安装目录下

1
./bin/bochs -f boot.disk

老规矩,启动后首先按下数字6,然后再按键盘c,成功之后就会看到屏幕显示的字符串“1 MBR”

这就说明计算机启动后成功把我们的mbr程序加载到内存并运行成功了

image-20241210001730283

参考

《x86汇编语言:从实模式到保护模式》

《操作系统真象还原》

网站已运行
发表了20篇文章 · 总计 138,209字