assemblygccx86osdevmultiboot

Can't setup GDT on Multiboot (Triple fault?)


i am trying to setup GDT from my simple kernel that uses multiboot and i don't know what's happening (i think it's triple fault). when using the qemu monitor trying to debug it but i didn't success addressing the issue so i came here.

here is the code:

// boot.S
#ifndef __BOOT_S
#define __BOOT_S

#define ASM_FILE                1
#include <multiboot.h>

#ifdef ASM_HAS_USCORE
#       define EXT(sym)                         _ ## sym        // EXT(main) = _main
#else
#       define EXT(sym)                         sym             // EXT(main) = main
#endif

#ifdef __ELF__
#       define AOUT_KLUDGE                      0
#else
#       define AOUT_KLUDGE                      MULTIBOOT_AOUT_KLUDGE
#endif

#define STACK_SIZE                              0x4000
#define MULTIBOOT_HEADER_FLAGS                  MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | AOUT_KLUDGE

.text
.globl start, _start
.globl __lgdt // , __sgdt
// .globl __lidt, __sidt

__lgdt:
        pushl %eax
        pushl %ebp
        mov %esp, %ebp
        cli
        pushl (%eax)
        lgdt (%esp)
        ljmp $0x08, $__flush_end

__flush_end:
        popl %eax
        popl %ebp
        movl %ebp, %esp

start:
_start:
        jmp     mb_entry
        .align  MULTIBOOT_INFO_ALIGN

mb_header:
        .long   MULTIBOOT_HEADER_MAGIC
        .long   MULTIBOOT_HEADER_FLAGS
        .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
#ifndef __ELF__
        /* header_addr */
        .long   mb_header
        /* load_addr */
        .long   _start
        /* load_end_addr */
        .long   _edata
        /* bss_end_addr */
        .long   _end
        /* entry_addr */
        .long   mb_entry
#else /* ! __ELF__ */
        .long   0
        .long   0
        .long   0
        .long   0
        .long   0
#endif /* __ELF__ */

mb_entry:
        movl    $(stack + STACK_SIZE), %esp
        pushl   $0
        popfl

        pushl   %ebx
        pushl   %eax

        call    EXT(main)
        ret

loop:
        hlt
        jmp     loop

.comm   stack, STACK_SIZE

#endif
// gdt.h
#include <stdint.h>

#define SEG_DESCTYPE(x)  ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data)
#define SEG_PRES(x)      ((x) << 0x07) // Present
#define SEG_SAVL(x)      ((x) << 0x0C) // Available for system use
#define SEG_LONG(x)      ((x) << 0x0D) // Long mode
#define SEG_SIZE(x)      ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32)
#define SEG_GRAN(x)      ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB)
#define SEG_PRIV(x)     (((x) &  0x03) << 0x05)   // Set privilege level (0 - 3)

#define SEG_DATA_RD        0x00 // Read-Only
#define SEG_DATA_RDA       0x01 // Read-Only, accessed
#define SEG_DATA_RDWR      0x02 // Read/Write
#define SEG_DATA_RDWRA     0x03 // Read/Write, accessed
#define SEG_DATA_RDEXPD    0x04 // Read-Only, expand-down
#define SEG_DATA_RDEXPDA   0x05 // Read-Only, expand-down, accessed
#define SEG_DATA_RDWREXPD  0x06 // Read/Write, expand-down
#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed
#define SEG_CODE_EX        0x08 // Execute-Only
#define SEG_CODE_EXA       0x09 // Execute-Only, accessed
#define SEG_CODE_EXRD      0x0A // Execute/Read
#define SEG_CODE_EXRDA     0x0B // Execute/Read, accessed
#define SEG_CODE_EXC       0x0C // Execute-Only, conforming
#define SEG_CODE_EXCA      0x0D // Execute-Only, conforming, accessed
#define SEG_CODE_EXRDC     0x0E // Execute/Read, conforming
#define SEG_CODE_EXRDCA    0x0F // Execute/Read, conforming, accessed

#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
                SEG_LONG(0)     | SEG_SIZE(1) | SEG_GRAN(1) | \
                SEG_PRIV(0)     | SEG_CODE_EXRD

#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
                SEG_LONG(0)     | SEG_SIZE(1) | SEG_GRAN(1) | \
                SEG_PRIV(0)     | SEG_DATA_RDWR

#define GDT_CODE_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
                SEG_LONG(0)     | SEG_SIZE(1) | SEG_GRAN(1) | \
                SEG_PRIV(3)     | SEG_CODE_EXRD

#define GDT_DATA_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \
                SEG_LONG(0)     | SEG_SIZE(1) | SEG_GRAN(1) | \
                SEG_PRIV(3)     | SEG_DATA_RDWR

int64_t create_descriptor(uint32_t base, uint32_t limit, uint16_t flag) {
        uint64_t descriptor;
        // Create the high 32 bit segment
        descriptor  =  limit       & 0x000F0000;         // set limit bits 19:16
        descriptor |= (flag <<  8) & 0x00F0FF00;         // set type, p, dpl, s, g, d/b, l and avl fields
        descriptor |= (base >> 16) & 0x000000FF;         // set base bits 23:16
        descriptor |=  base        & 0xFF000000;         // set base bits 31:24

        // Shift by 32 to allow for low part of segment
        descriptor <<= 32;

        // Create the low 32 bit segment
        descriptor |= base  << 16;                       // set base bits 15:0
        descriptor |= limit  & 0x0000FFFF;               // set limit bits 15:0

        return descriptor;
}

struct gdt_pointer_s {
        uint16_t size;
        uint32_t offset;
} __attribute__((packed));

#ifndef __DEBUG
extern void __lgdt(void* gdtp);
#endif

#ifdef __DEBUG
#       define printf           __builtin_printf
#endif

int setup_gdt(void) {
        uint64_t gdt[5];
        gdt[0] = create_descriptor(0, 0, 0);
        gdt[1] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0));
        gdt[2] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0));
        gdt[3] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3));
        gdt[4] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3));

        #ifdef __DEBUG
        for (int i = 0; i <= 2; i += 1) {
                printf("%lx\n", gdt[i]);
        }
        #endif

        struct gdt_pointer_s gdtp;
        gdtp.size = sizeof(gdt) - 1;
        gdtp.offset = (uint32_t)&gdt[0];
        #ifndef __DEBUG
        __lgdt(&gdtp);
        #endif
        return 0;
}

