0

我正在制作一个操作系统,但我被困在 GDT 上。我尝试了不同的教程,例如http://www.osdever.net/bkerndev/Docs/gdt.htmhttp://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and% 20IDT.html,但我的操作系统总是崩溃。我怎样才能解决这个问题?我使用 grub,所以内核已经处于保护模式。

启动.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:

内核.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();
}

链接器.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

内核生成文件:

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

1 回答 1

1

initterm假设您没有向我们展示的代码没有错误或使用STI指令打开中断,您的代码似乎没问题。未处理的中断或没有适当的中断描述符表 (IDT) 将导致三重故障。

我怀疑问题可能根本不是上述问题。一般来说,如果您正在创建打算由兼容 Multiboot(2) 的引导加载程序加载的ELF对象,您应该在链接描述文件中明确设置入口点。设置它明确地告诉链接器您希望引导加载程序开始执行代码的位置。在您的情况下start,您的代码中有一个标签,所以我认为您打算将其作为入口点。

在链接描述文件的顶部添加:

ENTRY(start) 

链接器期望该符号start是一个全局标签。在您的带有 multiboot2 标头的汇编文件中,请确保start此行是全局的:

global start

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

如果该ENTRY指令不存在,您将不会收到任何警告。在这种情况下,链接器通常会悄悄地搜索一个名为的全局标签start,如果找不到,则将入口点设置为ELF对象的起始 VMA。在链接描述文件中使用指令指定起始地址ENTRY将告诉您是否存在问题以及如果缺少它用作入口点的 VMA。

一般经验法则:始终使用ENTRY指令在链接描述文件中指定一个入口点,并在代码中全局导出该标签。

于 2017-09-19T06:56:26.927 回答