General Protection Fault after loading IDT and executing sti
General Protection Fault after loading IDT and executing sti
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
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
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: General Protection Fault after loading IDT and executing sti
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.
Re: General Protection Fault after loading IDT and executing sti
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:
This is the function definition for irq_remap()
This is the idt_set_gate() definition:
Let me know if you need to see more, and I will post it. Thanks again in advance!
-Jay
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);
}
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);
}
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;
}
-Jay
Re: General Protection Fault after loading IDT and executing sti
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
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
Re: General Protection Fault after loading IDT and executing sti
Hmmm... it gets stranger... I commented out the following line of code:
And now it is failing on the timer IRQ as originally suggested. ???
Code: Select all
idt_set_gate(47, (unsigned)irq0F, 0x08, 0x8E);
Re: General Protection Fault after loading IDT and executing sti
Okay, I have more details. It is causing the GPF at the assembler line noted below, based on all my tracing:
Any ideas? I hope this helps. Thanks again in advance.
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
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: General Protection Fault after loading IDT and executing sti
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?
Re: General Protection Fault after loading IDT and executing sti
Sure, thanks Nick.
Here's the code that defines the GDT:
And here's the code that sets it up:
Let me know if I am missing anything you might need to make sense of it. I appreciate it a lot, again.
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)
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
Re: General Protection Fault after loading IDT and executing sti
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
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: General Protection Fault after loading IDT and executing sti
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.
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.
-
- 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
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
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
Re: General Protection Fault after loading IDT and executing sti
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:
Instead of this, which was causing the GPF because gs wasn't receiving a valid selector:
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
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
Code: Select all
mov ax, 0x10
mov ds, ax ; load global data selector into ds
-Jay
- NickJohnson
- Member
- Posts: 1249
- Joined: Tue Mar 24, 2009 8:11 pm
- Location: Sunnyvale, California
Re: General Protection Fault after loading IDT and executing sti
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.
- Firestryke31
- 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
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.
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?
Re: General Protection Fault after loading IDT and executing sti
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.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.