内核加载程序(写在MBR上的引导程序)
boot.s
BOOTSEG = 0x07c0
SYSSEG = 0x1000
SYSLEN = 17
entry start
start:
jmpi go,#BOOTSEG
go: mov ax,cs
mov ds,ax
mov ss,ax
mov sp,#0x400
load_system:
mov dx,#0x00000
mov cx,#0x0002
mov ax,#SYSSEG
mov es,ax
xor bx,bx
mov ax,#0x200+SYSLEN
int 0x13
jnc ok_load
die: jmp die
ok_load:
cli
mov ax, #SYSSEG
mov ds, ax
xor ax, ax
mov es, ax
mov cx, #0x2000
sub si,si
sub di,di
rep
movw
mov ax, #BOOTSEG
mov ds, ax
lidt idt_48
lgdt gdt_48
mov ax,#0x0001
lmsw ax
jmpi 0,8
gdt: .word 0,0,0,0
.word 0x07FF
.word 0x0000
.word 0x9A00
.word 0x00C0
.word 0x07FF
.word 0x0000
.word 0x9200
.word 0x00C0
idt_48: .word 0
.word 0,0
gdt_48: .word 0x7ff
.word 0x7c00+gdt,0
.org 510
.word 0xAA55
从BISO启动原理浅析
当按下开机按键,计算机会做哪些事呢?
-
通电自检,检查硬盘,内存,显卡这些硬件设备是否正确和CPU连接和正常使用.
-
启动BIOS程序(启动前硬件会先设置好需要使用的各个BIOS中断),将引导扇区(MBR)中的引导程序加载到内存的0x07c0:0x0000(实地址模式)处,开始执行引导程序(汇编源码就是下面的boot.s)
-
引导程序执行的结果是将操作系统内核代码加载到内存中,并将内核代码移到某块指定内存中,修改cs:eip到内核程序的起始地址开始执行.
从代码分析BIOS启动过程
boot.s :
首先第一句
jmpi go,#BOOTSEG
就有说头.
jmpi的指令格式如下:
jmpi offset , segment address
简单来说就是跳转到某个段的某个偏移处,代码中可以看到,#BOOTSEG是0x07c0,因此这条语句的意思是跳转到段地址是0x7c00,偏移是go标签相对于第一行代码的偏移开始执行.
问题是为啥要特意使用jmpi来执行下一条语句呢,直接顺序执行不可以吗?老师给的解释是:
这个jmpi语句实际上是一个隐式赋值语句,修改cs和eip指向当前代码段和偏移。
下面通过调试来验证一下:
在jmpi执行前,cs和eip的值如下:

可以看到执行jmpi前还是保留着BIOS的cs和eip的值.而执行之后,cs和eip的值如下:

此时已经将段地址修改到了当前代码段基址,偏移设置成go标签对应的代码.
-
go:
初始化各段寄存器
-
load_system:
使用BIOS中断int 0x13来将内核代码加载到内存的0x10000处,因为这个地址离0x7c00较远,内核代码不会覆盖到当前正在执行的0x7c00的代码段.
-
ok_load:
第一行代码是一个
cli
关中断,这里关中断的原因是在地址0x0处,之前存储的是BIOS的中断向量表,移动后将覆盖这部分内容,同时,引导程序也没有安装新的中断向量表,没有中断向量表就不能响应中断,所以应该关闭中断避免发生故障。首先把被加载到内存的内核代码移动到线性地址0x00处,这样做的原因应该是为了是利用已经无用的地址空间,因为BIOS中断表被硬件默认填到了地址0x00处,由于现在BIOS的工作已经完成,所以可以覆盖掉这些无用的地址.
其次初始化idtr和gdtr,之后还有两个关键操作:
lmsw ax jmpi 0,8
-
lmsw将ax的最后一位复制到CR0的最后一位,这里的效果是将CR0的最低位置位(PE位).此后操作系统由实模式进入保护模式,对cs等段寄存器的解释由段地址转换为GDT表索引
-
jmpi 0,8的执行结果看来和本程序的第一句jmpi go,#BOOTSEG是一样的:将8赋值给cs,0赋值给偏移eip.但是上一条指令已经将CPU切换到保护模式,cs的值就会被解释为段选择子32位保护模式下的段选择子的结构如下:
segselector.png
-
因此cs为8代表以特权级0(ring0)索引GDT表的第1项(从0开始)并从中解析出需要跳转的基址,加上偏移值之后跳转到目标地址执行(这一系列工作均由硬件解析cs来完成).接下来就开始从地址0x00处开始执行真正的内核代码了.
**段选择子**的通用结构如下:

-
MBR标志
.org 510 .word 0xAA55
CPU加载MBR时会检查此字段,如果不为0xAA55就会拒绝开机.
网友评论