Page 1 of 1

Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 2:27 pm
by CalmBit
Hi all,

I've been working at this bug for an obscenely long amount of time and I'm completely stumped, so I'd like to submit it here just in case there's any dumb mistakes I've made along the way that have been missed in the blindness of looking over code, the bochs debugger, and objdump a thousand times over.

The main symptom of the bug is a triple fault upon `iret` from the PIC IRQ ISR. The only real leads I have are the code selector being invalid (Bochs yells at me about this), and the GDT dump seems to confirm this, although I don't know what is clobbering it.

Immediately upon entering the ISR, it seems like the Data segment gets clobbered by `pusha`, or at least that's what it appears to be.

The code called by the ISR:

Code: Select all

00100c61 <pitHandler>:
  100c61:       60                      pusha  
  100c62:       fc                      cld    
  100c63:       e8 98 fb ff ff          call   100800 <pit_handler>
  100c68:       61                      popa   
  100c69:       cf                      iret  
The code for pit_handler:

Code: Select all

00100800 <pit_handler>:
  100800:       83 ec 18                sub    $0x18,%esp
  100803:       68 a4 16 10 00          push   $0x1016a4
  100808:       e8 f3 01 00 00          call   100a00 <kprint>
  10080d:       b8 20 00 00 00          mov    $0x20,%eax
  100812:       e6 20                   out    %al,$0x20
  100814:       83 c4 1c                add    $0x1c,%esp
  100817:       c3                      ret    
  100818:       8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi
  10081f:       90                      nop

GDT at `hlt` instruction waiting for interrupts (in my kernel's main function)

Code: Select all

Global Descriptor Table (base=0x00106fe4, limit=24):
GDT[0x00]=??? descriptor hi=0x00400000, lo=0x00000000
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x02]=Data segment, base=0x00410010, limit=0x00001741, Read/Write, Expand-down, Accessed
(Note: I'm not sure why the data segment in this case has a weird base and limit - I did some digging to figure out what might be causing this, and I'm not sure if it's expected behaviour, but it doesn't seem to effect the functioning of the kernel at the moment, so I figured it was either a.) normal or b.) innocuous enough to be dealt with later. Please also note that the data segment is absolutely fine [i.e. base=0x0 limit=0xffffffff] after the load and reloading of segments.)


