如何在同一系统上发送nmi

时间:2011-09-14 20:40:20

标签: windows windows-7 x86 x86-64

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

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

由于

1 个答案:

答案 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 $2int 02h)。但是,如果您需要确保它没有被屏蔽,您将需要外部硬件来触发它,或者使用API​​C。


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

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

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