OSDev.org

The Place to Start for Operating System Developers
It is currently Sat Apr 27, 2024 10:39 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Loading GDT causes triple-fault
PostPosted: Thu Jul 26, 2007 9:07 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
Okay, I know that everyone has answered droves of questions related to newbie attempts at using Bran's tutorial. I've personally used the tutorial successfully on numerous occasions (cut and paste tinkering) for the sake of playing around. It's a nice piece of work. Now that I've sat down to actually work on a research kernel, I've managed to code a GDT that triple-faults.

I apologize for such a lenghty post but I sincerely appreciate anyone's help which might clear up this issue. I've worked on this for over a week and have made no progress. :x

I've attached the tarball of my code. It is heavily borrowed from Bran's tutorial to act as a skeleton while I rework his code and start on my additions. If you've fiddled with his tutorial any, the code will look familiar.

Code:
/* gdt.c */
#include <gdt.h>

/* setup descriptor in gdt */
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char granularity)
{
        /* set up descriptor base addr */
        gdt[num].base_lo = (base & 0xFFFF);
        gdt[num].base_mid = (base >> 16) & 0xFF;
        gdt[num].base_hi = (base >> 24) & 0xFF;

        /* set up descriptor limits */
        gdt[num].limit_lo = (limit & 0xFFFF);
        gdt[num].granularity = ((limit >> 16) & 0x0F);

        /* set up granularity and access flags */
        gdt[num].granularity |= (granularity & 0xF0);
        gdt[num].access = access;
}

/* called by main to set up gdt, first 3 entries in gdt, and call gdt_flush */
void gdt_install()
{
        /* setup gdt pointer and limit */
        gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
        gp.base = (unsigned int)(&gdt);

        /* null descriptor */
        gdt_set_gate(0, 0, 0, 0, 0);

        /* cs - base addr is 0, limit is 4gb w/ 4kb gran, 32-bit opcodes */
        gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);

        /* ds - same as code, but descriptor type is ds */
        gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

        /* flush old gdt and install new */
        gdt_flush();
}

Code:
/* include/gdt.h */
#ifndef __GDT_H
#define __GDT_H

/* define gdt entry - packed prevents compiler from dorking it up */
struct gdt_entry
{
        unsigned short limit_lo;
        unsigned short base_lo;
        unsigned char base_mid;
        unsigned char access;
        unsigned char granularity;
        unsigned char base_hi;
} __attribute__ ((__packed__));

/* special pointer - max bytes taken by gdt - 1 */
struct gdt_ptr
{
        unsigned short limit;
        unsigned int base;
} __attribute__ ((__packed__));

/* a simple 3-entry gdt and pointer */
struct gdt_entry gdt[3];
struct gdt_ptr gp;

/* the extern function in loader.asm */
extern void gdt_flush();

void gdt_set_gate(int, unsigned long, unsigned long, unsigned char, unsigned char);
void gdt_install();

#endif


When I run the code from Bochs (or VMware), I get a triple-fault at the far jump.

Code:
; snippet from loader.asm
        ;; the gdt (global descriptor table)
        [global gdt_flush]      ; allow c code to link this
        [extern gp]             ; gp is in gdt.c
gdt_flush:
        lgdt    [gp]
        jmp     0x08:gdt_flush2 ; 0x08 is offset to code seg - far jmp
gdt_flush2:
        mov     ax, 0x10        ; 0x10 is offset in gdt to ds
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax
        ret                     ; return back to c code


I've tried using the original code to look like:

Code:
        ;; the gdt (global descriptor table)
        [global gdt_flush]      ; allow c code to link this
        [extern gp]             ; gp is in gdt.c
gdt_flush:
        lgdt    [gp]
        mov     ax, 0x10        ; 0x10 is offset in gdt to ds
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax
gdt_flush2:
        ret                     ; return back to c code


but that triple-faults at the mov ds, ax operation.

My link.ld looks like:
Code:
OUTPUT_FORMAT("elf32-i386")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
        .text phys : AT(phys)
        {
                code = .;
                *(.text)
                *(.rodata*)
                . = ALIGN(4096);
        }
        .data : AT(phys + (data - code))
        {
                data = .;
                *(.data)
                . = ALIGN(4096);
        }
        .bss : AT(phys + (bss - code))
        {
                bss = .;
                *(.bss)
                *(.COMMON*)
                . = ALIGN(4096);
        }
        end = .;
}


