X86 如何初始化GDT?

X86 如何初始化GDT?,x86,kernel,osdev,linker-scripts,gdt,X86,Kernel,Osdev,Linker Scripts,Gdt,我正在制作一个操作系统,我被困在GDT。我尝试过不同的教程,如和,但我的操作系统总是崩溃。我怎样才能解决这个问题?我使用grub,因此内核已经处于保护模式 boot.asm: section .multiboot multiboot_start: dd 0xe85250d6 dd 0 dd multiboot_end - multiboot_start dd 0x100000000 - (0xe85250d6 + 0 + (multiboot_end - multiboot_start)) dw

我正在制作一个操作系统,我被困在GDT。我尝试过不同的教程,如和,但我的操作系统总是崩溃。我怎样才能解决这个问题?我使用grub,因此内核已经处于保护模式

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();
}
生成文件:

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
内核生成文件:

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

假设您没有向我们展示的
initterm
没有bug,或者使用STI指令打开中断,那么您的代码看起来是正常的。未经处理的中断或没有正确的中断描述符表(IDT)将导致三重故障

我怀疑,尽管问题可能根本不是上述问题。一般来说,如果要创建打算由多引导(2)兼容引导加载程序加载的ELF对象,则应该在链接器脚本中显式设置入口点。设置它会显式地告诉链接器您希望引导加载程序开始执行代码的位置。在您的代码中有一个
start
标签,因此我认为您打算将其作为入口点

在链接器脚本的顶部添加:

ENTRY(start) 
链接器希望符号
start
将是一个全局标签。在具有multiboot2头的汇编程序文件中,确保
start
是全局的,如下所示:

global start
这应该足以正确设置入口点。如果在链接器脚本中显式放置
ENTRY
指令,链接器将在找不到定义为入口点的全局标签时发出警告,并告诉您默认入口点地址。默认值通常是ELF对象中的起始虚拟内存地址(VMA)。在您的情况下,这将是0x100000

如果
条目
指令不存在,您将不会得到任何警告。在这种情况下,链接器通常会安静地搜索一个名为
start
global标签,如果没有找到,则将入口点设置为ELF对象的起始VMA。在链接器脚本中使用
ENTRY
指令指定起始地址将告诉您是否存在问题,以及如果缺少该问题,将使用哪个VMA作为入口点


一般经验法则:始终使用
entry
指令在链接器脚本中指定一个入口点,并在代码中全局导出该标签。

假设您没有向我们显示的
initterm
没有错误或使用STI指令打开中断,则您的代码看起来是正常的。未经处理的中断或没有正确的中断描述符表(IDT)将导致三重故障

我怀疑,尽管问题可能根本不是上述问题。一般来说,如果要创建打算由多引导(2)兼容引导加载程序加载的ELF对象,则应该在链接器脚本中显式设置入口点。设置它会显式地告诉链接器您希望引导加载程序开始执行代码的位置。在您的代码中有一个
start
标签,因此我认为您打算将其作为入口点

在链接器脚本的顶部添加:

ENTRY(start) 
链接器希望符号
start
将是一个全局标签。在具有multiboot2头的汇编程序文件中,确保
start
是全局的,如下所示:

global start
这应该足以正确设置入口点。如果在链接器脚本中显式放置
ENTRY
指令,链接器将在找不到定义为入口点的全局标签时发出警告,并告诉您默认入口点地址。默认值通常是ELF对象中的起始虚拟内存地址(VMA)。在您的情况下,这将是0x100000

如果
条目
指令不存在,您将不会得到任何警告。在这种情况下,链接器通常会安静地搜索一个名为
start
global标签,如果没有找到,则将入口点设置为ELF对象的起始VMA。在链接器脚本中使用
ENTRY
指令指定起始地址将告诉您是否存在问题,以及如果缺少该问题,将使用哪个VMA作为入口点


一般经验法则:始终使用
entry
指令在链接器脚本中指定一个入口点,并在代码中全局导出该标签。

您必须发布代码,并向我们展示您的尝试,该尝试没有达到预期效果。您是否尝试在BOCHS的内置调试器中单步执行它,或者其他的调试方式?是的,我使用过hlt指令,问题出在
mov-ds,ax
line这类问题让我困惑,只有一个例外。一种可能是您的代码没有按预期执行。我刚才注意到,
boot.asm
文件中没有
global start
指令。如果未在链接器脚本中指定
条目
指令,则默认情况下链接器将尝试查找全局符号
start
。如果没有全局
start
,则链接器使用第一个VMA地址的入口点,在您的情况下为ix 0x100000。那不是你想要的。这可能会给你带来严重的问题。尝试将
global start
添加到您的
boot.asm
中,看看会发生什么。就其本身而言,我看不出此代码有任何问题(关于类型转换的警告可能除外)。我怀疑存在与构建/链接相关的问题,或者
initterm
在某个点打开中断。中断将导致三重故障。但我更倾向于代码不指定全局
start
符号,并且内核的入口点实际上不在
start
标签上,这将产生未定义的行为(可能是三重错误)。您必须发布您的代码,并向我们展示您的失败尝试。您是否尝试过在BOCHS的内置调试器中单步执行它,或其他调试方式?是的,我使用了hlt指令,问题出在
mov ds,a