GRUB + GDT + IDT = TRIPLE FAULT??

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

I've been following lots of tutorials recently and learning and all that stuff but I just can't figure out why my os triple faults. I used Jamies tutorial for GDT and IDT and all that "C" code works and stuff but I've been having some troubles with setting GDT in asm. I am using Linux and GCC standard wiki.osdev setup.

I am using GRUB for my bootloader and it works just fine but is GRUB causing that issue and is there a way to fix that?

Here are important parts of my code:
boot.s

Code: Select all

.global idt_flush
.type idt_flush, @function

idt_flush:
   movl 4(%esp), %eax  #; Get the pointer to the IDT, passed as a parameter.
   lidt (%eax)        #; Load the IDT pointer.
   ret

.global gdt_flush
.type gdt_flush, @function

gdt_flush:
   #movl 4(%esp), %eax  #; Get the pointer to the GDT, passed as a parameter.
   #lgdt (%eax)       #; Load the new GDT pointer

   #mov %ax, 0x10      #; 0x10 is the offset in the GDT to our data segment
   #mov %ds, ax        #; Load all data segment selectors
   #mov %es, ax
   #mov %fs, ax
   #mov %gs, ax
   #mov %ss, ax
   #jmp $0x08, $flush
   movl 4(%esp), %eax
   movl 2(%eax),%eax
   movl %ax, 8(%esp)
   movl %eax, ax
   lgdt (%eax) 
   ret

.flush:
   ret

.macro ISR_NOERRCODE l  #; define a macro, taking one parameter
  .global isr\l     #; %1 accesses the first parameter.
  isr\l:
    cli
    push 0
    push \l
    jmp isr_common_stub
.endm

.macro ISR_ERRCODE l
  .global isr\l
  isr\l:
    cli
    push \l
    jmp isr_common_stub
.endm

#.extern isr_handler

#; This is our common ISR stub. It saves the processor state, sets
#; up for kernel mode segments, calls the C-level fault handler,
#; and finally restores the stack frame.
isr_common_stub:
   pusha                    #; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

   mov %ax, ds               ##; Lower 16-bits of eax = ds.
   push %eax                 #; save the data segment descriptor

   mov %ax, 0x10  #; load the kernel data segment descriptor
   mov %ds, ax
   mov %es, ax
   mov %fs, ax
   mov %gs, ax

   call isr_handler

   pop %eax       # ; reload the original data segment descriptor
   mov %ds, ax
   mov %es, ax
   mov %fs, ax
   mov %gs, ax

   popa                     #; Pops edi,esi,ebp...
   add %esp, 8     #; Cleans up the pushed error code and pushed ISR number
   sti
   iret           #; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP


ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE   8
ISR_NOERRCODE 9
ISR_ERRCODE   10
ISR_ERRCODE   11
ISR_ERRCODE   12
ISR_ERRCODE   13
ISR_ERRCODE   14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31


reloadSegments:  
   JMP $0x08, $.reload_CS 
.reload_CS:
   
   MOV   %AX, 0x10 
   MOV   %DS, %AX
   MOV   %ES, %AX
   MOV   %FS, %AX
   MOV   %GS, %AX
   MOV   %SS, %AX
   RET



.Stage3:
mov %ax,0x10
mov %ds,%ax
mov %ss,%ax
mov %es,%ax
mov %esp,90000

.section .text
.global _start
.type _start, @function
_start:
  movl $stack_top, %esp
  call kernel_early
  call _init
  call kernel_main
  jmp gdt_flush
  jmp idt_flush
  cli
  mov %eax, %cr0
  or %eax, 1
  mov %cr0, %eax
  jmp $0x08, $.Stage3

  cli
.Lhang:
  hlt
  jmp .Lhang
.size _start, . - _start

DATA32
.entergdtmode:
cli
pusha
jmp reloadSegments
jmp gdt_flush
jmp idt_flush
sti
popa
ret


.STOP:
cli
hlt
kernel.c

Code: Select all

.....
#include <kernel/descriptor_tables.h>
....

void kernel_main(void)
{
	init_descriptor_tables();
        ....
        
}
descriptor_tables.c

Code: Select all

#include <kernel/graphics/common.h>
#include <kernel/descriptor_tables.h>
// Lets us access our ASM functions from our C code.
extern void gdt_flush(uint32);
extern void idt_flush(uint32);

