以下是重置段的代码:
global reloadSegments
section .text
reloadSegments:
; Reload the CS register with an offset of 0x08
jmp 0x08:.reload_CS
.reload_CS:
; Reload data segment registers
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
跳转本身就是导致操作系统崩溃的原因。以下是加载 gdt 的代码:
void lgdt(void* gdt, uint16_t size) {
gdtr.base = (uint32_t)gdt;
gdtr.limit = size-1;
asm __volatile__("lgdt (%0)" : : "r" (&gdtr));
}
基数是 32 位值,而极限是 16 位。下面是我调用两个函数并填充 gdt 的地方:
void initGdt() {
create_descriptor(0, 0, 0, 0);
create_descriptor(1, 0, 0x000FFFFF, (GDT_CODE_PL0));
create_descriptor(2, 0, 0x000FFFFF, (GDT_DATA_PL0));
create_descriptor(3, 0, 0x000FFFFF, (GDT_CODE_PL3));
create_descriptor(4, 0, 0x000FFFFF, (GDT_DATA_PL3));
lgdt(&gdt, sizeof(gdt)-1);
extern void reloadSegments();
reloadSegments();
}
创建描述符的函数和常量均来自 osdev.org 上的 gdt 教程。我使用 QEMU 作为具有多重引导的虚拟机。以下是该项目的 github 链接:https ://github.com/Wardence1/Basic_OS
我希望 os 能够成功跳转到 reload_CS 标签并重新加载 cs 寄存器,而不会出现问题。在此先感谢您的帮助!
这个答案是评论中所有好心人所说的一切的总结。首先,gdt 应该由 64 位条目组成,而不是 16 位。其次,限制应该放在 gdtr 的结构中基数之前。第三,我从 gdt 的大小中减去两次,一次在 lgdt 函数参数中,一次在 lgdt 函数本身中。为了使代码更安全,我在内联 asm 中将“r”替换为“m”,并将一个值传递给 gdtr。再次感谢大家的评论和支持!