2017-09-17 270 views
0

我正在製作一個操作系統,我被困在GDT中。我嘗試過不同的教程,例如http://www.osdever.net/bkerndev/Docs/gdt.htmhttp://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html,但是我的操作系統總是崩潰。我怎樣才能解決這個問題?我使用grub,所以內核已經處於保護模式。我如何初始化GDT?

boot.asm:

section .multiboot 
multiboot_start: 
dd 0xe85250d6 
dd 0 
dd multiboot_end - multiboot_start 
dd 0x100000000 - (0xe85250d6 + 0 + (multiboot_end - multiboot_start)) 
dw 0 
multiboot_end: 
section .text 
global gdt_flush 
extern gp 
gdt_flush: 
lgdt [gp] 
mov ax, 0x10 
mov ds, ax; This line restarts the computer 
mov es, ax 
mov fs, ax 
mov gs, ax 
mov ss, ax 
jmp 0x08:flush2 
flush2: 
ret    
extern kernel_main 
start: 
mov esp, stack_space 
call kernel_main 
hlt 
section .bss 
resb 10240 
stack_space: 

kernel.c:

#include <tty.h> 
#include <log.h> 
struct gdt_entry { 
unsigned short limit_low; 
unsigned short base_low; 
unsigned char base_middle; 
unsigned char access; 
unsigned char granularity; 
unsigned char base_high; 
} __attribute__((packed)); 
struct gdt_ptr { 
unsigned short limit; 
unsigned int base; 
} __attribute__((packed)); 
struct gdt_entry gdt[3]; 
struct gdt_ptr gp; 
extern void gdt_flush(); 
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) { 
gdt[num].base_low = (base & 0xFFFF); 
gdt[num].base_middle = (base >> 16) & 0xFF; 
gdt[num].base_high = (base >> 24) & 0xFF; 
gdt[num].limit_low = (limit & 0xFFFF); 
gdt[num].granularity = ((limit >> 16) & 0x0F); 
gdt[num].granularity |= (gran & 0xF0); 
gdt[num].access = access; 
} 
void gdt_install() { 
gp.limit = (sizeof(struct gdt_entry) * 3) - 1; 
gp.base = &gdt; 
gdt_set_gate(0, 0, 0, 0, 0); 
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); 
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); 
gdt_flush(); 
} 
void kernel_main(void){ 
initterm(); 
put("Initializing system...\n"); 
gdt_install(); 
} 

linker.ld:

SECTIONS 
{ 
. = 1M; 
.text BLOCK(4K) : ALIGN(4K) 
{ 
    *(.multiboot) 
    *(.text) 
} 
.data BLOCK(4K) : ALIGN(4K) 
{ 
    *(.data) 
} 
.bss BLOCK(4K) : ALIGN(4K) 
{ 
    *(COMMON) 
    *(.bss) 
} 
} 

生成文件:

LINKFILES=kernel/boot.o kernel/kernel.o kernel/libk/string/strlen.o kernel/libk/tty/tty.o kernel/libk/ioport/inb.o kernel/libk/ioport/outb.o kernel/libk/serial/serwritechar.o kernel/libk/serial/writetoserial.o kernel/libk /tty/print.o kernel/libk/log/put.o 
compile: 
cd kernel && make compile 
build: 
ld -o devos.bin -Tkernel/linker.ld $(LINKFILES) -melf_i386 
mkdir -p iso/boot/grub 
mv devos.bin iso/boot/devos.bin 
cp grub.cfg iso/boot/grub/grub.cfg 
grub-mkrescue -o devos.iso iso 
.SILENT: 
all: compile build 

核心的makefile:

compile: 
cd libk && make compile 
nasm -felf32 boot.asm 
gcc -c kernel.c -I libk -std=gnu99 -m32 -ffreestanding 
+0

您必須發佈您的代碼,並告訴我們您的嘗試失敗,你希望工作。 –

+2

您是否嘗試過在BOCHS的內置調試器中單步調試或者其他調試方式? –

+0

是的,我已經使用hlt指令,問題出現在'mov ds,ax'行 – MSathieu

回答

2

您的代碼似乎是好的假設initterm你不告訴我們沒有錯誤或打開與STI指令中斷。未處理的中斷或沒有適當的中斷描述符表(IDT)將導致三重故障。

我懷疑,雖然問題可能不是上述所有。一般來說,如果您要創建ELF有意通過兼容Multiboot(2)的引導加載程序加載的對象,則應該明確設置鏈接描述文件中的入口點。設置它可以明確地告訴鏈接器你想讓引導加載程序開始執行代碼的位置。在你的情況下,你的代碼中有一個start標籤,所以我認爲你打算成爲入門點。

在你的鏈接腳本的頂部添加:

ENTRY(start) 

鏈接器預計,符號start將是一個全球性的標籤。在與multiboot2頭你的彙編文件,確保start是全球這一行:

global start 

這應該是足夠的正確設置的入口點。如果您明確地在鏈接器腳本中放入ENTRY指令,則鏈接器會發出警告,看它是否找不到您定義的全局標籤作爲入口點,並會告訴您默認入口點地址。默認情況下,通常是ELF對象中的起始虛擬內存地址(VMA)。在你的情況下,這將是0x100000。

如果ENTRY指令不存在,您將不會收到任何警告。在那種情況下,鏈接器通常安靜地搜索名爲start的全局標籤,並且如果它沒有找到一個設置入口點到對象的起始VMA的入口點。在鏈接器腳本中使用ENTRY指令指定起始地址會告訴您是否存在問題,以及如果缺少VMA,它將用作入口點。

一般經驗法則:始終在鏈接描述文件中用ENTRY指令指定一個入口點,並在代碼中全局導出該標籤。