Loading the segment register in the IRQ handler causes #GP
Loading the segment register in the IRQ handler causes #GP
Hi.
I am trying to handle IRQ and exceptions, my exceptions are handled as needed, but processing IRQ causes General Protection Fault.
I will describe what is happening.
After setting up the GDT and IDT I allow interrupts and immediately takes place from the PIT interrupt(IRQ0).
Since I'm redefining interrupts for master and slave PIC here (the function is taken from the wiki), I have the 32nd interrupt triggered.
An interrupt handler is called that should take care of the segment registers, but there is a problem with this line(#GP).
At this point, the handler for the 13th interrupt is called.
However, in its case, this error does not occur, it successfully loads the registers and passes control back to the handler for IRQ0. And everything repeats itself over and over again.
It turns out that the exception handler from PIT can't load segment registers, but the processor exception handler does it successfully.
I am trying to handle IRQ and exceptions, my exceptions are handled as needed, but processing IRQ causes General Protection Fault.
I will describe what is happening.
After setting up the GDT and IDT I allow interrupts and immediately takes place from the PIT interrupt(IRQ0).
Since I'm redefining interrupts for master and slave PIC here (the function is taken from the wiki), I have the 32nd interrupt triggered.
An interrupt handler is called that should take care of the segment registers, but there is a problem with this line(#GP).
At this point, the handler for the 13th interrupt is called.
However, in its case, this error does not occur, it successfully loads the registers and passes control back to the handler for IRQ0. And everything repeats itself over and over again.
It turns out that the exception handler from PIT can't load segment registers, but the processor exception handler does it successfully.
Re: Loading the segment register in the IRQ handler causes #
I think you should pack the registers structure. That should fix it.
Re: Loading the segment register in the IRQ handler causes #
I added __attribute__((packed)) to the structure with registers but it didn't change anything.nexos wrote:I think you should pack the registers structure. That should fix it.
I don't understand why the handler behaves this way when interacting with IRQ.
Re: Loading the segment register in the IRQ handler causes #
Is there any substantial difference between the interrupt gates for IRQ 8 and the GPF? For example, do they reference the same CS? Not that you're trying to run the IRQ handler in usermode...
Carpe diem!
Re: Loading the segment register in the IRQ handler causes #
When configuring IDT, I specify the same data for the core segment selector (0x08) and access flags (0x8E)nullplan wrote:Is there any substantial difference between the interrupt gates for IRQ 8 and the GPF? For example, do they reference the same CS? Not that you're trying to run the IRQ handler in usermode...
-
- Member
- Posts: 5759
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Loading the segment register in the IRQ handler causes #
So, what value is in AX here? Is it a valid segment selector?mrjbom wrote:but there is a problem with this line(#GP).
It sounds like you enabled interrupts while you had invalid selectors in your segment registers.
Re: Loading the segment register in the IRQ handler causes #
Here is the data on the registers at different points in code:Octocontrabass wrote:So, what value is in AX here? Is it a valid segment selector?mrjbom wrote:but there is a problem with this line(#GP).
before pusha
Code: Select all
EAX=00000000 EBX=00010000 ECX=000003ec EDX=012472b4
ESI=00000000 EDI=00000000 EBP=0114a8c8 ESP=0114a898
EIP=00108e39 EFL=00200006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0018 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00ff9a00 DPL=0 CS64 [-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= 00147f60 00000017
IDT= 00147fa0 000007ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
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
Code: Select all
EAX=00000020 EBX=00010000 ECX=0114a32f EDX=00000020
ESI=00000000 EDI=00000000 EBP=0114a8c8 ESP=0114a874
EIP=00108e4f EFL=00200002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00ff9a00 DPL=0 CS64 [-R-]
SS =0018 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00147f60 00000017
IDT= 00147fa0 000007ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
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
Code: Select all
EAX=00000018 EBX=00010000 ECX=0114a32f EDX=00000020
ESI=00000000 EDI=00000000 EBP=0114a8c8 ESP=0114a878
EIP=00108e50 EFL=00200002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00ff9a00 DPL=0 CS64 [-R-]
SS =0018 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00ff9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00147f60 00000017
IDT= 00147fa0 000007ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
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
The value must be correct, I don't know why it doesn't like mov %eax, %ds
Here I turn off interruptsOctocontrabass wrote:It sounds like you enabled interrupts while you had invalid selectors in your segment registers.mrjbom wrote:but there is a problem with this line(#GP).
I turn then on here
isr_handler() does not enable interrupts during its work.
In the register information, I noticed changes in EFLAGS, maybe they lead to something?
-
- Member
- Posts: 5759
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Loading the segment register in the IRQ handler causes #
No, not necessarily. The value is just a selector, but segment registers contain a selector and a descriptor. The descriptor part of a segment register can't be accessed directly. Any time you load a segment register, you're writing the selector part of the register, and the CPU automatically fills in the descriptor part using a descriptor from the GDT or LDT.mrjbom wrote:The value must be correct,
So, what if the GDT has changed since the last time you wrote that value into the segment register?
Re: Loading the segment register in the IRQ handler causes #
Hmm, I have no idea what to do...Octocontrabass wrote:No, not necessarily. The value is just a selector, but segment registers contain a selector and a descriptor. The descriptor part of a segment register can't be accessed directly. Any time you load a segment register, you're writing the selector part of the register, and the CPU automatically fills in the descriptor part using a descriptor from the GDT or LDT.mrjbom wrote:The value must be correct,
So, what if the GDT has changed since the last time you wrote that value into the segment register?
In addition, I don't know how GDT could have changed while the handler was running, and I also don't understand why this doesn't happen when processing interrupts from processor exceptions.
Do you have any ideas or suggestions?
-
- Member
- Posts: 5759
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Loading the segment register in the IRQ handler causes #
No, I'm talking about before you ever enable interrupts.mrjbom wrote:In addition, I don't know how GDT could have changed while the handler was running,
Re: Loading the segment register in the IRQ handler causes #
What? I don't fully understand what you mean.Octocontrabass wrote:No, I'm talking about before you ever enable interrupts.mrjbom wrote:In addition, I don't know how GDT could have changed while the handler was running,
-
- Member
- Posts: 5759
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Loading the segment register in the IRQ handler causes #
Segment registers contain a copy of the descriptor from the GDT (or LDT).
When you modify the GDT, the descriptor inside the segment register does not change, so your OS does not crash even if the segment selector is no longer valid.
When an interrupt happens, you save and restore the invalid segment selector. You get #GP from trying to restore an invalid segment selector.
Does that make sense?
When you modify the GDT, the descriptor inside the segment register does not change, so your OS does not crash even if the segment selector is no longer valid.
When an interrupt happens, you save and restore the invalid segment selector. You get #GP from trying to restore an invalid segment selector.
Does that make sense?
Re: Loading the segment register in the IRQ handler causes #
What Octocontrabass is basically saying is that when you run lgdt, directly after you should put the new data segment selector in ds, es, fs, gs, and ss, and the code segment selector in cs. 0x18 is GRUB's data segment. You are trying to load that into ds. It is invalid, causing a GPF. It worked before because the CPU cached it. For example, I do (Intel syntax, and it is x86_64 code)
Code: Select all
HalGdtFlush:
lgdt [gdtptr]
mov ax, 10h
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push 0x08
push Flush
retfq
Flush:
ret
Re: Loading the segment register in the IRQ handler causes #
nexos wrote:What Octocontrabass is basically saying is that when you run lgdt, directly after you should put the new data segment selector in ds, es, fs, gs, and ss, and the code segment selector in cs. 0x18 is GRUB's data segment. You are trying to load that into ds. It is invalid, causing a GPF. It worked before because the CPU cached it.
Well, I didn't think the processor was caching data and that's the reason.Octocontrabass wrote:Segment registers contain a copy of the descriptor from the GDT (or LDT).
When you modify the GDT, the descriptor inside the segment register does not change, so your OS does not crash even if the segment selector is no longer valid.
When an interrupt happens, you save and restore the invalid segment selector. You get #GP from trying to restore an invalid segment selector.
Does that make sense?
Thank you very much for your help and explanation♥
Re: Loading the segment register in the IRQ handler causes #
Caching the segment registers is an easy way to get tripped-up, but it's a fairly fundamental way the processor operates.mrjbom wrote:Well, I didn't think the processor was caching data and that's the reason.
Consider this standard sequence for going into protected mode:
- load GDT
- set bit 0 of cr0
- jmp to new cs:offset
If the CPU didn't cache segments registers, the jmp would fail, because cs wouldn't have a valid segment selector.
Technically, despite what the documentation says, you don't HAVE to load the GDTR before you go into protected mode, though you won't get very far if you don't. Instructions that follow setting cr0:PE will continue to work as long as they don't reference data segments or cause far jumps, calls, or exceptions. (you'll note that some example code loads the data segment registers after enabling PE but before the jmp).