GDT after entering the ISR - (one instruction in, i.e. resting on the `cld` instruction, which makes me think it's the `pusha` instruction?)

Code: Select all

Global Descriptor Table (base=0x00106fe4, limit=24):
GDT[0x00]=??? descriptor hi=0x00400000, lo=0x00000000
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Non-Conforming, Accessed, 32-bit
GDT[0x02]=??? descriptor hi=0x00000000, lo=0x00000000
After two more steps (with the debugger resting on the stack pointer subtraction in pit_handler), it gets even worse.

Code: Select all

GDT[0x00]=??? descriptor hi=0x00400000, lo=0x00000000
GDT[0x01]=32-Bit Call Gate target=0x0000:0x0010ffff, DPL=0
GDT[0x02]=??? descriptor hi=0x00000000, lo=0x00000000
This is, to the best of my knowledge, NOT a proper GDT :D

What might I be missing offhand?

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 2:54 pm
by Octocontrabass
Looks like your stack might be overwriting your GDT.

The CPU caches the GDT whenever a segment register is loaded, so it doesn't use the corrupt GDT until the IRET.

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 2:57 pm
by Combuster
GDT[0x00]=??? descriptor hi=0x00400000, lo=0x00000000
And technically, your GDT is already clobbered before you start :wink:

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 3:23 pm
by CalmBit
Octocontrabass wrote:Looks like your stack might be overwriting your GDT.

The CPU caches the GDT whenever a segment register is loaded, so it doesn't use the corrupt GDT until the IRET.
Bingo! Wow, I really should have put that together, but unfortunately my brain just just completely fried. I'll see what I can do to make my stack not kill it, thank you very much!
Combuster wrote:
GDT[0x00]=??? descriptor hi=0x00400000, lo=0x00000000
And technically, your GDT is already clobbered before you start :wink:
Is it? I was tying to follow the advice of the wiki to add a null segment at the beginning - I guess that isn't very null though, is it...

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 3:53 pm
by CalmBit
I hope I'm not sounding like a beggar or an idiot here, but I'm not exactly sure what I'd need to do to fix this. I've expanded the stack size multiple times to try to see if it would help push the two a bit away, but the error seems to be sticking around. Perhaps I'm misunderstanding how the linker file is even supposed to function.

Would reserving space in BSS assist with any of this? The GDT seems to be inexorably tied near the stack pointer, and moves whenever I reserve more space. Makes sense if the GDT is being allocated on the BSS, but I'm not sure how or why my stack is clobbering the GDT.

EDIT: Well, something is absolutely wrong. The GDT seems to be allocated on the stack rather than in BSS. I'm immensely confused what I've done to get to this point, honestly.

The stack is between 0x00103030 and 0x00113030. My GDT is apparently allocated at 0x00112fe4, about 0x4C up the stack.

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 4:17 pm
by alexfru
CalmBit wrote: EDIT: Well, something is absolutely wrong. The GDT seems to be allocated on the stack rather than in BSS. I'm immensely confused what I've done to get to this point, honestly.

The stack is between 0x00103030 and 0x00113030. My GDT is apparently allocated at 0x00112fe4, about 0x4C up the stack.
Can you show us the code that declares and sets up the GDT and GDTR?

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 5:25 pm
by eryjus
From experience: Is paging enabled? If so, could you have multiple pages mapped to the frame containing the GDT?

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 9:22 pm
by CalmBit
alexfru wrote:
CalmBit wrote: EDIT: Well, something is absolutely wrong. The GDT seems to be allocated on the stack rather than in BSS. I'm immensely confused what I've done to get to this point, honestly.

The stack is between 0x00103030 and 0x00113030. My GDT is apparently allocated at 0x00112fe4, about 0x4C up the stack.
Can you show us the code that declares and sets up the GDT and GDTR?
The main code for assembling the GDT and GDTR looks like this:

(might look familiar - this is a bit of a barebones/tutorial kernel, and although I've tried my best to learn from it and refactor it, sometimes if something "works" you just keep it)

Code: Select all

void encodeGdtEntry(uint8_t *target, GDTEntry source)
{
  if((source.limit > 65536) && ((source.limit & 0xFFF) != 0xFFF))
    {
	kpanic("Source limit over integer limit and not a 0xFFF number");
    }
  else if(source.limit > 65536)
    {
      source.limit = source.limit >> 12;
      target[6] = 0xC0;
    }
  else
    {
      target[6] = 0x40;
    }

  target[0] = source.limit & 0xFF;
  target[1] = (source.limit >> 8) & 0xFF;
  target[6] |= (source.limit >> 16) & 0xF;

  target[2] = source.base & 0xFF;
  target[3] = (source.base >> 8) & 0xFF;
  target[4] = (source.base >> 16) & 0xFF;
  target[7] = (source.base >> 24) & 0xFF;
  target[5] = source.type;
}

void setup_gdt()
{
  GDTEntry gdtEnt[3]= {	{.base=0, .limit=0, .type=0},
			{.base=0, .limit=0xffffffff, .type=0x9A},
			{.base=0, .limit=0xffffffff, .type=0x92} };

  uint8_t gdtTarg[3*8];
  encodeGdtEntry(&gdtTarg[0], gdtEnt[0]);
  encodeGdtEntry(&gdtTarg[8], gdtEnt[1]);
  encodeGdtEntry(&gdtTarg[16], gdtEnt[2]);
  setGdt(gdtTarg, sizeof(gdtTarg));
}
That, in turns, calls this piece of assembly.

Code: Select all

gdtr:
limit: .word 0
base:  .long 0

.globl setGdt
setGdt:
	movl 4(%esp), %eax
	movl %eax, (base)
	movw 8(%esp), %ax
	movw %ax, (gdtr)
	lgdt (gdtr)
	ret

.globl reloadSegments
reloadSegments:
	ljmp $0x08, $reload_CS

reload_CS:
	movw $0x10, %ax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw %ax, %gs
	movw %ax, %ss
	xchg %bx, %bx
	ret
eryjus wrote:From experience: Is paging enabled? If so, could you have multiple pages mapped to the frame containing the GDT?
Paging is not enabled, but good thought :)

Re: Triple Fault on PIC IRQ

Posted: Sun Nov 18, 2018 11:57 pm
by alexfru
So, what storage class does gdtTarg have? Automaic?

Re: Triple Fault on PIC IRQ

Posted: Mon Nov 19, 2018 12:33 am
by CalmBit
alexfru wrote:So, what storage class does gdtTarg have? Automaic?
Oh boy...this might be just an absolutely giant egg on my face.

For some unknowable, downright awful reason, I assumed that by declaring the array with constants, it would be preserved. Thinking about it again, critically...yeah, it's probably automatic. Whoops.

Re: Triple Fault on PIC IRQ

Posted: Mon Nov 19, 2018 12:46 am
by CalmBit
Thanks to everyone for helping - it has been fixed! The final fix was the following:

The stack was indeed clobbering the GDT - I had accidentally allocated it on the stack as a result of gdtTarg, the main array where my GDT was located, being declared inside of the function. I didn't think about the semantics of this, and the fix was to simply declare it outside of the function in order to allocate it in .bss :)