如何以编程方式禁用不可屏蔽中断?

How do I disable non maskable interrupts programmatically?

我已经读到,为了根据 Intel 的系统编程指南(第 3 卷第 9.9 章)暂时关闭分页,我应该在做任何其他事情之前禁用中断。我可以使用 cli 轻松禁用可屏蔽中断,但是手册中关于禁用 NMI 的所有内容都是

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

我在 this OSDEV page 找到了看起来像 C 代码的禁用 NMI 的代码,但我不太明白它的意思

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

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

感觉代码没有上下文,不知道函数 outb 和 inb 的作用就没有意义。

"with external circuitry"表示板上在处理器芯片的NMI管脚之前有门,如果这些门被关闭(关闭),中断信号将不会到达处理器芯片的NMI管脚。

outb 调用可能 activate/deactivate 这些门。

NMI 表示不可屏蔽,这意味着您不能仅通过软件禁用它们。

CPU 有一个用于触发 NMI 的不可屏蔽中断 (NMI) 引脚(或等效硬件)。存在外部电路(或等效硬件)以防止 NMI 到达 CPU。由于 80286 使用的机制是通过与 CMOS/Realtime Clock(RTC) 控制器关联的 IO 端口。今天的硬件仍然在模仿同样的机制。

CMOS/RTC 端口是 0x70 和 0x71。端口 0x70 用于 select 读取或写入的 CMOS/RTC 地址。 CMOS/RTC 地址的前 2 位不构成实际地址的一部分。最高位被重新用作 NMI 开关。如果您将字节写入端口 0x70,其中第 7 位(最高有效位)已设置,NMI 将被禁用。如果您写入第 7 位清零的值,则启用 NMI。

inboutb 函数是 C 低级 IN (byte) and OUT(字节)指令的包装器。这些指令读取和写入 IO 端口 space。 C 代码来自 NMI_enable:

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

相当于:

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 是位模式 01111111。将 01111111 与当前字节进行与操作会清除最高位(启用 NMI)。

C 代码来自 NMI_disable:

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

相当于:

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 是位模式 10000000。将 10000000 与当前字节进行或运算设置最高位(禁用 NMI)。

基本上,从 OS 开发人员的角度来看,您可以安全地 考虑 NMI 无法禁用(即使您实际上可以找到禁用它的方法) .如果 NMI 发生在 OS bootstrap 阶段意味着发生了严重的事情(通常是硬件,电源),机器不工作,所以你立即停止你的 OS 因为你的 OS 无论如何都行不通。

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.

英特尔错了

NMI 通常表示严重的硬件故障(特别是如果您的软件不是故意引起的,尤其是如果您没有启用 "machine check exception")。忽略关键硬件故障(通过屏蔽 NMI)会导致极端的未定义行为;这很糟糕。

理想情况下,您需要有保证的行为。最简单的方法是保证 CPU 在有 NMI 的情况下会出现三重故障(并重置计算机);通过将 IDT 限制设置为零。这在启动期间非常好(当没有可能丢失的最终用户数据时)。

一种"less easy"方法是使用临时IDT。要关闭分页,您可以使用身份映射 IDT(以及 GDT、堆栈等),以便无论分页打开或关闭时是否发生 NMI,都可以启动 NMI 处理程序。在 CPU 模式切换期间可以使用类似的技巧(依赖于 IVT/IDT 条目的大小发生变化的事实,这导致 NMI 的 IVT/IDT 条目位于不同的地址,具体取决于 "CPU mode at time of NMI").

当然,理智的人不会在启动后暂时禁用分页(或 CPU 模式切换);所以没有理由想要超过 "guarantee triple fault on NMI"(在引导期间)。