Режим виртуального 8086

Режим виртуального 8086 (V86, VM86, иногда просто виртуальный режим) — режим адресации процессоров семейства x86 совместимый с прародителем семейства — процессором Intel 8086. Является подрежимом защищенного. Впервые появился в процессоре 80386 и предназначался главным образом для создания т. н. «виртуальных DOS-машин», виртуальных сред для исполнения приложений господствовавшей в то время в мире персональных ЭВМ операционной системы MS-DOS. Является первой попыткой корпорации Intel внедрить в свои процессоры технологии аппаратной виртуализации (хотя называть этот режим "виртуализацией" не совсем корректно, это скорее "гнездо совместимости" со старым DOSовским ПО).

Представляет собой аппаратную поддержку исполнения ПО, разработанного для MS-DOS, как одного из процессов "большой" 32 битной многозадачной ОС, такой, как UNIX-совместимые или Windows NT.

При этом под "большой" ОС может пониматься и обертка поверх самой MS-DOS, например, Quarterdeck Desqview или же более позднее ядро VMM из версий Windows 386/3.x/95/98/Me. Исторически VMM и начинался как "многозадачник" поверх MS-DOS (отправляющий всю реальную работу туда), однако уже в Windows 3.11 практически весь функционал MS-DOS (например, работа с файловой системой) был реализован заново в кернел-модулях VMM, называемых VxD. Начиная с Windows 95, VMM практически не зависим от нижележащей DOS, используя ее только как загрузчик и как командную строку аварийного режима.

Как минимум V86 режим позволяет исполнять ПО для MS-DOS (и ее саму) с включенной страничной виртуальной памятью. Одним из первых продуктов, использовавших этот режим именно с такой целью, был QEMM386 компании Quarterdeck, немногоурезанный аналог которого под названием EMM386.EXE был впоследствии включен в поставку поздней MS-DOS компанией Microsoft. Эти продукты реализовали чисто программную, независимую от чипсета, поддержку EMS на компьютерах с процессорами 386 и новее.

Кроме того, V86 режим позволяет виртуализировать аппаратуру, т.е. создать полную эмуляцию аппаратуры (возможно, без нее самой) для MS-DOS приложений. Эмулируются и порты, и регистры, отображенные в память, и память (например, видеопамять), и прерывания, и ISA DMA.

В отличие от реального режима, переключение в обычный защищенный режим прямо в приложении путем исполнения специальных команд машинного кода невозможно. Все дело в том, что реальный режим - это "неограниченный" Ring 0 (kernel mode), а V86 - это "ограниченный" Ring 3 (user mode). Единственный способ это сделать (а это нужно реализациям DPMI, расширителям DOS, и разработанному на основе их ПО, такому, как игра Doom) - это попросить об этом V86 monitor (ядро "большой" ОС илти же его подобие, предназначенное только для поддержки V86). API для таких "просьб" называется VCPI, реализован как расширение EMS API (int 67h), включен в QEMM386 и в EMM386, и НЕ включен ни в Windows, основанные на VMM, ни в Windows NT. Другое дело, что туда включена готовая реализация DPMI для DOS-приложений, однако же некоторые расширители DOS (например, ранние версии Phar Lap), и все основанное на них прикладное ПО не умеют пользоваться внешним DPMI, настаивая на своем внутреннем. Такое ПО не будет работать под Windows. Игра Doom под Windows - работает, она построена на поздней версии Phar Lap без этой недоработки.

Кроме того, ПО, использующее незадокументированный unreal mode процессора (такое, как игры Ultima VII) - также не будет работать в V86, а значит, и под Windows, и, до появления полных эмуляторов, таких, как DOSBox - и как DOS-задача любой другой многозадачной 32-битной ОС.

Особенности

править

