Pull to refresh

Устройство файла UEFI BIOS, часть полуторная: UEFI Platform Initialization

Reading time6 min
Views90K
В первой части этой статьи мы познакомились с форматом UEFI Capsule и Intel Flash Image. Осталось рассмотреть структуру и содержимое EFI Firmware Volume, но для понимания различий между модулями PEI и драйверами DXE начнем с процесса загрузки UEFI, а структуру EFI Firmware Volume отставим на вторую часть.

UEFI Platform Initialization


С высоты птичьего полета процесс загрузки UEFI выглядит так:

Вообще говоря, нас интересует не весь это процесс, а его часть — Platform Initialization (PI), которая делится на 3 фазы: SEC, PEI и DXE.
Всю документацию по PI можно свободно загрузить с сайта UEFI Forum. Фазы SEC и PEI описаны в Volume 1, фаза DXE — в Volume 2, общие архитектурные элементы, в том числе интересующие нас форматы файлов и заголовков EFI FFS — в Volume 3, субфаза SMM (стартует в середине DXE и идет параллельно) — в Volume 4, стандарты на совместимое с PI оборудование и ПО — в Volume 5. Про оборудование и ПО здесь я писать не стану, а вот остальные фазы нужно упомянуть, т.к. не зная их, сложно понять, зачем в файле BIOS'а столько всего и чем это всё отличается друг от друга.

Фаза SEC

Первая фаза загрузки, задачи которой следующие:
  1. Обработать все виды platform restart'ов: включение питания после неактивного состояния, перезагрузка из активного состояния, выход из режима глубокого сна, различного рода исключительные ситуации
  2. Подготовить временную память
  3. Стать Root of Trust системы: или доверять остальным частям PI, или проверить их валидность каким-либо способом
  4. Подготовить необходимые структуры данных и передать их и управление в фазу PEI. Как минимум, передаются состояние платформы, адрес и размер BFV, адрес и размер временной RAM, адрес и размер стека

По факту, на x86-64 фаза SEC проходит так:
  1. Reset Vector: сброс кэша и переход на главную процедуру иницилизации в ROM
  2. Switch to protected mode: переключение в защищенный режим процессора с плоской памятью без подкачки
  3. Initialize MTRRs for BSP: запись в кэш известных значений для различных областей памяти
  4. Microcode Patch Update: обновление микрокода всех доступных процессоров
  5. Initialize NEM: свободный кэш помечается как несбрасываемый, после чего его можно использовать как временную RAM до инициализации основной, а также позволяет написать эту самую инициализацию на обычных ЯП со стеком, в данном случае на C
  6. Early BSP/AP interactions: отправка всем AP прерывания INIT IPI, затем Start-up IPI, получение данных BIST со всех AP
  7. Hand-off to PEI entry point: передача управления и данных в фазу PEI

Видно, что в участникам фазы SEC из образа BIOS понадобятся как минимум хранящиеся там патчи для микрокода CPU, а также адрес и размер Boot Firmware Volume. Да и сам код SEC записан в той же микросхеме и пока еще выполняется там же.

Фаза PEI

Вторая фаза, основная задача которой — инициализировать достаточное количество непрерывной RAM для того, чтобы можно было запустить фазу DXE, подготовить и передать в фазу DXE данные об обнаруженных устройствах, чтобы драйверы DXE смогли их правильно инициализировать. Исполняемый код PEI состоит из ядра, называемого PEI Foundation, которое является общим для процессоров с одинаковой архитектурой и модулей PEIM, выполняющих начальную инициализацию конкретных устройств и разработанные производителями этих устройств. Модули поддержки Chain of Trust, выполняющие проверку валидности других модулей, также могут присутствовать. Архитектура PEI позволяет независимую разработку и отладку модулей, и никто не мешает написать и интегрировать свой собственный модуль, если понадобится.
У PEIM может присутствовать список зависимостей от других модулей, поэтому порядок их запуска не случаен и выбирается диспетчером PEI. Модули PEIM могут заполнять независимые от позиции структуры данных — HOB'ы, в которых содержатся данные для передачи драйверу DXE и GUID этого драйвера.
По факту, на x86-64 фаза PEI происходит так:
  1. Establish use of «memory»: перенос данных из ROM в раннюю RAM (т.е. в кэш)
  2. PEI Dispatcher: запуск модулей PEIM в порядке от не имеющих зависимостей до имеющих сложные зависимости. Это цикл, который заканчивается в момент, когда не запущенных модулей не остается
  3. CPI PEIM: инициализация CPU, настройка MSR и т.п. (Мы вернемся к этому модулю при обсуждении патча CPU PM)
  4. Platform PEIM: ранняя инициализация MCH, ICH, встроенных интерфейсов платформы (SMBus, Reset, и т.п.). Определение режима загрузки (обычный, Recovery, S3 Resume), используя данные, полученные в фазе SEC.
  5. Memory Initialization PEIM: инициализация основной RAM и перенос в нее данных из кэша, которым теперь можно пользоваться нормально. процесс зависит от определенного на предыдущем шаге состояния системы, например, при S3 Resume тестирование памяти не выполняется, что сокращает время загрузки
  6. Если система не находится в S3 Resume, то происходит передача HOB'ов и управления в фазу DXE, а фаза PEI на этом завершается
  7. Если все же находится — выполняется CPU PEIM for S3 Boot Script, выполняющий возврат всех процессоров в их сохраненное состояние
  8. S3 Boot Script Executor: восстановление состояния других устройств
  9. OS Resume Vector: переход к ОС

