c19_core.asm

程序流程

入口点
ds指向核心数据段
es指向4G数据段
创建中断描述符表IDT
设置实时时钟中断处理过程
加载IDTR
设置8259A中断控制器
设置和时钟中断相关的硬件
开放硬件中断,sti
显示处理器品牌信息
创建内核的页目录表PDT
创建PDT表项和页表
开启页功能
将低1MB的虚拟内存映射到高虚拟内存处
刷新CS
ss指向核心栈段
ds指向核心数据段
开放硬件中断,sti
安装系统服务的调用门
开始创建和确立内核任务
开始创建用户任务
执行任务切换

1. 创建中断描述符表IDT

IDT中保存着发生中断或者异常时的处理过程的描述符
X86支持256个中断,其中前20个用于系统内部异常,使用统一的异常处理过程general_exception_handler;后236个用于其他的异常和中断,也使用统一的中断处理过程general_interrupt_handler
本章程序中指定了IDT在内存中的位置,为idt_linear_address
创建IDT就是把256个中断/异常处理过程的描述符表安装到这个内存位置
安装前需要根据中断处理过程的段选择子、段内偏移和属性值来构造一个描述符,然后安装到IDT中。构造门描述符使用以下过程

1.1 make_gate_descriptor

;构造门的描述符
;输入:EAX=门代码在段内偏移地址
; BX=门代码所在段的选择子
; CX=段类型及属性等(各属性位都在原始位置)
;返回:EDX:EAX=完整的描述符

1.2 设置实时时钟中断处理过程

本章是在第18章的代码清单之上做了修改,因此继续保留了第18章的很多功能,比如实时时钟中断。
第18章利用实时时钟中断实现了抢占式任务,实时时钟中断开启之后,处理器的处理流程会周期性的进入到实时时钟中断的中断处理过程,实时时钟中断的中断号是0x70,处理过程是rtm_0x70_interrupt_handle,将其安装到IDT中即可。
设置8259A中断控制器
设置和时钟中断相关的硬件

1.3 创建内核的页目录表PDT

页目录表大小为4KB,有1024个表项,每个表项的大小为4B,表项中保存的是页表的物理地址。
一个任务一个页目录表。
本章代码清单中直接指定了PDT的物理地址,即0x00020000。
在这里就是把PDT所在的物理地址的内容都清零。
创建PDT表项和页表
本章代码清单中创建了2个PDT表项和一个页表。
页表的表项中记录的是页的物理地址,即一个页表的表项可以管理4KB的内存空间,一个页表有1024个表项,则一个页表可以管理4MB的物理内存。本章中假定内核只占据了内存的低1MB,所以只需要一个页表即可实现对内核所占内存的管理。
PDT中的2个表项一个对应着PDT自己,即表项中保存的是PDT自己的物理地址,一个对应着唯一的那一个页表,即表项中保存的是页表的物理地址。2个页表项的值分别为0x00020003,0x00021003。
给页表的表项赋值。因为只需要管理1MB的物理内存空间,因此只需要初始化256个表项即可。其余的表项设置为0.

开启页功能

做2件事,第1件,把PDT的物理地址赋值给CR3,第2件,将CR0的PG域置位。
分页功能开启之后,页部件会使用下面的方法将线性地址转换成物理地址

将低1MB的虚拟内存映射到高虚拟内存处

开启分页功能后,每个任务都有自己的虚拟4G内存空间,这4G的虚拟内存又分为高2G和低2G。低2G为任务自己的局部空间,高2G为任务的全局空间。任务的全局空间一般为系统内核所在的位置。因此需要把内核的虚拟内存地址映射到高2G,即地址从0x8000000开始的地方。
内核现在在低1M处,只需将内核的线性地址加上0x80000000即可。需要2个步骤,第1步,使PDT中偏移为0x800的位置记录内核任务唯一页表的物理地址,即0x00021003。第2步,使GDT中全部描述符表的基地址都加上0x80000000,让IDT的线性地址加上0x80000000。
32位的线性地址被分成3段,第1段10位,作为PDT的索引,找到页表的物理地址;第2段10位,作为页表的索引,找到页的物理地址;第3段,12位,作为偏移地址,和找到的物理地址一起构成最终的要访问的位置的物理地址。
这里采用了迂回的方式访问了PDT的表项。让段部件发出线性地址0xfffff800,3段的值分别是0x3ff,0x3ff和0x800,第1段的0x3ff对应着PDT的最后一个表项,其值为0x00020003,是PDT自己的物理地址;第2段0x3ff对应着PDT的最后一个表项,其值为0x00020003,是PDT自己的物理地址;第3段0x800,则最终访问的是0x00020000+0x800,即0x00020800,这里是线性地址0x80000000对应的表项,即这里记录着线性地址0x80000000对应的页表的物理地址。

刷新CS

jmp core_code_seg_sel:flush

安装系统服务的调用门

内核数据段中有一个符号表salt,每个表项的值包含一个字符串、系统调用的偏移地址和系统调用的段选择子。这里会根据每个表项里的偏移地址和段选择子构建一个调用门,将调用门安装到GDT,然后将调用门的在GDT中的选择子回填到符号表中该表项的选择子域。
在用户程序中也有一个salt表,称为U-SALT,U-SALT的表项在代码中是字符串,在用户程序被加载的过程中会被更新成对应过程的偏移地址和段选择子,这里的段选择子就是调用门的选择子。
在用户程序中使用call名调用系统服务的时候,call的操作数是U-SALT的相应表项的标号,即一个有效地址,处理器会从这个有效地址处获取6个字节,分别对应着系统调用的4字节的偏移地址和2字节的段选择子。然后根据这个段选择子从GDT中获取到一个调用门,从调用门中获取到偏移地址和该系统调用的段选择子,再根据这个系统调用的选择子从GDT中获取到该系统调用的基地址。

