Page 1 of 1

GPF after any interrupt gate is called

Posted: Wed Jan 08, 2020 8:27 pm
by aphexia
Ok so I've just started coding my OS and it has been fun so far. However I ran into a problem.

Whenever an interrupt fires, be it an exception or an IRQ, a loop of general protection faults is fired after the handler for that interrupt.

I've spent hours debugging already and found a bunch of things that aren't the problem.

-The GDT is set up correctly
-The IDT is also set up correctly
-The PIC is set up correctly
-The timer is masked

So after a while i became frustrated and tried bochs.
Error code: cs_check(0x0010): not a valid code segment!

interrupts.c

Code: Select all

#include "Headers/interrupts.h"

void initializeIDT(const uint16_t selector, const uint8_t exceptionTypeAttr, const uint8_t IRQTypeAttr) {
	for (uint16_t i = 0; i < 256; i++)
		setGateDescriptor(i, (uint32_t*)&_unused, selector, IRQTypeAttr);
	
	setGateDescriptor(0, (uint32_t*)&_exception0, selector, exceptionTypeAttr);
	setGateDescriptor(1, (uint32_t*)&_exception1, selector, exceptionTypeAttr);
	setGateDescriptor(2, (uint32_t*)&_exception2, selector, exceptionTypeAttr);
	setGateDescriptor(3, (uint32_t*)&_exception3, selector, exceptionTypeAttr);
	...
	

	setGateDescriptor(32, (uint32_t*)&_irq32, selector, IRQTypeAttr);
	setGateDescriptor(33, (uint32_t*)&_irq33, selector, IRQTypeAttr);
	setGateDescriptor(34, (uint32_t*)&_irq34, selector, IRQTypeAttr);
	...
}

void enableInterrupts() {
	asm volatile ("sti");
}

void disableInterrupts() {
	asm volatile ("cli");
}

void resetPIC(const uint8_t offsetVector) {
	uint8_t PIC1Mask = inb(PORT_NB_PIC_MASTER_DATA);
	uint8_t PIC2Mask = inb(PORT_NB_PIC_SLAVE_DATA);
	//Restart PICs (CW4 needed | cascade mode | edge triggered mode)
	outb(PORT_NB_PIC_MASTER_CMD, 0b00010001);
	outb(PORT_NB_PIC_SLAVE_CMD, 0b00010001);
	//Set vector offset to 32
	outb(PORT_NB_PIC_MASTER_DATA, offsetVector);
	outb(PORT_NB_PIC_SLAVE_DATA, offsetVector + 8);
	//Set up cascade mode
	outb(PORT_NB_PIC_MASTER_DATA, 0x04);
	outb(PORT_NB_PIC_SLAVE_DATA, 0x02);
	//ICW4 (8086 mode)
	outb(PORT_NB_PIC_MASTER_DATA, 0x1);
	outb(PORT_NB_PIC_SLAVE_DATA, 0x1);
	//Clearing masks
	outb(PORT_NB_PIC_MASTER_DATA, PIC1Mask);
	outb(PORT_NB_PIC_SLAVE_DATA, PIC2Mask);
}

void setPICMask(const uint16_t mask) {
	outb(PORT_NB_PIC_MASTER_DATA, (uint8_t)(mask & 0x00FF));
	outb(PORT_NB_PIC_SLAVE_DATA, (uint8_t)(mask >> 8));
}

void resetPICMask() {
	outb(PORT_NB_PIC_MASTER_DATA, 0x0);
	outb(PORT_NB_PIC_SLAVE_DATA, 0x0);
}

uint16_t getPICMask() {
	return ((uint16_t)(inb(PORT_NB_PIC_MASTER_DATA)) |
		((uint16_t)(inb(PORT_NB_PIC_SLAVE_DATA))) << 8);
}

void sendEOIMasterPIC() {
	outb(PORT_NB_PIC_MASTER_CMD, 0x20);
}

void sendEOISlavePIC() {
	outb(PORT_NB_PIC_SLAVE_CMD, 0x20);
}

void handleIRQ(uint8_t nb) {
	printf(" INTERRUPT:");
	hexDump(&nb, 1);
	if (nb  >= 32 && nb < 40)
		sendEOIMasterPIC();
	else if (nb >= 40 && nb < 48) {
		sendEOISlavePIC();
		sendEOIMasterPIC();
	}
}

void handleException(uint8_t nb) {
	clearScreen();
	printf("EXCEPTION ");
	hexDump(&nb, 1);
	printRegisters();
	printf("          STACK: ");
	hexDump(&nb - 16, 24);
	while (1) {}
}
kernelMain.c

