ucore练习三

练习三 分析bootloader进入保护模式的过程

  • 进入bootloader之前的准备工作

1、首先CPU加电之后,从地址0xFFFFFFF0地址开始读入第一条指令,该指令为跳转指令,跳转到BIOS,由BIOS执行自检指令,自检完成后,读取第一扇区(主引导扇区)到内存的0x7c00处,执行完BIOS指令后,CPU执行内存的0x7c00处的指令,也就是bootloader。此时系统处于实模式下

上述cpu加电后从地址0xFFFFFFF0处开始执行的原因:cpu加电后,cpu会将ROM存储器中存放的初始值给各个寄存器,其中CS:IP = 0Xf000 : fff0,这个值决定了从内存中读取数据的位置,此时cs:ip中存放的就是第一条指令。

  • bootloader执行阶段

在这里插入图片描述
.code16 : 由于代码段在实模式下运行 所以要告诉编译器使用16位的模式编译
cli : 中断置零指令,设置IF=0 表示不响应可屏蔽终端
cld : 清除方向标志,将 direct flag 标志位清零,设置字符串为递增方向

xorw %ax,%ax : 利用异或指令将寄存器ax置为0
movw %ax,%ds,movw %ax,%es,movw %ax,%ss: 利用mov指令将段寄存器ds,es,ss置为0

在这里插入图片描述
首先是开启A20,根据上文我们知道需要将第20位为1即可, 但是我们需要知道在UCore里是如何将A20置为1的。
根据说明书我们可以知道,A20地址线由键盘控制器8042进行控制, 我们的A20所对应的是8042里面的P21引脚,所以问题就变成了我们需要将P21引脚置1。
对于8042芯片来说,有两个端口地址60h和64h。 对于这两个端口来说, 0x64用来发送一个键盘控制命令, 0x60用来传递参数,所以将P21引脚置1的操作就变成了, 我们首先利用0x64端口传递一个写入的指令, 然后由0x60端口读进去相应的参数来将P21置1。
我们首先要先向 64h 发送 0xd1 的指令,写之前需要等待键盘控制器8042空闲, 可以通过判断 64h端口中的状态寄存器的值0x2,来判断是否空闲。 然后向 60h 发送 0xdf 的指令。

A20地址线:由于现在的机器要兼容早期的PC机,地址线的第20位默认为0,在此状态下,如果访问的内存超过了1MB,那么就会重新回到0x00000地址访问。当A20激活之后,就可以访问所有的物理内存,可以运行保护模式

A20地址位是由键盘控制器芯片8042管理,所以要通过给8042发命令来激活A20,8042有两个I/O端口: 0x64和0x60
激活流程 : 发送0xdi命令到0x64端口 发送0xdf到0x60端口

发送命令之前,首先应该等待键盘输入缓冲区为空,通过8042的状态寄存器的第二位来观察,状态寄存器的值可以通过读0x64端口获得

inb $0x64,%al : 表示从0x64端口读取一个字节到寄存器al中
testb $0x2,%al : 用来测试状态寄存器的第2位是否为1,
jnz seta20.1 如果是1 则重新跳转到seta20.1处,如果是0,则代表缓冲区为空,继续向下执行
0xd1表示写输出端口命令,参数随后通过60h端口传入
0xdf表示将A20设置为1

在这里插入图片描述如果键盘输入缓冲区为空,则将0xdf存放在寄存器al中
outb %al, $0x64 : 将al中的值发送到端口0x64中

在这里插入图片描述
这里也是等待键盘输入缓冲区为0 同seta20.1的效果一样

在这里插入图片描述movb $0xdf,%al : 将数据0xdf的值存放在寄存器al中
outb %al,$0x60 : 将al中的值发送到端口0x60处

到此处 A20地址位激活 可以运行保护模式

从实模式转到保护模式,需要使用一个引导程序GDT,来翻译逻辑地址

在这里插入图片描述
lgdt gdtdesc : 表示将gdt desc段中表示的内容加载到gdt中
movl %cr0,%eax : 表示将32位的控制寄存器CR0的值存放在eax中
orl $CR0_PE_ON,%eax : CR0_PE_ON是在程序的开头定义的,值为1,在这里表示讲eax的第一位置为1
movl %eax,%cr0 : 表示将eax寄存器中的值复制到cr0中,复制之后将cr0的第一位也就是PE值为1,PE置为1,表示开启了保护模式

以上表示bootloader进入保护模式的过程

发布了33 篇原创文章 · 获赞 2 · 访问量 1736

猜你喜欢

转载自blog.csdn.net/weixin_42469716/article/details/89036058