操作系统开发系列—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("














"
		 "-----"cstart" begins-----
");

	/* 将 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/

运行结果如下:

源码

原文地址:https://www.cnblogs.com/joey-hua/p/5404762.html