Page 1 of 1

Problem with calling ISRs

Posted: Mon Sep 05, 2016 6:57 am
by Aspen
Hi everyone,
I've got a newbie problem - I cannot seem to get my ISRs to be called.

What's happening:
My code to test my ISRs (a simple divide by zero) does not appear to call the appropriate ISR to handle the exception, causing the machine to triple fault and reboot.

How I got here:
I followed the Meaty Skeleton template on the Wiki, and then built on that using Bran's Kernel Development. Up to now I have used this tutorial to load a new GDT, and have then gone on to use its pages on the IDT and ISRs to attempt to implement interrupt handling. I have not done too much yet: aside from all of the stuff covered in Meaty Skeleton, I have only written screen scrolling code and GDT-loading code.

My code:
I'll explain my interpretation of how this works, so any misconceptions I may have can be identified:

1) The kernel calls the function loadidt(), which is located in idt.c. This function creates a new, empty IDT and then calls idt_init(), which is in boot.S, to actually do the lidt. Now we should have an unpopulated IDT table. This function returns to the kernel.

2) The kernel then calls another function, loadisr(), which is located in isr.c. This function calls idt_setgate() (located in idt.c) for each interrupt, to populate the idt.

3) If an interrupt occurs, the processor looks up the correct ISR in the IDT and executes it as follows:
ISRs are defined in assembly. They push a dummy error code (if one is not already pushed) and a number identifying the isr. Then they jump to an assembly label - isr_common_stub - which takes care of
pushing the registers, resetting them to the data segment, and then calling a function defined in isr.c to print the relevant error message (working this out using the pushed registers and a structure defined in a header file) and halt the system.

4) In the main kernel function, I've done this to test my interrupts:

Code: Select all

char c = 10 / 0;
printf("%c\n", c);


The code can be seen here:
idt.c
Section of boot.S that deals with interrupts.
isr.c

From my attempts at debugging (inserting infinite loops at places in the code), the problem seems to be occurring before the ISRs are called; that is to say that they are not being called at all. This leads me to suspect that there is something wrong with the implementation of the IDT.

What's interesting is that everything appears to work fine before any interrupts are called: both loadidt() and loadisr() return back to the kernel without any crashing.

How I've tried to resolve this:
I have checked the Bran's Known Bugs page on the Wiki, and searched the Wiki, the forums and the rest of the Internet for any possible resolution. I have also tried starting the OS from scratch and recoding everything, to no avail - I think this should rule out any mistyped code.

Does anyone know if I may have made a common mistake in my code? I feel that maybe I'm either missing something pretty fundamental about interrupts.
I hate to ask such a question on here, but I'm at my wits' end: I have been trying to solve this for weeks.

Thanks very much.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 7:07 am
by Ch4ozz
In your code I saw this:

Code: Select all

extern void idt_init();
This wont call the func but declare it.
To call it you have to add:

Code: Select all

idt_init();
I would also put the extern declaration into a header.

I just skimmed through the rest but it looks okay to me

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 7:23 am
by Octocontrabass
Aspen wrote:

Code: Select all

char c = 10 / 0;
The compiler won't emit a div instruction for a constant expression, so no division by zero occurs.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 7:33 am
by Octacone
Oh. Try something like:

Code: Select all

int a = 0;
int b = 201;
int c = a/b;
yourPrintFunction(c);
Static expressions do not count as devisions by zero.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 7:42 am
by Octocontrabass
Expressions that can be trivially reduced to constant expressions won't cause division by zero either, unless you're able to disable that optimization.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 7:46 am
by Ch4ozz
Why even doing that in C?

Code: Select all

asm volatile("xor %eax, %eax; div %eax");
Always check your code results in a disassembler to make sure your stuff is right.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 8:16 am
by Aspen
Ch4ozz wrote:This wont call the func but declare it.
Oh dear, how could I miss that? Thanks very much for spotting that! Fixed.

I did have my suspicions as to the proper generation of the DIV instruction, but stuck with the printf() method because I saw someone else do something similar. I've now replaced the test code with Ch4ozz's code that divides 0 by 0 and of course, this properly generates the DIV instruction. Thanks for pointing that out.

However, it still triple faults when it encounters the divide by 0, as well as int 0x0, so I must have something else wrong, as well as not actually calling the function that loads the IDT.

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 1:34 pm
by Aspen
Ok, so I stepped though execution in gdb and found that it faults when I try to indirectly call the interrupt handler though the following assembly code:

Code: Select all

mov   isr_handle,	%eax
call  %eax
I should have mentioned that I was translating this code from NASM to GAS syntax with my not-so-great knowledge of GAS, so I'm not feeling confident on a lot of this assembly code.

