任务四:
Bootloader如何做准备并进入保护模式的?
通过查询相关指令含义总结bootasm.s的具体执行流程为:
首先在实模式下运行---〉打开A20端口---〉加载GDTR,进入保护模式---〉调用bootmain函数
.setPROT_MODE_CSEG, 0x8 #kernel code segment selector
.set PROT_MODE_DSEG, 0x10 #kernel data segment selector
.set CR0_PE_ON, 0x1 #protected mode enable flag
.globl start
start:
.code16 #Assemble for 16-bit mode
cli //CLI可以屏蔽中断,防止运行中中断
cld //CLD使DF复位,即DF=0,串操作方向控制
#Set up the important data segment registers (DS, ES, SS). //设置段寄存器的值
xorw%ax, %ax //使用异或指令,将ax寄存器的值设置为0
movw%ax, %ds //将ax寄存器的值移动到DS寄存器中
movw %ax, %es //同上
movw%ax, %ss
//上述指令将DS,ES,SS,AS寄存器的值全部设置为0
#Enable A20:
# For backwards compatibility with the earliestPCs, physical
# address line 20 is tied low, so thataddresses higher than
# 1MB wrap around to zero by default. This codeundoes this.
seta20.1:
inb$0x64, %al //从0x64端口读一字节数据到al寄存器中,返回i8042中状态寄存器的内容
testb$0x2, %al
//将al按位与0010进行AND操作并对标志寄存器进行置位,运算结果0,ZF置1,否则置0
jnzseta20.1
//JNZ就是ZF不为1时跳转,到seta20.1重新开始运行,说明如果0x64中倒数第二位为0时程序才正常执行
movb$0xd1, %al //如果完成上述操作将端口0x64的值置位0xd1,
outb%al, $0x64
seta20.2:
inb$0x64, %al #Wait for not busy
testb$0x2, %al
jnzseta20.2
//同样判断键盘控制器是否繁忙
movb$0xdf, %al //如果不繁忙将0xdf的值传给0x60
outb%al, $0x60
//上述代码的作用是打开A20地址线
#Switch from real to protected mode, using a bootstrap GDT
#and segment translation that makes virtual addresses
#identical to physical addresses, so that the
#effective memory map does not change during the switch.
lgdtgdtdesc
//通过lgdt汇编指令可以把GDTR描述符表的大小和起始位置存入gdtr寄存器中
movl%cr0, %eax
//将CRO寄存器的内容移动到ax中
orl$CR0_PE_ON, %eax
//通过逻辑或操作将eax寄存器的第一位置设为1。
movl%eax, %cr0
//将CR0的最后一位设置为1,进入保护模式(!!!)
#Jump to next instruction, but in 32-bit code segment.
#Switches processor into 32-bit mode.
ljmp$PROT_MODE_CSEG, $protcseg
//指令跳转由代码段选择子0x8跳到protcseg的起始位置
.code32 #Assemble for 32-bit mode
protcseg:
#Set up the protected-mode data segment registers
//下面进入保护模式的操作
movw$PROT_MODE_DSEG, %ax #Our data segment selector
//将数据段选择子0x10赋值给ax
movw%ax, %ds #-> DS: Data Segment
//将数据段选择子赋值给DS
movw%ax, %es #-> ES: Extra Segment
movw%ax, %fs #-> FS
movw%ax, %gs #-> GS
movw%ax, %ss #-> SS: Stack Segment
#Set up the stack pointer and call into C.
movl$0x0, %ebp
//将bp寄存器的值设置为0x0
movl$start, %esp
//将sp寄存器的值设置为START标示处的地址
callbootmain
//调用bootmain函数
# Ifbootmain returns (it shouldn't), loop.
spin:
jmpspin
# Bootstrap GDT
.p2align 2 #force 4 byte alignment
gdt:
SEG_NULLASM # null seg
//通过查找asm.h文件可知,实际此段为8个值为0的字节
SEG_ASM(STA_X|STA_R,0x0, 0xffffffff) # code seg forbootloader and kernel
//此段为定义代码段
SEG_ASM(STA_W,0x0, 0xffffffff) # dataseg for bootloader and kernel
//定义数据段
gdtdesc: //此为段描述符表的地址
.word0x17 #sizeof(gdt) - 1 //大小
.longgdt #address gdt //起始位置