第二篇章 引导程序head.s

日期:2022-09-20

版本号:V1.0

作者:snow

一、基础概念

1.1、段选择符

段寄存器是16位寄存器,(包含代码段CS、数据段DS、栈段SS、附加段ES、分段FS、保留GS)。在实模式环境是指向对应段的基址,在保护模式下,这些寄存器代表的是段选择选择符。段选择符含义如下,每个段选择符即可对应全局描述表GDT或者局部描述符表中的某一个段描述符。

15                                                                                                             2           1     0

INDEX

TI

RPI

TI代表指向的段描述符表类型(0=GDT,1=LDT)

INDEX=描述符表内偏移

RPI=访问权限,必须高于或等于段描述符中的权限等级,否则不可通过该段选择符访问段描述符所对应的内容

注:段寄存器cs\ds\es\sp等通常指代16位寄存器,ecs\eds\ees\esp等属于32位寄存器,rcs\rds\res\rsp属于64位寄存器。在芯片设计上,16位寄存器是32位寄存器的低16位,同理64位寄存器。

二、重建GDT&IDT

2.1 更新段段选择符

       由于系统已经从实模式切换为保护模式,系统的GDT表已经开始使用,故相关的寄存器的功能也需要从段基址变更为相应的段选择符,以实现后续与GDT表的联动过程。

2.1.1 绑定普通段地址

将DS、ES、FS、GS分别置为0x10,即这4个寄存器均指向GDT表的第2个段描述符(从0开始),GDT表的第2个段描述符通常对应为内核数据段描述符。

2.1.2 绑定栈段地址

2.1.2.1 定义栈空间

linux的PAGE_SIZE默认为4KB

在sched.c中声明为

long user_stack[PAGE_SIZE>>2]

user_stack在内核引导阶段会作为内核栈使用,在引导结束后,或作为任务0和任务1的用户栈使用

struc {

long * a;

short b;

} stack_start = { &user_stack[PAGE_SIZE>>2], 0x10 };

即user_stack占用了一个页,stack_start的a指向这个页,同时这个页的段选择符位默认0x10(注:访问user_stack的我第一个字-4B,即对应成员变量a)。

2.1.2.2 加载栈段

栈空间在代码编译阶段好后,接下来就是要将SS寄存器指向该段区域。

lss _stack_start, %esp

(ps:这行代码是AT&T汇编,不是intel汇编,两者不兼容)

对于lds/les/lfs/lgs/lss等段地址加载指令,若目标寄存器为16位,则将mem的低16位送入目标寄存器,高16位送入对应的段寄存器ds/es/fs/gs/ss,若目标寄存器为32位,则将mem的低32位送入目标寄存器,高16位送入对应的段寄存器ds/es/fs/gs/ss。

所以上述指令即是将stack_start的低32位(对应user_stack的地址)送入esp寄存器,stack_start的高16位(对应0x10)送入SS寄存器,即栈段描述符SS更新为0x10,与其他普通段指向相同的段描述符。

2.2、设置IDT

将IDT中的256个门(中断)描述符全部指向一个空函数,该函数无任何实际代码,例如

void ignore_int(void)

{

       printf(“hello”);

}

2.3、重建GDT

由于原有GDT表在setup程序内部,而setup程序后续将会被缓冲区覆盖掉,所以需要在其他位置重建一份GDT(小彩蛋,不能在setup程序之后初次建立GDT,否则无法实现setup程序到head程序的跳转)。

2.3.1 定义的新GDT

利用汇编语言gdt_descr中定义如下变量

gdt:

.quad 0x0000 0000 0000 0000 //GDT第0项 保留,空地址

.quad 0x00C0 9A00 0000 0FFF //GDT第1项

.quad 0x00C0 9200 0000 0FFF //GDT第2项

.quad 0x0000 0000 0000 0000 //GDT第3项 保留,空地址

.fill 252.8.0 //给LDT TSS等的空间

(小彩蛋:在编译连接时,新GDT表会定义head.s文件最末尾,并占用2KB存储空间,新GDT位置后面就是system模块的内核代码main)

其中GDT第1项段描述符解析为:段单位4KB,数据/指令宽度32位,段存在,需最高内核权限,程序/代码段,代码段,非一致性,支持读,未被访问,段长度4KB,段基址0(总结就是从0开始的16M空间)

其中第2项段描述符解析为:段单位4KB,数据/指令宽度32位,段存在,需最高内核权限,程序/代码段,数据段,不可向下扩展,支持写,未被访问,段长度4KB,段基址0(总结就是从0开始的16M空间)

2.3.2 加载新GDT

通过lgdt命令将新的gdt表传入到gdtr寄存器中

lgdt gdt_descr

2.3.3 更新段容量

       更新过程与与2.1节一致,虽然各个段选择符内容一致,但是需要通过更新寄存器值的方式实现段容量的刷新(由8M变为16M)

三、创建分页机制

3.1、基础准备工作

3.1.1 检查A20

利用实模式下的回滚机制,在1M的位置写入特定数据,若写入后与回滚地址0的内容一致,则当前还处于实模式;若不一致则说明处于保护模式

3.1.2 检查协处理器

       检查协处理,即浮点运算器。

3.1.3 压入main函数地址

       后续程序将会通过jmp的方式(非call调用)跳转到分页程序,而分页程序执行返回后,会在最后将main函数出栈病跳转到main函数

3.2 创建分页机制

3.2.1 初始化页表

(1)在内存的起始位置定义一个页目录表及4个页表,并将其清0。

(2)将页目录表的第0~3项分别指向上述4个页表(页表0~3)

(3)填写4个页表,如下,使4个页表能指向0~16M的内存空间

3.2.2 激活分页机制

(1)设置页目录基址

将CR3寄存器指向页目录表(也即是0x000000)

(2)激活系统分页功能

将CR0寄存器的第1位(PE)置为1

3.2.3 跳转main

通过ret指令,执行返回

由于main函数入口地址已经压入栈顶,返回后该地址会弹出给CS:EIP

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值