i did some modifications to the setup_gdt function where it's know setting the gdt entries to 0xfafafafa

x/64x $esp
00007f9c: 0x0000185b 0x00007fc2 0x00000000 0x00001846
00007fac: 0x00007fc8 0x00000000 0x00000000 0x00000000
00007fbc: 0x2badb002 0x00020000 0x00007fc8 0xfafafafa
00007fcc: 0x00000000 0xfafafafa 0x00000000 0xfafafafa
00007fdc: 0x00000000 0x00000000 0x00000000 0x00008008
00007fec: 0x00010000 0x00000000 0x00000000 0x00000000
....

i was playing around from last night until now also i've searched about it. i searched for it across the internet and found this post on osdev forums but it didn't help.


Solution

  • Thank God Solved.

    boot.S:

    __lgdt:
            mov 4(%esp), %eax
            lgdt (%eax)
            mov $0x10, %eax
            mov %eax, %ds
            mov %eax, %es
            mov %eax, %fs
            mov %eax, %gs
            mov %eax, %ss
            jmp $0x08, $1f
    1:
            ret
    

    gdt.h: NOTE: the __lgdt accepts a value not referance and this value must be the gdt decriptor pointer. Thanks to Michael Petch.

    extern void __lgdt(u32);
    ...
    u64_t gdt[5];               // declared globaly
    struct gdt_pointer_s gdtp;  // same
    
    int setup_gdt(void) {
            gdt[0] = create_descriptor(0, 0, 0);
            gdt[1] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL0));
            gdt[2] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL0));
            gdt[3] = create_descriptor(0, 0x000FFFFF, (GDT_CODE_PL3));
            gdt[4] = create_descriptor(0, 0x000FFFFF, (GDT_DATA_PL3));
    
            #ifdef __DEBUG
            for (int i = 0; i <= 2; i += 1) {
                    printf("%lx\n", gdt[i]);
            }
            #endif
            gdtp.size = sizeof(gdt) - 1;
            gdtp.offset = (uint32_t)&gdt[0];
            #ifndef __DEBUG
            __lgdt(&gdtp);
            #endif
            return 0;
    }