[Solved] Interrupt descriptor table problem

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
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

[Solved] Interrupt descriptor table problem

Post by Matt1223 »

Hi, I have a problem because my interrupts doesn't works. I set IDT and then divide by 0 to get interrupt. Unfortunately it gets triple fault instead. I run my OS on bochs. It's 32 bits OS. I use Nasm inline assembly by adding -masm=intel while compilation.

Here are the source files:

_____________________kernel.c________________________

Code: Select all

#include "kernel.h"

void _start(void)
{
	term = New_TerminalB8000();
	T_Clear(term);
	idt_init();
	
	int a = 5;
	int b = 0;
	T_Print(term, "%d", a/b);
		
	for(;;);
}
_____________________idt.h________________________

Code: Select all

#pragma once
#include "common.h"
#include "int_handlers.h"
#include "terminal.h"
#include "kernel.h"

struct IDTEntry
{
   uint16_t offset_0_15; // offset bits 0..15
   uint16_t selector; // a code segment selector in GDT or LDT
   uint8_t zero;      // unused, set to 0
   uint8_t flags; // type and attributes, see below
   uint16_t offset_16_31; // offset bits 16..31
}__attribute__((packed));

struct IDTR
{
	uint16_t limit;
	uint32_t base;
}__attribute__((packed));

typedef struct IDTEntry IDTEntry;
typedef struct IDTR IDTR;


void idt_init();
_____________________idt.c________________________

Code: Select all

#include "idt.h"

#define SETIDTDESCR(d, offset) { \
	d.offset_0_15 = ((uint32_t)offset & 0xffff); \
	d.selector = 0x8; \
	d.zero = 0; \
	d.flags = 0x8E; \
	d.offset_16_31 = (((uint32_t)offset >> 16) & 0xffff); \
}

IDTEntry IDT[256];

void idt_init()
{
	for(int i=0; i<=32; i++)
		SETIDTDESCR(IDT[i], interrupt_handler);
	
	IDTR ptr = {
		(uint16_t)((256 * 8 ) - 1),
		(uint32_t)&IDT
	};
	
	__asm("lidt [0]" : : "m"(ptr));
}
_____________________int_handlers.h________________________

Code: Select all

#pragma once
#include "common.h"
#include "kernel.h"

void interrupt_handler();
_____________________int_handlers.c________________________

Code: Select all

#include "int_handlers.h"

void interrupt_handler()
{
	T_Print(term, "Interrupt");
	for(;;);
}
Please help me!
Last edited by Matt1223 on Wed Aug 01, 2018 2:42 am, edited 5 times in total.
frabert
Member
Member
Posts: 38
Joined: Wed Jul 25, 2018 2:47 pm
Location: Pizzaland, Southern Europe
Contact:

Re: Interrupt descriptor table problem

Post by frabert »

You can use [ code ] tags to show your source listings so they are nicer to read. That being said, have you read this? You can't simply use a standard C function as an ISR, it does not have the correct cleanup/return procedure.
quirck
Member
Member
Posts: 42
Joined: Sun Nov 23, 2008 5:56 am
Location: Russia, Saint-Petersburg

Re: Interrupt descriptor table problem

Post by quirck »

Shouldn't

Code: Select all

__asm("lidt [0]" : : "m"(ptr));
be

Code: Select all

__asm("lidt [%0]" : : "m"(ptr));
instead?

Also, what error does Bochs print?
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

frabert wrote:You can use [ code ] tags to show your source listings so they are nicer to read. That being said, have you read this? You can't simply use a standard C function as an ISR, it does not have the correct cleanup/return procedure.
Thanks for the tip with [ code ] :D !
I know that I can't use standard C function as an ISR, this is temporarily solution. I want to make IDT working and then I will make proper ISR.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

quirck wrote:Shouldn't

Code: Select all

__asm("lidt [0]" : : "m"(ptr));
be

Code: Select all

__asm("lidt [%0]" : : "m"(ptr));
instead?

Also, what error does Bochs print?

Code: Select all

__asm("lidt [%0]" : : "m"(ptr));
It doesn't work, while compiling I get this message:

C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s: Assembler messages:
C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s:44: Error: unsupported instruction `lidt'

Bochs doesn't show any message, it's just reseting. It's called Triple Fault. By the way, I use Nasm assembly by adding -masm=intel while compilation.
Last edited by Matt1223 on Mon Jul 30, 2018 10:15 am, edited 1 time in total.
frabert
Member
Member
Posts: 38
Joined: Wed Jul 25, 2018 2:47 pm
Location: Pizzaland, Southern Europe
Contact:

Re: Interrupt descriptor table problem

Post by frabert »

Add reset_on_triple_fault=0 to your cpu configuration in Bochs to keep it running after a triple fault. Also, run Bochs via a terminal so you can see its output.
quirck
Member
Member
Posts: 42
Joined: Sun Nov 23, 2008 5:56 am
Location: Russia, Saint-Petersburg

Re: Interrupt descriptor table problem

Post by quirck »

Matt1223 wrote:

Code: Select all

__asm("lidt [0]" : : "m"(ptr));
It doesn't work, while compiling I get this message:

C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s: Assembler messages:
C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s:44: Error: unsupported instruction `lidt'
Hm, it's tricky to get gcc to work with lidt using intel syntax. I can't get rid of "QWORD PTR" in assembly, and lidt QWORD PTR ... is obviously wrong, hence the error.