The output from Bochs using the far jump:
Code:
00079523319e[CPU  ] fetch_raw_descriptor: GDT: index (f)1 > limit (0)
00079523319e[CPU  ] interrupt(): gate descriptor is not valid sys seg
00079523319e[CPU  ] interrupt(): gate descriptor is not valid sys seg
00079523319i[CPU  ] protected mode
00079523319i[CPU  ] CS.d_b = 32 bit
00079523319i[CPU  ] SS.d_b = 32 bit
00079523319i[CPU  ] | EAX=2badb002  EBX=0002cc80  ECX=00103000  EDX=00000001
00079523319i[CPU  ] | ESP=00102fcc  EBP=00067ebc  ESI=000538f4  EDI=000538f5
00079523319i[CPU  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00079523319i[CPU  ] | SEG selector     base    limit G D
00079523319i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00079523319i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00079523319i[CPU  ] | EIP=00100032 (00100032)
00079523319i[CPU  ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00079523319i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00079523319i[CPU  ] >> jmp far 0008:00100039 : EA390010000800
00079523319p[CPU  ] >>PANIC<< exception(): 3rd (13) exception with no resolution

The output from Bochs when I use the original gdt_flush looks similar, but hoses at the mov ds, ax.
Code:
00074203321e[CPU  ] fetch_raw_descriptor: GDT: index (17)2 > limit (0)
00074203321e[CPU  ] interrupt(): gate descriptor is not valid sys seg
00074203321e[CPU  ] interrupt(): gate descriptor is not valid sys seg
00074203321i[CPU  ] protected mode
00074203321i[CPU  ] CS.d_b = 32 bit
00074203321i[CPU  ] SS.d_b = 32 bit
00074203321i[CPU  ] | EAX=2bad0010  EBX=0002cc80  ECX=00103000  EDX=00000001
00074203321i[CPU  ] | ESP=00102fcc  EBP=00067ebc  ESI=000538f4  EDI=000538f5
00074203321i[CPU  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af pf cf
00074203321i[CPU  ] | SEG selector     base    limit G D
00074203321i[CPU  ] | SEG sltr(index|ti|rpl)     base    limit G D
00074203321i[CPU  ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00074203321i[CPU  ] | EIP=00100036 (00100036)
00074203321i[CPU  ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00074203321i[CPU  ] | CR3=0x00000000 CR4=0x00000000
00074203321i[CPU  ] >> mov ds, ax : 8ED8
00074203321p[CPU  ] >>PANIC<< exception(): 3rd (13) exception with no resolution

The first lines look suspicious, but I can also see that DS, SS, ES, DS, and GS all look to have 0x10 mov'd to them (which likely happened earlier in the run). :cry: FWIW: I'm using GRUB and I know that GRUB sends us to protected mode and sets up a GDT (of sorts) and that a triple-fault will occur if you step on that one. Could it be that this is what's occuring in my code?

1000 thanks!


Attachments:
kernel-0.1.tar.gz [5.7 KiB]
Downloaded 96 times
Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 9:57 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
I couldn't get your code to triplefault like you did. It triplefaulted on the divide-by-zero you put in in kernel.c. With that removed, it worked fine (see screenshot below).


Attachments:
snapshot12.png
snapshot12.png [ 51.08 KiB | Viewed 3300 times ]
Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 10:11 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
JamesM wrote:
I couldn't get your code to triplefault like you did. It triplefaulted on the divide-by-zero you put in in kernel.c. With that removed, it worked fine (see screenshot below).


Brilliant, I'm on my way to making a Microsoft styled OS (works on some, BSODs on others). Based on your results, I could continue with coding up my IDT, but what gives on my attempts? :(

I guess that this might be related to my dev environment? I'm using Fedora 7 (upgraded from FC6) and it's got gcc-4.1.2-13.fc6, libgcc-4.1.2-13.fc6, and binutils-2.17.50.0.12-4. NASM is 0.99.01 and GRUB 0.97. VMware server is 1.0.3. Bochs is compiled from source using:
Code:
./configure --prefix=/usr/local --with-x11 --enable-debugger --enable-disasm --enable-magic-breakpoints --enable-vbe


Could it be GRUB?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 2:33 pm 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
I doubt it's the toolchain or GRUB. I noticed that you don't disable interrupts anywhere. That could be the problem.

If it's not, feel free to PM me and I can email you the floppy image I made (just copied your kernel.bin into my standard image with grub) and the kernel.bin file (and any obj files you may want to diff).

JamesM


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 2:43 pm 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
JamesM wrote:
I doubt it's the toolchain or GRUB. I noticed that you don't disable interrupts anywhere. That could be the problem.


This is what I've changed the asm code for gdt to (added cli and sti to beginning and end):

Code:
        ;; the gdt (global descriptor table)
        [global gdt_flush]      ; allow c code to link this
        [extern gp]             ; gp is in gdt.c
gdt_flush:
        cli
        lgdt    [gp]
        mov     ax, 0x10        ; 0x10 is offset in gdt to ds
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax
        ;ret      ; EDIT: that ret isn't supposed to be there - DOH!
        jmp     0x08:gdt_flush2 ; 0x08 is offset to code seg - far jmp
gdt_flush2:
        sti
        ret                     ; return back to c code


Same results. I remember reading somewhere (I think in Bran's headers) that GRUB leaves us with interrupts disabled. PM on the way! :)


Last edited by KrnlHckr on Thu Jul 26, 2007 6:47 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Jul 26, 2007 3:46 pm 
Offline
Member
Member

Joined: Sun Jan 14, 2007 9:15 pm
Posts: 2566
Location: Sydney, Australia (I come from a land down under!)
No, you must load the segment registers after the 'jmp 0x08:gdt_flush2'. This basically flushes out all the old segment data and loads the data from the GDT. If it doesn't work, something is wrong with your GDT.

In this case, it's obvious what the problem is (I hope). Somehow your GDT pointer (gp) is not being setup properly and your GDT is being created with a limit of 0 (which means you have no possible way of having data in it).

_________________
Pedigree | GitHub | Twitter | LinkedIn


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 1:06 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 10, 2007 5:27 am
Posts: 2935
Location: York, United Kingdom
pcmattman:

No, that is incorrect. You don't *have* to load the segment registers after the far jump. I do it that way and have no troubles. I also think (IIRC) that bran's tutorial does it that way.

Whatever the error is it's transient, because I cannot reproduce it on my machine. I got his PM and will mail him the code when i get home. I'll also run it on QEMU instead of bochs and see what I can see.

JamesM


Top
 Profile  
 
 Post subject: Bochs 'info cpu'
PostPosted: Fri Jul 27, 2007 9:57 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
Code:
<bochs:2> info cpu
eax:0x00100010, ebx:0x0002cc80, ecx:0x00000001, edx:0x00000001
ebp:0x00102ff8, esp:0x00102fcc, esi:0x000538f4, edi:0x000538f5
eip:0x00100037, eflags:0x00010002, inhibit_mask:0
cs:s=0x0008, dl=0x0000ffff, dh=0x00cf9a00, valid=1
ss:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=7
ds:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=7
es:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=1
fs:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=1
gs:s=0x0010, dl=0x0000ffff, dh=0x00cf9300, valid=1
ldtr:s=0x0000, dl=0x0000ffff, dh=0x00008200, valid=1
tr:s=0x0000, dl=0x0000ffff, dh=0x00008300, valid=1
gdtr:base=0x00000000, limit=0x0
idtr:base=0x00000000, limit=0xffff
dr0:0x00000000, dr1:0x00000000, dr2:0x00000000
dr3:0x00000000, dr6:0xffff0ff0, dr7:0x00000400
cr0:0x00000011, cr1:0x00000000, cr2:0x00000000
cr3:0x00000000, cr4:0x00000000


If I'm reading this correct, the GDT does have a limit of 0x0, even though I pass 0xFFFFFFFF in the function call to gdt_set_gate().

I wrote some lines of C to test the bitwise shifting and masking found in gdt_set_gate() It appears to do what I want it to do (unless I dorked this up too). Still poking around with this to test some ideas...

Code:
$ ./ostest
gp.limit = 0X23
gp.base  = 0x40
gdt[1].base_lo = 0
gdt[1].base_mid = 0
gdt[1].base_hi = 0
gdt[1].limit_lo = 0XFFFF
gdt[1].granularity = 0XF
gdt[1].granularity = 0XC0
gdt[1].access = 0X9A


Code:
$ cat ostest.c
#include <stdio.h>

#define uint32 long
#define uint16 unsigned int
#define uint8  unsigned char

struct gdt_entry
{
        uint16 limit_lo;
        uint16 base_lo;
        uint8  base_mid;
        uint8  access;
        uint8  granularity;
        uint8  base_hi;
} __attribute__ ((__packed__));

struct gdt_ptr
{
        uint16 limit;
        uint8  base;
} __attribute__ ((__packed__));

struct gdt_entry gdt[3];
struct gdt_ptr   gp;

int main(int argc, char *argv[])
{
        int    num    = 1;
        uint32 base   = 0;
        uint32 limit  = 0xFFFFFFFF;
        uint8  access = 0x9A;
        uint8  gran   = 0xCF;

        gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
        gp.base  = (uint16)&gdt;

        printf("gp.limit = %#X\n", gp.limit);
        printf("gp.base  = %p\n", gp.base);

        gdt[num].base_lo  = (base & 0xFFFF);
        gdt[num].base_mid = (base >> 16) & 0xFF;
        gdt[num].base_hi  = (base >> 24) & 0xFF;

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

        printf("gdt[%d].base_lo = %#X\n", num, gdt[num].base_lo);
        printf("gdt[%d].base_mid = %#X\n", num, gdt[num].base_mid);
        printf("gdt[%d].base_hi = %#X\n", num, gdt[num].base_hi);

        printf("gdt[%d].limit_lo = %#X\n", num, gdt[num].limit_lo);
        printf("gdt[%d].granularity = %#X\n", num, gdt[num].granularity);

        gdt[num].granularity = (gran & 0xF0);
        gdt[num].access      = access;

        printf("gdt[%d].granularity = %#X\n", num, gdt[num].granularity);
        printf("gdt[%d].access = %#X\n", num, gdt[num].access);

        return 0;
}


Still hunting though, and I'll post any changes in the situation. Thanks JamesM and pcmattman for your insights. I'm all ears for other suggestions or ideas! :)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 10:09 am 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
pcmattman: I took a stroll through MatisseOS's code (BTW, good job), and I can see the rough outline of the tutorial. I noticed that despite the customization you've added to the skeleton of Bran's tutorial, my test kernel still follows the same path.

Since your OS image works on my Bochs install, I'm going to desk check your code against mine and see if anything obvious stands out. Still, the fact that JamesM can run my kernel image and my system won't befuddles me. My eyes are crossed from staring at the code so long.

Something good has come of this though --- my emacs-fu is getting better! :)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 5:10 pm 
Offline
Member
Member

Joined: Sun Jan 14, 2007 9:15 pm
Posts: 2566
Location: Sydney, Australia (I come from a land down under!)
JamesM wrote:
No, that is incorrect. You don't *have* to load the segment registers after the far jump. I do it that way and have no troubles. I also think (IIRC) that bran's tutorial does it that way.


Loading them afterwards is the best way of knowing for sure that the GDT is loaded and working properly. It also means you have the correct code segment setup already when you start loading the data segments.

@KrnlHckr:

Two things. First of all, the data I see here is wrong:
Code:
gdtr:base=0x00000000, limit=0x0

No base, no limit. Something is wrong with your 'gp' struct.

Second of all, the limits on of your GDT entries are wrong.

I suggest rewriting all the GDT code to match (exactly) Bran's code. At least it's been tested. If you still have trouble, then there's something else happening that we can help with.

_________________
Pedigree | GitHub | Twitter | LinkedIn


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 5:26 pm 
Offline
Member
Member

Joined: Fri Jun 29, 2007 8:36 pm
Posts: 62
are you trying to enter 32-bit protected mode, or just switch the 32-bit


[edit]
from what i see you'll are entering protected mode. the jump has to be right after cr0 is set. according to the intel manuals doing otherwise can cause a broad range of issues. not sure what to look for exactly in c code but i dont even see were the cr0 is being set. but that might be why it works on some systems and not others.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 6:27 pm 
Offline
Member
Member

Joined: Sun Jan 14, 2007 9:15 pm
Posts: 2566
Location: Sydney, Australia (I come from a land down under!)
Ninjarider wrote:
from what i see you'll are entering protected mode. the jump has to be right after cr0 is set. according to the intel manuals doing otherwise can cause a broad range of issues. not sure what to look for exactly in c code but i dont even see were the cr0 is being set. but that might be why it works on some systems and not others.


He already is in protected mode (can't you see the CR0 register in the bochs log?).

_________________
Pedigree | GitHub | Twitter | LinkedIn


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 7:11 pm 
Offline
Member
Member

Joined: Fri Jun 29, 2007 8:36 pm
Posts: 62
i saw that but didn't know if that was before or after the code was ran.
that being the case it is probably the values of the gdt.

can't tell by looking at the cr? values but is paging enabled?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 7:13 pm 
Offline
Member
Member

Joined: Sun Jan 14, 2007 9:15 pm
Posts: 2566
Location: Sydney, Australia (I come from a land down under!)
If paging was enabled CR0 would be 0x80000011 (iirc).

_________________
Pedigree | GitHub | Twitter | LinkedIn


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 27, 2007 7:34 pm 
Offline
Member
Member
User avatar

Joined: Tue Jul 17, 2007 9:16 am
Posts: 36
Location: Washington, DC Metro Area
Lots of stuff to chew on! I'm going to poke around in gdt.c and desk check it against the original Bran code as pcmattman suggested. Weary eyes looking over the same code going on 1+ weeks so far ... I'm sure it's me doing something (anything) stupid.

Ya'll are great! I'll post any success (along with results from JamesM's findings diff'ed against mine).

Side note: I'm setting up CVS. Working on the only copies of these code files is getting dangerous really quickly.

:shock:


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 36 posts ]  Go to page 1, 2, 3  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], SemrushBot [Bot] and 17 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