[SOLVED] GDT Load Causing Triple Fault
Posted: Mon Mar 05, 2018 9:11 am
Sorry to create another one of these, however I haven't found suitable answers in any of the others I've found on this forum.
I've been trying to setup a GDT from 32-bit protected mode, so far only with entries for kernel data and code (and, of course, the null entry). However, every time I try running it I get a triple fault. I've narrowed it down to the actual `lgdt' instruction, telling me that I've most likely set something up wrong either in the registry or in the entries themselves. So I double checked all the flags I was setting and so on and couldn't find anything that seemed out of the norm (`1001 1010' for kernel code sector, `1001 0010` for kernel data sector).
Here's what I've done so far (sorry for GAS syntax if that's not what you're used to):
EDITS:
- Fixed typo overwritting GDT code entry with data entry.
I've been trying to setup a GDT from 32-bit protected mode, so far only with entries for kernel data and code (and, of course, the null entry). However, every time I try running it I get a triple fault. I've narrowed it down to the actual `lgdt' instruction, telling me that I've most likely set something up wrong either in the registry or in the entries themselves. So I double checked all the flags I was setting and so on and couldn't find anything that seemed out of the norm (`1001 1010' for kernel code sector, `1001 0010` for kernel data sector).
Here's what I've done so far (sorry for GAS syntax if that's not what you're used to):
Code: Select all
# boot.s
# declare constants for the multiboot header
.set ALIGN, 1 << 0 # align loaded modules on page boundaries
.set MEMINFO, 1 << 1 # provide memory map
.set FLAGS, ALIGN | MEMINFO # the multiboot `FLAG' field
.set MAGIC, 0x1BADB002 # 'magic number' letting the boot loader know we're here
.set CHECKSUM , -(MAGIC + FLAGS) # checksum of the above to prove we're multiboot
/*
* Declare the multiboot header marking this program as a kernel. The bootloader
* will search for these values in the first 8 KiB of the kernel file aligned at
* 32-bit boundaries (4 bytes). We put the signature in its own section to force
* it within the first 8 KiB of the kernel file.
*/
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
/*
* Create a 16 byte aligned stack with 16 KiB of size. We create labels at the
* bottom and top of the stack.
*/
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
/*
* The linker script specifies the `_start' label as the entry point to the kernel
* and the bootloader will jump to this position. That is, this is where the kernel
* starts.
*/
.section .text
.global _start
.type _start, @function
_start:
# set the position of `%esp' to the top of the stack
mov $stack_top, %esp
# GDT, paging, and other features
call gdt_install
boot_kernel:
# enter high-level kernel (C)
call kernel_main
# put computer into infinite loop
cli # disable interrupts by clearing the interrupt flag in `eflags'
end_loop:
hlt # wait for next interrupt
jmp end_loop # jump to the `hlt' instruction if it ever leaves it
.size _start, . - _start
.global gdt_flush
.type gdt_flush, @function
gdt_flush:
mov -4(%esp), %eax
lgdt (%eax) # load GDT registry into processor
movw $0x10, %ax # 0x10 is the offset of the DATA segment in the GDT
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
ljmp $0x0008, $flush_end
flush_end:
ret
Code: Select all
/* gdt.h */
#pragma once
#include <stdint.h>
#include <stddef.h>
#define GDT_SIZE 3
// GDT registry
struct gdtr {
uint16_t limit;
uint32_t base;
} __attribute__((packed));
// a GDT entry
struct gdt_entry {
uint16_t limit_low; // bits 0-15
uint16_t base_low; // bits 0-15
uint8_t base_mid; // bits 16-23
uint8_t access; // access byte
uint8_t flags; // granularity and size flags
uint8_t base_high; // bits 24-31
} __attribute__((packed));
struct gdt_entry gdt[GDT_SIZE];
struct gdtr gdtr;
/*
* Flush the GDT registry to the processor. This function is
* defined in `boot.s'.
*/
extern void gdt_flush(struct gdtr *gdtr);
void gdt_set(size_t num, uint32_t base, uint32_t limit,
uint8_t access, uint8_t flags);
void gdt_install();
Code: Select all
/* gdt.c */
#include "gdt.h"
#include <kernel/tty.h>
#include <kernel/system.h>
void gdt_set(size_t num, uint32_t base, uint32_t limit,
uint8_t access, uint8_t flags) {
if(num >= GDT_SIZE)
{
tty_init();
tty_write_s("gdt_set(): num is too big. Aborting.\n");
abort();
}
struct gdt_entry *entry = &gdt[num];
// base
entry->base_low = base & 0xFFFF;
entry->base_mid = (base >> 16) & 0xFF;
entry->base_high = (base >> 24) & 0xFF;
// limit
entry->limit_low = limit & 0xFFFF;
entry->flags = (limit >> 16) & 0x0F;
// access and granularity flags
entry->flags |= flags & 0xF0;
entry->access = access;
}
void gdt_install() {
gdtr.limit = (sizeof(struct gdt_entry) * GDT_SIZE) - 1;
gdtr.base = (uint32_t)&gdt;
// null-descriptor
gdt_set(0, 0, 0, 0, 0);
/*
* Code segment entry: base address 0, limit of 4GiB,
* 4KiB granularity, 32-bit opcodes, code segment set.
*/
gdt_set(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/*
* Data segment entry: base address 0, limit of 4GiB,
* 4KiB granularity, 32-bit opcodes, data segment set.
*/
gdt_set(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
gdt_flush(&gdtr);
}
- Fixed typo overwritting GDT code entry with data entry.