Виртуализация в духе AMD SVM

Как известно, современные про­цес­со­ры Intel и AMD под­дер­жи­ва­ют тех­но­ло­гии вир­ту­а­ли­за­ции, по­зво­ля­ю­щие ис­поль­зо­вать один ком­пью­тер как не­сколь­ко вир­ту­аль­ных ма­шин, на каж­дой из ко­то­рых ра­бо­та­ет своя опе­ра­ци­он­ная сис­те­ма.

Для архитектуры x86, идея вир­ту­а­ли­за­ции процессора и системных ресурсов не но­ва. Еще око­ло двух де­ся­ти­ле­тий на­зад, в процессоре 80386 впервые был пред­ус­мот­рен режим вир­ту­аль­но­го процессора 8086, ко­то­рый по­зво­лял про­г­рам­ме, работающей в за­щи­щен­ном режиме, запустить некоторую дочернюю задачу, на­пи­сан­ную для вы­пол­не­ния в ре­аль­ном ре­жиме. При этом про­цес­сор обеспечивает перехват обращений до­чер­ней за­да­чи к за­дан­ным об­лас­тям па­мя­ти, пор­там вво­да-вы­во­да и системным регистрам процессора. При попытке дочерней задачи сделать что-то не­по­зво­ли­тель­ное уп­рав­ле­ние пе­ре­да­ет­ся про­г­рам­ме-су­пер­ви­зо­ру для вы­пол­не­ния эмуляции по за­дан­но­му сценарию или вы­во­да сообщения об ошибке. При запуске DOS-программы из-под Windows ис­поль­зу­ет­ся именно этот ре­жим.

Современные тех­но­ло­гии Intel Virtualization Technology и AMD Virtualization Technology используют тот же при­н­цип, но если 32-битный 80386 позволял вир­ту­а­ли­зи­ро­вать только 16-битный 8086, ра­бо­та­ю­щий в реальном ре­жи­ме, то се­го­дня объектом вир­ту­а­ли­за­ции может быть пол­но­фун­к­ци­о­наль­ный 64-битный процессор. Тех­но­ло­гии вир­ту­а­ли­за­ции от Intel и AMD до­ста­точ­но похожи, но на уро­в­не ма­шин­но­го кода несовместимы.

Виртуализация: практикум по AMD SVM

 

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

Основные определения AMD SVM

Технология AMD SVM определяет два спе­ци­аль­ных состояния процессора, предназначенные для вы­пол­не­ния про­г­рам­мных мо­ду­лей двух видов. Virtual Machine Monitor (VMM) или Hypervisor имеет не­ог­ра­ни­чен­ные полномочия при доступе к системным ресурсам. Координирует работу всей системы и создает за­дан­ное ко­ли­че­ст­во вир­ту­аль­ных ма­шин для выполнения гостевых программ. Guests или гостевые про­г­рам­мы ог­ра­ни­че­ны в доступе к сис­тем­ным ре­сур­сам. При по­пыт­ке гостевой программы обратиться к ресурсу, до­ступ к ко­то­ро­му ей запретил ги­пер­ви­зор, выполнение гостевой программы прерывается и управление пе­ре­да­ет­ся в ги­пер­ви­зор для обработки данного события. Такой ме­ха­низм обеспечивает эмуляцию заданного ко­ли­че­ст­ва вир­ту­аль­ных машин по сценарию, определяемому ги­пер­ви­зо­ром. Важно и то, что сбой в вир­ту­аль­ной ма­ши­не не приводит к сбою всей системы.

Гипервизор и его полномочия

Для включения режима SVM и при­об­ре­те­ния пол­но­мо­чий ги­пер­ви­зо­ра, программа должна установить бит SVME (SVM Enable). Это бит 12 регистра IA32_EFER MSR (MSR с адресом C0000080h). Затем, после под­го­тов­ки в опе­ра­тив­ной па­мя­ти структур, необходимых для запуска виртуальных машин, программа может за­пус­кать гостей, ис­поль­зуя ин­ст­рук­цию VMRUN. Если гость обратился к системному ресурсу, доступ к ко­то­ро­му для него запрещен или ис­тек квант вре­ме­ни, вы­де­лен­ный для его работы, управление передается в ги­пер­ви­зор. Эта операция называется #VMEXIT.

