为什么要避免使用POPF恢复中断标志状态?

时间:2019-02-25 12:37:30

标签: assembly x86 nasm interrupt eflags

我的问题不是关于 BX 用作返回值,而不是将其放置在全局内存位置或堆栈中的事实。我观察到此代码最近在评论中发布。该代码适用于使用BIOS的实模式鼠标处理程序。保存/恢复FLAGS寄存器状态的两个小功能如下:

EFLAGS_IF        equ 0x200         ; Bit mask for IF flag in FLAGS register

; Function: save_if_flag
;           save the current state of the Interrupt Flag (IF)
;
; Inputs:   None
; Returns:  BX = 0x200 if interrupt flag is set, 0 otherwise

save_if_flag:
    pushf
    pop bx                      ; Get FLAGS into BX
    and bx, EFLAGS_IF           ; BX=0 if IF is clear, BX=0x200 if set
    ret

; Function: restore_if_flag
;           restore Interrupt Flag (IF) state
;
; Inputs:   BX = save Interrupt Flag state
; Clobbers: None
; Returns:  EFLAGS IF flag restored

restore_if_flag:
    test bx, bx                 ; Is saved Interrupt Flag zero?
    jz .if_off                  ;     If zero, then disable interrupts & finish
    sti                         ; Otherwise enable interrupts
    ret                         ; We're finished
.if_off:                        
    cli                         ; Disable interrupts
    ret

我想了解为什么restore_if_flag函数会这样做:

restore_if_flag:
    test bx, bx                 ; Is saved Interrupt Flag zero?
    jz .if_off                  ;     If zero, then disable interrupts & finish
    sti                         ; Otherwise enable interrupts
    ret                         ; We're finished
.if_off:                        
    cli                         ; Disable interrupts
    ret

而不是像这样简单地使用POPF

restore_if_flag:
    push bx
    popf
    ret

为什么使用 STI / CLI 显式保存/恢复中断标志,而不是简单地使用 POPF 恢复先前的FLAGS寄存器?

1 个答案:

答案 0 :(得分:3)

您正在查看的代码很有用。撰写此内容的人很可能知道POPF在所有不同的操作模式下对中断标志( IF )的处理方式都不相同。 enter image description here enter image description here


他们可能会尝试避免以下两种代码模式:

sti
pushf                     ; Save flags including interrupts (IF)
cli
; Do work here with interrupts off
popf                      ; Restore interrupts (re-enable IF) 
; Interrupts may still be off at this point depending on mode and IO Privileges

cli
pushf                     ; Save flags including interrupts (IF)
sti
; Do work here with interrupts on
popf                      ; Restore interrupts to previous state 
; Interrupts may still be on at this point depending on mode and IO privileges

这2种情况是 IF 实际发生了更改,并且预计 POPF 会将 IF 恢复到以前的值。如果您查看第一个图表,我已经圈了 N 。在这些情况下,您处于保护模式,兼容模式或64位模式,并且当前特权级别(CPL)为1,2,3,并且IOPL(IO特权级别) POPF 不会产生一般的保护错误,并且会默默地忽略对 IF 的更改。

因为没有故障,内核不知道有尝试更改 IF 的想法,因此没有机会虚拟化 IF 。当 STI CLI 没有适当的IOPL特权时,它们将作为特权指令使用,并且会出错,从而使 IF 可以被虚拟化。 CPU。

为什么要在原始代码中进行明确的 STI / CLI STI / CLI 将保证在您没有适当的IOPL特权来更新 IF 的情况下内核可以拦截的错误。 POPF 可能允许 IF 与程序认为标志应为的概念不同步。通过使用 STI CLI 更改 IF ,您可以使内核更轻松地使事物保持同步。


DPMI(DOS保护模式接口)specification讨论了此问题,并对此进行了描述:

  

2.3中断标志管理       popf和iret指令可能不会修改中断标志的状态       因为大多数DPMI实现都将以IOPL      

这意味着以下代码序列将使中断保持禁用:

     ;
     ; (Assume interrupts are enabled at this point)
     ;
     pushf
     cli
     .
     .
     popf            ; Interrupts are still OFF!
     

请注意,由于DPMI的某些实现将维护虚拟中断       保护模式DOS程序的状态,中断标志的当前值       可能无法反映当前的虚拟中断状态。保护模式程序       应该使用虚拟中断状态服务来确定当前       中断标志状态(请参阅第99页)。

     

由于cli和sti是特权指令,它们将引起保护       违反,DPMI提供程序将模拟指令。因为       处理异常所涉及的开销,应使用cli和sti作为       尽可能少。一般而言,您应该遵循以下任一说明       至少需要300个时钟。