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

时间:2018-07-19 06:23:15

标签: x86 inline-assembly eflags

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

下面尝试清除TF标志,但遇到错误***** Unhandled interrupt vector *****

__asm__ volatile("pushl %eax\n\t"
                        "pushfl\n\t"
                        "popl %eax\n\t"
                        "xorl $0x0100, %eax\n\t"
                        "pushl %eax\n\t"
                        "popfl\n\t"
                        "popl %eax\n\t");

2 个答案:

答案 0 :(得分:3)

XOR会翻转而不是始终清除它。 AND是一个选项,BTR (bit-test-reset)是另一个选项。具有寄存器目标的BTR在寄存器源的情况下确实很慢,但是立即使用(在Haswell上只有2 uops,在Skylake上只有3 uo。在AMD上最多4,但在{{{ 1}}。)

btr $9, %eax相当慢(9微秒,在Skylake上每20个周期1个)。或在Ryzen上,每35个微循环35次。 (http://agner.org/optimize)。因此,优化周围的代码不会有太大的不同,但是找到一种使代码大小保持紧凑的方法很有趣。

您不需要自己保存/恢复EAX,只需告诉编译器您要使用popf作为EAB列表来对其进行破坏,也可以使用虚拟输出操作数(请注意,我使用的是GNU C扩展的asm,不是基本的)。

: "eax"

或者根本不去碰任何寄存器:这也是非常有效的,尤其是在AMD Ryzen上,那里没有堆栈同步的uops和内存目标,并且是单寄存器的。

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

对于较小的代码大小,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. } 可能很好。在Haswell上仍然只有2微码(在Skylake上只有3微码),但是比btrl $9, (%esp)小2个字节。 andl的大小也相同,但是在andb $0xfe, 1(%esp)之后使用时,在Intel上会导致存储转发停顿,并且是2 oups +堆栈同步uop。 push的大小也相同,也为3微码(加上部分寄存器合并的uop,它本身在一个周期内在Haswell / SKL上发出)。但这在AMD上很好。


可移植性

顺便说一句,在x86-64 System V用户空间代码中,如果不破坏编译器的红色区域,就不能安全地进行推送/弹出操作,因此您可能希望在pop %%eax; and $0xfe, %ah; push %eax之前add $-128, %rsp,并且之后将其恢复。

在内核代码中没有红色区域,因此内联asm中的push / pop很好。

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

答案 1 :(得分:1)

使用以下代码可以正常工作。谢谢

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