如何在同一系统上发送 nmi

发布于 2024-12-04 09:17:14 字数 173 浏览 2 评论 0原文

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

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

谢谢

I need to send an nmi on the system I am working on. I want to test few things which I have implemented. Is there any windows driver routine which allows us to do that? I think I can write to a port using __outword. Is there any other way to do it?

I have one more question. Are there any specific scenarios which causes an NMI? (However, I dont want system to BSOD or triple fault.)

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冰魂雪魄 2024-12-11 09:17:14

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

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

  • 外部硬件断言 NMI 引脚。
  • 处理器通过传输模式 NMI 在系统总线(Pentium 4、Intel Core Duo、Intel Core 2、Intel Atom 和 Intel Xeon 处理器)或 APIC 串行总线(P6 系列和 Pentium 处理器)上接收消息。

可以向向量 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,您需要:

  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;

From Intel's Software Development Manual: System Programming Guide:

The nonmaskable interrupt (NMI) can be generated in either of two ways:

  • External hardware asserts the NMI pin.
  • The processor receives a message on the system bus (Pentium 4, Intel Core Duo, Intel Core 2, Intel Atom, and Intel Xeon processors) or the APIC serial bus (P6 family and Pentium processors) with a delivery mode NMI.

and

It is possible to issue a maskable hardware interrupt (through the INTR pin) to vector 2 to invoke the NMI interrupt handler; however, this interrupt will not truly be an NMI interrupt. A true NMI interrupt that activates the processors NMI-handling hardware can only be delivered through one of the mechanisms listed above.

So, if all you want to do is trigger the NMI handler, you can simply use int $2 (int 02h in Intel syntax). But, if you need to ensure that it is not masked, you will either need external hardware to trigger it, or to use the APIC.


If you choose to use the APIC to send an NMI, the easiest way to do it is to send an inter-processor interrupt. To do this, you will need access to the local APIC's registers, which are mapped into physical memory, by default at the address 0xFEE00000, although that can be changed. You will need to find the physical page containing the APIC's registers and map it into virtual memory so that you can access them.

In order to send an IPI, you need to write into the interrupt configuration register. The ICR's low 32 bits are located at 0x300 within the APIC's page, and the upper 32 bits are at 0x310. To send the NMI, you need to:

  1. Get the APIC ID of the processor you want to send the NMI to. If you want to send it to the processor you are running on, this is simple since you can read it from the APIC at 0x20 in bits 24-31.
  2. Write the APIC ID into the destination field, bits 24-31 of the high ICR register.
  3. Write the value 0x4400 into the low ICR register. Bits 8-10 of this value indicate that you are sending an NMI, and bit 14 indicates that you are using the assert trigger mode.

When writing to an APIC register, you must write a full 32 bit value. Also, bits 13, 16-17, and 20-55 in the ICR are reserved, so you should not change their values. You also must write to the high bits of the ICR before the low bits, since the IPI is triggered by the write to the low bits.

Here is an example of sending an NMI to the current processor in C.

#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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文