General Protection Fault after loading IDT and executing sti

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.
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Greetings!

I am a new(er) OS hobbiest and have been following the excellent "Bran's Kernel Development Tutorial" (http://osdever.net/bkerndev/Docs/pit.htm) but have run into a problem that I have been trying to solve on my own for about six hours now and cannot seem to cut it.

I am somehow generating a General Protection Fault as soon as I invoke "sti" after setting up the IDT, ISR's, and IRQ's. The interesting part about it is that I can invoke software interrupts that I have programmed that print to the screen just fine as long as I don't invoke "sti", so the IDT seems to be at least generally correct. The other interesting part about this is that if I remove the code that reprograms the PIC with the new IRQ mappings, the sti command will NOT cause a GPF.

I have gone over my PIC interrupt remapping code over and over, and it is exactly like it is in the tutorial. I am at my wits end at this point after searching the web, wiki, and forums over and over to no avail. I am using Virtual PC 2007 if that makes a difference, and very much appreciate any feedback in advance. Thank you so much!

-Jay
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: General Protection Fault after loading IDT and executing sti

Post by NickJohnson »

Using "sti" allows IRQs to fire. You probably didn't set the flags correctly on the IRQ 0 entry in the IDT, which would mean that the first time the PIT fires, it raises a GPF. That's why it doesn't fault when you don't set things up - a null entry for an IRQ simply does nothing.
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Nick, thank you for the reply.

After your reply, I went back and checked the flags I am setting in the IDT for IRQ 0. For all of the IRQ entries in the IDT, I have my flags set to 10001110, which appears to be correct. I will paste some code so that someone might be able to spot where I am going wrong in this attempt to use "sti":

This is where I set up the flags for the entries:

Code: Select all

void irq_install()
{
    irq_remap();

	idt_set_gate(32, (unsigned)irq00, 0x08, 0x8E);
	idt_set_gate(33, (unsigned)irq01, 0x08, 0x8E);
	idt_set_gate(34, (unsigned)irq02, 0x08, 0x8E);
	idt_set_gate(35, (unsigned)irq03, 0x08, 0x8E);
	idt_set_gate(36, (unsigned)irq04, 0x08, 0x8E);
	idt_set_gate(37, (unsigned)irq05, 0x08, 0x8E);
	idt_set_gate(38, (unsigned)irq06, 0x08, 0x8E);
	idt_set_gate(39, (unsigned)irq07, 0x08, 0x8E);
	idt_set_gate(40, (unsigned)irq08, 0x08, 0x8E);
	idt_set_gate(41, (unsigned)irq09, 0x08, 0x8E);
	idt_set_gate(42, (unsigned)irq0A, 0x08, 0x8E);
	idt_set_gate(43, (unsigned)irq0B, 0x08, 0x8E);
	idt_set_gate(44, (unsigned)irq0C, 0x08, 0x8E);
	idt_set_gate(45, (unsigned)irq0D, 0x08, 0x8E);
	idt_set_gate(46, (unsigned)irq0E, 0x08, 0x8E);
    idt_set_gate(47, (unsigned)irq0F, 0x08, 0x8E);
}
This is the function definition for irq_remap()

Code: Select all

void irq_remap(void)
{
	// Initialize the master PIC
    outportb(0x20, 0x11);
	// Initialize the slave PIC
	outportb(0xA0, 0x11);
	// Tell the master PIC that interrupt 0x20 should be mapped to IRQ 0
    outportb(0x21, 0x20);
	// Tell the slace PIC that interrupt 0x28 should be mapped to IRQ 8
    outportb(0xA1, 0x28);
	// Tell the master PIC that IRQ line 2 is mapped to the slave PIC
    outportb(0x21, 0x04);
	// Tell the slave PIC that IRQ line 2 is mapped to the master PIC
    outportb(0xA1, 0x02);
	// Setting bit 0 enables 80x86 mode
	outportb(0x21, 0x01);
	// Setting bit 0 enables 80x86 mode
    outportb(0xA1, 0x01);
	// All done - Null out the data registers
	outportb(0x21, 0x00);
    outportb(0xA1, 0x00);
}
This is the idt_set_gate() definition:

Code: Select all

void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
    /* The interrupt routine's base address */
    idt[num].base_lo = (base & 0xFFFF);
    idt[num].base_hi = (base >> 16) & 0xFFFF;

    /* The segment or 'selector' that this IDT entry will use
    *  is set here, along with any access flags */
    idt[num].sel = sel;
    idt[num].always0 = 0;
    idt[num].flags = flags;
}
Let me know if you need to see more, and I will post it. Thanks again in advance!

