本章主要讲解BSP的初始化过程,对应的代码为mbr.asm。
系统加电启动后,BIOS进行必要的初始化,并将硬盘的0扇区512字节的数据搬运到内存地址0x7c00处。之后,BSP的IP被置为0x7c00,开始运行。
初始化代码mbr.asm将顺序执行以下功能:
正确设置sp指针,保障函数调用的成功
利用BIOS中断,让CPU进入图形化模式
在全局GDT表中,安装正确的代码段和数据段
打开A20并设置CR0的PE位,使BSP进入32位保护模式
将BSP的32位保护模式下的执行代码,从硬盘的逻辑扇区2读取到内存的0x16000处
初始化BSP的页目录和页表
进行虚拟地址映射,将BSP的代码段从低地址段映射到2G以上
设置CR3寄存器指向页目录
映射GDT段的虚拟地址到2G以上
设置CR0寄存器,启动分页机制
重新调整BSP的esp堆栈指针
跳转到BSP真正的执行代码处(0x80016000),开始执行
下面,我们将对mbr.asm代码进行详细解释。
2.1 设置图形模式

整个代码段的起始地址为0x7c00。数据段段地址置为0,堆栈段段地址设为跟CS相同,同时栈指针指向0x7c00。堆栈指针默认向低地址移动,设置为0x7c00不会覆盖0x7c00之上的执行代码。

临时打开系统中断功能,通过BIOS的0x10号中断将系统设置成模式号为0x14C的VBE图形模式。该图形模式使用线性帧缓存区模式,分辨率1152×864,32位色。设置完成后先关闭系统中断功能,防止不明中断造成宕机。
2.2 进入32位保护模式

GDT段段地址已经在mbr.asm的下方初始化为gdt_global_addr(0x1000)。在这里,将GDT段段地址先赋予eax(实模式下可以使用32位通用寄存器),跳过GDT的0描述符(该描述符不被使用),设置描述符1为代码段。

GDT描述符格式
对于描述符1,低32位写入0x0000ffff,表示:
段基址(16~0)为0;
段界限(16~0)为0x0ffff。
高32位写入0x00cf9800,表示:
段基址(31~24)为0;
段以4K为单位;
段界限(19~16)为0x0f;(20位段界限为全1,单位4K,表示该段为4G大小)
段存在;
段的权限为0;
段类型为只执行的代码段;
段基址(23~16)为0。
接着设置描述符2为数据段。对于描述符2,低32位写入0x0000ffff,表示:
段基址(16~0)为0;
段界限(16~0)为0x0ffff。
高32位写入0x00cf9200,表示:
段基址(31~24)为0;
段以4K为单位;
段界限(19~16)为0x0f;(20位段界限为全1,单位4K,表示该段为4G大小)
段存在;
段的权限为0;
段类型为可读写数据段;
段基址(23~16)为0。
GDT段长度写入23,表明段中已有3个描述符(GDT段长度=描述符个数×8-1)。
最后将GDT段加载到GDT寄存器。
到此,BSP使用的代码段和数据段都已经加载到GDT段。

通过读写0x92号端口的方式打开第20号地址线(A20),之后,将CR0控制器的第0位置1启动保护模式。之后使用远跳转指令,CS加载GDT中第1号描述符(代码段),进入32位保护模式。
2.3 启动页式管理机制

此处通过[bits 32]来标明下面的代码已进入32位保护模式。各段寄存器统一加载GDT中的描述符2:数据段描述符。重置esp指针。

将BSP后续的执行代码从硬盘2号扇区开始,拷贝到地址0x16000处,连续拷贝16个扇区,共8K字节。
下面,初始化BSP的页目录和页表。

页目录页地址为0x00003000。先让页目录页的最后一项指向页目录自己,使之后的守护线程在更新页目录或页表时方便操作。如虚拟地址0x FFC0 0000(二进制 1111111111 0000000000 000000000000),指向页目录第0号页表的地址0。
页目录地址在写入页目录之前或上0x3,表示该页可读写,同时已存在。

初始化页表0。页表0的地址为0x00004000。这里是将从0x0地址开始的前1024页内存写入页表0,表示从0x0开始的前4M内存被映射到虚拟地址0x0~0x3FFFFF,且这些地址都可读写。

第0x380号页表对应地址为0x00005000。这一段的操作是将0xE0000000~0xE00FFFFF(4M)这一片区域的内存映射到虚拟地址0xE0000000~0xE00FFFFF,这4M内存是VBE图形缓存帧内存,在界面上画图就是对这块地址区域进行操作。该内存区域的起始地址0xE0000000对应二进制前10位为11100000 00,也就是为页目录的第0x380项。

第0x3BF号页表的地址为0x00006000。这一段操作是将0xFEC00000~0xFEEFFFFF(3M)这一片区域的内存映射到虚拟地址0xFEC00000~0xFEEFFFFF,这3M内存对应APIC的各类寄存器。APIC为高级可编程中断控制器,我们的对系统中断的配置都将通过APIC来实现。APIC的内存区域的起始地址0xFEC00000对应二进制前10位为001111111011,为页目录的第0x3FB项。

将页目录的第0项和第0x800项都指向页表0,这表示0x0~0x3FFFFF这4M的内存既可以通过虚拟地址0x0~0x3FFFFF,也可以通过虚拟地址0x80000000~0x803FFFFF来访问。

将第0x380号页表写入页目录。(0x380×4=0xE00)

将第0x3BF号页表写入页目录。(0x3BF×4=0xFEC)
到此,BSP的页目录、页表都已就绪。

将页目录地址写入CR3控制器。

调整GDT段的基地址,虚拟地址更改为0x80001000。在守护线程的操作中,所有地址都将在2G以上。

CR0控制器的最高位置1,正式启动页式管理。
2.4 跳转到BSP后续代码继续执行

将BSP使用的esp修改到0x80018000页的顶部。

跳转到0x80016000执行BSP后面的代码。
mbr.asm后面的read_hard_disk_0函数主要用于读取硬盘扇区,这里就不详细介绍了。可参考该文章如下:https://blog.csdn.net/scyatcs/article/details/82770853
mrb.asm源代码下载地址:https://download.csdn.net/download/hanspruce_bird/87502143
头文件源代码下载地址:https://download.csdn.net/download/hanspruce_bird/87502152