Отметим, что после установки бита SVME, про­г­рам­ма дол­жна выполнить считывание регистра IA32_EFER MSR и про­ве­рить, что SVME=1. Это связано с тем, что режим SVM может быть заблокирован. Бло­ки­ров­кой управляет бит SVMDIS (SVM Disable). Это бит 4 регистра VM_CR MSR (MSR с адресом C0010114h). Для разрешения ус­та­нов­ки би­та SVME, бит SVMDIS должен быть обнулен.

Процессор позволяет установить защиту от не­санк­ци­о­ни­ро­ван­но­го включения режима SVM. Если ус­та­нов­лен бит LOCK (бит 3 регистра VM_CR MSR), обнулить бит SVMDIS можно только после записи пароля в ре­гистр SVM_KEY MSR (MSR с адресом C0010118h).

Идентификация ресурсов AMD SVM

Выполнив функцию 80000001h инструкции CPUID, мы можем получить в регистрах EDX и ECX битовую карту, в ко­то­рой каждый бит ин­ди­ци­ру­ет поддержку процессором определенной технологии: 0=не под­дер­жи­ва­ет­ся, 1=под­дер­жи­ва­ет­ся (CPU Extended Feature Flags). Технологии SVM соответствует бит 2 регистра ECX.

Если SVM поддерживается, доступна функция 8000000Ah инструкции CPUID. Она позволяет прочитать но­мер ре­ви­зии SVM, ряд параметров и наличие режимов, поддерживаемых опционально.

Регионы Host Save Area и VMCB,
используемые в AMD SVM

Технология AMD SVM определяет следующие две специальные области памяти:

Host Save Area — этот регион ис­поль­зу­ет­ся для со­хра­не­ния статуса ги­пер­ви­зо­ра при запуске гостевой про­г­рам­мы и вос­ста­нов­ле­ния этого ранее сохраненного состояния при возврате из гостевой программы в ги­пер­ви­зор. Пред­по­ло­жи­тель­но, размер этого блока не превышает 4KB (к сожалению, в документации AMD точ­ное значение размера не ука­за­но). Его 64-бит­ный фи­зи­че­ский адрес находится в регистре VM_HSAVE_PA MSR (MSR с адресом C0010117h). Адрес дол­жен быть выровнен на 4KB, и находиться в пре­де­лах максимального фи­зи­че­ско­го адреса, под­дер­жи­ва­е­мо­го про­цес­со­ром. Регион Host Save Area ап­па­рат­но используется про­цес­со­ром, и программные обращения к этому ре­ги­ону не ре­ко­мен­ду­ют­ся по двум при­чи­нам: его фор­ма­ти­ро­ва­ние может различаться в зависимости от мо­де­ли про­цес­со­ра, его содержимое кэ­ши­ру­ет­ся во внут­рен­них буферах процессора, при этом не гарантируется ко­ге­рен­т­ность содержимого этих бу­фе­ров и региона па­мя­ти. Отметим, что у Intel аналогичная ситуация с регионом VMXON.

VMCB (Virtual Machine Control Block) — этот регион ис­поль­зу­ет­ся для управления виртуальной машиной и ра­бо­та­ю­щей на ней гостевой программой. Его размер — 4 KB, а базовый физический адрес указывается в ре­гист­ре EAX (для 32-битного режима) или RAX (для 64-битного режима) при выполнении инструкции VMRUN. Эта ин­ст­рук­ция запускает виртуальную машину. Требования к базовому физическому адресу та­кие же, как для региона Host Save Area. В отличие от региона Host Save Area и в отличие от региона VMCS у Intel, форматирование региона VMCB документировано. Он состоит из двух логических групп:

  • Control Area используется для уп­рав­ле­ния ра­бо­той виртуальной машины и задания списков сис­тем­ных ре­сур­сов и машинных инструкций, использование которых запрещено гостю. При обращении по­след­не­го к этим ре­сур­сам, его работа прерывается, и управление передается в ги­пер­ви­зор для эму­ля­ции или ге­не­ра­ции со­об­ще­ния об ошибке. Здесь также находятся статусные поля, аппаратно об­нов­ля­е­мые про­цес­со­ром при за­вер­ше­нии работы виртуальной машины. По их содержимому ги­пер­ви­зор может определить при­чи­ну за­вер­ше­ния гостя.
  • State Save Area используется для за­груз­ки со­сто­я­ния гостя при его запуске и сохранения его состояния при возврате в ги­пер­ви­зор.

