Problem with calling ISRs

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
User avatar
Aspen
Posts: 5
Joined: Mon Sep 05, 2016 4:37 am
Location: United Kingdom

Problem with calling ISRs

Post 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.
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: Problem with calling ISRs

Post 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
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem with calling ISRs

Post 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.
User avatar
Octacone
Member
Member
Posts: 1138
Joined: Fri Aug 07, 2015 6:13 am

Re: Problem with calling ISRs

Post 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.
OS: Basic OS
About: 32 Bit Monolithic Kernel Written in C++ and Assembly, Custom FAT 32 Bootloader
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem with calling ISRs

Post 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.
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: Problem with calling ISRs

Post 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.
User avatar
Aspen
Posts: 5
Joined: Mon Sep 05, 2016 4:37 am
Location: United Kingdom

Re: Problem with calling ISRs

Post 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.
User avatar
Aspen
Posts: 5
Joined: Mon Sep 05, 2016 4:37 am
Location: United Kingdom

Re: Problem with calling ISRs

Post 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?
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem with calling ISRs

Post 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.
Boris
Member
Member
Posts: 145
Joined: Sat Nov 07, 2015 3:12 pm

Re: Problem with calling ISRs

Post 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.
User avatar
Aspen
Posts: 5
Joined: Mon Sep 05, 2016 4:37 am
Location: United Kingdom

Re: Problem with calling ISRs

Post 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?
User avatar
Kazinsal
Member
Member
Posts: 559
Joined: Wed Jul 13, 2011 7:38 pm
Libera.chat IRC: Kazinsal
Location: Vancouver
Contact:

Re: Problem with calling ISRs

Post 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.
User avatar
Aspen
Posts: 5
Joined: Mon Sep 05, 2016 4:37 am
Location: United Kingdom

Re: Problem with calling ISRs

Post 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.
Last edited by Aspen on Tue Sep 06, 2016 2:02 pm, edited 1 time in total.
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: Problem with calling ISRs

Post 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
Post Reply