Прерывания и особые ситуации: Режим V86

Печать
Программирование - Архитектура и система команд микропроцессоров x86

 

Обработка прерываний в режиме V86 очень похожа на то, как это происходит в защищенном режиме. Однако существуют некоторые особенности, вызванные прежде всего спецификой режима V86 и стремлением достичь максимальной совместимости с программами процессора 8086. Специальный механизм виртуальных прерываний, управляемый битом CR4.VME и реализуемый в процессорах, начиная с Pentium, дополняет возможные способы обработки прерываний в режиме V86.

 

Особенности работы процессора при передаче управления обработчику прерываний в режиме V86

Особенность режима V86 прежде всего состоит в том, что любая задача V86 обязательно имеет 32-битный сегмент состояния задачи (TSS). 16-битный сегмент состояния задачи (TSS), поддерживаемый всеми моделями процессоров, начиная с Intel286 (и более поздними – для совместимости), содержит только младшее слово регистра EFLAGS, не включающее бит VM. Для правильного обслуживания прерываний в 32-битной программной среде в таблице дескрипторов прерываний (IDT) должны использоваться только 32-битные шлюзы (гипотетически это не касается только случая использования шлюза задачи) – здесь не могут использоваться 16-битные шлюзы, так как они не обеспечивают сохранение всех 32-битных значений регистров, которые нужны для нормального возврата в режим V86 после обработки прерывания. Такой возврат обеспечивается только командой IRETD, выполняемой на нулевом уровне привилегий, которая загружает 32-битный образ из стека в регистр EFLAGS с установленным битом VM, либо, если вход в обработчик прерывания был произведен как во вложенную задачу через шлюз задачи – при возврате из него командами IRET/IRETD с одновременным переключением задач.