// Internal function prototypes.
static void init_gdt();
static void init_idt();
static void gdt_set_gate(sint32,uint32,uint32,uint8,uint8);
static void idt_set_gate(uint8,uint32,uint16,uint8);

gdt_entry_t gdt_entries[5];
gdt_ptr_t   gdt_ptr;
idt_entry_t idt_entries[256];
idt_ptr_t   idt_ptr;

// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
void init_descriptor_tables()
{
   // Initialise the global descriptor table.
   init_gdt();
   init_idt();
}

static void init_gdt()
{
   gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
   gdt_ptr.base  = (uint32)&gdt_entries;

   gdt_set_gate(0, 0, 0, 0, 0);                // Null segment
   gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
   gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
   gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
   gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment

   gdt_flush((uint32)&gdt_ptr);
}

// Set the value of one GDT entry.
static void gdt_set_gate(sint32 num, uint32 base, uint32 limit, uint8 access, uint8 gran)
{
   gdt_entries[num].base_low    = (base & 0xFFFF);
   gdt_entries[num].base_middle = (base >> 16) & 0xFF;
   gdt_entries[num].base_high   = (base >> 24) & 0xFF;

   gdt_entries[num].limit_low   = (limit & 0xFFFF);
   gdt_entries[num].granularity = (limit >> 16) & 0x0F;

   gdt_entries[num].granularity |= gran & 0xF0;
   gdt_entries[num].access      = access;
}

static void init_idt()
{
    idt_ptr.limit = sizeof(idt_entry_t) * 256 -1;
    idt_ptr.base  = (uint32)&idt_entries;

    memset(&idt_entries, 0, sizeof(idt_entry_t)*256);

    idt_set_gate( 0, (uint32)isr0 , 0x08, 0x8E);
    idt_set_gate( 1, (uint32)isr1 , 0x08, 0x8E);
    idt_set_gate( 2, (uint32)isr2 , 0x08, 0x8E);
    idt_set_gate( 3, (uint32)isr3 , 0x08, 0x8E);
    idt_set_gate( 4, (uint32)isr4 , 0x08, 0x8E);
    idt_set_gate( 5, (uint32)isr5 , 0x08, 0x8E);
    idt_set_gate( 6, (uint32)isr6 , 0x08, 0x8E);
    idt_set_gate( 7, (uint32)isr7 , 0x08, 0x8E);
    idt_set_gate( 8, (uint32)isr8 , 0x08, 0x8E);
    idt_set_gate( 9, (uint32)isr9 , 0x08, 0x8E);
    idt_set_gate(10, (uint32)isr10, 0x08, 0x8E);
    idt_set_gate(11, (uint32)isr11, 0x08, 0x8E);
    idt_set_gate(12, (uint32)isr12, 0x08, 0x8E);
    idt_set_gate(13, (uint32)isr13, 0x08, 0x8E);
    idt_set_gate(14, (uint32)isr14, 0x08, 0x8E);
    idt_set_gate(15, (uint32)isr15, 0x08, 0x8E);
    idt_set_gate(16, (uint32)isr16, 0x08, 0x8E);
    idt_set_gate(17, (uint32)isr17, 0x08, 0x8E);
    idt_set_gate(18, (uint32)isr18, 0x08, 0x8E);
    idt_set_gate(19, (uint32)isr19, 0x08, 0x8E);
    idt_set_gate(20, (uint32)isr20, 0x08, 0x8E);
    idt_set_gate(21, (uint32)isr21, 0x08, 0x8E);
    idt_set_gate(22, (uint32)isr22, 0x08, 0x8E);
    idt_set_gate(23, (uint32)isr23, 0x08, 0x8E);
    idt_set_gate(24, (uint32)isr24, 0x08, 0x8E);
    idt_set_gate(25, (uint32)isr25, 0x08, 0x8E);
    idt_set_gate(26, (uint32)isr26, 0x08, 0x8E);
    idt_set_gate(27, (uint32)isr27, 0x08, 0x8E);
    idt_set_gate(28, (uint32)isr28, 0x08, 0x8E);
    idt_set_gate(29, (uint32)isr29, 0x08, 0x8E);
    idt_set_gate(30, (uint32)isr30, 0x08, 0x8E);
    idt_set_gate(31, (uint32)isr31, 0x08, 0x8E);

    idt_flush((uint32)&idt_ptr);
}

