如何在同一系统上发送nmi

age*_*ith 2 windows x86 x86-64 windows-7

我需要在我正在使用的系统上发送一个 nmi。我想测试一些我已经实现的东西。是否有任何 Windows 驱动程序例程允许我们这样做?我想我可以使用 __outword 写入端口。有没有其他方法可以做到?

我还有一个问题。是否有任何导致 NMI 的特定情况?(但是,我不希望系统出现 BSOD 或三重故障。)

谢谢

ugh*_*fhw 5

来自英特尔的软件开发手册:系统编程指南

不可屏蔽中断 (NMI) 可以通过以下两种方式之一产生:

  • 外部硬件置位 NMI 引脚。
  • 处理器在系统总线(Pentium 4、Intel Core Duo、Intel Core 2、Intel Atom 和 Intel Xeon 处理器)或 APIC 串行总线(P6 家族和 Pentium 处理器)上使用交付模式 NMI 接收消息。

可以向向量 2 发出可屏蔽的硬件中断(通过 INTR 引脚)以调用 NMI 中断处理程序;然而,这个中断并不是真正的 NMI 中断。激活处理器 NMI 处理硬件的真正 NMI 中断只能通过上面列出的机制之一传递。

因此,如果您只想触发 NMI 处理程序,您可以简单地使用int $2( int 02hin Intel 语法)。但是,如果您需要确保它不被屏蔽,您将需要外部硬件来触发它,或者使用 APIC。


如果选择使用 APIC 发送 NMI,最简单的方法是发送处理器间中断。为此,您需要访问映射到物理内存的本地 APIC 寄存器,默认地址为 0xFEE00000,但可以更改。您需要找到包含 APIC 寄存器的物理页面并将其映射到虚拟内存中,以便您可以访问它们。

为了发送IPI,您需要写入中断配置寄存器。ICR 的低 32 位位于 APIC 页面内的 0x300,高 32 位位于 0x310。要发送 NMI,您需要:

  1. 获取要将 NMI 发送到的处理器的 APIC ID。如果您想将它发送到您正在运行的处理器,这很简单,因为您可以从 APIC 的 0x20 位 24-31 中读取它。
  2. 将 APIC ID 写入目标字段,即高 ICR 寄存器的位 24-31。
  3. 将值 0x4400 写入低 ICR 寄存器。该值的第 8-10 位表示您正在发送 NMI,第 14 位表示您正在使用断言触发模式。

写入 APIC 寄存器时,必须写入完整的 32 位值。此外,ICR 中的第 13、16-17 和 20-55 位是保留的,因此您不应更改它们的值。您还必须在低位之前写入 ICR 的高位,因为 IPI 是由写入低位触发的。

下面是在 C 中向当前处理器发送 NMI 的示例。

#define APIC_ID_OFFSET 0x20
#define ICR_LOW_OFFSET 0x300
#define ICR_HIGH_OFFSET 0x310
// Convenience macro used to access APIC registers
#define APIC_REG(offset) (*(unsigned int*)(apicAddress + offset))

void *apicAddress; // This should contain the virtual address that the APIC registers are mapped to

// Get the current APIC ID. Leave it in the high 8 bits since that is where it needs to be written anyway
unsigned int apicID = APIC_REG(APIC_ID_OFFSET) & 0xFF000000;
unsigned int high = APIC_REG(ICR_HIGH_OFFSET) & 0x00FFFFFF;
high |= apicID;
unsigned int low = APIC_REG(ICR_LOW_OFFSET) & 0xFFF32000;
low |= 0x4400;
APIC_REG(ICR_HIGH_OFFSET) = high;
APIC_REG(ICR_LOW_OFFSET) = low;
Run Code Online (Sandbox Code Playgroud)