How to distinguish between NMI and regular interrupt in IRQ?

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
lillyliv7
Posts: 2
Joined: Thu Oct 13, 2022 8:09 am
Libera.chat IRC: dont have one

How to distinguish between NMI and regular interrupt in IRQ?

Post by lillyliv7 »

This is my first post here so hi!
I'm currently writing a real mode OS (not to use BIOS features, just because I want it to run on ancient hardware) and I'm having a hard time figuring out how distinguish between a regular interrupt call and a NMI in my IRQs, for example:

Define the test IRQ, doesn't need to do much this is just for testing.

Code: Select all

intzero:
    mov si, interrupt_zero_string
    call printstring
    iret

interrupt_zero_string:db "zero", 0xa 0x0
Then enable the IRQ by putting it in position 0 of the IVT

Code: Select all

xor ax, ax
mov word [0], intzero
mov [2], ax
Now if I run "int 0" it calls the IRQ and prints "zero" as expected, but if I divide by zero by running:

Code: Select all

mov bl, 0
mov al, 7
div bl
It also calls the IRQ, but fills my screen because its running in a loop; how can I fix my IRQ's to behave differently (by kernel panicking) when they're triggered by an NMI? I searched the wiki and fourms but I can't find much on it.
Octocontrabass
Member
Member
Posts: 5418
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to distinguish between NMI and regular interrupt in IRQ?

Post by Octocontrabass »

lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmThis is my first post here so hi!
Hi!
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmI want it to run on ancient hardware
How ancient? Compatibility starts getting tricky if you go far enough back.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmI'm having a hard time figuring out how distinguish between a regular interrupt call and a NMI in my IRQs
You're actually trying to distinguish between a software interrupt and an exception in your ISRs (interrupt service routines).

And you can't. In real mode, there's no reliable way to distinguish between the two. You can try to guess by examining the code at the return address, but there are always situations where your guess will be wrong.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmbut fills my screen because its running in a loop
It's running in a loop because the return address pushed on the stack points to the DIV instruction, which immediately causes another exception when you return.

This is actually one of those tricky compatibility things I mentioned: an 8086 would have pushed the address of the next instruction, meaning your code wouldn't get stuck in a loop if you ran it on an 8086.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmhow can I fix my IRQ's to behave differently (by kernel panicking) when they're triggered by an NMI?
Since you can't tell whether your ISR was called by a software interrupt or an exception, it's probably best to assume it's an exception and always panic.
lillyliv7
Posts: 2
Joined: Thu Oct 13, 2022 8:09 am
Libera.chat IRC: dont have one

Re: How to distinguish between NMI and regular interrupt in IRQ?

Post by lillyliv7 »

Octocontrabass wrote: Sat Jun 22, 2024 4:07 pm
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmThis is my first post here so hi!
Hi!
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmI want it to run on ancient hardware
How ancient? Compatibility starts getting tricky if you go far enough back.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmI'm having a hard time figuring out how distinguish between a regular interrupt call and a NMI in my IRQs
You're actually trying to distinguish between a software interrupt and an exception in your ISRs (interrupt service routines).

And you can't. In real mode, there's no reliable way to distinguish between the two. You can try to guess by examining the code at the return address, but there are always situations where your guess will be wrong.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmbut fills my screen because its running in a loop
It's running in a loop because the return address pushed on the stack points to the DIV instruction, which immediately causes another exception when you return.

This is actually one of those tricky compatibility things I mentioned: an 8086 would have pushed the address of the next instruction, meaning your code wouldn't get stuck in a loop if you ran it on an 8086.
lillyliv7 wrote: Sat Jun 22, 2024 2:57 pmhow can I fix my IRQ's to behave differently (by kernel panicking) when they're triggered by an NMI?
Since you can't tell whether your ISR was called by a software interrupt or an exception, it's probably best to assume it's an exception and always panic.
By "ancient" I mean the 16 bit era of x86, probably 80286 or 80186 ideally.
I'm actually testing on i386 but not using 32 bit mode or the new features just because its the closest qemu offers.

I'm aware of why it was running in a loop and I was just using interrupt 0 as an easy exception to trigger. Assuming an exception makes sense for interrupt 0 but wouldn't be so helpful for something like interrupt 0x8 for example which is used for double fault and the PIT. Is there really no way to know if the PIT or a double fault is triggering interrupt 0x8?
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: How to distinguish between NMI and regular interrupt in IRQ?

Post by Gigasoft »

Since you are in real mode, as long as you did not set the IDT limit to anything less than 1023, a double fault exception can not occur. Stack faults and GPFs are possible, though. If you really need to bother about these exceptions, then simply reprogram the PIC.
Octocontrabass
Member
Member
Posts: 5418
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to distinguish between NMI and regular interrupt in IRQ?

Post by Octocontrabass »

lillyliv7 wrote: Sat Jun 22, 2024 4:49 pmBy "ancient" I mean the 16 bit era of x86, probably 80286 or 80186 ideally.
In the 16-bit era, there were PC-compatibles with an 8088 (or 8086), and there were AT-compatibles with an 80286. Most of the "standard" PC hardware actually comes from AT-compatibles, so if you want to run your code on an 8088 without using the BIOS, you'll find basic things like keyboard input are very different.

Anything with an 80186 is not going to resemble a standard PC, so if you want to run your OS on one of those, you might have to figure it out by yourself.
lillyliv7 wrote: Sat Jun 22, 2024 4:49 pmAssuming an exception makes sense for interrupt 0 but wouldn't be so helpful for something like interrupt 0x8 for example which is used for double fault and the PIT. Is there really no way to know if the PIT or a double fault is triggering interrupt 0x8?
If the CPU is one that doesn't have double faults, like an 8088, it's not a double fault. Otherwise, you can't be entirely sure.

At least with double faults, you might not need to know for sure: you can check whether IRQ0 is waiting to be serviced and assume anything else is a double fault, or you can reprogram the interrupt controller so that IRQ0 uses a different interrupt vector, or you can completely ignore double faults and assume they'll never happen.
Post Reply