【Raymond-OS】Chapter 1. MBR的加载
【Raymond-OS】Chapter 1. MBR的加载
MBR的加载
主要内容
简言之,主板通电后有 1MB 的固定内存布局,BIOS 自动执行自检等逻辑,一切顺利后会对 0 号扇区的内容进行格式校验后加载到地址 0x7c00 处,并将其送上 IP 开始执行,于是,0 号扇区得名主引导扇区 MBR,我们在其中写的代码可以直接被执行。
一、过程分析
在开机启动阶段,系统做的内容是一些软硬件相互配合的固定动作:
- 系统加电,CS:IP被固定设置为F000:FFF0,运算后得到的实际执行地址是FFFF0,也就是上述的BIOS入口地址,至此BIOS开始执行。
- FFFF0到FFFFF之间的16B只有一条跳转指令:jmp f000:e05b,即跳转到FE05B继续执行,这个阶段可以理解为BIOS内部的执行逻辑。BIOS在执行过程中会执行硬件检查、创建中断向量表之类的事情。
- BIOS执行的最后阶段,它会检查0盘0道1扇区的内容,格式无误后会将其加载到7C00处。格式校验的内容:此扇区末尾的两个字节分别是魔数0x55和0xaa
- BIOS执行jmp 0:7C00,相当于正式移交了执行权。注意这里会将CS中的内容由之前的F000变为0000
此后可以认为是我们的操作系统开始执行
二、核心代码
;simple_mbr
;告知编译器以16位进行编译
;计算机从16逐渐发展而来,不同位数的系统内寄存器使用方式不一样
;MBR加载时所处的实模式相当于16位模式,明确使用16位可避免不知名情况
;如果不显示指定,默认会是bits 16
bits 16;
;告知编译器编码从0x7c00开始
;汇编中涉及到很多地址操作,而我们明确得知代码会被加载到0x7c00处,
;此处保持一致也是避免一些地址使用方面的异常情况出现,不过我们的代码中没有这种逻辑
org 0x7c00
;tag
start:
;利用10中断打印
mov ah, 0x0e
mov al, 'X'
int 0x10
;死循环阻止程序退出
hang:
jmp hang;
;字节填充,凑够一个扇区的内容,其中$取当前地址,$$取代码段开始地址
times 510 - ($-$$) db 0;
;魔数(扇区签名),和Java的cafebaby一样,用于格式校验
;MBR 的魔数其实是 0X55AA,但是 x86 是小端序,所以这里赋值的时候使用的是 0XAA55
dw 0xAA55;
三、相关内容
3.1 相关工具介绍
- nasm:汇编代码有不同的风格,nasm是其中一种,我们采用的是这种,所以需要nasm编译器将我们的源代码编译成机器码
- dd:帮助我们进行数据复制。我们的学习过程是使用虚拟机+磁盘映像,MBR扇区有位置要求,所以我们需要借助dd工具将我们的代码放到指定位置。win环境下可以找dd的win版本
- bochs:一款虚拟机,是我们主要的运行环境。注意不同版本的有区别。笔者win下用的是2.6.2,mac下用的是2.8,二者使用上有差别。
3.2 实模式下的内存布局及机器启动后的固定流程
起始地址 | 大小 | 用途 |
---|---|---|
0x00000 | 1KB | BIOS的中断向量表 |
0x00400 | 256B | BIOS数据区 |
0x00500 | 29KB+768B | 可用区域 |
0x07C00 | 512B | 引导扇区数据,被BIOS加载到此处 |
0x07E00 | 607KB+512B | 可用区域 |
0x9FC00 | 1KB | 扩展BIOS数据区 |
0xA0000 | 64KB | 彩色显示适配器缓存 |
0xB0000 | 32KB | 黑白显示适配器缓存 |
0xB8000 | 32KB | 文本模式显示适配器缓存 |
0xC0000 | 32KB | 显示适配器BIOS |
0xC8000 | 160KB | 映射硬件适配器ROM或内存映射式的IO |
0xF0000 | 64KB | BIOS程序,入口地址在0xFFFF0 |
3.3 关于大端序与小端序:
我们知道一个字节8位,假设我有一个数据0x1234,显然它需要占用两个字节,现在我有两个字节的地址B1和B2,其中B1地址小于B2地址,那么我应该如何存储?数据12放在B1还是B2?
第一种方式:B1 B2 :1234,将12放到了B1中,此时12是数据高位,B1是地址低位,这种将高位数据优先放入地址的方式称为大端序(近似理解为大数据优先)
第二种方式:B1 B2 : 3412,将34放到了B1中,此时34是数据低位,B1是地址低位,这种将数据低位优先放入地址的方式称为小端序(近似理解为小数据优先)
小端序的优点:强制数据类型转换的时候不需要调整字节了。比如说现在0x1234是我定义的一个对象,它放在B1和B2中,注意对象地址指向的是B1这个地址,此时我对它进行强制类型转化,从2字节变为1字节,那最终对象的地址其实还是B1,而B1中存放的是34,数据上也符合强制类型转化的规则,如果是大端序的话就需要重新调整。
大端序的优点:判断数据符号的时候方便。对于有符号数,符号放在高位中,根据大端序的特点,从对象的地址就可以直接计算出符号位。
常见大端序:IBM、Sun、PowerPC
常见小端序:X86、DEC
3.4 实模式下寄存器介绍
寄存器就是硬件层面的全局变量,起初是16位,后来是32位,再后来是64位,当向前兼容时,宽度大的寄存器会通过只使用低位的方式来模拟宽度小的寄存器。目前我们只需要看一下混个眼熟即可
寄存器 | 功能 |
---|---|
AX, BX, CX, DX, DI, SI, BP | 通用寄存器,按你的需要可以放数据、地址等 |
IP | 程序计数器,始终指向下一条指令地址 |
SP | 栈顶指针 |
CS, DS, ES, SS | 段寄存器。16位时代的寻址时段基址偏移+指令地址,段寄存器内存放的就是段基址 |
FLAGS | 控制寄存器,里面有复杂的控制位 |