Loading the segment register in the IRQ handler causes #GP

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.
Post Reply
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Loading the segment register in the IRQ handler causes #GP

Post by mrjbom »

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.
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Loading the segment register in the IRQ handler causes #

Post by nexos »

I think you should pack the registers structure. That should fix it.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

nexos wrote:I think you should pack the registers structure. That should fix it.
I added __attribute__((packed)) to the structure with registers but it didn't change anything.
I don't understand why the handler behaves this way when interacting with IRQ.
nullplan
Member
Member
Posts: 1871
Joined: Wed Aug 30, 2017 8:24 am

Re: Loading the segment register in the IRQ handler causes #

Post by nullplan »

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!
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

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...
When configuring IDT, I specify the same data for the core segment selector (0x08) and access flags (0x8E)
Octocontrabass
Member
Member
Posts: 5759
Joined: Mon Mar 25, 2013 7:01 pm

Re: Loading the segment register in the IRQ handler causes #

Post by Octocontrabass »

mrjbom wrote:but there is a problem with this line(#GP).
So, what value is in AX here? Is it a valid segment selector?

It sounds like you enabled interrupts while you had invalid selectors in your segment registers.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

Octocontrabass wrote:
mrjbom wrote:but there is a problem with this line(#GP).
So, what value is in AX here? Is it a valid segment selector?
Here is the data on the registers at different points in code:

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
before pop %eax

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
before mov %eax, %ds and #GP exception

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
I don't notice any errors here, after pop %eax and before mov the value is the same as it was before calling the interrupt handler.
The value must be correct, I don't know why it doesn't like mov %eax, %ds
Octocontrabass wrote:
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.
Here I turn off interrupts
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?
Octocontrabass
Member
Member
Posts: 5759
Joined: Mon Mar 25, 2013 7:01 pm

Re: Loading the segment register in the IRQ handler causes #

Post by Octocontrabass »

mrjbom wrote:The value must be correct,
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.

So, what if the GDT has changed since the last time you wrote that value into the segment register?
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

Octocontrabass wrote:
mrjbom wrote:The value must be correct,
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.

So, what if the GDT has changed since the last time you wrote that value into the segment register?
Hmm, I have no idea what to do...

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?
Octocontrabass
Member
Member
Posts: 5759
Joined: Mon Mar 25, 2013 7:01 pm

Re: Loading the segment register in the IRQ handler causes #

Post by Octocontrabass »

mrjbom wrote:In addition, I don't know how GDT could have changed while the handler was running,
No, I'm talking about before you ever enable interrupts.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

Octocontrabass wrote:
mrjbom wrote:In addition, I don't know how GDT could have changed while the handler was running,
No, I'm talking about before you ever enable interrupts.
What? I don't fully understand what you mean.
Octocontrabass
Member
Member
Posts: 5759
Joined: Mon Mar 25, 2013 7:01 pm

Re: Loading the segment register in the IRQ handler causes #

Post by Octocontrabass »

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?
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Loading the segment register in the IRQ handler causes #

Post by nexos »

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
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: Loading the segment register in the IRQ handler causes #

Post by mrjbom »

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.
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?
Well, I didn't think the processor was caching data and that's the reason.
Thank you very much for your help and explanation♥
sj95126
Member
Member
Posts: 151
Joined: Tue Aug 11, 2020 12:14 pm

Re: Loading the segment register in the IRQ handler causes #

Post by sj95126 »

mrjbom wrote:Well, I didn't think the processor was caching data and that's the reason.
Caching the segment registers is an easy way to get tripped-up, but it's a fairly fundamental way the processor operates.

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).
Post Reply