static void idt_set_gate(uint8 num, uint32 base, uint16 sel, uint8 flags)
{
    idt_entries[num].base_lo = base & 0xFFFF;
    idt_entries[num].base_hi = (base >> 16) & 0xFFFF;

    idt_entries[num].sel     = sel;
    idt_entries[num].always0 = 0;
    // We must uncomment the OR below when we get to using user-mode.
    // It sets the interrupt gate's privilege level to 3.
    idt_entries[num].flags   = flags /* | 0x60 */;
}

descriptor_tables.h

Code: Select all

#include <kernel/graphics/common.h>
// Initialisation function is publicly accessible.
void init_descriptor_tables();


// This structure contains the value of one GDT entry.
// We use the attribute 'packed' to tell GCC not to change
// any of the alignment in the structure.
struct gdt_entry_struct
{
    uint16 limit_low;           // The lower 16 bits of the limit.
    uint16 base_low;            // The lower 16 bits of the base.
    uint8  base_middle;         // The next 8 bits of the base.
    uint8  access;              // Access flags, determine what ring this segment can be used in.
    uint8  granularity;
    uint8  base_high;           // The last 8 bits of the base.
} __attribute__((packed));

typedef struct gdt_entry_struct gdt_entry_t;

// This struct describes a GDT pointer. It points to the start of
// our array of GDT entries, and is in the format required by the
// lgdt instruction.
struct gdt_ptr_struct
{
    uint16 limit;               // The upper 16 bits of all selector limits.
    uint32 base;                // The address of the first gdt_entry_t struct.
} __attribute__((packed));

typedef struct gdt_ptr_struct gdt_ptr_t;

// A struct describing an interrupt gate.
struct idt_entry_struct
{
    uint16 base_lo;             // The lower 16 bits of the address to jump to when this interrupt fires.
    uint16 sel;                 // Kernel segment selector.
    uint8  always0;             // This must always be zero.
    uint8  flags;               // More flags. See documentation.
    uint16 base_hi;             // The upper 16 bits of the address to jump to.
} __attribute__((packed));

typedef struct idt_entry_struct idt_entry_t;

// A struct describing a pointer to an array of interrupt handlers.
// This is in a format suitable for giving to 'lidt'.
struct idt_ptr_struct
{
    uint16 limit;
    uint32 base;                // The address of the first element in our idt_entry_t array.
} __attribute__((packed));

typedef struct idt_ptr_struct idt_ptr_t;

// These extern directives let us access the addresses of our ASM ISR handlers.
extern void isr0 ();
extern void isr1 ();
extern void isr2 ();
extern void isr3 ();
extern void isr4 ();
extern void isr5 ();
extern void isr6 ();
extern void isr7 ();
extern void isr8 ();
extern void isr9 ();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
I hope you will help me, I was searching and searching and found nothing. Tell me if you need my isr.c and isr.h files.
Sorry for long post I don't know how to make a [spoiler] on this forum.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Brendan »

Hi,
thehardcoreOS wrote:I am using GRUB for my bootloader and it works just fine but is GRUB causing that issue and is there a way to fix that?
GRUB isn't causing the issue.

It looks like you're using AT&T (GAS) in "intel syntax mode". I don't use GAS, but people who do typically say it's a bad idea, especially if/when its mixed with code generated by GCC.

Code: Select all

.global idt_flush
.type idt_flush, @function

idt_flush:
   movl 4(%esp), %eax  #; Get the pointer to the IDT, passed as a parameter.
   lidt (%eax)        #; Load the IDT pointer.
   ret
I think the first instruction here should be "movl %eax,4(%esp)".

Code: Select all

gdt_flush:
   #movl 4(%esp), %eax  #; Get the pointer to the GDT, passed as a parameter.
   #lgdt (%eax)       #; Load the new GDT pointer
And the same problem here (should've probably been "#movl %eax,4(%esp)").

Code: Select all

   #mov %ax, 0x10      #; 0x10 is the offset in the GDT to our data segment
   #mov %ds, ax        #; Load all data segment selectors
   #mov %es, ax
   #mov %fs, ax
   #mov %gs, ax
   #mov %ss, ax
   #jmp $0x08, $flush
   movl 4(%esp), %eax
   movl 2(%eax),%eax
   movl %ax, 8(%esp)
   movl %eax, ax
   lgdt (%eax) 
   ret
For this code, I don't know if the caller passes the address of the GDTR (in which case the commented out code is right and the "added later" code is not), or if the caller passes a pointer to the address of the GDTR (which sounds too convoluted to me).

Code: Select all

.macro ISR_NOERRCODE l  #; define a macro, taking one parameter
Let's forget about IDT entries and interrupt handlers until after you've got GDT working (and CS, SS, DS, ES, .. loaded) properly.. ;)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