开始创建和确立内核任务

创建TCB。core_lin_alloc_at
append_to_tcb_link
创建内核任务的TSS
创建内核任务的TSS
分配内存。allocate_memory
将TSS的基地址登记到TCB中
填充TSS。填充的内容包括反向链,PDT基地址,LDT基地址T位,I/O位图。
创建TSS描述符,并安装到GDT中
登记TSS选择子到TCB
将TSS的选择子加载到TR。此时可以认为内核任务正在执行

开始创建用户任务

创建TCB。TCB大小为0x4a。任务可用于分配的初始线性地址是0;任务状态是就绪。
load_relocate_program
append_to_tcb_link
创建另一个用户任务;
创建TCB。任务状态是空闲。
load_relocate_program
append_to_tcb_link

load_relocate_program

;加载并重定位用户程序
;输入: PUSH 逻辑扇区号
; PUSH 任务控制块基地址
;输出:无

清空当前页目录表的前半部分

创建LDT。分配内存,将LDT基地址登记到TCB,将LDT的初始界限登记到TCB
ds指向内核数据段
读取用户程序的第一个扇区到core_buf
获取用户程序的总大小,分配内存
将用户程序的基地址登记到TCB
创建描述符表并登记到LDT中
重定位SALT
创建特权级为0、1、2的栈并登记到TCB
在GDT中登记LDT描述符
创建用户程序的TSS
登记基本的TSS表格内容
在GDT中登记TSS描述符
创建用户任务的页目录;create_copy_cur_pdir

清空当前页目录表的前半部分

现在是要给用户任务分配内存,本来应该要在用户任务自己的虚拟内存中分配,但是当前使用的页目录表是内核任务的页目录表,基于内核任务的页目录表和页表分配内存,然后在用户任务创建好之后,将内核任务的页目录表和页表的内容复制到用户任务的页目录表和页表也是可以的。
所有任务的虚拟内存都可以分成低2G的局部地址空间和高2G的全局地址空间,对于内核来说,它用到全局地址空间。在借用内核任务的页目录表和页表进行内存分配的时候,需先将低2G的部分清空,这是假定了之前创建了别的任务,使用了这些地方。
清空页目录表需要访问页目录表的表项,当段部件发出线性地址0xfffff000时,页部件就会获取到页目录表的物理地址,然后将512个表项,共2048个字节的位置清零。

task_alloc_memory

;在指定任务的虚拟内存空间中分配内存
;输入:EBX=任务控制块TCB的线性地址
; ECX=希望分配的字节数
;输出:ECX=已分配的起始线性地址

ds指向4G数据段
ecx是已分配的内存空间的起始线性地址

alloc_inst_a_page

;分配一个页,并安装在当前活动的层级分页结构中
;输入:EBX=页的线性地址

访问线性地址在页目录表中对应的表项,检测它的P位是否为1。
如果页表不存在,则先分配一个新页,并把新页的地址登记到页目录表中,
清空新分配的这个页。新页是页表。
如果页表存在,则检查页表中对应的页是否存在。取得页表中页的物理地址,校验P位。
如果页不存在,则先分配一个新页,并把新页的地址登记到页表中。
如果页存在,则返回。

访问线性地址在页目录表中对应的表项
因此需要找到线性地址在页目录表和页表中对应的表项,check其P位。
访问页目录表的某个表项,首先发出的线性地址的前5位需要是0xfffff,这样就获取到了页目录表的物理地址。然后发出的线性地址的低3位是EBX前10位,这个前10位是页目录表的索引,再乘以4就是要访问的位置在页目录表中的偏移;
0xfffff000 or (EBX>>22<<2)

清空页表

清空页表需要先访问页表
要发出这样一个地址,前10位是0xffc,访问页目录表最后一项,页目录表最后一个是也页目录表的物理地址;中间10位是线性地址的前10位,是线性地址在页目录表中的索引,获取到的是线性地址对应页表的物理地址。这样,发出的地址对应的就是页表的基地址,将其上1024个表项,每个表项4字节的内容清零。
访问页表中的页表项
前10位,0xffc,得到PDT的物理地址,PDT被当成页表
中间10位,取自线性地址的前10位,是页表的索引,得到页的物理地址
对页的物理地址中的P位进行校验

allocate_a_4k_page

;分配一个4KB的页
;输入:无
;输出:EAX=页的物理地址

ds指向内核数据段
找到页映射位串中第一个空闲页并保存在EAX。
找到页映射位串中第一个空闲页
本章的程序假设只有2M的内存,则对应的只有2M/4K=512个页,因此只需要长度为512位=64字节的页映射位串。在内核数据段定义了page_bit_map来保存页映射位串。
找空闲页就是从低到高的轮询page_bit_map的位,找到第一个值为0的位。
测试一个位,
如果不是空闲位,即该位的值为1,CF值为1,则索引加1,如果索引值没有超出界限,则测试下一位;如果超出索引界限,打印错误信息,停机
如果是空闲位,即该位的值为0,CF值为0,则让索引值乘以4096即可得到该页的物理地址,并返回。

append_to_tcb_link

create_copy_cur_pdir

创建新页目录,并复制当前页目录内容
;输入:无
;输出:EAX=新页目录的物理地址

ds指向4G数据段
es指向4G数据段
创建页目录表
将内核的页目录表的内容全部复制到新的用户任务的页目录表中。
创建页目录表
分配一个页,页的物理地址在EAX,我们把这个物理地址登记到内核的页目录表的倒数第二项。0xfffffff8,访问的是页目录表偏移位置为0xff8的位置,即页目录表的倒数第二项。

执行任务切换

do_task_clean

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值