Таким образом, при S3 Resume запуск фазы DXE не происходит вообще, что позволяет сильно ускорить загрузку. При включении FastBoot загрузку можно ускорить еще сильнее, выполняя минимальный набор тестов и модулей PEIM.
Видно, что в фазе PEI из образа BIOS нужны будут как минимум PEI Foundation и модули для всего оборудования, нуждающегося в ранней инициализации. Также стоит рассказать о том, что формат модулей PEI может как совпадать с форматом драйверов DXE (PE32+), так и отличаться от него заголовком, т.к. заголовок PE32+ содержит множество неиспользуемых в фазе PEI полей, а место в кэше процессора не резиновое. Поэтому для PEIM был разработан специальный формат TE, заголовок которого содержит только необходимые поля. TE бывают исполняемые-на-месте (XIP), перемещаемые (relocatable) и независимые от позиции (PIC). Также встречаются гибридные DXE/PEI-модули с двумя точками входа, но они обязаны быть в формате PE32+, поскольку иначе как драйвер DXE такой модуль не запустится.

Фаза DXE

Здесь выполняется основная и окончательная инициализация всего на основе полученных от PEI HOB'ов. Код DXE состоит из ядра, оно же DXE Foundation, диспетчера и драйверов. Ядро инициализирует и запускает различные службы UEFI: Boot Services, Runtime Services и DXE Services. Диспетчер отвечает за поиск и запуск DXE-драйверов, которые также имеют зависимости. Драйверы проводят окончательную инициализацию аппаратуры и предоставляют аппаратную абстракцию для служб. Весь код DXE, кроме Runtime-частей Foundation и Runtime DXE драйверов выгружается из памяти по окончанию фазы BDS, которую я здесь рассматривать не буду.
Расписывать досконально процесс запуска DXE тоже нет смысла, можно описать его в двух словах: загружается ядро, создает нужные структуры данных, затем запускается диспетчер и грузит все доступные драйверы со всех доступных носителей, затем запускается бутлоадер и пытается найти на этих носителях загрузчик ОС и передать ему управление. Если нашелся — отлично, если нет — пробуем дальше, пока не найдем. Если так ничего и не нашли — выполняем код модуля Platform Policy, который для нас написал производитель материнской платы, выводящий нам сообщение о том, что «Operating System is missing».
Видно, что из образа BIOS'а для этой фазы нужны DXE-драйверы и все, что им может понадобится. Большая часть файлов в EFI FS используется именно здесь.

Cубфаза SMM

Во время фазы DXE наступает момент, когда диспетчер загружает драйвер SMM Init, с которого и начинается эта субфаза. SMM — специальный режим процессора, в который он переходит при получении специального прерывания — SMI, которое может быть как программным, так и аппаратным. Большую часть (или вообще все) источников SMI можно отключить, если переход в SMM не требуется. Код SMM выполняется в SMRAM, которая становится недоступной для ОС после окончания фазы DXE, поскольку драйвер SMM намеренно закрывает к ней доступ. Код SMM выполняется и после окончания фазы DXE, до самого выключения ПК.
Драйвер SMM Init открывает SMRAM, создает ее карту и структуры данных, необходимые для запуска других драйверов SMM, а перед окончанием фазы DXE закрывает доступ к SMRAM полностью. Драйверы SMM зависят от оборудования и не имеют доступа к интерпретатору байткода, поэтому написание драйверов SMM на EBC не поддерживается. Бывают эти самые драйверы двух видов: чистые SMM, которые загружаются Init-ом непосредственно в SMRAM, и SMM/DXE-гибриды, которые сначала запускаются диспетчером DXE, а потом уже копируют часть себя в SMRAM. Сам SMM Init — именно такой гибрид.
Видно, что для этой субфазы из образа BIOS'а нужны драйверы SMM.

Заключение


Теперь вы знаете, как происходит загрузка UEFI и какие модули необходимы для нее.
Я принял решение разделить планируемую вторую часть еще на две, чтобы уменьшить размер поста и снизить когнитивную нагрузку на читателя.
Во второй части статьи мы наконец рассмотрим структуру файла EFI FV, и сведения из этой вам там очень пригодятся.
Спасибо за внимание.

Литература


  1. Analysis of the building blocks and attack vectors associated with the Unified Extensible Firmware Interface (UEFI), Paper by Jean-François Agneessens
  2. Reducing Platform Boot Time, Paper by Michael Rothman, Genliu Xing, Yan Wang and Jiong Gong
  3. UEFI Platform Initialization Specification 1.2.1 Errata A, Documents by UEFI Forum
Tags:
Hubs:
Total votes 39: ↑37 and ↓2+35
Comments11

Articles