Pull to refresh

Теория и практика EFI Byte Code

Reading time5 min
Views13K
В конце 90-х компания Number Nine Visual Technology, тогдашний светоч дизайна видеокарт, предлагала на сайте VGA BIOS для своих PCI-устройств. Ничего примечательного в этом событии нет. Разве что, видеокарты Number Nine могли работать как на IBM PC-совместимых платформах, так и в MAC-системах, использующих Power PC. Поэтому одно и то же устройство комплектовалось различными файлами BIOS.

Скорее всего, тогда и не могло быть иначе. Как сейчас дело обстоит с поддержкой устройств, рассчитанных на работу в разных аппаратных средах? Ответ на этот вопрос дает спецификация UEFI, в рамках которой предлагается изящное решение – EFI Byte Code или EBC. С его помощью можно создавать кроссплатформенные приложения для firmware.

Как работает EBC


В рамках UEFI-стандарта определяется архитектура виртуальной машины регистрового типа EFI Byte Code Virtual Machine. Интерпретатор команд входит в состав firmware системной платы. Встроенное программное обеспечение плат расширения пишется в системе команд виртуальной машины, в идеале, без использования инструкций центрального процессора. Таким образом, плата расширения будет работоспособна на любой системной плате, поддерживающей EBC, независимо от типа центрального процессора. На сегодня их список не блещет разнообразием: как обычно, здесь есть AMD и Intel в 32-битном и 64-битном вариантах, Itanium, ARM.

Архитектура виртуального процессора EBC


64-разрядный виртуальный процессор EBC содержит 8 регистров общего назначения (R0-R7), поддерживает прямую, косвенную и непосредственную адресацию операндов. Система команд включает арифметические и логические операции, сдвиги, пересылки операндов с поддержкой знакового расширения, условную и безусловную передачу управления, вызовы подпрограмм и возвраты, а также ряд вспомогательных операций. Поддерживается стек, при этом указатель стека (регистр R0) согласно традициям архитектуры x86, классифицируется как регистр общего назначения. Примечательно, что специальная форма инструкции CALL, позволяет из EBC-подпрограмм вызывать подпрограммы, написанные на «родном» языке платформы, в силу того, что иногда такая необходимость все же возникает. Таким же образом из EBC-программ можно вызывать процедуры поддержки UEFI-протоколов, используя при этом модель передачи входных и выходных параметров, не зависящую от типа центрального процессора.

Приступаем к экспериментам


Предлагаемый пример «Hello, EBC!» является UEFI-приложением, написанным в системе команд виртуальной машины EBC (EFI Byte Code). Как уже сказано выше, интерпретатор команд EBC, позволяющий запускать модули данного типа, резидентно входит в состав UEFI firmware системной платы. Использование EBC вместо машинного кода позволяет создавать кроссплатформенные приложения и драйверы, включая firmware различных плат расширения, что делает данные устройства совместимыми с платформами, использующими центральные процессоры архитектуры, отличной от x86.

Пояснения к примеру


Программа выводит текстовое сообщение, используя процедуру вывода строки из набора функций EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. Рассмотрим детальнее ее исходный код.

Для вызова данной EFI-функции в стеке необходимо передать два параметра: указатель на интерфейсный блок используемого протокола и указатель на строку, представленную в формате UNICODE. Эти параметры подготавливаются в регистрах R1 и R2, затем заносятся в стек инструкциями PUSHn, начиная с последнего параметра. Затем, в регистре R3 размещается адрес для вызова процедуры, прочитанный из интерфейсного блока используемого протокола и выполняется вызов целевой процедуры вывода строки. После возврата, освобождаем стек инструкциями POPn.

Системные таблицы и кроссплатформенность


Для вызова сервисных процедур UEFI-протоколов, приложения используют указатели, находящиеся в системных таблицах UEFI и различных интерфейсных блоках. В 32-битных реализациях UEFI используются указатели размером 4 байта, в 64-битных – размером 8 байт. Следовательно, адрес указателя внутри таблицы будет зависеть от разрядности центрального процессора. Как же обеспечивается кроссплатформенность?

Рассмотрим пример инструкции, передающей в регистр R1 содержимое ячейки памяти, адрес которой равен исходному значению регистра R1 плюс смещение.

MOVnw R1,@R1(+5,+24)

Смещение задано в виде двух слагаемых: +5 и +24.

Первое слагаемое +5 является номером адресуемого указателя. Интерпретатор команд EBC умножает это значение на размер указателя, который равен 4 для 32-битных реализаций UEFI и 8 для 64-битных.

Второе слагаемое +24 является константой, не зависящей от типа платформы. Оно используется для задания размера заголовка таблицы EFI_SYSTEM_TABLE.

Подобным образом работают инструкции PUSHn (Push Natural), используемые при подготовке стекового фрейма для вызываемых процедур. Разрядность параметров, записываемых в стек (32 или 64 бита) зависит от разрядности центрального процессора. Так обеспечивается шлюзование между EBC-кодом приложения и процедурами, входящими в состав UEFI-firmware написанными в системе команд центрального процессора.

Трансляция и запуск


Для трансляции программы и генерации EBC-приложения используется FASM 1.69.50. Инструкции виртуальной машины EFI Byte Code заданы в виде шестнадцатеричных констант. Руководствуясь исследовательским интересом, мы намеренно отказались от использования языков высокого уровня и написали наш пример на ассемблере EBC. При этом нам пришлось решить несколько задач, связанных с тем, что транслятор FASM не поддерживает EBC.

После трансляции, в заголовке файла helloebc.efi, по адресам 84h, 85h байты 64h, 86h необходимо заменить на BCh, 0Eh. Таким образом, поле Machine Type, исходно содержащее 8664h (x86-64 machine) заменяем на 0EBCh (EBC machine). Для запуска редактора, встроенного в UEFI Shell, в командной строке требуется набрать: hexedit helloebc.efi.

Коррекция поля Machine Type в заголовке приложения

Рис 1. Коррекция поля Machine Type в заголовке приложения

После этого можно запускать приложение.

Результат работы приложения

Рис 2. Результат работы приложения

Приложение также можно запустить под отладчиком Intel EBC Debugger.

Загрузка отладчика командой load и запуск EBC-приложения

Рис 3. Загрузка отладчика командой load и запуск EBC-приложения под отладчиком Intel EBC Debugger

Резюме


Работу тестового примера мы проверили в средах IA32 EFI и x64 UEFI. Теоретически, он должен работать на платформах с процессорами Itanium и ARM, но из-за недоступности указанных систем мы не смогли в этом убедиться.

Приложение транслируется в режиме PE64 (Portable Executable 64-bit). Некоторые устаревшие EFI-реализации (например, эмулятор Intel EFI Version 1.10.14.59 Sample Implementation, запускаемый с загрузочной дискеты) не совместимы с данным форматом приложения. Это выражается в некорректной интерпретации таблицы перемещаемых элементов, используемой при настройке модуля на адреса загрузки. Один из путей решения – выполнять настройку программно.

Так как транслятор FASM не поддерживает EFI Byte Code, для обеспечения эффективного программирования на уровне ассемблера EBC в среде FASM, нам предстоит сделать следующее:

  1. Написать небольшую сервисную утилиту для автоматизации перезаписи поля Machine Type в заголовке UEFI-приложения с пересчетом контрольной суммы модуля.
  2. Подготовить набор макросов для использования ассемблерных мнемоник EBC в среде FASM.


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


Tags:
Hubs:
Total votes 11: ↑11 and ↓0+11
Comments2

Articles