我需要在我正在处理的系统上发送一个nmi。我想测试一些我已经实现的东西。是否有任何Windows驱动程序例程允许我们这样做?我想我可以用__outword写一个端口。还有其他办法吗?
我还有一个问题。是否存在导致NMI的特定情况? (但是,我不希望系统出现BSOD或三重故障。)
由于
答案 0 :(得分:4)
来自Intel's Software Development Manual: System Programming Guide:
可以通过以下两种方式之一生成不可屏蔽中断(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 02h
)。但是,如果您需要确保它没有被屏蔽,您将需要外部硬件来触发它,或者使用APIC。
如果您选择使用APIC发送NMI,最简单的方法是发送处理器间中断。为此,您需要访问本地APIC的寄存器,这些寄存器映射到物理内存,默认情况下位于地址0xFEE00000,尽管可以更改。您需要找到包含APIC寄存器的物理页面并将其映射到虚拟内存中,以便您可以访问它们。
要发送IPI,您需要写入中断配置寄存器。 ICR的低32位位于APIC页面内的0x300,高32位位于0x310。要发送NMI,您需要:
写入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;