@Brendan, I fixed all my coding syntax issues and removed everything that had to do with IDT and I am still facing the same issue. As you can see I am trying to enter protected mode but isn't it already on by default because I am using GRUB?
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Brendan »

Hi,
thehardcoreOS wrote:@Brendan, I fixed all my coding syntax issues and removed everything that had to do with IDT and I am still facing the same issue.
That's a pity - you might want to post the newest version of your code (including the GDT you're using).
thehardcoreOS wrote:As you can see I am trying to enter protected mode but isn't it already on by default because I am using GRUB?
You're not enabling protected mode, you're loading a sane GDT. GRUB does leave you in protected mode, but you should assume that the GDT that GRUB was using is now dead and gone and unusable (which is why you need to load your own GDT, even though you are in protected mode).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

Hi Brendan, this is updated version of my code:

boot.s:

Code: Select all

# Declare constants used for creating a multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

# Declare a header as in the Multiboot Standard.
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

# Reserve a stack for the initial thread.
.section .bootstrap_stack, "aw", @nobits
stack_bottom:
.skip 16384 # 16 KiB
stack_top:


#[GLOBAL gdt_flush]    #; Allows the C code to call gdt_flush().
.global gdt_flush
.type gdt_flush, @function

gdt_flush:
   #movl 4(%esp), %eax  #; Get the pointer to the GDT, passed as a parameter.
   #lgdt (%eax)       #; Load the new GDT pointer

   #mov %ax, 0x10      #; 0x10 is the offset in the GDT to our data segment
   #mov %ds, ax        #; Load all data segment selectors
   #mov %es, ax
   #mov %fs, ax
   #mov %gs, ax
   #mov %ss, ax
   #jmp $0x08, $flush
   movl %eax,4(%esp)
   movl 2(%eax),%eax
   movl %ax, 8(%esp)
   movl %eax, ax
   lgdt (%eax) 
   ret


   
.flush:
   ret

.macro ISR_NOERRCODE l  #; define a macro, taking one parameter
  .global isr\l     #; %1 accesses the first parameter.
  isr\l:
    cli
    push 0
    push \l
    jmp isr_common_stub
.endm

.macro ISR_ERRCODE l
  .global isr\l
  isr\l:
    cli
    push \l
    jmp isr_common_stub
.endm

#.extern isr_handler

#; This is our common ISR stub. It saves the processor state, sets
#; up for kernel mode segments, calls the C-level fault handler,
#; and finally restores the stack frame.
isr_common_stub:
   pusha                    #; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

   mov %ax, ds               ##; Lower 16-bits of eax = ds.
   push %eax                 #; save the data segment descriptor

   mov %ax, 0x10  #; load the kernel data segment descriptor
   mov %ds, ax
   mov %es, ax
   mov %fs, ax
   mov %gs, ax

   call isr_handler

   pop %eax       # ; reload the original data segment descriptor
   mov %ds, ax
   mov %es, ax
   mov %fs, ax
   mov %gs, ax

   popa                     #; Pops edi,esi,ebp...
   add %esp, 8     #; Cleans up the pushed error code and pushed ISR number
   sti
   iret           #; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP


ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE   8
ISR_NOERRCODE 9
ISR_ERRCODE   10
ISR_ERRCODE   11
ISR_ERRCODE   12
ISR_ERRCODE   13
ISR_ERRCODE   14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31


reloadSegments:  
   JMP $0x08, $.reload_CS 
.reload_CS:
   
   MOV   %AX, 0x10 
   MOV   %DS, %AX
   MOV   %ES, %AX
   MOV   %FS, %AX
   MOV   %GS, %AX
   MOV   %SS, %AX
   RET



.Stage3:
mov %ax,0x10
mov %ds,%ax
mov %ss,%ax
mov %es,%ax
mov %esp,90000

