如何在用户模式下设置/清除x86 IA32 Intel CPU上的TF标志

如何在用户模式下设置/清除x86 IA32 Intel CPU上的TF标志,x86,inline-assembly,eflags,X86,Inline Assembly,Eflags,我想知道在x86 IA32 Intel CPU上以用户模式设置/清除EFLAGS.TF的步骤 尝试在下面清除TF标志,但得到错误******未处理的中断向量***** 使用下面的代码,它工作得很好。多谢各位 __asm__ volatile("pushl %eax;\ pushfl;\ popl %eax;\ andl $0xFFFFFEFF, %eax;\

我想知道在x86 IA32 Intel CPU上以用户模式设置/清除EFLAGS.TF的步骤

尝试在下面清除TF标志,但得到错误******未处理的中断向量*****


使用下面的代码,它工作得很好。多谢各位

  __asm__ volatile("pushl %eax;\
                    pushfl;\
                    popl %eax;\
                    andl $0xFFFFFEFF, %eax;\
                    pushl %eax;\
                    popfl;\
                    popl %eax;"
                    );

使用下面的代码,它工作得很好。多谢各位

  __asm__ volatile("pushl %eax;\
                    pushfl;\
                    popl %eax;\
                    andl $0xFFFFFEFF, %eax;\
                    pushl %eax;\
                    popfl;\
                    popl %eax;"
                    );

XOR会翻转一点,而不是总是清除它。这是一种选择,也是另一种选择。有一个内存目标的BTR在寄存器源的情况下速度非常慢,但在Haswell上只有2个UOP,在Skylake上只有3个UOP,这一点也不坏。但在AMD上,即使btr$9、%eax,其成本也高达2 UOP

在Skylake上,popf的速度相当慢,为9 UOP,每20个周期1次。或在Ryzen,35个UOP,每13个循环一个。因此,优化周围的代码不会产生很大的差异,但是找到一种保持代码大小紧凑的方法是很有趣的

您不需要自己保存/还原EAX,只需告诉编译器您想使用:EAX作为clobber列表对其进行clobber,或者使用一个伪输出操作数。请注意,我使用的是GNU C扩展asm,而不是basic

static inline
void clear_tf(void) {
    long dummy;       // there's no type that's always 32-bit on 32-bit, and always 64 on 64-bit.  x32 uses 32-bit pointers in long mode so uintptr_t or size_t doesn't work.
   // if porting to x86-64 System V user-space: beware that push clobbers the red-zone
    __asm__ volatile("pushf \n\t"
                     "pop   %[tmp] \n\t"
                     "btr   $9, %[tmp]\n\t"   // reset bit 9
                     "push  %[tmp] \n\t"
                     "popf"
                    : [tmp] "=r"(dummy)
                    : // no inputs
                    : // no clobbers.  // "memory" // would block reordering with loads/stores.
                );
}
或者干脆不要碰它的任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步uop和内存目标,只有一个uop

static inline
void clear_tf(void) {
   // if porting to x86-64 System V user-space: beware that push clobbers the red-zone
    __asm__ volatile("pushf \n\t"
                     "andl $0xFFFFFEFF, (%esp) \n\t"  // 1 byte larger than the pop/btr/push version
                     "popf"
                );
    // Basic asm syntax: no clobbers.
}
对于较小的代码大小,btrl$9,%esp可能是好的。在天湖的Haswell 3上仍然只有2个UOP,但比andl小2字节。和B$0xfe,1%esp的大小也相同,但会导致存储转发暂停,在推送后使用时,在Intel上为2 uop+堆栈同步uop。pop%%eax;和$0xfe,%ah;推送%eax的大小也相同,还有3个uop加上一个部分寄存器合并uop,该uop在Haswell/SKL上以一个周期自行发出。但是它在AMD上很好

便携性

顺便说一句,在x86-64 System V用户空间代码中,如果不关闭编译器的红色区域,就无法安全地推送/弹出,因此您可能希望在推送之前添加$-128,%rsp,然后在推送之后恢复

在内核代码中没有红色区域,所以内联asm中的push/pop就可以了


Windows使用不同的ABI,没有红色区域。

XOR会翻转一点,而不是总是清除它。这是一种选择,也是另一种选择。有一个内存目标的BTR在寄存器源的情况下速度非常慢,但在Haswell上只有2个UOP,在Skylake上只有3个UOP,这一点也不坏。但在AMD上,即使btr$9、%eax,其成本也高达2 UOP

在Skylake上,popf的速度相当慢,为9 UOP,每20个周期1次。或在Ryzen,35个UOP,每13个循环一个。因此,优化周围的代码不会产生很大的差异,但是找到一种保持代码大小紧凑的方法是很有趣的

您不需要自己保存/还原EAX,只需告诉编译器您想使用:EAX作为clobber列表对其进行clobber,或者使用一个伪输出操作数。请注意,我使用的是GNU C扩展asm,而不是basic

static inline
void clear_tf(void) {
    long dummy;       // there's no type that's always 32-bit on 32-bit, and always 64 on 64-bit.  x32 uses 32-bit pointers in long mode so uintptr_t or size_t doesn't work.
   // if porting to x86-64 System V user-space: beware that push clobbers the red-zone
    __asm__ volatile("pushf \n\t"
                     "pop   %[tmp] \n\t"
                     "btr   $9, %[tmp]\n\t"   // reset bit 9
                     "push  %[tmp] \n\t"
                     "popf"
                    : [tmp] "=r"(dummy)
                    : // no inputs
                    : // no clobbers.  // "memory" // would block reordering with loads/stores.
                );
}
或者干脆不要碰它的任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步uop和内存目标,只有一个uop

static inline
void clear_tf(void) {
   // if porting to x86-64 System V user-space: beware that push clobbers the red-zone
    __asm__ volatile("pushf \n\t"
                     "andl $0xFFFFFEFF, (%esp) \n\t"  // 1 byte larger than the pop/btr/push version
                     "popf"
                );
    // Basic asm syntax: no clobbers.
}
对于较小的代码大小,btrl$9,%esp可能是好的。在天湖的Haswell 3上仍然只有2个UOP,但比andl小2字节。和B$0xfe,1%esp的大小也相同,但会导致存储转发暂停,在推送后使用时,在Intel上为2 uop+堆栈同步uop。pop%%eax;和$0xfe,%ah;推送%eax的大小也相同,还有3个uop加上一个部分寄存器合并uop,该uop在Haswell/SKL上以一个周期自行发出。但是它在AMD上很好

便携性

顺便说一句,在x86-64 System V用户空间代码中,如果不关闭编译器的红色区域,就无法安全地推送/弹出,因此您可能希望在推送之前添加$-128,%rsp,然后在推送之后恢复

在内核代码中没有红色区域,所以内联asm中的push/pop就可以了


Windows使用不带红色区域的不同ABI。

要清除,您需要确保它已设置或在未设置版本的位掩码上使用或。谢谢您,我最初使用过WIZZ,如下所示。谢谢您,我最初使用WIZZ,如下所示,它不起作用,$0xFEFF,%eax\n\t;但对于EFlags,我们需要使用andl correct,在这种情况下,我想下面可能是正确的,如果我错了,请纠正我andl$0xFFFFFEFF,%eax;有了这个变化,我也不会例外,谢谢你,你说得对;我不知道为什么我建议把它。。。如果你把下面的答案框贴出来作为答案,这会帮助很多其他人;这样,下次有人遇到这个问题时,他们会发现这个问题并立即得到答案。要清除,您需要确保它已设置或使用或在您的位掩码的NOTted版本上。谢谢WIZZ最初我尝试过,以及BelowAnk you WIZZ最初我尝试过,如下所示,它不工作并且$0xFEFF,%eax\n\t;但对于EFlags,我们需要使用andl correct,在这种情况下,我想下面的可能是正确的,请原谅
如果我错了,请纠正我,$0xFFFFFEFF,%eax;有了这个变化,我也不会例外,谢谢你,你说得对;我不知道为什么我建议把它。。。如果你把下面的答案框贴出来作为答案,这会帮助很多其他人;这样,下次有人遇到这个问题时,他们会发现这个问题并立即得到答案。如果你在EAX上声明了一个clobber,你就不必在asm语句周围保存/恢复它。asmblah blah使用%%eax扩展asm::%eax;或者更好,使用一个伪输出操作数让编译器选择一个寄存器供您弹出。或者不要触碰eax,使用andl$0xFFFFFEFF、%%esp.或用于较小的代码大小,但用于存储转发暂停,和B$0xFE、1%esp.或btrl$9、%esp是代码大小和UOP之间的一个有用的折衷,尽管popf非常慢,所以它控制着成本,您可能不会经常这样做。如果您在eax上声明一个失败,您不必围绕asm语句保存/恢复它。asmblah blah使用%%eax扩展asm::%eax;或者更好,使用一个伪输出操作数让编译器选择一个寄存器供您弹出。或者不要触碰eax,使用andl$0xFFFFFEFF、%%esp.或用于较小的代码大小,但用于存储转发暂停,使用B$0xFE、1%esp.或btrl$9、%esp是代码大小和UOP之间的一个有用的折衷,尽管popf非常慢,所以它控制着成本,您可能不会经常这样做。@MichaelPetch:x86-64系统V ABI指定了一个红色区域。我没有意识到许多使用它的非Linux操作系统不支持红色区域,谢谢你指出这一点。我想在注释中加入一些与代码内联的内容,但没有空间容纳全部细节。如果你只需要确认你的操作系统没有使用它,并且你的代码永远不会被移植到用户空间Linux,那就没关系了。@MichaelPetch:x86-64 System V ABI指定了一个红色区域。我没有意识到许多使用它的非Linux操作系统不支持红色区域,谢谢你指出这一点。我想在注释中加入一些与代码内联的内容,但没有空间容纳全部细节。如果你只需要确认你的操作系统没有使用它,并且你的代码永远不会被移植到用户空间Linux,那就没关系了。