Can't get IDT working :/

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
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Can't get IDT working :/

Post by angods »

I tried setting up a long mode IDT without much success (constant triple faulting). I can't figure out what's wrong..

Code: Select all

struct idt_entry;

struct idt_desc {
	word_t            Size;
	struct idt_entry *Entries;
} __packed;

struct idt_entry {
	word_t  Offset0;
	word_t  Selector;
	byte_t  Ist;
	byte_t  TypeAttr;
	word_t  Offset1;
	dword_t Offset2;
	dword_t Zero;
} __packed;

Code: Select all

static struct idt_desc Idt = {0};
static struct idt_entry IdtEntries[0x10] = {0};

void
SetupIDT(void) {
	InterruptDisable(); //cli

	Idt.Size = sizeof(IdtEntries) - 1;
	Idt.Entries = IdtEntries;
	for(int i = 0; i < 0x10; ++i) {
		const qword_t Offset = (qword_t)InterruptHandler;
		IdtEntries[i].Offset0 = (word_t)(Offset & 0xffff);
		IdtEntries[i].Offset1 = (word_t)((Offset >> 16) & 0xffff);
		IdtEntries[i].Offset2 = (dword_t)((Offset >> 32) & 0xffffffff);
		IdtEntries[i].Ist = 0;
		IdtEntries[i].Zero = 0;
		IdtEntries[i].Selector = 8;
		IdtEntries[i].TypeAttr = 0b10001000;
	}
	LoadInterruptTable(&Idt); //lidt [rdi]
	InterruptEnable(); //"sti"

}

Code: Select all

InterruptHandler:
	iretq
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Can't get IDT working :/

Post by nullplan »

Well, this simply can't work. For one, you are initializing only the first sixteen entries. That is not even enough to cover the exception range. For two, you make no distinction between exceptions with and without error code (you cannot simply iret from exceptions with error code, you have to remove the error code first). And finally, you enable interrupts not having initialized any interrupt controllers. So what is going to happen? Very quickly some sort of timer interrupt will fire on some unknown vector that is likely beyond the size of the IDT. This will invoke the double fault interrupt, which luckily you have, but you immediately iret from it. Leaving aside that there is probably an error code (not quite sure on that off the cuff), the processor manual states that you cannot trust the RIP portion of the interrupt frame to point to anything sensible for a double fault (since DF is an Abort type exception). If you look at, say, Linux, you will see that it handles double fault by testing for one known case where they can happen (but then the RIP can be set to something well known), and in all other cases, they will just panic.

So, putting it all together: You must initialize all 256 IDT entries to something slightly more comprehensive that "iret", and you must not "sti" until you have discovered and initialized all interrupt controllers, because otherwise you will not know what an interrupt means.
Carpe diem!
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Can't get IDT working :/

Post by angods »

nullplan wrote:Well, this simply can't work. For one, you are initializing only the first sixteen entries. That is not even enough to cover the exception range. For two, you make no distinction between exceptions with and without error code (you cannot simply iret from exceptions with error code, you have to remove the error code first). And finally, you enable interrupts not having initialized any interrupt controllers. So what is going to happen? Very quickly some sort of timer interrupt will fire on some unknown vector that is likely beyond the size of the IDT. This will invoke the double fault interrupt, which luckily you have, but you immediately iret from it. Leaving aside that there is probably an error code (not quite sure on that off the cuff), the processor manual states that you cannot trust the RIP portion of the interrupt frame to point to anything sensible for a double fault (since DF is an Abort type exception). If you look at, say, Linux, you will see that it handles double fault by testing for one known case where they can happen (but then the RIP can be set to something well known), and in all other cases, they will just panic.

So, putting it all together: You must initialize all 256 IDT entries to something slightly more comprehensive that "iret", and you must not "sti" until you have discovered and initialized all interrupt controllers, because otherwise you will not know what an interrupt means.
For testing purposes, can I not use "sti", but invoke #PF via accessing an address outside of range I've previously mapped (first 1MB)?
Also, should I change idt_entry#TypeAttr field per entry, does it ever differ (I wasn't really sure what to set it to and I figured out this value might be appropriate looking at other people's reference code)?

And finally, how big is the error code? - Found the answer myself.

Sorry if I'm asking trivial questions. I just found the interrupt tutorial very confusing in many ways. :|

Thanks :D
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Can't get IDT working :/

Post by Octocontrabass »

angods wrote:Also, should I change idt_entry#TypeAttr field per entry, does it ever differ (I wasn't really sure what to set it to and I figured out this value might be appropriate looking at other people's reference code)?
You may set it to either an interrupt gate or a trap gate. The only difference between the two is that interrupt gates guarantee interrupts are disabled when the handler is called. If you're not sure, just use interrupt gates for now. You can always go back and change it later. (Where exactly did you see someone using 0x88? That's not a valid descriptor type.)
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Can't get IDT working :/

Post by angods »

Octocontrabass wrote:
angods wrote:Also, should I change idt_entry#TypeAttr field per entry, does it ever differ (I wasn't really sure what to set it to and I figured out this value might be appropriate looking at other people's reference code)?
You may set it to either an interrupt gate or a trap gate. The only difference between the two is that interrupt gates guarantee interrupts are disabled when the handler is called. If you're not sure, just use interrupt gates for now. You can always go back and change it later. (Where exactly did you see someone using 0x88? That's not a valid descriptor type.)
I can't remember where I saw someone using it, but I was just experimenting.. I forgot to change it to 'normal' (0x8e) before sending the code.
Last edited by angods on Sun Oct 31, 2021 3:39 pm, edited 1 time in total.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Can't get IDT working :/

Post by Octocontrabass »

angods wrote:Do I miss something?
In 64-bit mode, everything pushed to the stack is padded to 64 bits (8 bytes).
Post Reply