我正在用C从头开始编写一个x86_32位内核,在编写了VGA驱动程序之后,我一直在尝试实现GDT以及键盘驱动程序的中断。但是,从程序集调用的 GDT_flush() 函数失败并导致引导循环。评论该函数,它启动得很好(但显然没有 GDT)。 我正在 Qemu 上模拟内核,并使用特定于目标的工具链来编译所有文件并将其链接在一起。
GDT.c:
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_pointer{
unsigned short limit;
unsigned int base;
}__attribute__((packed));
struct gdt_entry gdt[3];
struct gdt_pointer gdtptr;
//extern void flush_gdt(unsigned char gdtptr);
extern void flush_gdt();
void set_gdt_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 & 0x0F);
gdt[num].access = access;
}
void init_gdt(){
gdtptr.limit = (sizeof(struct gdt_entry) * 3) - 1;
gdtptr.base = (uint32_t)&gdt;
print_string("setting up kernel segments \n");
set_gdt_gate(0, 0, 0, 0, 0); //Null segment
set_gdt_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Code segment
set_gdt_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //data segment
print_string("kernel segments have been setup hehehhehehe\n");
//flush_gdt((unsigned char)&gdtptr);
flush_gdt();
}
描述符.asm:
global flush_gdt
[extern gdtptr]
flush_gdt:
;mov eax, [esp + 4]
;lgdt[eax]
lgdt[gdtptr]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08: .flush_two
.flush_two:
ret
我查找了其他类似的项目,我的实现并没有太大不同。我已经尝试解决这个问题几周了,非常感谢任何帮助。
谢谢:)
我尝试将描述符指针作为参数传递给被调用的函数,但这也会导致引导循环。
您将粒度位和限制位合并为相同的 4 位。第二行可能应该是:
gdt[num].granularity |= (gran & 0xF0);
但是然后:
set_gdt_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Code segment
set_gdt_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //data segment
“0xCF”值是多少?由于上面提到的掩蔽,无论如何你都会扔掉一半。如果是
0xC-
部分打算进入粒度位,则它缺少“AVL”(“可用”)位,因此加载它会导致错误。值
0xD-
将设置 AVL 位。