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

时间:2017-07-09 07:32:44

标签: x86 driver cpu-architecture smp

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

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

1 个答案:

答案 0 :(得分:7)

monitor指令使用RAX/EAX/AX中指定的地址为地址监控硬件提供支持。来自英特尔的报价
监视器的状态由指令mwait使用。

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

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

{em> CPUID.01H:ECX.MONITOR [bit 3] 1位表示monitor(和mwait)指令的可用性。 SUP>。
这是特权指令,但英特尔声称:

  

指令有条件地以大于0的级别提供。

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

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

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

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

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

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

  

奔腾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,否则会使处理器停止执行指令,直到监视器被触发为止。

Hardware monitor status and MONITOR/MWAIT interaction

mwait也可以提供扩展程序提示
扩展程序在ecx中设置,并在eax中提示 在撰写本文时,唯一的扩展名是:

  

位0 即使屏蔽了中断,也将中断视为中断事件(例如,即使EFLAGS.IF = 0)。只有在设置时才可以设置    CPUID.05H:ECX [bit 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之后始终必须检查是否写入了受监视的范围 有一个example here,其格式如下:

  1. 使用特定值(例如0)初始化观察到的结构。
  2. 使用了monitor / mwait对。
  3. 稍后,另一个人再次向观察结构写一个特定值(比如说1)。
  4. 监视器被触发并且mwait"返回",被监视的结构值被比较为1(发生写入),如果它不相等,则执行跳回到2.
  5. 一些示例未经测试的伪代码可能是:

    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));
    }
    

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

    为了监控写入/读取到线性地址,另一种方法是使用调试寄存器
    请参阅Intel manual 3的第17章,并检查操作系统文档以正确使用这些寄存器。

    1 含义:执行cpuid并将eax设置为01h,然后测试ecx的第3位。请注意,IA32_MISC_ENABLE允许操作系统或固件禁用monitor/mwait