Инструкции AMD SVM

Технология AMD SVM определяет сле­ду­ю­щие про­цес­сор­ные инструкции.

VMRUN — эту инструкцию использует ги­пер­ви­зор для запуска гостя на виртуальной машине. В регистре EAX (для 32-битного режима) или RAX (для 64-битного режима) находится базовый физический адрес блока VMCB, который будет использоваться запускаемой виртуальной машиной. Контекст ги­пер­ви­зо­ра, со­хра­ня­ет­ся в блоке Host Save Area. Кон­текст гостя загружается из блока VMCB. Выполнение гостя на виртуальной ма­ши­не продолжается до тех пор, пока не воз­ни­к­нет одно из событий, подлежащих перехвату ги­пер­ви­зо­ром. Список таких событий зависит от содержимого Con­trol Area в бло­ке VMCB. Когда перехватываемое со­бы­тие возникает, выполняется операция #VMEXIT. Вы­пол­не­ние гос­тя пре­ры­ва­ет­ся, его кон­текст сохраняется в State Save Area в блоке VMCB. Контекст ги­пер­ви­зо­ра загружается из бло­ка Host Save Area. Начинает вы­пол­ня­ться ги­пер­ви­зор. По содержимому статусных полей в блоке VMCB, ги­пер­ви­зор мо­жет определить при­чи­ну завершения гостя.

VMMCALL — эту инструкцию использует гость для явной передачи управления ги­пер­ви­зо­ру. Она инициирует опе­ра­цию #VMEXIT, описанную выше. Отметим, что для правильного функционирования инструкции VMMCALL, бит Inter­cept VMMCALL Instruction в блоке VMCB должен быть установлен в 1.

VMSAVE — сохраняет в блоке VMCB ряд дополнительных параметров контекста процессора для гостевой про­г­рам­мы, которые не сохраняются автоматически при выполнении операции #VMEXIT. Базовый фи­зи­че­ский адрес ис­поль­зу­е­мо­го блока VMCB находится в регистре EAX (для 32-битного режима) или RAX (для 64-бит­но­го режима). Спи­сок сох­ра­ня­е­мых параметров следующий: регистры FS, GS, TR, LDTR, model-specific регистры KernelGSbase, STAR, LSTAR, CSTAR, SFMAS, SYSENTER_CS, SYSENTER_EIP, SYSENTER_ESP.

VMLOAD — загружает из блока VMCB ряд дополнительных параметров контекста процессора для гостевой про­г­рам­мы, которые не загружаются автоматически при выполнении инструкции VMRUN. Базовый фи­зи­че­ский адрес ис­поль­зу­е­мо­го блока VMCB находится в регистре EAX (для 32-битного режима) или RAX (для 64-бит­но­го режима). Спи­сок за­гру­жа­е­мых параметров такой же, как в инструкции VMSAVE.

CLGI — обнуляет глобальный флаг разрешения прерываний (GIF). Если GIF=0, запрещены все виды ап­па­рат­ных пре­ры­ва­ний (NMI, SMI, INTR), запрещено восприятие сигнала мягкого сброса INIT, управление ли­ни­ей A20 и ряд дру­гих опе­ра­ций, полный список которых приведен в [8].

