How can I initialise the GDT?

0

I'm making an operating system and I'm stuck at the GDT. I've tried different tutorials, such as http://www.osdever.net/bkerndev/Docs/gdt.htm and http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html, but my os crashes always. How can I fix this? I use grub so the kernel is already in protected mode.

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)
}
}

Makefile:

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

kernel makefile:

compile:
cd libk && make compile
nasm -felf32 boot.asm
gcc -c kernel.c -I libk -std=gnu99 -m32 -ffreestanding
x86
kernel
osdev
linker-scripts
gdt
asked on Stack Overflow Sep 17, 2017 by MSathieu • edited Sep 15, 2019 by Michael Petch

1 Answer

2

Your code appears to be okay assuming that initterm which you don't show us doesn't have a bug or turns on interrupts with the STI instructions. Unhandled interrupts or no proper Interrupt Descriptor Table (IDT) will cause a triple fault.

I suspect though the issue may not be the above at all. Generally speaking if you are creating ELF objects that intend to be loaded by a Multiboot(2) compatible bootloader you should be explicitly setting the entry point in your linker script. Setting it tells the linker explicitly where you want the bootloader to start executing your code. In your case you have a start label in your code so I think you intended that to be the entry point.

At the top of your linker script add:

ENTRY(start) 

The linker expects that the symbol start will be a global label. In your assembler file with the multiboot2 header make sure start is global with this line:

global start

This should be enough to properly set the entry point. If you explicitly place an ENTRY directive in your linker script the linker will warn you if it can't find the global label you define as the entry point and will tell you the default entry point address. The default is usually the starting Virtual Memory Address (VMA) in the ELF object. In your case that would be 0x100000.

You will not get any warning if the ENTRY directive is not present. In that case the linker usually quietly searches for a global label called start and if it doesn't find one sets the entry point to the starting VMA of the ELF object. Specifying a starting address with ENTRY directive in the linker script will tell you if there is a problem and what VMA it used as an entry point if it is missing.

General rule of thumb: Always specify an entry point in the linker script with the ENTRY directive and globally export that label in your code.

answered on Stack Overflow Sep 19, 2017 by Michael Petch

User contributions licensed under CC BY-SA 3.0