# The kernel entry point.
.section .text
.global _start
.type _start, @function
_start:
  movl $stack_top, %esp
  call kernel_early
  call _init
  call kernel_main
  jmp gdt_flush
  cli
  mov %eax, %cr0
  or %eax, 1
  mov %cr0, %eax
  jmp $0x08, $.Stage3

  cli
.Lhang:
  hlt
  jmp .Lhang
.size _start, . - _start

DATA32
.entergdtmode:
cli
pusha
jmp reloadSegments
jmp gdt_flush
sti
popa
ret


.STOP:
cli
hlt
descriptor_tables.c

Code: Select all

#include <kernel/graphics/common.h>
#include <kernel/descriptor_tables.h>
// Lets us access our ASM functions from our C code.
extern void gdt_flush(uint32);
extern void idt_flush(uint32);

// Internal function prototypes.
static void init_gdt();
static void init_idt();
static void gdt_set_gate(sint32,uint32,uint32,uint8,uint8);
static void idt_set_gate(uint8,uint32,uint16,uint8);

gdt_entry_t gdt_entries[5];
gdt_ptr_t   gdt_ptr;
idt_entry_t idt_entries[256];
idt_ptr_t   idt_ptr;

// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
void init_descriptor_tables()
{
   // Initialise the global descriptor table.
   init_gdt();
   init_idt();
}

static void init_gdt()
{
   gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
   gdt_ptr.base  = (uint32)&gdt_entries;

   gdt_set_gate(0, 0, 0, 0, 0);                // Null segment
   gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
   gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
   gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
   gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment

   gdt_flush((uint32)&gdt_ptr);
}

// Set the value of one GDT entry.
static void gdt_set_gate(sint32 num, uint32 base, uint32 limit, uint8 access, uint8 gran)
{
   gdt_entries[num].base_low    = (base & 0xFFFF);
   gdt_entries[num].base_middle = (base >> 16) & 0xFF;
   gdt_entries[num].base_high   = (base >> 24) & 0xFF;

   gdt_entries[num].limit_low   = (limit & 0xFFFF);
   gdt_entries[num].granularity = (limit >> 16) & 0x0F;

   gdt_entries[num].granularity |= gran & 0xF0;
   gdt_entries[num].access      = access;
}

static void init_idt()
{
    idt_ptr.limit = sizeof(idt_entry_t) * 256 -1;
    idt_ptr.base  = (uint32)&idt_entries;

    memset(&idt_entries, 0, sizeof(idt_entry_t)*256);

    idt_set_gate( 0, (uint32)isr0 , 0x08, 0x8E);
    idt_set_gate( 1, (uint32)isr1 , 0x08, 0x8E);
    idt_set_gate( 2, (uint32)isr2 , 0x08, 0x8E);
    idt_set_gate( 3, (uint32)isr3 , 0x08, 0x8E);
    idt_set_gate( 4, (uint32)isr4 , 0x08, 0x8E);
    idt_set_gate( 5, (uint32)isr5 , 0x08, 0x8E);
    idt_set_gate( 6, (uint32)isr6 , 0x08, 0x8E);
    idt_set_gate( 7, (uint32)isr7 , 0x08, 0x8E);
    idt_set_gate( 8, (uint32)isr8 , 0x08, 0x8E);
    idt_set_gate( 9, (uint32)isr9 , 0x08, 0x8E);
    idt_set_gate(10, (uint32)isr10, 0x08, 0x8E);
    idt_set_gate(11, (uint32)isr11, 0x08, 0x8E);
    idt_set_gate(12, (uint32)isr12, 0x08, 0x8E);
    idt_set_gate(13, (uint32)isr13, 0x08, 0x8E);
    idt_set_gate(14, (uint32)isr14, 0x08, 0x8E);
    idt_set_gate(15, (uint32)isr15, 0x08, 0x8E);
    idt_set_gate(16, (uint32)isr16, 0x08, 0x8E);
    idt_set_gate(17, (uint32)isr17, 0x08, 0x8E);
    idt_set_gate(18, (uint32)isr18, 0x08, 0x8E);
    idt_set_gate(19, (uint32)isr19, 0x08, 0x8E);
    idt_set_gate(20, (uint32)isr20, 0x08, 0x8E);
    idt_set_gate(21, (uint32)isr21, 0x08, 0x8E);
    idt_set_gate(22, (uint32)isr22, 0x08, 0x8E);
    idt_set_gate(23, (uint32)isr23, 0x08, 0x8E);
    idt_set_gate(24, (uint32)isr24, 0x08, 0x8E);
    idt_set_gate(25, (uint32)isr25, 0x08, 0x8E);
    idt_set_gate(26, (uint32)isr26, 0x08, 0x8E);
    idt_set_gate(27, (uint32)isr27, 0x08, 0x8E);
    idt_set_gate(28, (uint32)isr28, 0x08, 0x8E);
    idt_set_gate(29, (uint32)isr29, 0x08, 0x8E);
    idt_set_gate(30, (uint32)isr30, 0x08, 0x8E);
    idt_set_gate(31, (uint32)isr31, 0x08, 0x8E);

    //idt_flush((uint32)&idt_ptr);
}