Примечание. Всем, кто имел дело с ассемблером x86 хорошо знаком флаг разрешения прерываний IF (Inter­rupt Flag) и ин­ст­рук­ции для его сброса и установки CLI и STI. Флаг GIF (Global Interrupt Flag) и связанные с ним ин­ст­рук­ции CLGI и STGI работают по тому же принципу, с той лишь разницей, что при GIF=0 за­пре­ще­но восприятие не только мас­ки­ру­е­мых прерываний, но и ряда других асинхронных событий, пе­ре­чис­лен­ных выше.

STGI — устанавливает глобальный флаг разрешения прерываний (GIF). См. комментарий к инструкции CLGI.

INVLPGA — объявляет недостоверными заданные элементы буфера страничной трансляции (TLB). Ин­ст­рук­ция при­ни­ма­ет в регистре EAX (для 32-битного режима) или RAX (для 64-битного режима) линейный ад­рес ячейки па­мя­ти. Очи­ща­ют­ся те элементы TLB, которые участвуют в трансляции этого адреса. Инструкция так­же принимает в регистре ECX номер виртуального адресного пространства (ASID - Address Space Iden­ti­fier) для которого выполняется операция.

SKINIT (Secure Init and Jump with Attestation) — обеспечивает аппаратную поддержку защиты программных мо­ду­лей от не­сан­к­ци­о­ни­ро­ван­ной модификации. Использует внешнее устройство Trusted Platform Module (TPM). Процессор ини­ци­а­ли­зи­ру­ет­ся для выполнения защищенного фрагмента (Secure Loader), регион па­мя­ти, содержащий за­дан­ный мо­дуль, защищается от внешнего доступа, его содержимое ве­ри­фи­ци­ру­ет­ся с при­ме­не­ни­ем TPM, затем на этот про­г­рам­мный мо­дуль передается управление. Инструкция принимает в ре­гист­ре EAX базовый физический ад­рес за­щи­ща­е­мо­го модуля Secure Loader, адрес должен быть вы­ров­нен на 64 KB. Размер модуля 64 KB.

Примечание. Не все процессоры, поддерживающие AMD SVM, поддерживают инструкцию SKINIT. Перед ее ис­поль­зо­ва­ни­ем необходимо выполнить функцию CPUID 80000001h и проверить бит 12 регистра ECX (SKINIT feature).

Описание работы программы

Программа демонстрирует возможности технологии ви­р­ту­а­ли­за­ции AMD SVM. В последующих публикациях ав­тор планирует рассмотреть аналогичный пример для Intel VMX. В целях монопольного и бес­пре­пят­ст­вен­но­го вза­и­мо­дей­ст­вия отлаживаемой программы с аппаратным обеспечением, использована древняя тех­но­ло­гия отладки под DOS. Ар­гу­мен­та­ция такого шага и рекомендации по организации рабочего места при­ве­де­ны в одной из наших публикаций.

Вместе с тем, полученные знания и навыки универсальны, поэтому будут полезны при написании подобных про­це­дур для любой ОС. В той же статье детально описана процедура переключения процессора в 64-бит­ный за­щи­щен­ный режим, которая понадобится и в данном примере. Напомним, что для использования тех­но­ло­гии AMD SVM про­цес­сор должен работать в защищенном режиме.

В рассматриваемом примере, программа-ги­пер­ви­зор запускает программу-гостя. Гость выполняет вывод бай­та в порт 0080h и завершается инструкцией VMMCALL. Если ги­пер­ви­зор разрешил гостю доступ к данному пор­ту, вы­вод дан­ных про­ис­хо­дит и управление возвращается в ги­пер­ви­зор при выполнении инструкции VMMCALL. Если ги­пер­ви­зор за­пре­тил гостю доступ к данному порту, вывод данных не происходит и уп­рав­ле­ние возвращается в ги­пер­ви­зор при по­пыт­ке выполнения инструкции OUT. Разрешать и запрещать доступ к пор­ту можно, мо­ди­фи­ци­руя фраг­мент, вы­пол­ня­е­мый на шаге 10 рассмотренного ниже алгоритма. Ви­зу­а­ли­за­ция статусной информации происходит на шаге 34.

