OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 16, 2024 10:35 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: [SOLVED] GDT Load Causing Triple Fault
PostPosted: Mon Mar 05, 2018 9:11 am 
Offline

Joined: Mon Mar 05, 2018 3:07 am
Posts: 6
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):

Code:
# 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:
/* 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:
/* 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);
}


EDITS:
- Fixed typo overwritting GDT code entry with data entry.


Last edited by nortega on Thu Mar 08, 2018 9:10 am, edited 3 times in total.

Top
 Profile  
 
 Post subject: Re: GDT Load Causing Triple Fault
PostPosted: Mon Mar 05, 2018 5:30 pm 
Offline

Joined: Thu Oct 20, 2016 12:26 pm
Posts: 15
Looks like you're calling 'set_gdt(1, ...)' twice, overwriting your code descriptor with the data one.


Top
 Profile  
 
 Post subject: Re: GDT Load Causing Triple Fault
PostPosted: Tue Mar 06, 2018 3:10 am 
Offline

Joined: Mon Mar 05, 2018 3:07 am
Posts: 6
Thanks for the catch. However, it seems that sadly there was another issue as well. I just got done rerunning it in GDB and it continues to fail at `lgdt'. I've updated the code above with the fixes.


Top
 Profile  
 
 Post subject: Re: GDT Load Causing Triple Fault
PostPosted: Thu Mar 08, 2018 9:09 am 
Offline

Joined: Mon Mar 05, 2018 3:07 am
Posts: 6
Alright, I've been fucking around a lot and finally I decided to code this directly in assembly in the `boot.s' file, and it seems to work now (probably because I wasn't setting flags correctly in C). Here's the updated code:

Code:
# 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:

/*
* Create the GDT
*/
.section .data
gdt_start:
gdt_null:
.long 0x0
.long 0x0

gdt_kcode:
.word 0xFFFF             # limit
.word 0x0                # base
.byte 0x0                # base
.byte 0b10011010         # 1st flags | type flags
.byte 0b11001111         # 2nd flags | limit
.byte 0x0                # base

gdt_kdata:
.word 0xFFFF             # limit
.word 0x0                # base
.byte 0x0                # base
.byte 0b10010010         # 1st flags | type flags
.byte 0b11001111         # 2nd flags | limit
.byte 0x0                # base
gdt_end:

gdtr:
.word (gdt_end - gdt_start - 1)
.long gdt_start

/*
* 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
flush_gdt:
   cli
   lgdt (gdtr)
   movw $0x10, %ax
   movw %ax, %ds
   movw %ax, %es
   movw %ax, %fs
   movw %ax, %gs
   movw %ax, %ss
   ljmp $0x08, $flush_end
flush_end:

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


Top
 Profile  
 
 Post subject: Re: [SOLVED] GDT Load Causing Triple Fault
PostPosted: Mon Apr 02, 2018 11:28 am 
Offline

Joined: Mon Mar 05, 2018 3:07 am
Posts: 6
Okay, I've been looking through it again (as well as looking through some MINIX code) and it seems one of my major mistakes was forgetting that an array is already a pointer, and therefore the line should have been:
Code:
gdtr.base = (uint32_t)gdt;
//gdtr.base = (uint32_t)&gdt; //WRONG!!!!


Unfortunately this hasn't solved all my problems with writing this in C yet, but at least it's something. This also means that the following line can be simplified:
Code:
gdtr.limit = sizeof(gdtr) - 1;
//gdtr.limit = (sizeof(struct gdt_entry) * GDT_SIZE) - 1; // OLD


Top
 Profile  
 
 Post subject: Re: [SOLVED] GDT Load Causing Triple Fault
PostPosted: Wed Apr 04, 2018 10:04 am 
Offline

Joined: Mon Mar 05, 2018 3:07 am
Posts: 6
Okay, after a while of dealing with GDB I finally found out what the issue was with my initial C code, and now I feel rather stupid. The issue was how I was getting the parameter from the stack as well as a few other minor issues. Here's the `gdt_flush' function rewritten to work using proper calling conventions.

Code:
.section .text
.global gdt_flush
.type gdt_flush, @function
gdt_flush:
   pushl %ebp
   movl %esp, %ebp
   movl 8(%ebp), %eax
   cli
   lgdt (%eax)
   movw $0x10, %ax
   movw %ax, %ds
   movw %ax, %es
   movw %ax, %fs
   movw %ax, %gs
   movw %ax, %ss
   ljmp $0x0008, $gdt_flush_end
gdt_flush_end:
   popl %ebp
   ret


Top
 Profile  
 
 Post subject: Re: [SOLVED] GDT Load Causing Triple Fault
PostPosted: Mon Apr 23, 2018 3:41 am 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 692
I would have believed the main problem was this:
Code:
mov -4(%esp), %eax
. You don't need a frame pointer. I think you meant to have:
Code:
mov 4(%esp), %eax


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot], suspectedowl and 561 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group