- Войдите или зарегистрируйтесь, чтобы оставлять комментарии
Современные процессоры Intel и AMD поддерживают технологии виртуализации, позволяющие использовать одну платформу как несколько виртуальных машин, в каждой из которых работает своя операционная система. Эта статья рассчитана на подготовленного читателя, желающего получить практический опыт работы с данными технологиями, на уровне написания собственного монитора виртуального режима. Приводится описание исследовательской работы, выполненной автором и исходные тексты программы.
Основные определения Intel VMX
Технология Intel VMX определяет два специальных состояния процессора:
- Состояние VMX Root Operation (режим гипервизора) используется для выполнения программы, создающей виртуальные машины на базе одного компьютера и осуществляющей координацию их работы. Эта программа называется VMM или Virtual Machine Monitor. Она имеет неограниченный доступ к ресурсам процессора и платформы.
- Состояние VMX Non-Root Operation (режим «гостя») используется для выполнения программ или операционных систем пользователя, для каждой из которых создается своя виртуальная машина. При этом программа или ОС, выполняемая на виртуальной машине, предполагает, что выполняется на обычном компьютере и в идеале не должна знать о существовании гипервизора и других виртуальных машин. Очевидно, что для этого гипервизор должен уметь перехватывать обращения гостей к аппаратным ресурсам процессора и платформы, разрешать конфликты, возникающие при одновременном доступе нескольких гостей к одному аппаратному ресурсу, а также не допускать ситуаций, при которых сбой в одной виртуальной машине может привести к сбою всей системы.
Для включения режима VMX с полномочиями гипервизора, программа должна выполнить инструкцию VMXON. Затем, после подготовки в оперативной памяти структур, необходимых для запуска виртуальных машин, программа может запускать гостей выполняя инструкцию VMLAUNCH. Эта операция, при которой процессор переключается из режима гипервизора в режим гостя называется VM Entry, то есть запуск виртуальной машины.