Задача виртуального 8086 представляет собой обычную задачу защищенного режима со следующими особенностями:

  • Режим активируется установкой флажка EFLAGS.VM с помощью привилегированной инструкции (IRET обязательно из Ring 0 с нужным значеним EFLAGS на стеке, или же аппаратное переключение задачи JMP FAR <tss>:00000000 на TSS, содержащий нужное значение EFLAGS, в обоих случаях надо уже быть в защищенном режиме). Непривилегированная команда POPF, даже исполняясь на уровне привилегий 0, состояния этого флажка не изменяет.
  • Интерпретация селекторов сегментов (сегментных "половин" дальних указателей, и содержимого сегментных регистров) аналогична реальному режиму, т.е. segmentBase = selector * 16 = selector << 4. В защищенном же режиме будет нечто следующее: segmentBase = ( ( selector & 1 ) ? _ldtr : _gdtr )[selector >> 3].baseAddress. При этом структура, содержащая поле baseAddress (дескриптор сегмента) загружается из аппаратных таблиц в "невидимую" часть сегментного регистра при каждой записи селектора в последний.
  • Исходя из формата адреса, возможна адресация только нижнего мегабайта памяти (+65520 байт HMA); однако, благодаря страничному отображению, в эту область могут быть отображены произвольные страницы памяти, что позволяет организовать многозадачность для задач DOS. Также можно отображать неприсутствующие страницы, и устанавливать (из кернел-модуля) для них обработчик page fault, регистрируя его в V86 мониторе. Это позволяет виртуализировать память аппаратуры (такую, как видеопамять), и ее регистры, отображаемые в память.
  • Задача исполняется с самыми низкими привилегиями (user mode) в кольце 3. Это означает, что команды INT, IRET, CLI, STI вызывают general protection fault, а путь этой ловушки в V86 мониторе способен их проэмулировать. Что же до команд IN и OUT, то тут все зависит от аппаратной IO permission bitmap, бит в которой может как разрешить приложению прямые обращения к аппаратуре, так и запретить их. В случае запрета возможна эмуляция портов в кернел-модуле - так же, как и памяти, см. выше. Windows VMM предоставлял для этого API Install_IOHander для кернел-модулей VxD.
  • Ловушки обрабатываются обычными обработчиками ОС защищенного режима (т.е. IDT). Таблица векторов прерываний реального режима по адресу 0 не используется (если не активно расширение VME). Речь, конечно, об ошибках процессора и об аппаратных прерываниях, ибо команда INT вызывает GP fault. Модуль операционной системы, часто называемый V86-монитором, может эмулировать прерывание реального режима, программно обращаясь к этой таблице. Для этого достаточно уложить на стек ядра нужные CS:EIP и EFLAGS, и сделать так, чтобы последний IRET - возврат из ядра в приложение - выполнился на этом стеке. Windows VMM предоставлял для этого API Simulate_Int для кернел-модулей VxD. Что же касается драйвера контроллера прерываний в VMM, который назывался VPICD, то последний по умолчанию и отправлял все аппаратные прерывания в Simulate_Int в текущую полноэкранную задачу, отключить это можно было, только подписав VxD на аппаратное прерывание, что называлось "виртуализировать прерывание".
  • Меняется значение поля EFLAGS.IOPL. В режиме V86 оно используется для перехвата некоторых инструкций (CLI, STI, PUSHF, POPF, INT, IRET), а для перехвата ввода-вывода требуется использование битовой карты разрешения портов в сегменте состояния задачи;
  • V86 отличается от Ring 3 защищенного режима только интерпретацией селекторов, что и делает возможным исполнение DOSовского кода.
  • Что же касается отличий от реального режима, то их три: а) ошибки процессора и аппаратные прерывания идут в IDT б) Ring 3, а не Ring 0 в) возможность использования страничной виртуальной памяти.

Расширения

править

Начиная с процессора Pentium в режим V86 были введены опциональные функции: таблица перенаправления прерываний и виртуализация флажка прерываний. Теперь процессор, без вмешательства ОС мог непосредственно использовать таблицу векторов прерываний по адресу 0, причем не для всех прерываний, а только для тех, что разрешила операционная система с помощью специальной битовой карты (аналогичной карте ввода-вывода) в сегменте состояния задачи. Виртуализация флага прерываний также уменьшает число исключительных ситуаций, требующих программной обработки операционной системой, что, в свою очередь, сказывается на общей производительности.

V86 и x86-64

править

Процессоры с архитектурой x86-64 поддерживают V86 только в наследственном, но не в длинном режиме. В длинном режиме флажок EFLAGS.VM попросту игнорируется. Поэтому, для поддержки виртуальных DOS-задач требуется переключение в наследственный режим, сопряженное с двойным сбросом MMU. А поскольку роль DOS и её приложений в наши дни практически сошла на нет, операционные системы x86-64 не включают такого рода поддержку V86. Тем не менее, некоторые современные средства аппаратной виртуализации позволяют виртуализовать как реальный режим, так и V86, получая в результате двойную виртуализацию.

Поддержка операционными системами

править
  • MS-DOS — В DOS режим V86 использовался для эмуляции расширенной памяти по стандарту LIM/EMS при помощи специального драйвера EMM386. Поскольку в этом режиме, в отличие от реального, возможна трансляция страниц, расширенная память эмулировалась с помощью отображения дополнительной в окно адресов UMB/EMS. Также драйвер позволял в адресах UMB размещать данные и резидентные программы.
  • OS/2 — В составе OS/2 имелась штатная виртуальная DOS-машина.
  • Windows — В составе Windows начиная с версии 3.0 появился 386 расширенный режим, который позволял создавать V86-задачи для программ DOS («DOS в окне»).
  • Linux — ОС Linux/x86 поддерживается системный вызов vm86() которым активно пользуется программа DOSEMU — свободная реализация виртуальной DOS-машины (в последнее время вытесненяемая эмулятором DOSBox, поскольку эмуляция, несмотря на большие ресурсозатраты более точно имитирует работу компьютера, что было важно для программ того времени, довольно часто обращающихся к аппаратным средствам непосредственно).
  • FreeBSD — В ОС FreeBSD/i386 имеется поддержка V86 и встренная программа doscmd, обладающая гораздо меньшими возможностями, чем DOSEMU, поэтому практически не используемая.