Is there some special syntax when calling functions through registers like this for GAS?
Additionally, I would be interested to know why we call the function in this way: Bran's Kernel Development says that this is 'a special call' which 'preserves the 'eip' register', but I don't understand this: surely eip has to change to the address of the isr_handle function to start executing it anyway, so how do we 'preserve' it? I thought eip was pushed automatically along with cs, eflags etc. when the interrupt is triggered?

To reiterate, isr_handle is a function defined in C which actually prints out the error messages and receives what was pushed onto the stack by the assembly code (isr_common_stub) before it.

Any ideas?

Re: Problem with calling ISRs

Posted: Mon Sep 05, 2016 3:12 pm
by Octocontrabass
GAS supports (a variety of) Intel syntax, so you don't need to struggle with the insanity of AT&T syntax. Put ".intel_syntax noprefix" at the top of your assembly file to tell GAS you want to use Intel syntax.GCC can also handle Intel syntax in inline assembly, using the "-masm=intel" command line option.

Bran clearly has no idea what he's talking about; there's no reason to call a function through EAX when you can call it directly. (There's also no reason to move ESP to EAX and then push EAX since you can push ESP.)

Can you give more details of the fault? I've only taken a quick glance at your code, but I didn't see anything obviously wrong. Bochs is famous for providing huge amounts of useful debugging information when a triple fault occurs, so you could run your OS in Bochs and give us the section of the log from when it crashes.

Re: Problem with calling ISRs

Posted: Tue Sep 06, 2016 12:08 am
by Boris
Hi,
If you triple fault on a call instruction, I would double check :
- The address of isr_handle . Any " second half kernel" issue ?
- The value of ESP before the call.
- Your code segment, and it's GDT entry.

Re: Problem with calling ISRs

Posted: Tue Sep 06, 2016 12:12 pm
by Aspen
Hi Octocontrabass and Boris, and thanks very much for your replies.

About the Intel syntax in GAS: I had no idea I could do that. Thanks for pointing that out.

I've changed the code so the function is called directly, rather than through eax, but the plot thickens: now the code appears to run successfully in QEMU (apart for some issues with printing, but I suspect that this a bug with my printing code, and is unrelated to the problems with the ISRs), but fails on real hardware; that is, in QEMU the interrupt routine is successfully called and executed, but on real hardware the routine is not called at all and the computer reboots.

I haven't yet been able to get Bochs working properly, but I'm looking into it. I'll post some more details when (if?) I manage to get it working.

Boris - since changing Bran's strange 'indirect' call (using eax) to a normal call, the issue with calling seems to be resolved. Thank you anyway - I've double-checked the code segment anyway, and it all looks fine...

So, has anyone ever heard of instances where ISRs are successfully called in QEMU, but not on real hardware? This seems to be a very strange issue, and I'll try and get Bochs working so we can see what it says about it all...

Edit: I've tried to run this on two real machines now, and got the same result, so I guess that eliminates a hardware issue.

Edit 2: Makes a little more sense now: I think it might be GRUB interfering. I was running my kernel directly from QEMU using the -kernel option. If I try to boot QEMU with the generated iso with GRUB as my bootloader, it fails. This implies that somehow GRUB is the culprit, since the crashes simply don't happen with QEMU's built-in bootloader. I don't understand this though, because I load a new IDT, clear it completely and write my own interrupts, so surely any ISRs created by GRUB are made redundant? Any ideas?

Re: Problem with calling ISRs

Posted: Tue Sep 06, 2016 1:07 pm
by Kazinsal
Please don't work with known buggy tutorials from a decade ago.

This is why we have a wiki with a decent amount of checked, safe, up-to-date material.

Re: Problem with calling ISRs

Posted: Tue Sep 06, 2016 1:57 pm
by Aspen
Kazinsal wrote:Please don't work with known buggy tutorials from a decade ago.
Yes, I suppose you're right - I guess the best step to take now would be to start again and write the ISRs my own way, entirely from scratch (with the help of the Wiki), and see if that works.

On the plus side, following BKD has shown me how ISRs work, so now I feel more equipped for writing them myself, despite all of Bran's bugs and incorrect information, such as the strange 'call through a register'.

Still, it would certainly be interesting to know exactly what was going wrong here.

Thanks again, everyone.

Re: Problem with calling ISRs

Posted: Tue Sep 06, 2016 2:01 pm
by Ch4ozz
When you rewrote it and it works you will know what was wrong :)
Using the wiki everything worked instantly fine for me and I didnt have any issues