Когда расширение виртуального режима не инициализировано (CR4.VME = 0), при поступлении запроса на прерывание в режиме V86 процессор автоматически переключается в защищенный режим с уровнем привилегий CPL = 0 (в связи с этим происходит замена указателя стека SS:ESP на стек нулевого уровня привилегий из сегмента состояния задачи TSS) и обращается к соответствующему дескриптору прерывания в таблице IDT. Если в качестве такого дескриптора используется 32-битный шлюз ловушки или шлюз прерывания, то он должен указывать на несогласованный кодовый сегмент с нулевым уровнем привилегий (максимум привилегий), в противном случае генерируется ошибка общей защиты (#GP) с селектором этого сегмента в коде ошибки.

После переключения на нулевой уровень привилегий и загрузки новых значений указателя стека SS:ESP процессор сохраняет в стеке текущие значения сегментных регистров GS, FS, DS, ES, а затем очищает эти регистры, помещая в них нулевые селекторы. Далее действия процессора идентичны процедуре обработки прерываний защищенного режима: он сохраняет в стеке старые значения SS, ESP, EFLAGS, CS и EIP, сбрасывает флаги VM, NTTF, RF и IF (последний сбрасывается, только если используется шлюз прерывания) регистра флагов EFLAGS и передает управление в обработчик прерывания (см. рис. 3.5.).

Если при вызове программных прерываний (за исключением команды INT 3) текущий уровень привилегий ввода/вывода EFLAGS.IOPL меньше текущего уровня привилегий задачи (то есть EFLAGS.IOPL < 3), то генерируется ошибка общей защиты (#GP) и управление передается в обработчик этой ошибки.

Обработчик прерывания может либо выполнить обработку прерывания самостоятельно, либо передать эту функцию монитору задач V86, либо возвратить управление прерванной задаче, эмулируя поступление прерывания по протоколу процессора 8086.

При возврате из прерывания по завершении работы программы обработчика защищенного режима процессор получает команду IRETD. Последовательно загружая сохраненные в стеке значения, он считывает и значение флага EFLAGS.VM. При обнаружении единицы в этом флаге производится обратное переключение в режим V86 и восстановление из стека сегментных регистров ES, DS, FS, GS, после чего управление передается в прерванную программу. Если команда IRETD будет получена на уровне привилегий, отличном от нуля, то процессор не сможет установить флаг EFLAGS.VM – и соответственно – не переключится в режим V86 — возврат будет произведен некорректно.

 

Стек после вызова прерывания или особой ситуации в режиме V86

Рис. 3.5. Стек после вызова прерывания или особой ситуации в режиме V86

 

Если в качестве дескриптора шлюза в таблице IDT используется шлюз задачи, то вызов прерывания инициализирует стандартный процесс переключения задач с установлением в новой задаче флага EFLAGS.NT и сохранением селектора TSS прерванной задачи в поле LINK сегмента TSS новой задачи. Команда IRET анализирует флаг EFLAGS.NT и, если он установлен, производит обратное переключение задач при возврате управления.

 

Виртуальные прерывания в режиме V86 (режим EV86)

В процессорах, начиная с Pentium, реализуется специальный механизм, позволяющий улучшить совместимость с программами процессора 8086 при их исполнении в режиме V86. Это механизм виртуальных прерываний, который управляется флагом CR4.VME. Если этот механизм активирован (CR4.VME = 1), то соответствующий режим работы процессора стали называть режимом EV86 (Enhanced V86). Существует также в чем-то аналогичный механизм виртуальных прерываний защищенного режима, который включается установкой флага CR4.PVI, но никак не влияет на процессор в режиме V86.

Особенности работы процессора в режиме EV86, то есть при инициализированных виртуальных прерываниях (CR4.VME = 1), состоят в следующем:

 

Всего в режиме EV86 (при CR4.VME = 1) возможны следующие пять вариантов обработки процессором внешних маскируемых прерываний и программных прерываний.

 

Режим 1 (для внешних маскируемых прерываний, когда EFLAGS.IOPL < 3)

Спецальные флаги EFLAGS.VIP и EFLAGS.VIF используются процессором для обработки таких прерываний. Флаг EFLAGS.VIF — это виртуальная копия флага EFLAGS.IF, а флаг EFLAGS.VIP — это индикатор наличия отложенных прерываний.

В процессе выполнения задачи V86 (или задачи EV86) процессору могут встретиться команды CLI, STI, POPF/POPFD, PUSHF/PUSHFD которые пытаются изменить или сохранить в стеке для последующего анализа значение флага маскирования внешних прерываний EFLAGS.IF. В обычном режиме V86 (когда CR4.VME = 0 и EFLAGS.IOPL < 3) все эти команды вызывают генерацию ошибки общей защиты (#GP) и не изменяют значение флага EFLAGS.IF. Но когда виртуальные прерывания инициализированы (CR4.VME = 1 и EFLAGS.IOPL < 3), специальный флаг EFLAGS.VIF используется вместо флага EFLAGS.IF. Этот флаг используется всеми командами (кроме 32-битных POPFD и PUSHFD, при использовании которых в указанном режиме по-прежнему будет сгенерирована ошибка общей защиты (#GP) ), которые пытаются изменять или считывать значение флага EFLAGS.IF (в том числе: CLI, STI, PUSHF, POPF). Во всех случаях в режиме EV86 только значение флага EFLAGS.VIF будет всякий раз изменяться и считываться этими командами. То есть у программы, выполняемой в таком режиме вообще нет никакой возможности узнать текущее значение флага EFLAGS.IF, так как она все время будеть иметь дело с его виртуальным аналогом EFLAGS.VIF. При этом отпадает и необходимость и в какой-то сложной эмуляции работы упомянутых команд средствами монитора V86, что существенно замедляло работу программ до появления в процессорах режима EV86.

При поступлении внешнего маскируемого прерывания процессор производит стандартное обращение к обработчику прерываний защищенного режима 2. Обработчик прерывания, поступившего в процессе исполнения задачи V86, должен произвести анализ бита VIF в сохраненном образе и в зависимости от его значения произвести следующие действия:

  • если в момент поступления запроса на прерывание EFLAGS.VIF = 1, значит прерывания были разрешены в задаче V86 (программе процессора 8086), обработчик защищенного режима может либо сам обработать это прерывание, либо передать управление в прерванную задачу, эмулируя поступления соответствующего внешнего маскируемого прерывания;
  • если в момент поступления запроса на прерывание EFLAGS.VIF = 0, значит прерывания были запрещены в задаче V86 (программе процессора 8086), обработчик защищенного режима может сам обработать поступившее прерывание, но если это необходимо делать внутри прерванной задачи, обработчик устанавливает специальный флаг отложенного виртуального прерывания EFLAGS.VIP и возвращает управление обратно в прерванную программу.

Если при выполнении задачи V86 в режиме EV86 процессор разрешает прием внешних маскируемых прерываний, уставливая флаг EFLAGS.VIF (например, командой STI или POPF), но при этом флаг EFLAGS.VIP = 1, то перед установкой флага EFLAGS.VIF срабатывает вызов отложенного виртуального прерывания и генерируется ошибка общей защиты (#GP). Обработчик ошибки общей защиты (монитор V86) должен распознавать такие ситуации и соответствующим образом обрабатывать поступившие запросы на прерывание. Рекомендуемая последовательность его действий перед возвратом управления в прерванную программу:

  • очистка флага EFLAGS.VIP,
  • установка бита VIF в сохраненном в стеке образе регистра EFLAGS и возврат управления в прерванную программу к соответствующему обработчику (таким образом эмулируется поступление ранее отложенного внешнего маскируемого прерывания).

В некоторых редких случаях флаги EFLAGS.VIP и EFLAGS.VIF могут оказаться установленными одновременно (например, после переключения задач командой IRET/IRETD), тогда перед выполнением очередной команды задачи V86 генерируется ошибка общей защиты (#GP).

Таким образом, флаг EFLAGS.VIP никогда автоматически не изменяется процессором, а должен устанавливаться и сбрасываться программным обеспечением самостоятельно.

2 В первоначальной документации Intel на процессор Pentium в этом месте присутствовал странный пассаж «при записи в стек значения регистра EFLAGS процессор записывает поле IOPL = 3 и бит IF равным текущему значению флага VIF». Позднее компания исключила эту фразу из руководства. Трудно сказать, было ли это ошибкой документирования или процессоры действительно меняли значения битов IOPL и IF при передаче управления в обработчик прерывания защищенного режима. Можно предполагать, что имела место ошибка при реализации микрокода команды INT n, в котором было продублировано поведение процессора для команды PUSHF, что выглядит логичным только до того момента, пока процессор не выходит из режима V86. Но в том то и дело, что в данном случае сам обработчик прерывания защищенного режима функционирует именно в обычном защищенном режиме на нулевом уровне привилегий. При возврате управления в прерванную программу командой IRETD процессор полностью восстанавливает из стека все биты регистра EFLAGS, включая поле IOPL и флаги IF, VIF, VIP. Сам обработчик прерывания может (в том числе через обращение к монитору V86), конечно, подкорретировать хранимый в стеке образ для того, чтобы при возврате в режим EV86 восстановились требуемые значения во всех битах EFLAGS, но в этом бы и не возникло необходимости, если бы все сохранялось «как есть» в момент вызова обработчика. Современные модели процессоров сохраняют этот образ без изменений, если управление передается в обработчик прерывания защищенного режима. Код, написанный для процессоров с дефектом, должен был бы учитывать и обходить возникающие ограничения. Одновременно с этим следует обратить внимание на Режим 5, в котором не происходит переключения на нулевой уровень привилегий защищенного режима, а управление остается по сути внутри задачи V86. Именно в этом режиме происходит подмена значений полей IOPL = 0 и IF = VIF при сохранении в стеке образа EFLAGS. Но теперь, по окончании обработки прерывания и поступлении команды IRET в режиме EV86, восстановление битов регистра EFLAGS из образа в стеке будет учитывать все особенности такого режима и вообще не затронет текущие значения поля EFLAGS.IOPL и флага EFLAGS.IF.

 

Режим 2 (для программных прерываний, когда EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1)

Для всех, генерируемых программно прерываний и особых ситуаций (кроме команды INT 3), если в момент их генерации в задаче V86 (всегда для обычного V86, а для режима EV86 — только при условии, что соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1) текущий уровень привилегий ввода/вывода EFLAGS.IOPL < 3, то генерируется ошибка общей защиты (#GP) и управление передается в обработчик этой ошибки как это описано для обработчика защищенного режима в соответствии с ее дескриптором в таблице IDT.

 

Режим 3 (для программных прерываний, когда EFLAGS.IOPL = 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 0)

При поступлении в режиме EV86 запроса на прерывание (кроме специального программного прерывания точки останова INT 3) процессор не переключается в защищенный режим, а управление не передается из текущей задачи V86. В этом случае протокол реакции на прерывание полностью идентичен тому, как это происходит в процессоре 8086 и при обработке прерываний в реальном режиме. Сначала сохраняются в стеке 16-битные текущие значения регистра флагов FLAGSNT = 0 и IOPL = 0)1 и указателя команд CS:IP, сбрасываются флаги EFLAGS.IF, EFLAGS.TF, а затем управление передается по адресу, задаваемому вектором соответствующего прерывания в таблице векторов прерываний (IVT), расположенной в начале адресного пространства задачи V86 (задачи процессора 8086). Аналогично происходит и возврат из прерывания по команде IRET.

1 По всей видимости, инженеры Intel столкнулись с какими-то проблемами в конкретном программном обеспечении, которое по значению поля IOPL в стеке идентифицировало, что программа выполняется в режиме V86. Поэтому в механизме реакции на прерывания в Режиме 3 (а также в Режиме 5) появилась очистка этого поля перед сохранением образа регистра FLAGS в стеке и передачей управления в задачу V86. Характерно, что, например, команды PUSHF/PUSHFD, выполняемые в режиме EV86 при EFLAGS.IOPL < 3 устанавливают поле IOPL = 3 перед записью в стек. И именно такое поведение имеет несколько больше оснований называться «эмуляцией поведения процессора 8086». Дело в том, что в процессорах до Intel286 биты 12 и 13 регистра флагов FLAGS, соответствующие полю IOPL в более старших моделях процессоров, считались зарезервированными, а их значения всегда были равны единице. Кроме того, в обычном режиме V86, когда виртуальные прерывания не активированы (CR4.VME = 0), все попытки выполнения команд PUSHF/PUSHFD при EFLAGS.IOPL < 3 приводят к генерации ошибки общей защиты (#GP), которая должна обрабатываться монитором V86 (внешние маскируемые прерывания в этом режиме обслуживаются через обработчик прерываний защищенного режима в соответствии с дескрипторами в таблице IDT и также могут перенаправляться в монитор V86, когда обработчик «опознает» режим V86 по значению бита VM в образе регистра EFLAGS в стеке). То есть значение поля IOPL, отличное от 3, уже «приводит в недоумение» программы (или сам монитор V86), написанные с учетом особенностей режима V86. К этому можно добавить, что процессор Intel286 в режиме реальной адресации не позволяет изменять значения в поле IOPL регистра флагов FLAGS, сохраняя его нулевым (как оно устанавливается в момент инициализации процессора), а вот более поздние модели процессоров, начиная с Intel386, такого ограничения уже не имеют.

 

Режим 4 (для программных прерываний, когда EFLAGS.IOPL = 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 1)

Обработка прерываний в режиме EV86 производится также, как это происходит при отключенном расширении виртуального режима (CR4.VME = 0) в обычном режиме V86. Все поступающие запросы перенаправляются в обработчик прерываний защищенного режима в соответствии с дескрипторами в таблице IDT.

 

Режим 5 (для программных прерываний, когда EFLAGS.IOPL < 3, а соответствующий прерыванию бит карты перенаправления прерываний IRB равен 0)

Обработка прерываний в таком режиме представляет собой совокупность методов, описанных для Режима 1 и Режима 3. То есть при поступлении внешнего маскируемого прерывания действует механизм в точности такой, как это предусмотрено Режимом 1. Это и есть Режим 1, так как значения в карте перенаправления прерываний IRB не влияют на обработку внешних маскируемых прерываний. Вместе с тем, при установке EFLAGS.IOPL < 3 в режиме EV86 процессор использует флаг виртуального прерывания EFLAGS.VIF вместо флага разрешения прерываний EFLAGS.IF во всех ситуациях, когда происходит его сохранение или изменение внутри задачи V86. Это также имеет место и при обработке программных прерываний, которые предполагают сохранение в стеке образа регистра флагов FLAGS — то есть содержимое стека в данном случае будет отличаться от описанного для Режима 3. Отсюда и возникает Режим 5, в котором при сохранении в стеке образа FLAGSNT = 0 и IOPL = 0)1 значение бита IF подменяется на значение из флага EFLAGS.VIF, а сам флаг EFLAGS.IF не сбрасывается перед передачей управления в обработчик — вместо него сбрасывается флаг EFLAGS.VIF.

 

Таблица 3.2. Все возможные режимы реагирования на программные и внешние маскируемые прерывания при работе в режиме V86

CR4.VME

EFLAGS.IOPL

Соответствующий бит в карте перенаправления прерываний IRB

Режим реагирования на прерывания

0

3

x

Все прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT с учетом особенностей режима V86.

0

< 3

x

Программные прерывания направляются в обработчик ошибки общей защиты (#GP) в соответствии с его дескриптором в таблице IDT.

Внешние маскируемые прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT с учетом особенностей режима V86.

1

3

0

Программные прерывания направляются в обработчик внутри текущей задачи V86 согласно соответствующего вектора в таблице векторов прерываний (IVT) задачи (Режим 3).

Внешние маскируемые прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT.

1

< 3

0

Программные прерывания направляются в обработчик внутри текущей задачи V86 согласно соответствующего вектора в таблице векторов прерываний (IVT) задачи (Режим 5).

Внешние маскируемые прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT, флаги EFLAGS.VIF и EFLAGS.VIP используются для поддержки виртуальных прерываний (Режим 1).

1

3

1

Все прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT с учетом особенностей режима V86 (Режим 4).

1

< 3

1

Программные прерывания направляются в обработчик ошибки общей защиты (#GP) в соответствии с его дескриптором в таблице IDT (Режим 2).

Внешние маскируемые прерывания направляются в обработчик защищенного режима в соответствии с дескрипторами в таблице IDT, флаги EFLAGS.VIF и EFLAGS.VIP используются для поддержки виртуальных прерываний (Режим 1).