Почему бы не использовать POPF для восстановления состояния флага прерывания? - PullRequest
0 голосов
/ 25 февраля 2019

Мой вопрос не о том, что 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 ?

1 Ответ

0 голосов
/ 25 февраля 2019

Код, на который вы смотрите, имеет свои достоинства.Вероятно, тот, кто написал это, знает, что 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

В этих двух случаях IF фактически был изменен, и POPF , как ожидается, восстановит IF до его предыдущего значения.Если вы просматриваете первую диаграмму, я обвел ее N .Это те случаи, когда вы находитесь в защищенном режиме, режиме совместимости или 64-битном режиме, и текущий уровень привилегий (CPL) равен 1,2,3, а IOPL (уровень привилегий IO) POPF не будет генерировать общую ошибку защиты и будет молча игнорировать изменение на IF .

Поскольку ошибки нет, ядро ​​не имеетлюбая идея, что была попытка изменить IF , поэтому не имеет шансов виртуализировать IF . STI и CLI действуют как привилегированные инструкции, когда они не имеют надлежащих привилегий IOPL, и будут вызывать ошибки в ядре, где IF может быть виртуализирован процессором.

На вопрос, почему в исходном коде делается явное STI / CLI ? STI / CLI гарантирует ошибку, которую ядро ​​может перехватить в случаях, когда у вас нет надлежащих привилегий IOPL для обновления IF . POPF может позволить IF стать не синхронизированным с понятием того, что программа думает, что флаг должен быть.Используя STI и CLI для изменения IF , вы позволяете ядру более легко поддерживать синхронизацию.


DPMI (DOSИнтерфейс защищенного режима) спецификация обсуждает эту проблему и описывается следующим образом:

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 часов.

...