Адрес порта — 0080h выбран не случайно. Если экспериментатор располагает POST-картой, результат вы­во­да бай­та можно наблюдать на ее индикаторах. Автор использовал устройство IC80 V5.0 производства ком­па­нии IC Book Labs, описанное в [17]. Оно поддерживает 16-битный порт данных, что расширяет воз­мож­но­с­ти для экспериментов. Выбор в качестве объекта ви­р­ту­а­ли­за­ции порта вывода с индикацией, делает наши опы­ты простыми и наглядными.

Примечание. Простейшая POST-карта — это 8-битный порт с адресом 0080h. Данные, выводимые в этот порт, ото­бра­жа­ют­ся на индикаторах. Основное назначение такого устройства — отображение POST-кодов, ко­то­рые с ди­аг­но­сти­че­ской целью выводит BIOS при старте платформы.

Структура программы

Прилагаемый каталог WORK содержит следующие файлы:

ASM_TD.BAT — выполняет ас­сем­б­ли­ро­ва­ние, лин­ков­ку и запуск программы под отладчиком. При запуске TASM и TLINK используются опции, обеспечивающие добавление отладочной информации в EXE файл и отладку с про­с­мот­ром исходного текста программы.

ASM_EXE.BAT — выполняет ас­сем­б­ли­ро­ва­ние и лин­ков­ку. Генерируется EXE файл.

MAKE_SVM.ASM — основной модуль программы. Проверяет условия запуска программы, выполняет под­го­тов­ку кон­тек­ста для работы в защищенном режиме со страничной трансляцией и вызова гостевой процедуры с ис­поль­зо­ва­ни­ем технологии AMD SVM.

CODESEGS.INC — сегменты кода для защищенного режима. Сегмент CODE_32 содержит процедуру Transit_32, из ко­то­рой выполняется вызов гостевой процедуры. Сегмент TARGET_32 содержит процедуру Guest_Routine_32. Сег­мент TARGET_64 зарезервирован для 64-битных гостевых процедур. Сегмент INTERRUPTS_16 за­ре­зер­ви­ро­ван для обработки прерываний в защищенном режиме.

DATASEGS.INC — сегменты данных. DATA_16 содержит переменные и текстовые строки. GLOBAL_DESCRIPTOR_TABLE — глобальная деск­рип­тор­ная таблица. PAGING_AND_VMCONTROL — динамически создаваемые таблицы страниц и таблицы для поддержки виртуального режима. STACK_16 — сегмент стека.

A20.INC — библиотека процедур для управления адресной линией A20.

DOSMSG.INC — процедура вывода текстовых строк с использованием сервиса DOS.

NUMPRINT.INC — библиотека процедур для визуализации шестнадцатеричных и десятичных чисел.

Описание работы программы

Рассмотрим выполнение основного модуля. Нумерация пунктов данного описания соответствует нумерации пунк­тов комментариев в исходном тексте — файле WORK\make_svm.asm.

1) Установка адреса стека, загрузка регистров SS и SP.

2) Выдача сообщения о начале процедуры проверки конфигурации.

3) Проверка поддержки 32-битной системы команд. Проверяем доступность для записи бита 14 (NT-Nested Task) регистра флагов. Такая проверка нужна для того, чтобы при попытке запуска на компьютерах ниже 80386, программа выдала сообщение об ошибке, а не зависла.

4) Проверка поддержки технологии AMD SVM. Сначала проверяем поддержку инструкции CPUID, затем, ис­поль­зуя функ­ции 80000000h (Processor vendor and largest extended function number) и 80000001h (Processor family, model, stepping and features) инструкции CPUID, проверяем, что процессор AMD и SVM поддерживается.

5) Проверка режима работы процессора. Программа использует непосредственный доступ к системным ре­сур­сам, поэтому процессор на момент ее запуска должен работать в реальном режиме.

6) Чтение и визуализация версии AMD SVM и наличия поддержки инструкции SKINIT. Используются функции 80000001h (Processor family, model, stepping and features) и 8000000Ah (SVM Revision and Feature Identification) инструкции CPUID.

