现在,内核已经被我们加载进内存了,该是跳入保护模式的时候了。
首先是GDT以及对应的选择子,我们只定义三个描述符,分别是一个0~4GB的可执行段、一个0~4GB的可读写段和一个指向显存开始地址的段:
; GDT
; 段基址 段界限, 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR|DA_32|DA_LIMIT_4K ;0-4G
LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW|DA_32|DA_LIMIT_4K;0-4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW|DA_DPL3 ; 显存首地址 GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1 ; 段界限
dd BaseOfLoaderPhyAddr + LABEL_GDT ; 基地址 ; GDT 选择子
SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
在之前学习保护模式时,大部分描述符的段基址都是运行时计算后填入相应位置的,因为那时我们的程序是由BIOS或者DOS加载的,我们不知道段地址,于是也就不知道程序运行时在内存中的位置。
如今,Loader是由我们自己加载的,段地址已经被确定为BaseOfLoader,所以在Loader中出现的标号(变量)的物理地址可以用下面的公式来表示:
标号(变量)的物理地址=BaseOfLoader*10h+标号(变量)的偏移
我们把它以及相应的声明放在load.inc中。
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址 BaseOfLoaderPhyAddr equ BaseOfLoader*10h ; LOADER.BIN 被加载到的位置 ---- 物理地址 BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
OffsetOfKernelFile equ 0h ; KERNEL.BIN 被加载到的位置 ---- 偏移地址
我们定义了一个宏BaseOfLoaderPhyAddr用以代替BaseOfLoader*10h,它在loader.asm中被用到一次,用来计算GDT的基址。
运行结果如下:
【源码】