static void idt_set_gate(uint8 num, uint32 base, uint16 sel, uint8 flags)
{
    idt_entries[num].base_lo = base & 0xFFFF;
    idt_entries[num].base_hi = (base >> 16) & 0xFFFF;

    idt_entries[num].sel     = sel;
    idt_entries[num].always0 = 0;
    // We must uncomment the OR below when we get to using user-mode.
    // It sets the interrupt gate's privilege level to 3.
    idt_entries[num].flags   = flags /* | 0x60 */;
}
descriptor_tables.h

Code: Select all

#include <kernel/graphics/common.h>
// Initialisation function is publicly accessible.
void init_descriptor_tables();


// This structure contains the value of one GDT entry.
// We use the attribute 'packed' to tell GCC not to change
// any of the alignment in the structure.
struct gdt_entry_struct
{
    uint16 limit_low;           // The lower 16 bits of the limit.
    uint16 base_low;            // The lower 16 bits of the base.
    uint8  base_middle;         // The next 8 bits of the base.
    uint8  access;              // Access flags, determine what ring this segment can be used in.
    uint8  granularity;
    uint8  base_high;           // The last 8 bits of the base.
} __attribute__((packed));

typedef struct gdt_entry_struct gdt_entry_t;

// This struct describes a GDT pointer. It points to the start of
// our array of GDT entries, and is in the format required by the
// lgdt instruction.
struct gdt_ptr_struct
{
    uint16 limit;               // The upper 16 bits of all selector limits.
    uint32 base;                // The address of the first gdt_entry_t struct.
} __attribute__((packed));

typedef struct gdt_ptr_struct gdt_ptr_t;

// A struct describing an interrupt gate.
struct idt_entry_struct
{
    uint16 base_lo;             // The lower 16 bits of the address to jump to when this interrupt fires.
    uint16 sel;                 // Kernel segment selector.
    uint8  always0;             // This must always be zero.
    uint8  flags;               // More flags. See documentation.
    uint16 base_hi;             // The upper 16 bits of the address to jump to.
} __attribute__((packed));

typedef struct idt_entry_struct idt_entry_t;

// A struct describing a pointer to an array of interrupt handlers.
// This is in a format suitable for giving to 'lidt'.
struct idt_ptr_struct
{
    uint16 limit;
    uint32 base;                // The address of the first element in our idt_entry_t array.
} __attribute__((packed));

typedef struct idt_ptr_struct idt_ptr_t;

// These extern directives let us access the addresses of our ASM ISR handlers.
extern void isr0 ();
extern void isr1 ();
extern void isr2 ();
extern void isr3 ();
extern void isr4 ();
extern void isr5 ();
extern void isr6 ();
extern void isr7 ();
extern void isr8 ();
extern void isr9 ();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
I really want to solve this tripe boot issue it is driving me crazy for days.

-thehardcoreOS
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Antti »

I did not read it all, but are you sure

Code: Select all

mov %ax, 0x10
does what you expected? In NASM assembly it could be

Code: Select all

mov ax, 0x0010      ; register ax = 0x0010
mov ax, [0x0010]    ; register ax = value read from memory address 0x0010
so I recommend you to test

Code: Select all

mov %ax, $0x10		# ; postfix '$'
and if this does not help, please post disassembly.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

Nothing really changes only thing is get is an error. I can't use $ because of syntax for this method.

-thehardcoreOS
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Brendan »

Hi,

This piece of code:

Code: Select all

gdt_flush:
   movl %eax,4(%esp)
   movl 2(%eax),%eax
   movl %ax, 8(%esp)
   movl %eax, ax
   lgdt (%eax) 
   ret