7) Включение режима SVM. Для этого сначала необходимо обнулить бит VM_CR .SVMDIS, затем установить в 1 бит IA32_EFER.SVME. Выполняется проверка на предмет блокировки SVM. Содержимое мо­ди­фи­ци­ру­е­мых ре­гист­ров сохраняется для последующего восстановления при выходе из программы.

8) Инициализация указателей для специальных областей памяти: Load_CR3 — указатель на корневую таб­ли­цу страниц, используется для загрузки регистра управления CR3. Load_VM_HSAVE_PA — указатель на блок Host Save Area, в котором сохраняется контекст ги­пер­ви­зо­ра при запуске гостевой программы. Ука­за­тель используется для загрузки младшего 32-битного слова 64-битного регистра VM_HSAVE_PA MSR. Load_VMCB — указатель на блок Virtual Machine Control Block, который используется для управления вы­пол­не­ни­ем гостевой программы и из которого загружается ее контекст. Указатель используется для загрузки в ре­гистр EAX операнда инструкции VMRUN. Load_IOPM — указатель на блок Input/Output Permission Map, ко­то­рый используется для управления доступом гостевой программы к портам ввода-вывода. Все указатели со­дер­жат 32-битные физические адреса, выровненные на 4 KB в соответствии с требованиями процессора к раз­ме­ще­нию соответствующих областей памяти.

9) Предварительная очистка специальных областей памяти. Таблица страниц, блоки Host Save Area и VMCB об­ну­ля­ю­тся. Блок Input/Output Permission Map заполняется константой FFh.

10) Это за­ком­мен­ти­ро­ван­ный фрагмент, используемый при отладке. Обнуление бита, отвечающего за порт 0080h в таблице IOPM. Если этот бит обнулен, доступ гостя к порту 0080h разрешен. Если этот бит в 1, то при по­пыт­ке гостя обратиться к порту 0080h, выполнение гостя прерывается, управление передается в ги­пер­ви­зор. По содержимому статусных полей блока VMCB, ги­пер­ви­зор может определить причину завершения гос­тя, адрес перехваченного порта, разрядность перехваченной операции и другие параметры.

11) Загрузка регистра VM_HSAVE_PA MSR, определяющего адрес Host Save Area. Исходное значение ре­гист­ра сохраняется для восстановления при выходе из программы. Исходное и загруженное значения ви­зу­а­ли­зи­ру­ют­ся.

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

13) Загрузка регистра CR3, определяющего базовый физический адрес корневой таблицы страниц.

14) Построение таблиц страниц PML4, PDP, PDE. Подробности в [8]. Так как область памяти, в которой про­ис­хо­дит построение таблиц, была обнулена на шаге 9, на здесь мы записываем значения только тех полей, ко­то­рые должны быть отличны от нуля.

15) Инициализация регистра CR4. Устанавливаем биты, отвечающие за опции страничной трансляции: PSE (Page Size Extension), PAE (Physical Address Extension).

16) Загрузка в регистр GDTR базового адреса глобальной деск­рип­тор­ной таблицы (GDT).

17) Построение глобальной деск­рип­тор­ной таблицы, описывающей все сегменты, используемые при работе в за­щи­щен­ном режиме.

18) Подготовка адреса для межсегментного вызова подпрограммы Transit_32, находящейся в 32-битном сег­мен­те кода CODE_32.

19) Подготовка параметров в блоке управления виртуальной машиной (VMCB), которые будут ис­поль­зо­вать­ся для загрузки контекста процессора при вызове гостевой процедуры, в том числе полей, из которых за­гру­жа­ют­ся значения регистров CS, RIP адресующих точку входа в гостевую процедуру. Так как область па­мя­ти, в которой происходит построение VMCB, была обнулена на шаге 9, на здесь мы записываем зна­че­ния только тех полей, которые должны быть отличны от нуля.

20) Разрешение адресной линии A20. Исходное состояние A20 сохраняем для восстановления при выходе из про­г­рам­мы.

