接續「重新放置核心-整理記憶體中的核心並將控制權交給它」。
esp, GDT 等內容目前還在 Loader中,為了方便控制,我們預計將它們放進核心中。
再者,目前已經可以採用 C 語言了,因此我們將盡量避免用編譯。在此,我們透過 C 語言將 Loader 中的原 GDT 全部複製給新的 GDT,並將 gdt_ptr 中的內容換成新的 GDT 的基底位址和界限。
kernel.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; kernel.asm ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Forrest Yu, 2005 ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ---------------------------------------------------------------------- ; 編譯連接方法: ; $ rm -f kernel.bin ; $ nasm -f elf -o kernel.o kernel.asm ; $ nasm -f elf -o string.o string.asm ; $ nasm -f elf -o klib.o klib.asm ; $ gcc -c -o start.o start.c ; $ ld -s -Ttext 0x30400 -o kernel.bin kernel.o string.o start.o klib.o ; $ rm -f kernel.o string.o start.o ; $ ; ---------------------------------------------------------------------- SELECTOR_KERNEL_CS equ 8 ; 導入函數 extern cstart ; 導入全局變量 extern gdt_ptr [SECTION .bss] StackSpace resb 2 * 1024 StackTop: ; 堆疊頂 [section .text] ; 程式碼在此 global _start ; 導出 _start _start: ; 此時記憶體看上去是這樣的(更詳細的記憶體情況在 LOADER.ASM 中有說明): ; ┃ ┃ ; ┃ ... ┃ ; ┣━━━━━━━━━━━━━━━━━━┫ ; ┃■■■■■■Page Tables■■■■■■┃ ; ┃■■■■■(大小由LOADER決定)■■■■┃ PageTblBase ; 00101000h ┣━━━━━━━━━━━━━━━━━━┫ ; ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M ; 00100000h ┣━━━━━━━━━━━━━━━━━━┫ ; ┃□□□□ Hardware Reserved □□□□┃ B8000h ← gs ; 9FC00h ┣━━━━━━━━━━━━━━━━━━┫ ; ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp ; 90000h ┣━━━━━━━━━━━━━━━━━━┫ ; ┃■■■■■■■KERNEL.BIN■■■■■■┃ ; 80000h ┣━━━━━━━━━━━━━━━━━━┫ ; ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr) ; 30000h ┣━━━━━━━━━━━━━━━━━━┫ ; ┋ ... ┋ ; ┋ ┋ ; 0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss ; ; ; GDT 以及相應的描述符是這樣的: ; ; Descriptors Selectors ; ┏━━━━━━━━━━━━━━━━━━┓ ; ┃ Dummy Descriptor ┃ ; ┣━━━━━━━━━━━━━━━━━━┫ ; ┃ DESC_FLAT_C (0~4G) ┃ 8h = cs ; ┣━━━━━━━━━━━━━━━━━━┫ ; ┃ DESC_FLAT_RW (0~4G) ┃ 10h = ds, es, fs, ss ; ┣━━━━━━━━━━━━━━━━━━┫ ; ┃ DESC_VIDEO ┃ 1Bh = gs ; ┗━━━━━━━━━━━━━━━━━━┛ ; ; 注意! 在使用 C 程式碼的時候一定要保證 ds, es, ss 這幾個段寄存器的值是一樣的 ; 因為編譯器有可能編譯出使用它們的程式碼, 而編譯器默認它們是一樣的. 比如串拷貝操作會用到 ds 和 es. ; ; ; 把 esp 從 LOADER 挪到 KERNEL mov esp, StackTop ; 堆疊在 bss 段中 sgdt [gdt_ptr] ; cstart() 中將會用到 gdt_ptr call cstart ; 在此函數中改變了gdt_ptr,讓它指向新的GDT lgdt [gdt_ptr] ; 使用新的GDT ;lidt [idt_ptr] jmp SELECTOR_KERNEL_CS:csinit csinit: ; “這個跳轉指令強制使用剛剛初始化的結構”——<<OS:D&I 2nd>> P90. push 0 popfd ; Pop top of stack into EFLAGS hlt
kliba.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; klib.asm ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Forrest Yu, 2005 ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ [SECTION .data] disp_pos dd 0 [SECTION .text] ; 導出函數 global disp_str ; ======================================================================== ; void disp_str(char * info); ; ======================================================================== disp_str: push ebp mov ebp, esp mov esi, [ebp + 8] ; pszInfo mov edi, [disp_pos] mov ah, 0Fh .1: lodsb test al, al jz .2 cmp al, 0Ah ; 是ENTER嗎? jnz .3 push eax mov eax, edi mov bl, 160 div bl and eax, 0FFh inc eax mov bl, 160 mul bl mov edi, eax pop eax jmp .1 .3: mov [gs:edi], ax add edi, 2 jmp .1 .2: mov [disp_pos], edi pop ebp ret
string.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; string.asm ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Forrest Yu, 2005 ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ [SECTION .text] ; 導出函數 global memcpy ; ------------------------------------------------------------------------ ; void* memcpy(void* es:pDest, void* ds:pSrc, int iSize); ; ------------------------------------------------------------------------ memcpy: push ebp mov ebp, esp push esi push edi push ecx mov edi, [ebp + 8] ; Destination mov esi, [ebp + 12] ; Source mov ecx, [ebp + 16] ; Counter .1: cmp ecx, 0 ; 判斷計數器 jz .2 ; 計數器為零時跳出 mov al, [ds:esi] ; ┓ inc esi ; ┃ ; ┣ 逐字元移動 mov byte [es:edi], al ; ┃ inc edi ; ┛ dec ecx ; 計數器減一 jmp .1 ; 循環 .2: mov eax, [ebp + 8] ; 返回值 pop ecx pop edi pop esi mov esp, ebp pop ebp ret ; 函數結束,返回 ; memcpy 結束-------------------------------------------------------------
start.c
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ start.c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Forrest Yu, 2005 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #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 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; }
const.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ const.h ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Forrest Yu, 2005 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #ifndef _ORANGES_CONST_H_ #define _ORANGES_CONST_H_ /* 函数类型 */ #define PUBLIC /* PUBLIC is the opposite of PRIVATE */ #define PRIVATE static /* PRIVATE x limits the scope of x */ /* GDT 和 IDT 中描述符的个数 */ #define GDT_SIZE 128 #endif /* _ORANGES_CONST_H_ */
type.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ type.h ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Forrest Yu, 2005 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #ifndef _ORANGES_TYPE_H_ #define _ORANGES_TYPE_H_ typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; #endif /* _ORANGES_TYPE_H_ */
protect.h
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ protect.h ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Forrest Yu, 2005 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #ifndef _ORANGES_PROTECT_H_ #define _ORANGES_PROTECT_H_ /* 存儲段描述符/系統段描述符 */ typedef struct s_descriptor /* 共 8 個字節 */ { u16 limit_low; /* Limit */ u16 base_low; /* Base */ u8 base_mid; /* Base */ u8 attr1; /* P(1) DPL(2) DT(1) TYPE(4) */ u8 limit_high_attr2; /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */ u8 base_high; /* Base */ }DESCRIPTOR; #endif /* _ORANGES_PROTECT_H_ */
loader.asm
將原 loader.asm 的 356~357 行註解掉。因為我們已經卻任何新順利載入,是故可省略此測試用的資訊了。
boot.asm/ fat12hdr.inc, load.inc, pm.inc
與原來的檔案相同。
步驟
- 編譯 boot.asm (nasm boot.asm -o boot.bin)
- 編譯 loader.asm (nasm loader.asm -o loader.bin)
- 編譯 kliba.asm 為 ELF 格式 (nasm -f elf kliba.asm -o kliba.o)
- 編譯 string.asm 為 ELF 格式 (nasm -f elf string.asm -o string.o)
- 編譯 start.c (gcc -c -fno-builtin -o start.o start.c)
- 編譯 kernel.asm 為 ELF 格式,並設定程式入口位址 0x30400
nasm -f elf kernel.asm -o kernel.o ld -s -Ttext 0x30400 kernel.o string.o start.o kliba.o -o kernel.bin
- 用 bximage 建立 floppy image,命名為 a.img
- 將 boot.bin 寫入 a.img 開機磁區(dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc)
- 複製 kernel.bin 與 loader.bin 至 a.img
mount tmp sudo mount -o loop a.img tmp sudo cp loader.bin tmp sudo cp kernel.bin tmp sudo umount tmp rmdir tmp
![]() |
執行結果 |
留言
張貼留言