...it makes my head hurt.

I tried adding comments:

Code: Select all

# void gdt_flush(uint32_t address_of_gdt_ptr, uint32_t arg2);

gdt_flush:
   movl %eax,4(%esp)      # eax = address_of_gdt_ptr
   movl 2(%eax),%eax      # Store address_of_gdt_ptr at address "address_of_gdt_ptr + 2" 
   movl %ax, 8(%esp)      # eax = (arg2 & 0x0000FFFF) | (address_of_gdt_ptr & 0xFFFF0000)
   movl %eax, ax          # *** invalid instruction ***
   lgdt (%eax)            # Load GDTR from either:
                          #   - the address "(arg2 & 0x0000FFFF) | (address_of_gdt_ptr & 0xFFFF0000)", or
                          #   - maybe the address "(arg2 & 0x0000FFFF)"
   ret
I don't even know where "arg2" came from - there's only one argument.

I'd recommend:
  • Stop using GAS with intel syntax. It confuses everyone including you. Either use NASM or use GAS with AT&T.
  • Write a single piece of assembly startup code; such that GRUB passes control to your assembly startup code; it sets up GDT, IDT, segment registers, stack, etc; and then it jumps to your "main()" C function.
  • Any assembly code that is used from C should be tiny (and probably should be done in inline assembly).
In addition...

There are mostly only 2 possible bugs that you can have in assembly. The first type of bug is "comments don't describe a sane algorithm/sequence". The second type of bug is "instruction doesn't correspond to its comment".

This makes it extremely easy to find bugs.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

I can't use default NASM because default makefile from osdev.org doesn't allow me that I guess.

I still don't know why am I getting tripe faults and do I need to replace "eax" with some numbers/pointers?
Is there any straight forward way of fixing this or it is just that code is so broken it can't be fixed?

-thehardcoreOS
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by iansjack »

No code is ever so broken that it can't be fixed, if you understand what it is doing. The latter seems to be the cause of most beginners' problems. Cutting and pasting is a dangerous way of working.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

iansjack wrote:No code is ever so broken that it can't be fixed, if you understand what it is doing. The latter seems to be the cause of most beginners' problems. Cutting and pasting is a dangerous way of working.
I guess I will just have to delete everything and idk.

-thehardcoreOS
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

Guys: I managed to set up GDT and all that stuff no more triple fault but now I am getting this crash:

Code: Select all

Writing to 'stdio:foxos.iso' completed successfully.

qemu: fatal: Trying to execute code outside RAM or ROM at 0x13f87db5

EAX=53f00010 EBX=00000000 ECX=000000f0 EDX=00000000
ESI=0000000d EDI=002f6571 EBP=16e7a8f6 ESP=f0115883
EIP=13f87db5 EFL=00200206 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0010 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0018 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0018 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0018 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0018 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00101d60 00000020
IDT=     00000000 00000000
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=000000f0 CCO=ADCL    
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
./qemu.sh: line 5: 31244 Aborted                 (core dumped) qemu-system-$(./target-triplet-to-arch.sh $HOST) -cdrom foxos.iso
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Roman »

I can't use default NASM because default makefile from osdev.org doesn't allow me that I guess.
Can't you modify it? Working without knowing how your build system works is a bad idea. By the way, the Wiki's Makefile is bad, because it doesn't detect files automatically.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by Octacone »

Roman wrote:
I can't use default NASM because default makefile from osdev.org doesn't allow me that I guess.
Can't you modify it? Working without knowing how your build system works is a bad idea. By the way, the Wiki's Makefile is bad, because it doesn't detect files automatically.
Yeah It is not that big of an issue to modify it...It is just that it is kind of complex and all that could be much more simple.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
isaacwoods
Member
Member
Posts: 47
Joined: Sun Sep 06, 2015 5:40 am

Re: GRUB + GDT + IDT = TRIPLE FAULT??

Post by isaacwoods »

Simplify it then. OS dev is not a place to be lazy, either you want to do it properly, make sure you understand what your code is doing and stop taking easy routes out.
thehardcoreOS wrote:I can't use default NASM because default makefile from osdev.org doesn't allow me that I guess.
Well, at some point there will be a target that compiles assembly files using GAS; replace it with a command that invokes NASM instead. Not hard.
Post Reply