-Jay
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Hmmm... I have been doing further investigating and have found more clues that might be helpful...
I started having text print to the screen during certain parts of the process, and have figured out that immediately upon executing sti, a single IRQ is being routed through IDT index 47, which is where IRQ 31 should be mapped to according to my code documented above. Upon further research, IRQ 31 should be the IDE controller. Why would this be causing a GPF? I have no idea... please let me know if you have any other info because this is truly a mystery to me.

-Jay
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Hmmm... it gets stranger... I commented out the following line of code:

Code: Select all

idt_set_gate(47, (unsigned)irq0F, 0x08, 0x8E);
And now it is failing on the timer IRQ as originally suggested. ???
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Okay, I have more details. It is causing the GPF at the assembler line noted below, based on all my tracing:

Code: Select all

extern _irq_handler
; This is a stub that we have created for IRQ based ISRs. This calls
; '_irq_handler' in our C code.
irq_common_stub:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    mov eax, _irq_handler
    call eax
    pop eax
    pop gs    <---- if I create an endless loop before this line, it doesn't trigger a GPF. This seems to be the problem
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
Any ideas? I hope this helps. Thanks again in advance.
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: General Protection Fault after loading IDT and executing sti

Post by NickJohnson »

It is probably just faulting whenever it gets any kind of IRQ, which means there is some global problem with how things are set up. If the problem is truly occurring when you reload the GS segment descriptor (or any segment descriptor), there is probably some problem with your GDT, most likely an improper flag or privilege level. Could you post the contents of your GDT / the code that sets it up?
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Sure, thanks Nick.

Here's the code that defines the GDT:

Code: Select all

