为了进入保护模式,首先需要在实模式下设置GDT,并用lgdt指令保存GDT的地址,这个地址实际上是线性地址,只是在实模式中线性地址就是物理地址;当控制权转到内核时需要将GDT重新设置一下,以便在内核中设置GDT,为了保证这一点需要在设置GDT之前,通过分页机制仍然能够找到原先的GDT,即线性地址要映射到相同的物理地址中,否则就找不到原来的GDT,无法进行拷贝。在本次操作系统实践中,分页机制非常简单即f(线性地址)=物理地址,所以可以保证这一点,下面是内核重置GDT的代码:
;kernel.asm
SELECTOR_KERNEL_CS equ 0x10
extern cstart
extern gdt_ptr
[section .bss]
StackSpace resb 2*1024
StackTop equ $
[section .text]
global _start
_start:
mov esp, StackTop;把esp从Loader挪到kernel
sgdt [gdt_ptr];加载原先的GDT
call cstart
lgdt [gdt_ptr];使用新的GDT
jmp SELECTOR_KERNEL_CS:csinit
csinit:
mov ah, 0Fh
mov al, 'K'
mov [gs:((80 * 1 + 39) * 2)], ax
jmp $
#include "type.h"
#include "const.h"
#include "protect.h"
PUBLIC void* memcpy(void*
pDst, void* pSrc, int iSize);
PUBLIC void disp_str(char * pszInfo);
PUBLIC t_8 gdt_ptr[6]; //
0~15:Limit 16~47:Base
PUBLIC DESCRIPTOR gdt[GDT_SIZE];
//start.c设置新的GDT
PUBLIC void cstart()
{
// 将 LOADER 中的 GDT 复制到新的 GDT 中
memcpy( gdt,
// New GDT
(void*)(*((t_32*)(&gdt_ptr[2]))),
// Base of Old GDT
*((t_16*)(&gdt_ptr[0]))
+
1
// Limit of Old GDT
);
// gdt_ptr[6] 共 6
个字节:0~15:Limit 16~47:Base。用作 sgdt 以及 lgdt
的参数。
t_16* p_gdt_limit =
(t_16*)(&gdt_ptr[0]);
t_32* p_gdt_base =
(t_32*)(&gdt_ptr[2]);
*p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) -
1;
*p_gdt_base = (t_32)gdt;
}