- PVSM.RU - https://www.pvsm.ru -
Использование технологии EFI Byte Code (сокращенно – EBC) позволяет создавать кроссплатформенные приложения [1] и драйверы, выполняемые на виртуальном процессоре, реализованном в составе firmware платформы. Архитектура этого процессора определена в спецификации Unified Extensible Firmware Interface. В идеале, EBC-программа должна взаимодействовать исключительно с ресурсами виртуальной машины, системными таблицами UEFI и другими объектами, абстрагированными от аппаратной реализации платформы. На практике, точное следование данному принципу существенно ограничивает функциональность программного продукта. Выйти из положения не потеряв кроссплатформенности можно, если использовать подпрограммы в нативном коде центрального процессора, условно получающие управление в случае детектирования заданной аппаратной платформы.
Рассмотрим пример чтения заданного регистра MSR (Model Specific Register) из EBC-приложения. Как известно, в системе команд x86 предусмотрена инструкция RDMSR (Read MSR), получающая в качестве входного параметра 32-битный адрес MSR в регистре ECX, и возвращающая 64-битное содержимое MSR в регистрах EDX (старшие 32 бита) и EAX (младшие 32 бита). В системе команд виртуальной машины EBC аналогичной функциональности не предусмотрено, именно поэтому требуется вызов подпрограммы в нативном коде.
Отметим, что в информационно-диагностической утилите UEFImark x64 Edition [2] инструкция RDMSR используется непосредственно, а в UEFImark EBC Edition [3] для этого требуется вызов нативных подпрограмм из EBC-программы.
Предполагается, что до передачи управления на рассматриваемую процедуру, EBC-программа детектировала платформу x86 и установлено, что поддерживается одна из архитектур IA32 или x64. Метод детектирования выходит за рамки данной статьи, его планируется рассмотреть в последующих публикациях.
Для трансляции примеров используется FASM 1.69.50. Инструкции EBC реализованы с помощью макросов, x86 код транслируется для 64-битного режима, особенности обеспечения его совместимости с 32-битным режимом рассмотрены в пункте 4.
Рис. 1. EBC-процедура, вызывающая x86-процедуру чтения MSR
Рассмотрим последовательность операций, выполняемых вызывающей EBC-процедурой.
Разрядность параметров, которые записываются в стек инструкциями PUSHN (Push Natural) и считываются из стека инструкциями POPN (Pop Natural) равна 32 бита для IA32 UEFI и 64-бита для x64 UEFI.
Рис. 2. Вызываемая x86-процедура чтения MSR
Рассмотрим последовательность операций, выполняемых вызываемой x86-процедурой.
Посмотрев на процедуру, можно найти противоречие: ряд инструкций 64-битного режима используются для 32-битной ветви выполнения, например, инструкции PUSH и POP, оперирующие с 64-битными регистрами. Как это работает? Дело в том, что 32-битные и 64-битные формы этих инструкций кодируются одинаково, а их интерпретация зависит от режима работы процессора. Так, код 53h в 32-битном режиме соответствует инструкции PUSH EBX, а в 64-битном режиме – инструкции PUSH RBX.
Рассмотрим механизмы передачи входных и выходных параметров подпрограмм.
Для IA32 EFI входные параметры вызываемой подпрограммы передаются через стек. В рассматриваемой процедуре первый параметр находится по адресу [ESP+16]. Смещение 16 складывается из двух слагаемых: 4 байта стека используются для хранения счетчика команд EIP, что необходимо при возврате из подпрограммы, 12 байт для регистров EBX, ECX, EDX, записанных в стек инструкцией PUSH. 4+12=16.
Для x64 UEFI четыре первых входных параметра вызываемой подпрограммы передаются через регистры RCX, RDX, R8, R9, последующие – через стек. В нашем примере используются только два параметра, передаваемые в RCX и RDX.
Для IA32 UEFI, содержимое 32-битного x86-регистра EAX после возврата из x86-подпрограммы, находится в 32 младших битах 64-битного EBC-регистра R7. Содержимое старших 32 бит R7 не определено. Для x64 UEFI, содержимое 64-битного x86-регистра RAX после возврата из x86-подпрограммы, находится в EBC-регистре R7. Данная функциональность удобна для передачи статусных кодов, в рассмотренном примере не используется.
Описанная технология применяется не только для вызова процедур, входящих в состав приложения, но и при обращении к UEFI API, обработка которых реализована в firmware. Например, при использовании функций CPU Architectural Protocol [4] и File I/O Protocol [5].
Применять описанный метод следует только тогда, когда требуется обеспечить функциональность, недостижимую в рамках EFI Byte Code [6]. Так, в информационно-диагностической утилитой UEFImark EBC Edition [7] для отображения модели процессора и списка поддерживаемых технологий используется инструкция CPUID в нативном коде.
Важно отметить, что любой непосредственный доступ к аппаратным ресурсам усложняет обеспечение кроссплатформенности. В частности, несмотря на то, что в выше приведенных примерах есть возможность различать x86-платформы IA32 и x64, до передачи управления приложение должно убедиться, что работает именно на платформе x86. Выполнение на ARM или Itanium приведет к непредсказуемым последствиям из-за различий в системе команд центрального процессора.
Автор: icbook
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/57436
Ссылки в тексте:
[1] кроссплатформенные приложения: http://habrahabr.ru/post/212363/
[2] UEFImark x64 Edition: http://jelezo.com.ua/programmy/utilita_uefimark.html
[3] UEFImark EBC Edition: http://jelezo.com.ua/programmy/uefimark_ebc_edition.html
[4] CPU Architectural Protocol: https://ru.intel.com/business/community/index.php?automodule=blog&blogid=59442&showentry=4648
[5] File I/O Protocol: https://ru.intel.com/business/community/index.php?automodule=blog&blogid=59442&showentry=4641
[6] EFI Byte Code: http://habrahabr.ru/post/214119/
[7] UEFImark EBC Edition: http://jelezo.com.ua/skrinshoty/utilita_uefimark_ebc_edition.html
[8] Источник: http://habrahabr.ru/post/216413/
Нажмите здесь для печати.