21) Выдача сообщения о начале процедуры вызова гостя.

22) Сохранение регистров SS, SP для последующего восстановления контекста реального режима.

23) Запрет аппаратных прерываний.

24) Переключение в защищенный режим. Сейчас программа выполняется в 16-битном сегменте кода.

25) Межсегментный переход на следующую инструкцию для загрузки атрибутов сегмента кода.

26) Загрузка селекторов сегментов данных DS и ES.

27) Загрузка селектора сегмента стека SS. Значение SP не перезагружается, так как используется тот же стек, что и в реальном режиме, с той лишь разницей, что в регистре SS находится не базовый адрес, а се­лек­тор сег­мен­та сте­ка.

28) Межсегментный вызов подпрограммы Transit_32, находящейся в 32-битном сегменте кода CODE_32, рас­по­ло­жен­ном в файле CODESEGS.INC исходного текста. Эта подпрограмма корректирует значения ре­гист­ров SS и RSP в соответствии с правилами 64-битного режима, включает трансляцию страниц и режим IA-32e, до­пус­ка­ю­щий выполнение 32 и 64-битных сегментов кода. В регистр FS загружается селектор для доступа к сег­мен­ту DATA_16.

Затем, используя инструкцию VMRUN, запускаем гостевую процедуру Guest_Routine_32, находящуюся в сег­мен­те TARGET_32. В регистре EAX находится адрес блока VMCB, подготовленного на шаге 19.

Контекст гостевой процедуры загружается из блока VMCB. После ее завершение, статусные поля блока VMCB бу­дут перезаписаны процессором, по их содержимому можно определить причину завершения гостя и про­чие па­ра­мет­ры, набор которых зависит от причины завершения.

Как видно из файла CODESEGS.INC, гостевая подпрограмма Guest_Routine_32 состоит из вывода байта 55h в порт с адресом 0080h и инструкции VMMCALL для явного возврата в ги­пер­ви­зор. Если доступ к порту 0080h раз­ре­шен, вы­вод бай­та состоится и процедура завершиться по команде VMMCALL. Если доступ запрещен, про­це­ду­ра за­вер­шить­ся при попытке выполнить команду OUT и вывод байта не состоится. Далее, на шаге 34 ин­фор­ма­ция о при­чи­не за­вер­ше­ния гостя будет ви­зу­а­ли­зи­ро­ва­на. Напомним, что разрешение доступа к пор­ту 0080h устанавливается на шаге 10.

Возврат из гостя выполняется на инструкцию, находящуюся после инструкции VMRUN. После этого, под­про­г­рам­ма TARGET_32 вы­клю­ча­ет трансляцию страниц и режим IA-32e. Восстанавливается 16-битный доступ к сег­мен­ту стека.

29) Управление вернулось из подпрограммы TARGET_32. Загружаем в сегментные регистры селекторы 16-бит­ных сег­мен­тов, очищаем буфер трансляции страниц и переключаемся в реальный режим.

30) Восстановление контекста реального режима. Выполняем межсегментный переход на следующую ин­ст­рук­цию для за­груз­ки атрибутов сегмента кода. Восстанавливаем значения в регистрах SS и SP.

31) Разрешение прерываний.

32) Восстановление исходного состояния адресной линии A20.

33) Восстановление значений в системных регистрах: VM_HSAVE_PA MSR, IA32_EFER MSR, VM_CR MSR.

34) Вывод сообщения о возврате управления из гостевой процедуры. Визуализация статусной информации, по ко­то­рой мож­но определить причину завершения гостевой процедуры: поля Exit Code, Exit Info 1 блока VMCB. Ожи­да­ем на­жа­тия клавиши. Затем выводим 00h в порт 0080h и выходим в DOS с кодом выхода 00h.

35) Сюда передается управление, если программа завершается из-за ошибок, возникших при проверке кон­фи­гу­ра­ции или установке контекста. В регистре BP находится адрес строки для вывода сообщения об ошиб­ке. Вы­во­дим со­об­ще­ние об ошибке. Ожидаем нажатия клавиши. Затем выводим 01h в порт 0080h и вы­хо­дим в DOS с ко­дом вы­хо­да 01h.