Code: Select all

#include "Headers/types.h"
#include "Headers/debug.h"
#include "Headers/GDT.h"
#include "Headers/IDT.h"
#include "Headers/interrupts.h"

void setUpGDT() {
	setSegmentDescriptor(0, 0, 0, 0, 0);
	setSegmentDescriptor(1, 0, 0xFFFFFFFF, GDT_ACCESS_CODE_SEGMENT, 0b1100);
	setSegmentDescriptor(2, 0, 0xFFFFFFFF, GDT_ACCESS_DATA_SEGMENT, 0b1100);
	loadGDT(3);
}

void setUpInterrupts() {
	setPICMask(0x01);
	resetPIC(0x20);
	initializeIDT(1 * 8, IDT_TYPE_ATTR_TRAP_GATE, IDT_TYPE_ATTR_INT_GATE);
	enableInterrupts();
}

extern void kernelMain(void *multiboot_structure, uint32_t magic) {

	clearScreen();
	
	setUpGDT();

	setUpInterrupts();

	while (1) {
	}
}
interruptStubs.s

Code: Select all

.extern handleIRQ
.extern handleException

.global _exception0
_exception0:
	pushal
	pushl $0
	call handleException
	add $4, %esp
	popal
	iretl

.global _exception1
_exception1:
	pushal
	pushl $1
	call handleException
	add $4, %esp
	popal
	iretl

.global _exception2
_exception2:
	pushal
	pushl $2
	call handleException
	add $4, %esp
	popal
	iretl
...

.global _unused
_unused:
	iretl

.global _irq32
_irq32:
	pushal
	pushl $32
	call handleIRQ
	add $4, %esp
	popal
	iretl

.global _irq33
_irq33:
	pushal
	pushl $33
	call handleIRQ
	add $4, %esp
	popal
	iretl

.global _irq34
_irq34:
	pushal
	pushl $34
	call handleIRQ
	add $4, %esp
	popal
	iretl
...

Thanks in advance for taking the time to help me

Re: GPF after any interrupt gate is called

Posted: Wed Jan 08, 2020 11:20 pm
by nullplan
Are you handling exception error codes? Some exceptions (notably #GP and #PF) push an error code to the stack you have to remove before the iret. Otherwise your iret will read garbled data. (See Intel SDM or AMD APM for info on which exceptions those are.)

Other than that I don't see anything obvious. Of course, I would be remiss if I didn't point out that the requested code segment 0x08 and the one error'd by Bochs (0x10) are only one shift apart. Error in setGateDescriptor()?

Re: GPF after any interrupt gate is called

Posted: Wed Jan 08, 2020 11:30 pm
by aphexia
Thanks for your reply!

I dumped a bunch of IDT entries and the selector is actually 0x8, so no error in setGateDescriptoe(). Btw, the handler for the interrupts causing the GPF gets called, so the selector must be right. Correct me if I'm wrong.

I'll try to add 4 to esp on the right exception handlers.

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:58 am
by aphexia
So i put an instruction to add 4 to the stack pointer in the appropriate exception handlers. That way, the error code is taken care of.

It still doesn't work however.

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 10:41 am
by Octocontrabass
What is the value of the CS register before you enable interrupts?

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 11:48 am
by aphexia
Ok so the value of CS before an interrupt gate gets called is 0x10 so that must be the problem.

Now the question is how do i change the value of CS to 0x08?

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:17 pm
by Octocontrabass
I suggest a far JMP. The correct syntax will depend on where in your code you want to put it.

If you're feeling clever, you might also be interested in a far RET. There are other instructions too.

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:20 pm
by aphexia
So what, I just jmpf 0x08:next instruction?

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:30 pm
by Octocontrabass
Yep.

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:45 pm
by aphexia
How do i know what the address of the next instruction is? Can i just save the value of eip and do some basic math?

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 12:46 pm
by Octocontrabass
Put a label before the next instruction and use the label. The assembler will fill in the correct address.

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 1:04 pm
by aphexia
Can i do that in inline assembly, cause i think labels aren't accepted?

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 1:30 pm
by Octocontrabass
Yes. For example:

Code: Select all

asm( "ljmp $0x08, $.Lnext%=\n.Lnext%=:\t" : );

Re: GPF after any interrupt gate is called

Posted: Thu Jan 09, 2020 1:44 pm
by aphexia
Omg thanks a lot!!!

Now i dont get the GPF anymore.

However I only get one interrupt so the EOI must be incorrectly done.