如何由驱动程序使用MONITOR指令(_mm_monitor intrinsic)?

Cod*_*nza 3 x86 driver cpu-architecture smp

我正在探索MONITOR指令的使用(或等效的内在_mm_monitor).虽然我发现了描述它们的文献,但我找不到任何关于如何使用它的具体例子/样本.

任何人都可以分享一个如何在驱动程序中使用此指令/内在函数的示例吗?基本上,我想用它来观察内存范围.

Mar*_*oom 8

monitor指令使用指定的地址为地址监视硬件提供支持RAX/EAX/AX.来自Intel
报价指令使用监视器的状态mwait.

使用的有效地址大小(16,32或64位)取决于编码指令的有效地址大小(即,它可以用67h前缀覆盖,默认情况下与代码大小相同).

给出的地址rax/eax/ax是逻辑地址的偏移部分,用于计算监视器的线性地址.
段部分ds默认情况下,段覆盖前缀可以应用于更改段.
作为用于监视器的线性地址,分页不会影响监视.

monitor(和mwait)指令的可用性由CPUID.01H:ECX.MONITOR [bit 3] 1 指示.
这是一项特权指令,但英特尔声称:

有条件的指令在大于0的级别上可用.

检测此类情况的建议方法是尝试执行monitor并处理最终的#UD异常(以自定义方式将OS报告给用户程序).

监视的地址范围必须是可回写的可缓存的.
由于涉及高速缓存和高速缓存一致性子系统,地址范围的大小是根据最小和最大大小给出的.
CPUID.01H:EAX [bit 15:0]给出最小范围大小.这是硬件监视器监视的区域的长度.
然而,高速缓存一致性流量可以与更大尺寸的"块"(线)一起工作,并且如果后者包括在前者中,则与被监视区域相邻的写入将触发它.
这会产生最大范围大小,可以在CPUID.01H:EBX [bit 15:0]中找到.
要正确使用,请monitor确保监视的数据结构符合最小范围大小,但也要确保没有代理在其旁边的地址中写入最大范围大小.

例如,如果最小范围大小为8个字节且最大大小为16个字节,请确保观察到的结构适合8个字节,但要用8个字节填充它以达到总共16个字节,这样就不会从第8个字节写入到第16个字节发生.

在单个集群系统中,上述两个值相等.我的都是64字节.
BIOS负责报告IA32_MONITOR_FILTER_LINE_SIZE多集群系统中的高速缓存一致性行大小.

出于指令排序和访问权限的目的,monitor是一个负载.

monitor允许程序员指定提示扩展名.提示是在提示中
指定的. 不支持的扩展会引发#GP异常,忽略不支持的提示.英特尔手册报道, 我不知道有任何扩展或提示ecxedx

monitor

对于Pentium 4处理器(15系列,型号3),未定义任何扩展或提示.

我相信这条线路一般都是正确的,它只有一个过时的处理器模型.
此外,用于monitor报告#GP 的伪代码If ECX ? 0.

在没有检查其状态的情况下布防显示器(使用mwait)不会造成任何伤害.

内在的是void _mm_monitor(void const *p, unsigned extensions,unsigned hints).


监视器布防后,可以通过不同的条件触发:

  • 外部中断:NMI,SMM,INIT,BINIT,MCERR
  • 故障,中止包括机器检查
  • 架构TLB失效,包括写入CR0,CR3,CR4和某些MSR写入
  • 由于快速系统调用和远程调用而导致的自愿转换
  • 屏蔽中断(如果已启用)
  • 在受监控的地址范围内写入

程序员看不到监视器的状态,但可以使用它进行测试mwait.
mwait进入实现定义的低功耗状态,直到监视器处于触发状态.
如果监视器未处于待命状态或已经触发,mwaitnop处理器将停止执行指令,直到监视器被触发为止.

硬件监视器状态和MONITOR/MWAIT交互

mwait也可以给出扩展提示.
扩展名设置ecx和提示eax.
在撰写本文时,唯一的扩展是:

位0即使被屏蔽也将中断视为中断事件(例如,即使EFLAGS.IF = 0).仅当CPUID.05H:ECX [位1] = 1 时才可以设置 .
位31-1保留

提示允许程序员指定实现定义的低功耗模式.

位3:0 C状态内的子C状态,由位[7:4]表示
7:4目标C状态
值0表示C1; 1表示C2,依此类推
01111B的值表示C0
注意:MWAIT扩展的目标C状态是处理器特定的C状态,而不是ACPI C状态

CPUID.05h.EDX中给出了C模式的子状态数(因此也就是可用性):

位03 - 00:使用MWAIT支持的C0*子C状态的数量.
位07-04:使用MWAIT支持的C1*子C状态数.
第11 - 08位:使用MWAIT支持的C2*子C状态数.
第15-12位:使用MWAIT支持的C3*子C状态数.
第19-16位:使用MWAIT支持的C4*子C状态数.
第23-20位:使用MWAIT支持的C5*子C状态数.
第27-24位:使用MWAIT支持的C6*子C状态数.
位31-28:使用MWAIT支持的C7*子C状态的数量.

请注意,将CPU置于高于C1的状态也会禁用其他线程,因此触发监视器的写入必须来自其他代理.

内在的是void _mm_mwait(unsigned extensions, unsigned hints).


monitor/ mwait机械引入帮助线程之间的同步,但是并不很适合用于监测访问的存储器范围,因为所述触发条件包括频繁发生的事件.
之后mwait总是强制性的检查监测范围被写入.
这里有一个例子,模式如下:

  1. 观察到的结构用特定值(比如0)初始化.
  2. 使用monitor/ mwaitpair.
  3. 稍后,另一个人再次向观察结构写一个特定值(比如说1).
  4. 监视器被触发并mwait"返回",监视的结构值被比较为1(发生写入),如果它不相等,则执行跳回到2.

一些未经测试的示例伪代码可能是:

struct MonitoredType
{
  int (*event)(struct MonitoredType const* m);              /*Return 0 to keep monitoring*/
  struct AnyType data;                                /*Less, in size, than MIN_MONITOR_RANGE*/
  char padding[MAX_MONITOR_RANGE - sizeof(AnyType)];
};

void wait_for_write(struct MonitoredType const* m)
{
   /* This may miss a write if it happens before MONITOR, beware of race conditions if necessary */
   do
   {
     _mm_monitor(&m->data, 0, 0);
     _mm_mwait(0, 0);
   } while ( ! m->event(m));
}
Run Code Online (Sandbox Code Playgroud)

必须注意确保退出条件mwait是写入而不是其他事件之一.
这就是函数指针的原因event.

为了监视写入/读取到线性地址,可以使用调试寄存器.
请参阅英特尔手册3的第17章,并检查您的操作系统文档以正确使用这些寄存器.


1个含义:执行cpuideax设定为01H和测试位的3 ecx之后.请注意,IA32_MISC_ENABLE允许OS或固件禁用monitor/mwait.