Using AT&T syntax, it's

Code: Select all

__asm("lidt %0" : : "m"(ptr));
With intel syntax, I managed to get the right output only using a register:

Code: Select all

__asm("lidt [%0]" : : "r"(&ptr));
Anyway, "lidt [0]" tries to load the idtr from address 0, and ptr is not used at all.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

frabert wrote:Add reset_on_triple_fault=0 to your cpu configuration in Bochs to keep it running after a triple fault. Also, run Bochs via a terminal so you can see its output.
I set it and it gives this message:
"exception(): 3rd (13) exception with no resolution"

There is nothing printed in terminal.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

quirck wrote:
Matt1223 wrote:

Code: Select all

__asm("lidt [0]" : : "m"(ptr));
It doesn't work, while compiling I get this message:

C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s: Assembler messages:
C:\Users\Admin\AppData\Local\Temp\ccQPDoTR.s:44: Error: unsupported instruction `lidt'
Hm, it's tricky to get gcc to work with lidt using intel syntax. I can't get rid of "QWORD PTR" in assembly, and lidt QWORD PTR ... is obviously wrong, hence the error.

Using AT&T syntax, it's

Code: Select all

__asm("lidt %0" : : "m"(ptr));
With intel syntax, I managed to get the right output only using a register:

Code: Select all

__asm("lidt [%0]" : : "r"(&ptr));
Anyway, "lidt [0]" tries to load the idtr from address 0, and ptr is not used at all.
You're right. I check it in IDA and it was reffering to 0. I changed it to:

Code: Select all

__asm("lidt [%0]" : : "r"(&ptr));
but it still doesn't work. From what I see in IDA it should. It's really weird!
IDA shows that:
mov eax, offset unk_402000
lidt fword ptr [eax]
quirck
Member
Member
Posts: 42
Joined: Sun Nov 23, 2008 5:56 am
Location: Russia, Saint-Petersburg

Re: Interrupt descriptor table problem

Post by quirck »

Matt1223 wrote:
frabert wrote:Add reset_on_triple_fault=0 to your cpu configuration in Bochs to keep it running after a triple fault. Also, run Bochs via a terminal so you can see its output.
I set it and it gives this message:
"exception(): 3rd (13) exception with no resolution"

There is nothing printed in terminal.
What is in bochs log? If you configured it, of course:

Code: Select all

log: bochsout.txt
One more question: is the code loaded at the address it expects? For example, does this work?

Code: Select all

asm volatile ( "call %0" : : "r"(interrupt_handler) );
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

quirck wrote:
Matt1223 wrote:
frabert wrote:Add reset_on_triple_fault=0 to your cpu configuration in Bochs to keep it running after a triple fault. Also, run Bochs via a terminal so you can see its output.
I set it and it gives this message:
"exception(): 3rd (13) exception with no resolution"

There is nothing printed in terminal.
What is in bochs log? If you configured it, of course:

Code: Select all

log: bochsout.txt
One more question: is the code loaded at the address it expects? For example, does this work?

Code: Select all

asm volatile ( "call %0" : : "r"(interrupt_handler) );
It's really huge file so I can't send all of it. Tell me which part should I send.

Code: Select all

asm volatile ( "call %0" : : "r"(interrupt_handler) );
This code works.
quirck
Member
Member
Posts: 42
Joined: Sun Nov 23, 2008 5:56 am
Location: Russia, Saint-Petersburg

Re: Interrupt descriptor table problem

Post by quirck »

Well, the lines around "3rd (13) exception with no resolution" :)
There should be the register dump, and usually there is some indication of what went wrong before it.

Take a look at this post for example.


One more idea: try using pack pragmas around structure definitions like:

Code: Select all

#pragma pack(push,1)
struct IDTR
{
   uint16_t limit;
   uint32_t base;
};
#pragma pack(pop)
In other words, verify that their sizes are correct. GCC I'm using ignored the __attribute__((packed)) and padded the base field.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

quirck wrote:Well, the lines around "3rd (13) exception with no resolution" :)
There should be the register dump, and usually there is some indication of what went wrong before it.

Take a look at this post for example.


One more idea: try using pack pragmas around structure definitions like:

Code: Select all

#pragma pack(push,1)
struct IDTR
{
   uint16_t limit;
   uint32_t base;
};
#pragma pack(pop)
In other words, verify that their sizes are correct. GCC I'm using ignored the __attribute__((packed)) and padded the base field.
Thank you very much! :D These pragmas worked! Do you have any idea why this attribute was ignored?
Anyway can I reward you somehow on this forum?
quirck
Member
Member
Posts: 42
Joined: Sun Nov 23, 2008 5:56 am
Location: Russia, Saint-Petersburg

Re: Interrupt descriptor table problem

Post by quirck »

As an alternative to the pragma, -mno-ms-bitfields switch can be used to make __attribute__((packed)) behave as expected. GCC documentation says it is done for windows compatibility.
Matt1223
Member
Member
Posts: 45
Joined: Mon Jul 30, 2018 2:58 am

Re: Interrupt descriptor table problem

Post by Matt1223 »

quirck wrote:As an alternative to the pragma, -mno-ms-bitfields switch can be used to make __attribute__((packed)) behave as expected. GCC documentation says it is done for windows compatibility.
Ok, thanks a lot! :D
Post Reply