При выполнении гостей периодически возникают ситуации, требующие вмешательства гипервизора, например попытка обращения к определенным системным ресурсам, аппаратное прерывание или истечение интервала времени, отведенного данной виртуальной машине. При этом управление возвращается в гипервизор. Эта операция называется VM Exit. После обработки ситуации, выполнение гостя можно возобновить инструкцией VMRESUME. Для выключения режима VMX используется инструкция VMXOFF. Подробности выполнения инструкций здесь.
Идентификация ресурсов Intel VMX
Перед включением режима VMX, программа должна проверить наличие его поддержки процессором и прочитать ряд параметров, характеризующих функционирование виртуальной машины. Выполнив функцию 1 инструкции CPUID, мы можем получить в регистрах EDX и ECX битовую карту, в которой каждый бит индицирует поддержку процессором определенной технологии: 0=не поддерживается, 1=поддерживается (CPU Feature Flags). Технологии VMX соответствует бит 5 регистра ECX. Подробности в [6].
Если VMX поддерживается, можно переходить к чтению параметров виртуальной машины. Для этого существует группа Model-Specific регистров (MSR), называемая VMX Capability MSR. Эти регистры имеют статус Read Only и доступны по адресам 480h-490h в пространстве MSR. Напомним, что для чтения MSR используется инструкция RDMSR, которая принимает адрес MSR в регистре ECX и возвращает 64-битное значение, прочитанное из заданного MSR в регистрах EDX (старшие биты) и EAX (младшие биты).
Группа VMX Capability MSR содержит следующие регистры:
IA32_VMX_BASIC MSR (адрес 480h) — содержит номер ревизии VMX, значение размера регионов VMXON и VMCS, и другие параметры.
IA32_VMX_PINBASED_CTLS MSR (481h)
IA32_VMX_TRUE_PINBASED_CTLS MSR (адрес 48Dh) — содержат битовые карты, отражающие способность процессора прерывать выполнение гостевых программ и передавать управление программе-гипервизору при возникновении различных асинхронных событий (аппаратных прерываний).
IA32_VMX_PROCBASED_CTLS MSR (482h)
IA32_VMX_TRUE_PROCBASED_CTLS MSR (48Eh)
IA32_VMX_PROCBASED_CTLS2 MSR (48Bh) — содержат битовые карты, отражающие способность процессора прерывать выполнение гостевых программ и передавать управление программе-гипервизору при возникновении различных синхронных событий (выполнение заданных инструкций и обращение к заданным системным ресурсам со стороны гостевой программы).
IA32_VMX_EXIT_CTLS MSR (483h)
IA32_VMX_TRUE_EXIT_CTLS MSR (48Fh) — содержат битовые карты, отражающие поддержку процессором ряда опций, управляющих операцией VM Exit (завершение выполнения гостевой программы и возврат в гипервизор).
IA32_VMX_ENTRY_CTLS MSR (484h)
IA32_VMX_TRUE_ENTRY_CTLS MSR (490h) — содержат битовые карты, отражающие поддержку процессором ряда опций, управляющих операцией VM Entry (передача управления из гипервизора в гостевую программу).
IA32_VMX_MISC MSR (485h) — содержит параметры, отражающие поддержку процессором ряда функций, управляющих контекстом виртуальной машины.
IA32_VMX_CR0_FIXED0 MSR (486h)
IA32_VMX_CR0_FIXED1 MSR (487h) — эти регистры побитно соответствуют системному регистру управления CR0 и отражают ограничения, накладываемые на его содержимое при переходе в режим VMX. Для каждого бита регистра CR0 они определяют допустимые значения этого бита. При этом может быть 3 варианта: бит должен быть в 0, бит должен быть в 1 или оба значения 0 и 1 допустимы.
IA32_VMX_CR4_FIXED0 MSR (488h)
IA32_VMX_CR4_FIXED1 MSR (489h) — эти регистры побитно соответствуют системному регистру управления CR4 и отражают ограничения, накладываемые на его содержимое при переходе в режим VMX. См. комментарий в предыдущем пункте.
IA32_VMX_VMCS_ENUM MSR (48Ah) — содержит максимальное значение индекса, используемое при адресации полей блока VMCS.
IA32_VMX_EPT_VPID_CAP MSR (48Ch) — содержит параметры, отражающие поддержку процессором режимов EPT (Extended Page Tables) и VPID (Virtual Processor Identifier).
Для управления режимом VMX также используются IA32_FEATURE_CONTROL MSR (адрес 3Ah) и бит CR4.VMXE (бит 13).
Регионы VMXON и VMCS, используемые в Intel VMX
Технология Intel VMX определяет следующие две специальные области памяти.
VMXON Region — размер этого блока не превышает 4KB. Точное значение размера зависит от модели процессора и может быть прочитано из битов 44-32 регистра IA32_VMX_BASIC MSR. Базовый физический адрес этого блока находится в 64-битном регистре VMXON Pointer, который загружается при выполнении инструкции VMXON, включающей режим VMX. Базовый адрес должен быть выровнен на 4KB (биты адреса 0-11 должны быть нулевые) и быть в пределах максимального физического адреса, поддерживаемого процессором. Первое 32-битное слово этого блока должно содержать 32-битный идентификатор версии VMX (VMCS Revision Identifier). При выполнении инструкции VMXON проверяется совместимость этого идентификатора с моделью процессора. Для успешного прохождения этой проверки, перед выполнением инструкции VMXON программа должна прочитать поддерживаемый номер версии VMX из битов 31-0 регистра IA32_VMX_BASIC MSR и записать его по смещению 0 в рассматриваемом блоке. Назначение остальных полей блока зависит от модели процессора и не документировано Intel. При работе в режиме VMX (в интервале времени от выполнения инструкции VMXON до выполнения инструкции VMXOFF) процессор использует этот блок аппаратно и программа не должна выполнять его чтение и запись. Описанная выше подготовка 32-битного идентификатора версии перед запуском режима VMX является единственной заботой программы о данном блоке.
VMCS Region (Virtual Machine Control Structure) — размер этого блока зависит от модели процессора и всегда равен размеру выше описанного блока VMXON Region. Базовый физический адрес блока находится в 64-битном регистре VMCS Pointer, который загружается при выполнении инструкции VMPTRLD. Как и для блока VMXON, базовый адрес блока VMCS должен быть выровнен на 4KB, и быть в пределах максимального физического адреса, поддерживаемого процессором. Первое 32-битное слово блока VMCS должно содержать номер версии VMX. Его записывает программа в соответствии с битами 31-0 регистра IA32_VMX_BASIC MSR. Это необходимо для успешного выполнения проверки при выполнении инструкции VMPTRLD. Следующее 32-битное слово используется как VMX Abort Indicator, процессор записывает в эту ячейку код ошибки при аварийном завершении гостевых программ. Форматирование остальной части блока не документировано и может различаться у процессоров разных моделей, но при этом, список переменных, хранящихся в этом блоке, документирован и фиксирован. Отметим, что для этих переменных предусмотрены инструкции VMREAD и VMWRITE, которые позволяют обратиться к заданной переменной не по адресу, а по ее идентификатору. Благодаря такому методу, программа, использующая эти инструкции для работы с блоком VMCS, не требует адаптации под конкретную модель процессора. Переменные объединены в 6 логических групп:
- Guest state area. Область сохранения данных для гостевой задачи. Состояние процессора сохраняется в эту область при операциях VM Exit (переход от виртуальной машины к гипервизору) и загружается из этой области при операциях VM Entry (переход от гипервизора к виртуальной машине).
- Host state area. Область сохранения состояния для гипервизора.
- VM execution control fields. Эти поля управляют поведением процессора в режиме гостевой задачи (VMX non-root). Они определяют список возможных причин для выполнения операции VM exit, то есть, по каким причинам управление из виртуальной машины может возвращаться в гипервизор.
- VM exit control fields. Управляют выполнением операций VM exit (завершение работы виртуальной машины).
- VM entry control fields. Управляют выполнением операций VM entry (запуск виртуальной машины).
- VM exit information fields. Принимают информацию при VM exit. Описывают причину и тип операции VM exit. Эти поля доступны только для чтения (Read Only).
Совокупность групп 3, 4, 5 иногда называют VM Controls.
Инструкции Intel VMX
Технология Intel VMX определяет следующие процессорные инструкции.
VMXON m64 — включает режим VMX root operation. Программа приобретает полномочия гипервизора виртуального режима. Операнд инструкции m64 – 64-битная ячейка памяти, из которой считывается указатель VMXON Pointer — физический адрес региона VMXON. Считывается поле VMCS Revision ID из региона VMXON и его значение проверяется на совместимость с моделью процессора. После перехода в режим VMX запрещены следующие функции: механизм программного сброса процессора (сигнал INIT), маскирование адресной линии A20 (сигнал A20M), то есть линия A20 будет разрешена, независимо от состояния этого сигнала. Инструкция может использоваться только в Protected Mode. Для успешного перехода в режим VMX, текущий режим работы процессора и состояние битов в регистрах управления CR0 и CR4 должны удовлетворять ряду условий, детально описанных в [3].
VMXOFF — выключает режим VMX root operation, отменяет действие инструкции VMXON. Процессор возвращается в обычный режим.
VMPTRLD m64 — загружает значение указателя VMCS Pointer из 64-битной ячейки памяти, адрес которой задан операндом m64. Регион VMCS, адресуемый указателем, приобретает статус активного и текущего региона VMCS. Считывается поле VMCS Revision ID из региона VMCS и его значение проверяется на совместимость с моделью процессора. После загрузки указателя VMCS Pointer, программа может начинать монтировать поля управления виртуальной машиной, используя инструкции VMREAD, VMWRITE.
VMPTRST m64 — сохраняет значение указателя VMCS Pointer в 64-битную ячейку памяти, адрес которой задан операндом m64.
VMCLEAR m64 — очищает состояние блока VMCS, адрес которого находится в 64-битной ячейке памяти, адрес которой задан операндом m64. Если указан адрес текущего блока VMCS, то последний приобретает статус недостоверного. Виртуальная машина, описываемая данным блоком, получает статус остановленной. Точный список полей блока VMCS, обновляемых в ходе очистки и записываемые в них данные зависят от модели процессора. Так как данные блока VMCS могут кэшироваться во внутренних буферах процессора, ожидая отложенной записи, в обязанности инструкции VMCLEAR также входит выполнение указанной отложенной записи.
VMREAD r/m64, r64 VMREAD r/m32, r32 — считывает из текущего блока VMCS элемент, код которого указан в операнде-источнике (регистре r64 или r32) и помещает данный элемент в операнд получатель (регистр или ячейку памяти r/m64 или r/m32). Инструкция обращается к элементам блока VMCS, используя их коды, а не адреса ячеек памяти. Коды элементов перечислены в [5]. Отметим, что обращаться к элементам блока VMCS, используя обычные инструкции работы с ячейками памяти, не рекомендуется по двум причинам:
- Форматирование региона VMCS зависит от модели процессора и не документировано. При использовании инструкций VMREAD и VMWRITE это учитывается автоматически, так как в данных инструкциях задается не адрес элемента, а его код, а адрес автоматически генерирует процессор по коду. Но если мы читаем элемент блока VMCS инструкцией MOV, используя адрес элемента, потребуется настройка адреса в соответствии с моделью процессора, что крайне неудобно.
- Данные региона VMCS кэшируются во внутренних буферах процессора. Инструкции VMREAD и VMWRITE автоматически учитывают это и гарантируют когерентность состояния блока VMCS в памяти и в процессоре. А при чтении элемента блока VMCS инструкцией MOV (как обычной ячейки памяти), мы можем прочитать устаревшие данные, не обновленные процессором. При записи элемента блока VMCS инструкцией MOV процессор может не заметить изменения состояния перезаписанного элемента, если использует его копию из внутреннего буфера.
VMWRITE r64, r/m64 VMWRITE r32, r/m32 — значение операнда-источника (r/m64 или r/m32) записывается в указанный элемент текущего блока VMCS. Код элемента указан в операнде-получателе (r64 или r32). Инструкция обращается к элементам блока VMCS, используя их коды, а не адреса ячеек памяти. См. пояснение в описании инструкции VMREAD.
VMLAUNCH — запускает виртуальную машину, управляемую текущим блоком VMCS, то есть тем блоком VMCS, адрес которого находится в регистре VMCS Pointer. Предполагается, что исходно данная виртуальная машина остановлена (Launch State = Clear). После выполнения серии проверок, описанной в [5], процессор переключается из режима гипервизора (VMX root operation) в режим гостя (VMX non-root operation) и начинает выполнение гостевой программы.
VMRESUME — запускает виртуальную машину, управляемую текущим блоком VMCS. Эта инструкция отличается от инструкции VMLAUNCH тем, что предназначена для возобновления работы уже запущенной виртуальной машины, которая временно вернула управление в гипервизор, например из-за попытки доступа к системным ресурсам, обращаться к которым гостевая программа не имеет права. VMRESUME применяется, когда текущее состояние Launch State = Launched.
VMCALL — завершает выполнение виртуальной машины и передает управление в гипервизор (VM Monitor). Выполняется VM Exit, то есть переключение процессора из состояния VMX non-root operation в состояние VMX root operation. Эта инструкция позволяет осуществлять явный вызов сервисных процедур гипервизора из гостевой программы.
Суть эксперимента
Предлагаемый пример демонстрирует возможности технологии виртуализации Intel VMX. В целях монопольного и беспрепятственного взаимодействия программы с аппаратным обеспечением, использована «древняя» технология отладки под DOS. Очевидно, что полученные знания и навыки универсальны, поэтому будут полезны при написании подобных процедур для любой ОС. В одной из наших публикаций детально описана процедура переключения процессора в 64-битный защищенный режим, которая понадобится и в данном примере. Напомним, что для использования технологии Intel VMX процессор должен работать в защищенном режиме.
В рассматриваемом примере, программа-гипервизор запускает программу-гостя, который выполняет вывод байта в порт 80h и завершается инструкцией VMCALL. Если гипервизор разрешил гостю обращение к портам ввода-вывода, вывод данных происходит и управление возвращается в гипервизор при выполнении инструкции VMCALL. Если доступ гостя к портам, запрещен, вывод данных не происходит и управление возвращается в гипервизор при попытке выполнения инструкции OUT. Разрешать и запрещать доступ можно, модифицируя фрагмент, выполняемый на шаге 24 рассмотренного ниже алгоритма. Статусная информация визуализируется на шаге 48.
Адрес порта – 80h выбран не случайно. Если экспериментатор располагает POST-картой, вывод байта можно наблюдать на ее индикаторах. Автор использовал устройство IC80 V5.0 производства компании IC Book Labs, описанное в [17]. Оно поддерживает 16-битный порт данных и переназначение адреса диагностического порта, что расширяет возможности для экспериментов. Выбор в качестве объекта виртуализации порта вывода с индикацией, делает наши опыты простыми и наглядными.
Примечание. Простейшая POST-карта – это 8-битный порт с адресом 80h. Данные, выводимые в этот порт, отображаются на индикаторах. Основное назначение такого устройства – отображение POST-кодов, которые с диагностической целью выводит BIOS при старте платформы.
Структура программы
Прилагаемый каталог WORK содержит следующие файлы:
ASM_TD.BAT – выполняет ассемблирование, линковку и запуск программы под отладчиком Turbo Debugger. При запуске TASM и TLINK используются опции, обеспечивающие добавление отладочной информации в EXE файл и отладку с просмотром исходного текста программы.
ASM_EXE.BAT – выполняет ассемблирование и линковку. Генерируется EXE файл. Полученный файл запускается на выполнение.
MAKE_VMX.ASM – основной модуль программы. Проверяет условия запуска программы, выполняет подготовку контекста для работы в защищенном режиме со страничной трансляцией и вызова гостевой процедуры с использованием технологии Intel VMX.
CODE32.INC – содержит 32-битный сегмент кода, в котором находится процедура Transit_32, включающая режим IA-32e и вызывающая целевые подпрограммы: 32-битную подпрограмму Pattern_32 в режиме совместимости и 64-битную подпрограмму Pattern_64 в 64-битном режиме. Процедура Transit_32 является связующим звеном между 16-битным защищенным режимом, в котором оказывается процессор после переключения из реального режима и подпрограммами, вызываемыми в режиме IA-32e.
TARGET32.INC – содержит 32-битный сегмент кода, в котором находится процедура Pattern_32, вызываемая из режима IA-32e в 32-битном режиме совместимости. Этот режим не используется в текущей версии программы и поэтому процедура Pattern_32 является заглушкой. Она выполняет вывод в порт 80h кода 32h и завершается.
TARGET64.INC – содержит 64-битный сегмент кода, в котором находится процедура Pattern_64, вызываемая из режима IA-32e в 64-битном режиме. Эта процедура начинается с вывода в порт 80h кода 64h. Она содержит гипервизор виртуального режима, который включает VMX, устанавливает указатели на блоки данных VMXON Region и VMCS Region, подготавливает содержимое блока VMCS Region для запуска гостя, запускает гостя, и после завершения последнего, считывает статусную информацию из блока VMCS Region, выключает VMX и завершается. Архитектура VMX описана в [5], инструкции описаны в [3].
Примечание 1. В нашем примере гипервизор и гость являются 64-битными процедурами.
Примечание 2. Так как транслятор, используемый автором, не поддерживает инструкции VMX, их коды определены с помощью операторов DB.
GUEST32.INC – содержит 32-битный сегмент кода, в котором находится гостевая процедура Guest_Routine_32, выполняющая вывод в порт 80h кода 33h и возврат в гипервизор инструкцией VMCALL. 32-битная гостевая процедура не используется в текущей версии программы.
GUEST64.INC – содержит 64-битный сегмент кода, в котором находится гостевая процедура Guest_Routine_64, выполняющая вывод в порт 80h кода 65h и возврат в гипервизор инструкцией VMCALL.
INTS16.INC – Сегмент для процедур обработки прерываний в защищенном режиме. В текущей версии, в целях упрощения примера, прерывания в защищенном режиме не разрешаются и данный сегмент не используется.
A20.INC – библиотека процедур для управления адресной линией A20.
DOSMSG.INC – процедура вывода текстовых строк с использованием сервиса DOS.
NUMPRINT.INC – библиотека процедур для визуализации шестнадцатеричных и десятичных чисел.
DATA.INC – сегменты данных. Сегмент DATA_16 содержит константы, переменные и текстовые строки. GLOBAL_DESCRIPTOR_TABLE – глобальная дескрипторная таблица для работы в защищенном режиме. PAGING_AND_VMCONTROL – динамически создаваемые таблицы страниц и таблицы для поддержки виртуального режима. STACK_16 – сегмент стека.
VMCSMAC.INC – содержит операторы для присвоения численных значений именам управляющих и статусных полей, содержащихся в блоке VMCS Region. Отметим, что эти значения являются не адресами соответствующих полей, а кодами, выбирающими заданное поле в инструкциях VMREAD и VMWRITE.
VMCSLIST.INC – содержит транзитные переменные, используемые при операциях с блоком VMCS Region. При установке состояния виртуальной машины перед запуском гостевой процедуры, содержимое этих переменных копируется в блок VMCS Region инструкциями VMWRITE. При анализе результатов после завершения гостевой процедуры, содержимое VMCS Region переносится в эти переменные инструкциями VMREAD.
Примечание. Поясним, для чего необходимо введение транзитных переменных и почему нельзя обращаться к элементам блока VMCS Region, напрямую, используя обычные инструкции работы с ячейками памяти. На это есть две причины:
- Форматирование блока VMCS Region зависит от модели процессора и не документировано. При использовании специальных инструкций VMREAD и VMWRITE это учитывается автоматически, так как в них задается не адрес элемента, а его код, а адрес автоматически генерирует процессор по коду. Но если мы читаем или записываем элемент блока VMCS Region инструкцией MOV, используя адрес элемента, то потребуется настройка адреса в соответствии с моделью процессора, что крайне неудобно.
- Данные региона VMCS Region кэшируются во внутренних буферах процессора. Инструкции VMREAD и VMWRITE автоматически учитывают это и гарантируют когерентность состояния блока VMCS Region в памяти и в процессоре. А при чтении элементов блока VMCS Region инструкцией MOV (как обычных ячеек памяти), мы можем прочитать устаревшие данные, не обновленные процессором. При записи элементов блока VMCS Region инструкцией MOV процессор может не заметить изменения состояния перезаписанных элементов, если использует их копию из внутреннего буфера.
Описание работы программы
Рассмотрим выполнение основного модуля. Нумерация пунктов данного описания соответствует нумерации пунктов комментариев в исходном тексте – в файле WORK\make_vmx.asm.
1) Установка адреса стека, загрузка регистров SS и SP.
2) Очистка экрана, установка текстового видеорежима 80x25, выдача сообщения о начале процедуры проверки конфигурации.
3) Проверка поддержки 32-битной системы команд. Проверяем доступность для записи бита 14 (NT-Nested Task) регистра флагов. Такая проверка нужна для того, чтобы при попытке запуска на компьютерах ниже 80386, программа выдала сообщение об ошибке, а не “зависла”.
4) Проверка поддержки технологии Intel VMX и ряда сопутствующих технологий, необходимых для работы программы. Сначала проверяем поддержку инструкции CPUID, затем, используя функции 00000000h (Vendor ID and largest standard function) и 00000001h (Feature information) инструкции CPUID, проверяем, что процессор Intel и технологии VMX, PAE, PSE, MSR поддерживается. Аналогичным образом, используя функции 80000000h (Largest extended function) и 80000001h (Extended feature information) проверяем поддержку технологии EM64T. Подробности в [6].
5) Проверка режима работы процессора. Программа использует непосредственный доступ к системным ресурсам, поэтому процессор на момент ее запуска должен работать в реальном режиме.
6) Чтение и визуализация версии VMX и размера блока VMCS Region. Данные параметры содержатся в регистре IA32_VMX_BASIC MSR (MSR с адресом 00000480h). Подробности в [5]. Напомним, что для чтения MSR (Model-Specific Register) используется инструкция RDMSR, которая принимает адрес MSR в регистре ECX и возвращает 64-битное значение, прочитанное из заданного MSR в регистрах EDX (старшие биты) и EAX (младшие биты). Подробности в [3].
7) Выдача сообщения о начале процедуры установки регистров управления.
8) Установка регистра управления CR4. Биты CR4.PSE, CR4.PAE, CR4.VMXE устанавливаем в “1”. Подробности в [4].
9) Установка регистра управления CR0. Биты CR0.NE устанавливаем в “1”, это необходимо для корректного переключения в режим VMX. Подробности в [4], [5].
10) Проверка и установка регистра IA32_FEATURE_CONTROL MSR (MSR с адресом 0000003Ah).
11) Выдача сообщения о начале процедуры очистки буферов.
12) Подготовка базовых адресов для размещения в памяти таблицы страниц и блоков VMXON Region, VMCS Region.
13) Обнуление блока памяти, выделенного для таблицы страниц и блоков VMXON Region, VMCS Region.
14) Выдача сообщения о начале процедуры подготовки контекста для работы процессора в защищенном режиме.
15) Установка указателя на корневую таблицу страниц в регистре CR3.
16) Построение таблицы страниц. Так как используемый блок памяти обнулен на шаге 13, здесь мы записываем значения только тех полей, которые должны быть отличны от нуля.
17) Подготовка базового адреса глобальной дескрипторной таблицы (GDT) и запись его в регистр GDTR.
18) Построение глобальной дескрипторной таблицы (GDT).
19) Подготовка указателей для вызова подпрограмм Transit_32, Pattern_32, Pattern_64 в защищенном режиме.
20) Запись значения VMCS Revision ID в первое 32-битное слово блока VMXON Region. Это необходимо для корректного выполнения инструкции VMXON.
21) Запись значения VMCS Revision ID в первое 32-битное слово блока VMCS Region. Это необходимо для корректного выполнения инструкции VMPTRLD.
22) Подготовка к инициализации транзитных переменных VMCS.
23) Инициализация переменой Pin-based VM Execution Controls, управляющей обработкой асинхронных событий в виртуальной машине. Подробности в [5].
24) Инициализация переменой CPU-based VM Execution Controls, управляющей обработкой синхронных событий в виртуальной машине. Бит 24 этой переменной управляет возможностью гостевой программы обращаться к портам. 0=обращение разрешено, доступ к портам выполняется обычным образом, 1=обращение запрещено, при попытке выполнения инструкций IN/OUT, а также строковых инструкций INS/OUTS, доступ к портам не выполняется и управление передается в гипервизор для обработки ситуации. При этом по содержимому статусных переменных блока VMCS Region гипервизор может определить адрес порта и тип операции, вызвавшей завершение гостя. При отладке программы можно раскомментировать инструкцию BTS EAX,24. В результате гость не сможет записать в порт 80h код 65h и будет сформирована соответствующая статусная информация. Отметим, что процессор также поддерживает режим “индивидуального” запрета-разрешения для каждого порта, используя битовые карты I/O Bitmaps. Подробности в [5].
25) Инициализация переменой VM Exit Controls, управляющей выполнением возврата из гостевой процедуры в гипервизор.
26) Инициализация переменой VM Entry Controls, управляющей выполнением запуска гостевой процедуры из гипервизора.
27) Инициализация переменных Host CR0, Guest CR0, задающих значения, загружаемые в системный регистр управления CR0 в режиме гипервизора и гостя соответственно.
28) Инициализация переменных Host CR3, Guest CR3, задающих значения, загружаемые в системный регистр управления CR3 в режиме гипервизора и гостя соответственно.
29) Инициализация переменных Host CR4, Guest CR4, задающих значения, загружаемые в системный регистр управления CR4 в режиме гипервизора и гостя соответственно.
30) Инициализация переменных Host GDTR Base, Guest GDTR Base, задающих значения базового адреса глобальной дескрипторной таблицы в режиме гипервизора и гостя соответственно.
31) Инициализация переменных Host RSP, Guest RSP, задающих значения указателя стека в режиме гипервизора и гостя соответственно. 32) Инициализация переменной Host RIP, задающей значение указателя команд в режиме гипервизора. Это адрес точки возврата из гостя в гипервизор.
33) Инициализация переменной Guest RIP, задающей значение указателя команд в режиме гостя Это адрес точки входа в гостевую процедуру.
Примечание 1. Шаги 23-33 устанавливают значения только тех транзитных переменных VMCS, для которых требуется настройка на конфигурацию системы или адрес загрузки программы. Значения остальных заданы в исходном тексте модуля VMCSLIST.INC как константы. Все транзитные переменные копируются в блок VMCS Region при выполнении шага 42. Подробности в [5].
34) Разрешение адресной линии A20. Это необходимо, так как далее последует переключение в защищенный режим.
35) Выдача сообщения о начале процедуры запуска гостя.
36) Сохранение адреса стека для реального режима, регистры SS:SP сохраняются в памяти.
37) Запрет прерываний.
38) Переключение в защищенный режим.
39) Межсегментный переход на следующую инструкцию для загрузки атрибутов сегмента кода.
40) Загрузка селекторов сегментов данных DS и ES.
41) Загрузка селектора сегмента стека SS. Значение SP не перезагружается, так как используется тот же стек, что и в реальном режиме, с той лишь разницей, что в регистре SS находится не базовый адрес, а селектор сегмента стека.
42) Межсегментный вызов подпрограммы Transit_32, находящейся в 32-битном сегменте кода CODE_32, расположенном в файле CODE32.INC исходного текста. Эта подпрограмма корректирует значения регистров SS и RSP в соответствии с правилами 64-битного режима, включает трансляцию страниц и режим IA-32e, допускающий выполнение 32 и 64-битных сегментов кода. В регистр FS загружается селектор для доступа к сегменту DATA_16. Работая в режиме IA-32e, подпрограмма Transit_32 вызывает две подпрограммы: Pattern_32 в 32-битном режиме совместимости и Pattern_64 в 64-битном режиме. Подпрограмма Pattern_32 (см. файл TARGET32.INC) в текущей версии является заглушкой, она зарезервирована для расширения возможностей программы. Подпрограмма Pattern_64 (см. файл TARGET64.INC) содержит гипервизор виртуального режима. Она выполняет следующие действия:
42.1) Контрольная точка, вывод в порт 80h кода 64h.
42.2) Инструкция VMXON включает режим VMX и устанавливает адрес блока VMXON Region.
42.3) Инструкция VMCLEAR выполняет очистку блока VMCS Region.
42.4) Инструкция VMPTRLD выполняет загрузку указателя на блок VMCS Region.
42.5) Ранее подготовленные переменные, копируются в блок VMCS Region. Используется 3 вызова подпрограммы Execute_VMWRITE для 16, 32 и 64-битных переменных. Эта подпрограмма использует инструкцию VMWRITE для записи полей блока VMCS Region.
42.6) Инструкция VMLAUNCH запускает гостевую процедуру Guest_Routine_64 (см. файл GUEST64.INC).
42.7) Гостевая процедура выводит в порт 80h код 65h и возвращает управление в гипервизор инструкцией VMCALL.
42.8) С помощью инструкций VMREAD, статусная информация из блока VMCS Region, копируется в транзитные переменные и может быть использована для анализа результатов.
42.9) Инструкция VMXOFF выключает режим VMX.
43) Управление вернулось из подпрограммы Transit_32. Загружаем в сегментные регистры селекторы 16-битных сегментов, очищаем буфер трансляции страниц и переключаемся в реальный режим.
44) Восстановление контекста реального режима. Выполняем межсегментный переход на следующую инструкцию для загрузки атрибутов сегмента кода. Восстанавливаем значения в регистрах SS и SP.
45) Разрешение прерываний.
46) Восстановление исходного состояния адресной линии A20.
47) Зарезервировано для восстановления состояния системных регистров процессора. Не выполняется в текущей версии программы.
48) Вывод сообщения о возврате управления из гостевой процедуры. Визуализация статусной информации, по которой можно определить причину завершения гостевой процедуры и проверить наличие ошибок, возникших при работе в режиме VMX. Код ошибки VM Instruction Error позволяет проверить ошибки, возникшие при выполнении инструкций VMX. Коды перечислены в [3]. Код Step определяет номер шага, на котором возникла ошибка, формируется программно в переменной VMX_Progress_Status. Поддерживаются следующие значения:
- 0=выполнение VMXON, переключение в режим VMX.
- 1=выполнение VMCLEAR, очистка блока VMCS Region.
- 2=выполнение VMPTRLD, загрузка указателя на блок VMCS Region.
- 3=копирование 16-битных данных из блока транзитных переменных в блок VMCS Region.
- 4=копирование 32-битных данных из блока транзитных переменных в блок VMCS Region.
- 5=копирование 64-битных данных из блока транзитных переменных в блок VMCS Region.
- 6=запуск гостевой процедуры Guest_Routine_64 инструкцией VMLAUNCH. Коды Exit Reason (причина завершения гостевой процедуры) и Exit Qualification (уточняющая информация о причине завершения) перечислены в [5]. После вывода статусной информации, ожидаем нажатия клавиши. Затем выводим 00h в порт 80h и выходим в DOS с кодом выхода 00h.
49) Сюда передается управление, если программа завершается из-за ошибок, возникших на ранних стадиях выполнения - при проверке конфигурации или установке контекста до перехода в режим VMX. В регистре BP находится адрес строки для вывода сообщения об ошибке. Выводим сообщение об ошибке. Ожидаем нажатия клавиши. Затем выводим 01h в порт 80h и выходим в DOS с кодом выхода 01h.
Условия эксперимента
Использовалась системная плата MicroStar MS-7357 на чипсете Intel G33, процессор DualCore Intel Pentium D 930, 3000MHz. Для установки контрольных точек при отладке программы, а также в качестве предмета виртуализации использовалась POST-карта IC80 V5.0 производства компании IC Book Labs, подробности в [17].
Анализ результатов
Проверим, как программа отображает результаты выполнения.
Если гостевой процедуре разрешен доступ к портам (инструкция BTS EAX,24 на шаге 24 закомментирована) программа должна вывести:
Exit Reason=00000012h, Exit Qualification=00000000h.
Если используется POST-карта, на индикаторе должен быть код 65h, выведенный гостевой процедурой в порт 80h. Согласно [5], Exit Reason=12h=18 означает завершение гостевой процедуры при выполнении инструкции VMCALL.
Если гостевой процедуре запрещен доступ к портам (инструкция BTS EAX,24 на шаге 24 выполнена) программа должна вывести:
Exit Reason=0000001Eh, Exit Qualification=00800040h.
Если используется POST-карта, на индикаторе должен быть код 64h, выведенный в порт 80h перед попыткой запуска гостевой процедуры. Exit Reason=1Eh=30 означает завершение гостевой процедуры при попытке запрещенного обращения к портам ввода-вывода. Exit Qualification=00800040h означает, что причиной завершения гостя была запись байта в порт с адресом 80h. Подробности в [5].
Если намеренно создать некорректный контекст гостевой процедуры, например, установив нулевое значение переменной Guest_RFLAGS в файле VMCSLIST.INC, получим:
Exit Reason=80000021h, Exit Qualification=00000000h.
Согласно [5], Exit Reason=21h=33 означает ошибку Invalid Guest State.
Если намеренно задать несуществующий код элемента для VMCS Region, например, установив Guest_ES_Selector в файле VMCSMAC.INC, равным 000000FFh, получим:
VM Instruction Error =0000000Ch, step=03h
Согласно [3], VM Instruction Error=0Ch=12 означает ошибку unsupported VMCS component, step=3 означает, что ошибка возникла на этапе копирования 16-битных данных из блока транзитных переменных в блок VMCS Region, при выполнении инструкции VMWRITE.
Если намеренно задать некорректное значение управляющих полей, например, закомментировать инструкцию mov ds:[Entry_Control_Prim_CPU_Based_VM_Exec][2],eax, выполняемую на шаге 24, получим: VM Instruction Error =00000007h, step=06h Согласно [3], VM Instruction Error =07h означает ошибку VM Entry with invalid control fields, step=6 означает, что ошибка возникла на этапе запуска гостевой процедуры, при выполнении инструкции VMLAUNCH.
Источники информации
Электронные документы, доступные на сайте developer.intel.com
- Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: Basic Architecture. Order Number 253665-034US
- Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2A: Instruction Set Reference, A-M. Order Number 253666-034US
- Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2B: Instruction Set Reference, N-Z. Order Number 253667-034US
- Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3A: System Programming Guide, Part 1. Order Number 253668-034US
- Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3B: System Programming Guide, Part 2. Order Number 253669-034US
- Intel Processor Identification and the CPUID Instruction. Application Note 485. Order Number 241618-033
Электронные документы, доступные на сайте developer.amd.com
- AMD64 Architecture Programmer’s Manual. Volume 1: Application Programming. Publication No. 24592
- AMD64 Architecture Programmer’s Manual. Volume 2: System Programming. Publication No. 24593
- AMD64 Architecture Programmer’s Manual. Volume 3: General-Purpose and System Instructions. Publication No. 24594
- AMD64 Architecture Programmer’s Manual. Volume 4: 128-Bit Media Instructions. Publication No. 26568
- AMD64 Architecture Programmer’s Manual. Volume 5: 64-Bit Media and x87 Floating-Point Instructions. Publication No. 26569
- AMD64 Architecture Programmer’s Manual. Volume 6: 128-Bit and 256-Bit XOP, FMA4 and CVT16 Instructions. Publication No. 43479
- BIOS and Kernel Developer’s Guide for AMD NPT Family 0Fh Processors. Publication No. 32559
- BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 10h Processors. Publication No. 31116
- BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 10h Processors. Publication No. 41256
- AMD CPUID Specification. Publication No. 25481.
Электронные документы, доступные на сайте icbook.com.ua
- IC80 V5.0. Диагностическая плата. Руководство пользователя. Ревизия 1.5. IC Book Labs, © 2006.
Загрузить ZIP-архив
с примером программирования
на ассемблере
можно здесь.