操作系统开发系列—12.d.扩充内核 ●
现在把esp、GDT等内容放进内核中,我们现在可以用C语言了,只要能用C,我们就避免用汇编。
下面看切换堆栈和GDT的关键代码:
; 导入函数 extern cstart ; 导入全局变量 extern gdt_ptr [SECTION .bss] StackSpace resb 2 * 1024 StackTop: ; 栈顶 ; 把 esp 从 LOADER 挪到 KERNEL mov esp, StackTop ; 堆栈在 bss 段中 sgdt [gdt_ptr] ; cstart() 中将会用到 gdt_ptr call cstart ; 在此函数中改变了gdt_ptr,让它指向新的GDT lgdt [gdt_ptr] ; 使用新的GDT
最后这4个语句完成了切换堆栈和更换GDT的任务。StackTop定义在.bss段中,堆栈大小为2KB。
PUBLIC void* memcpy(void* pDst, void* pSrc, int iSize); PUBLIC void disp_str(char * pszInfo); PUBLIC u8 gdt_ptr[6]; /* 0~15:Limit 16~47:Base */ PUBLIC DESCRIPTOR gdt[GDT_SIZE]; PUBLIC void cstart() { disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "-----\"cstart\" begins-----\n"); /* 将 LOADER 中的 GDT 复制到新的 GDT 中 */ memcpy(&gdt, /* New GDT */ (void*)(*((u32*)(&gdt_ptr[2]))), /* Base of Old GDT */ *((u16*)(&gdt_ptr[0])) + 1 /* Limit of Old GDT */ ); /* gdt_ptr[6] 共 6 个字节:0~15:Limit 16~47:Base。用作 sgdt/lgdt 的参数。*/ u16* p_gdt_limit = (u16*)(&gdt_ptr[0]); u32* p_gdt_base = (u32*)(&gdt_ptr[2]); *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1; *p_gdt_base = (u32)&gdt; }
函数首先把位于Loader中的原GDT全部复制给新的GDT,然后把gdt_ptr中的内容换成新的GDT的基地址和界限。SGDT——保存全局描述符,gdt_ptr[2]的值本身就是个地址,所以前面带上(void*)表示是个指针。memcpy之所以用void*,是因为需要一个字节一个字节复制。
编译:
nasm boot.asm -o boot.bin
nasm loader.asm -o loader.bin
nasm -f elf -o kernel.o kernel.asm
nasm -f elf -o string.o string.asm
nasm -f elf -o kliba.o kliba.asm
gcc -m32 -c -fno-builtin -o start.o start.c
----
dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
sudo mount -o loop a.img /mnt/hgfs/
sudo cp loader.bin /mnt/hgfs/ -v
sudo umount /mnt/hgfs/
----
ld -m elf_i386 -s -Ttext 0x30400 -o kernel.bin kernel.o string.o start.o kliba.o
sudo mount -o loop a.img /mnt/hgfs/
sudo cp kernel.bin /mnt/hgfs/ -v
sudo umount /mnt/hgfs/
运行结果如下:
【源码】