How do I disable non maskable interrupts programmatically?

I've read that in order to temporarily turn off paging according to Intel's system programming guide (volume 3 chapter 9.9) I should disable interrupts before doing anything else. I can easily disable maskable interrupts with cli but all the manual says about disabling NMI's is

NMI interrupts can be disabled with external circuitry.(Software must guarantee that no exceptions or interrupts are generated during the mode switching operation.)

I've found code that looks like C code for disabling NMI's at this OSDEV page but I don't quite understand what it's supposed to mean

void NMI_enable() {
    outb(0x70, inb(0x70) & 0x7F);
 }

 void NMI_disable() {
    outb(0x70, inb(0x70) | 0x80);
 }

it feels like that code has no context and doesn't make sense without knowing what the functions outb and inb do.

Upvotes: 6

Views: 3490

Answers (4)

Brendan
Brendan

Reputation: 37212

I've read that in order to temporarily turn off paging according to Intel's system programming guide (volume 3 chapter 9.9) I should disable interrupts before doing anything else.

Intel is wrong.

NMIs typically indicate critical hardware failures (especially if your software didn't deliberately cause them, and especially if you don't have "machine check exception" enabled). Ignoring critical hardware failures (by masking NMI) leads to extreme undefined behavior; which is bad.

Ideally you want guaranteed behavior. The easiest way to do this is to guarantee that the CPU will triple fault (and reset the computer) if there's an NMI; by setting the IDT limit to zero. This is perfectly fine during boot (when there's no end-user data that can be lost).

A "less easy" way is to use a temporary IDT. For turning paging off you can have an identity mapped IDT (and GDT, stack, etc) so that the NMI handler can be started regardless of whether the NMI occurs when paging is on or off. Similar tricks can be used during CPU mode switches (relying on the fact that the size of an IVT/IDT entry changes, which causes the IVT/IDT entry for NMI to be at different addresses depending on "CPU mode at time of NMI").

Of course sane people don't temporarily disable paging (or do CPU mode switches) after boot; so there's no reason to want more than "guarantee triple fault on NMI" (during boot).

Upvotes: 1

Alexey
Alexey

Reputation: 464

Basically, from OS developer's standpoint, you can safely consider the NMI can not be disabled ( even if you can actually figured out a way to disable it).If NMI happens during OS bootstrap stage means something serious (usually hardware, power) happened, the machine is not working, so you so stop your OS immediately since your OS will not work anyway.

Upvotes: 0

Michael Petch
Michael Petch

Reputation: 47593

The CPU has a Non-Maskable Interrupt (NMI) pin (or hardware equivalent) that is used to trigger an NMI. There is external circuitry (or hardware equivalent) to prevent NMIs from reaching the CPU. Since the 80286 the mechanism used was through IO ports associated with the CMOS/Realtime Clock(RTC) controller. This same mechanism is still mimicked in hardware today.

The CMOS/RTC ports are 0x70 and 0x71. Port 0x70 is used to select a CMOS/RTC address to read or write from. The top 2 bits of the CMOS/RTC address don't form part of the actual address. The top most bit was re-purposed to be the NMI toggle. If you write a byte to port 0x70 where bit 7 (most significant bit) is set, NMI is disabled. If you write a value where bit 7 is clear then NMIs are enabled.

The inb and outb functions are C wrappers around the low level IN (byte) and OUT (byte) instructions. These instructions read and write to the IO port space. This C code from NMI_enable:

outb(0x70, inb(0x70) & 0x7F);

Is the equivalent of:

uint8_t curbyte = inb(0x70);   /* Read current port 0x70 state */
outb(0x70, curbyte & 0x7F);    /* Update current state by clearing NMI bit */
                               /* and write new value back to port 0x70 */

0x7f is the bit pattern 01111111. ANDing 01111111 with the current byte clears the top most bit (enabling NMI).

This C code from NMI_disable:

outb(0x70, inb(0x70) | 0x80);

Is the equivalent of:

uint8_t curbyte = inb(0x70);   /* Read current port 0x70 state */
outb(0x70, curbyte | 0x80);    /* Update current state by setting NMI bit */
                               /* and write new value back to port 0x70 */

0x80 is the bit pattern 10000000. ORing 10000000 with the current byte sets the top most bit (disabling NMI).

Upvotes: 7

Paul Ogilvie
Paul Ogilvie

Reputation: 25286

"with external circuitry" means that on the board there are gates before the NMI pins of the processor chip and if these gates are turned off (closed), no interrupt signals will reach the processor chip's NMI pins.

The outb calls probably activate/deactivate these gates.

NMI means non-maskable and it means you cannot disable them with software only.

Upvotes: 6

Related Questions