Анализ результатов

Если в таблице IOPM, бит, разрешающий доступ к порту 0080h установлен в 1, то вывод в порт не вы­пол­ня­ет­ся, инструкция OUT перехватывается и вызывает завершение гостя. При этом ви­зу­а­ли­зи­ру­ет­ся результат: Exit Code = 0000007Bh, Exit Info 1 = 00800110h.

Если в таблице IOPM, бит, разрешающий доступ к порту 0080h обнулен, то вывод в порт выполняется, гость не за­вер­ша­ет­ся на инструкции OUT, а доходит до инструкции VMMCALL. При этом ви­зу­а­ли­зи­ру­ет­ся ре­зуль­тат: Exit Code = 00000081h, Exit Info 1 = 00000000h.

Управлять состоянием указанного бита таблицы IOPM, можно редактируя шаг 10 выше приведенного ал­го­рит­ма. Если экспериментатор располагает POST-картой, вывод байта также можно наблюдать на ее ин­ди­ка­то­рах.

Используя таблицу SVM Intercept Exit Codes, приведенную в документе [8], расшифруем наши результаты:

  • Exit Code = 0000007Bh означает выход при попытке обращения гостя к запрещенному порту ввода-вывода (VMEXIT_IOIO).
  • Exit Code = 00000081h означает выход при выполнении гостем инструкции VMMCALL (VMEXIT_VMMCALL).

Используя таблицу I/O Intercept Information, приведенную в документе [8], расшифруем дополнительный па­ра­метр, ге­не­ри­ру­е­мый при перехвате обращения к порту: Exit Info 1 = 00800110h.

  • Биты [31-15] = 0080h, это адрес порта, обращение к которому перехвачено.
  • Бит [8] = 1, означает 32-битную адресацию.
  • Бит [4] = 1, означает 8-битные данные.

Источники информации

Электронные документы, доступные на сайте developer.intel.com

  1. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: Basic Architecture. Order Number 253665-034US.
  2. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2A: Instruction Set Reference, A-M. Order Number 253666-034US.
  3. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2B: Instruction Set Reference, N-Z. Order Number 253667-034US.
  4. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3A: System Programming Guide, Part 1. Order Number 253668-034US.
  5. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3B: System Programming Guide, Part 2. Order Number 253669-034US.
  6. Intel Processor Identification and the CPUID Instruction. Application Note 485. Order Number 241618-033.

Электронные документы, доступные на сайте developer.amd.com

  1. AMD64 Architecture Programmer’s Manual. Volume 1: Application Programming. Publication No. 24592.
  2. AMD64 Architecture Programmer’s Manual. Volume 2: System Programming. Publication No. 24593.
  3. AMD64 Architecture Programmer’s Manual. Volume 3: General-Purpose and System Instructions. Publication No. 24594.
  4. AMD64 Architecture Programmer’s Manual. Volume 4: 128-Bit Media Instructions. Publication No. 26568.
  5. AMD64 Architecture Programmer’s Manual. Volume 5: 64-Bit Media and x87 Floating-Point Instructions. Publication No. 26569.
  6. AMD64 Architecture Programmer’s Manual. Volume 6: 128-Bit and 256-Bit XOP, FMA4 and CVT16 Instructions. Publication No. 43479.
  7. BIOS and Kernel Developer’s Guide for AMD NPT Family 0Fh Processors. Publication No. 32559.
  8. BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 10h Processors. Publication No. 31116.
  9. BIOS and Kernel Developer’s Guide (BKDG) for AMD Family 10h Processors. Publication No. 41256.
  10. AMD CPUID Specification. Publication No. 25481.

Электронные документы, доступные на сайте icbook.com.ua

  1. IC80 V5.0. Диагностическая плата. Руководство пользователя. Ревизия 1.5. IC Book Labs, © 2006.

Загрузить ZIP-архив
с примером программирования
на ассемблере
можно здесь.