; global descriptor table

        ; null selector (required)
        gdt dw 0, 0, 0, 0

        ; kernel code selector
        dw 0xffff     ; segment limit (4 gb total)
        dw 0          ; base address (bits 0-15)
        db 0          ; base address (bits (16-24)
        db 10011000b  ; dpl 0, code (execute only)
        db 11001111b  ; granlurarity (4k), 32-bit, limit high nibble = f
        db 0          ; base address (bits 24-32)

        ; kernel data selector
        dw 0xffff     ; segment limit (4 gb total)
        dw 0          ; base address (bits 0-15)
        db 0          ; base address (bits (16-24)
        db 10010010b  ; dpl 0, data (read/write)
        db 11001111b  ; granlurarity (4k), 32-bit, limit high nibble = f
        db 0          ; base address (bits 24-32)

        gdtptr dw 0x7ff ; limit (256 slots)
               dd 0x800 ; base (physical address)
And here's the code that sets it up:

Code: Select all

; setup global decriptor table

        mov ax, 0
        mov es, ax
        mov di, 0x800                   ; destination

        mov si, gdt                     ; source

        mov cx, 24                      ; length
        cld                             ; forward direction
        rep movsb                       ; move gtd to its new location

        lgdt [gdtptr]                   ; load gdt register
Let me know if I am missing anything you might need to make sense of it. I appreciate it a lot, again.
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Also, for what its worth, I have this bit of code just before entering protected mode. Please let me know if this is problematic. I tried disabling it by commenting it out (except for cli), but no change.

Code: Select all

; disable ALL interrupts

        cli                             ; disable interrupts

        mov al, 11111111b               ; select to mask of all irq's
        out 0x21, al                    ; write it to the pic controller

        in al, 0x70                     ; read a value
        or al, 10000000b                ; set the nmi disable bit
        out 0x70, al                    ; write it back again
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: General Protection Fault after loading IDT and executing sti

Post by NickJohnson »

The GDT itself seems to be fine. Are you reloading all of the segment descriptors after loading the GDT pointer? If the bootloader's GDT is different than yours (which is most likely is) and you are still using those segments, there will be trouble when you try and reload things automatically (you may get code and data switched, for example).

Also, I'm not too familiar with the x86 instruction set, so I'm not sure of the exact functionality of the movsb instruction you use to copy the GDT down to 0x800. Double check that everything gets there intact. You can also make the limit size in the GDT pointer structure a lot smaller, because you only have three descriptors, and it may be a security/stability problem in the future.
sebihepp
Member
Member
Posts: 195
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: General Protection Fault after loading IDT and executing sti

Post by sebihepp »

Hmmm, why you first "mov eax, esp" and then "push eax"?
You can push esp directly, if you don't need the value in eax, like in your code you overwrite eax after the
"push eax" operation. The call is still the same. You can write "call _irq_handler".

Next, you push esp, but pop eax. I don't have any clue, why to do this, but perhaps it solves your
problem, when "pop esp" instead?

By the way: Why adding 8 to esp right before iret? That would make sense, if you want to skip
Parameters for an exception, but this is for IRQs, right? Then you must not modify esp.

Perhaps it helps, if you show us your _irq_handler method.

Greetings
Sebihepp
jddy4fun
Posts: 8
Joined: Wed Jun 03, 2009 12:46 pm

Re: General Protection Fault after loading IDT and executing sti

Post by jddy4fun »

Oh.... my.... God......
I found the problem and it is now fixed. I didn't originally have gs loaded with a valid descriptor. I simply made sure my code read like this:

Code: Select all

        mov ax, 0x10
        mov ds, ax                      ; load global data selector into ds
        mov es, ax                      ; load global data selector into es
        mov fs, ax                      ; load global data selector into fs
        mov gs, ax                      ; load global data selector into gs
 
Instead of this, which was causing the GPF because gs wasn't receiving a valid selector:

Code: Select all

        mov ax, 0x10
        mov ds, ax                      ; load global data selector into ds
 
That just makes me feel like I'm having a blonde moment.... and I'm not even blonde!!! Thanks so much for the help... maybe this post will help someone else out there remember to load ALL of their segment registers. LOL!

-Jay
User avatar
NickJohnson
Member
Member
Posts: 1249
Joined: Tue Mar 24, 2009 8:11 pm
Location: Sunnyvale, California

Re: General Protection Fault after loading IDT and executing sti

Post by NickJohnson »

I don't know, I usually kind of like that facepalm moment when you realize what the problem is. That huge amount of agony and frustration is released at once. Generally that's the time you should ask someone for money - they're so intoxicated with serotonin, they'll probably be much nicer. :lol:
User avatar
Firestryke31
Member
Member
Posts: 550
Joined: Sat Nov 29, 2008 1:07 pm
Location: Throw a dart at central Texas
Contact:

Re: General Protection Fault after loading IDT and executing sti

Post by Firestryke31 »

Fortunately we have a thread for moments like this: http://forum.osdev.org/viewtopic.php?f=11&t=19076

Also, I'd guess that it's ES causing problems, not GS. IIRC FS and GS are never used unless specifically stated, while ES is used by instructions like movs* and the like, as well as a few others.
Owner of Fawkes Software.
Wierd Al wrote: You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?
User avatar
hailstorm
Member
Member
Posts: 110
Joined: Wed Nov 02, 2005 12:00 am
Location: The Netherlands

Re: General Protection Fault after loading IDT and executing sti

Post by hailstorm »

Firestryke31 wrote:Also, I'd guess that it's ES causing problems, not GS. IIRC FS and GS are never used unless specifically stated, while ES is used by instructions like movs* and the like, as well as a few others.
Please remember, that on loading the segment registers, using mov or pop, the cpu tries to invalidate the selector, whether it is going to be used or not. Should the selector fall outside the GDT boundaries, or have the wrong segment type, the cpu generates a fault. This should be common knowledge IMHO.
Post Reply