Внутренняя кухня UEFI: что это такое и как мы готовим его в YADRO

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр. На связи Сергей Пушкарёв, я руковожу отделом разработки BIOS в YADRO. Расскажу об устройстве UEFI и его применении в компании. Мы разрабатываем и выпускаем разные аппаратные платформы: серверы, системы хранения данных, клиентское и телеком-оборудование. 

Один из «кирпичиков», который обеспечивает инициализацию и функционирование оборудования, — это BIOS. В статье кратко разберем историю этой системы и ее современную реализацию — UEFI. Также поговорим о подходе к разработке и отладке этого ПО в YADRO. Вы узнаете, зачем нам нужна «синяя коробка» Intel, как мы прошиваем BIOS и проводим диагностику «в полях».

Сервер YADRO VEGMAN внутри
Сервер YADRO VEGMAN внутри
Содержание
  • Что такое BIOS: краткая история системы

  • UEFI как эволюция BIOS

    • Secure Boot

    • Human Interface Infrastructure для создания UI

    • Таблица разметки GPT, модульность и переносимость

    • Архитектура UEFI

    •  Что такое ACPI в BIOS

    • Режим SMM

    • Архитектурные особенности UEFI

    • Особенности реализации UEFI

    • Формат файлов

    • GUID

    • Как устроен UEFI «под капотом»

  • Как мы в YADRO разрабатываем и отлаживаем прошивки

    • Что такое BMC

    • Разработка на системах без BMC

    • Сборка прошивок

    • Отладка

    • Аппаратная отладка

    • Диагностика проблем в «полях»

  • Вместо заключения

  • Полезные материалы про UEFI

Что такое BIOS: краткая история системы

BIOS — это системное программное обеспечение, которое встроено прямо в материнскую плату и хранится в энергонезависимой памяти: как правило, это SPI Flash. Даже если на материнской плате нет жесткого диска или другого накопителя данных, BIOS уже присутствует. Сейчас BIOS правильнее называть UEFI, но по инерции многие продолжают называть эту систему BIOS. 

В начале 80-х годов компания IBM разработала для своих компьютеров микропрограмму, которая обеспечивала инициализацию оборудования. Также она предоставляла базовые сервисы для операционной системы того времени — DOS.

Примеры таких сервисов: работа с клавиатурой, дисками и выводом изображения на экран. Эта система получила название BIOS, то есть базовая система ввода-вывода. Это была закрытая проприетарная разработка, но со временем появились клоны IBM PC. И поскольку компания IBM запатентовала только конкретную реализацию BIOS, но не программные интерфейсы, то конкуренты начали копировать эту технологию, и она стала стандартом де-факто в мире настольных компьютеров.

Первые PC были системами с 16-битной адресацией и реальным режимом работы, они поддерживали максимум один мегабайт памяти, адресуемой посегментно. Использовать язык высокого уровня для написания ПО первичной инициализации таких систем было неэффективно, а порой и невозможно, поэтому для разработки BIOS использовали максимально низкоуровневый язык программирования — ассемблер.

Скриншот классической BIOS. Источник

В середине 90-х годов компания Intel начала разрабатывать процессоры с архитектурой Itanium. Анализируя BIOS, инженеры поняли, что его возможностей недостаточно для инициализации сложных систем с большим объемом памяти и 64-битными процессорами. В результате была разработана новая спецификация программной инициализации под названием EFI.

Второе поколение процессора Itanium. Источник

В 2005 году Intel передала спецификацию и реализацию консорциуму UEFI Forum — некоммерческой организации, в которую входят Intel, IBM, Microsoft и другие компании. После этого EFI получил название Unified EFI (UEFI). Основной задачей UEFI Forum стала разработка спецификации UEFI и продвижение этой системы «в массы» для постепенной замены BIOS.

Интерфейс прошивки UEFI на нашем сервере VEGMAN
Интерфейс прошивки UEFI на нашем сервере VEGMAN

На скриншоте — результат эволюции BIOS. Именно с этой системой мы работаем сейчас. UEFI может работать не только на процессорной архитектуре x86, но и на ARM, RISC-V, LoongArch и других.

UEFI как эволюция BIOS

Рассмотрим преимущества UEFI. Прежде всего, это наличие спецификации. Существует четкая спецификация на интерфейсы, которые прошивка предоставляет внешним сущностям. Например, загрузчику операционной системы. 

Прошивка может взаимодействовать с загрузчиками операционных систем, самими операционными системами или программами, которые занимаются инициализацией оборудования. Например, если вы вставляете PCIe-видеокарту, то при работе UEFI запускается специальный драйвер UEFI Option ROM, который инициализирует эту видеокарту и предоставляет сервисы для вывода изображения. Все — от формата Option ROM и до программного интерфейса вывода изображения — стандартизовано спецификацией.

Спецификация есть как на внешние, так и на внутренние интерфейсы — последняя называется PI Specification. Она регламентирует, как прошивка должна быть устроена и как хранит данные. Поэтому фрагментация между прошивками минимальна. Например, если вы сравните прошивки, разработанные разными компаниями, такими как AMI или Insyde, они будут устроены очень похоже. Прошивки YADRO также следуют этому подходу.

В написании прошивок много нюансов. Это интересная, но непростая работа. И мы ищем неравнодушных коллег в команду. Сейчас открыты такие вакансии:

→ Старший/ ведущий разработчик на C++ (Linux/OpenBMC)

→ Старший разработчик на С (BIOS/UEFI)

→ Тимлид разработки в OpenBMC

Существует также спецификация ACPI, которая определяет, как операционная система управляет энергосбережением и обнаруживает оборудование. Хотя ACPI изначально не была связана с UEFI, в итоге спецификацию передали под управление UEFI Forum. Теперь ее развитие происходит в рамках этого консорциума.

Еще одним элементом UEFI является встроенный командный интерпретатор. Это приложение может быть встроено в прошивку или запущено с внешнего носителя, например с флешки. Оно похоже на командную оболочку DOS.

Одно из ключевых преимуществ UEFI — наличие референсной реализации, известной как Tianocore EDK2. Этот проект, размещенный на GitHub, представляет собой одновременно и референсную имплементацию спецификации UEFI, и пакет разработчика UEFI.

В проекте содержится множество драйверов и описание программных интерфейсов, что делает его отличной базой для разработки отдельных приложений, драйверов или даже полной прошивки. Tianocore EDK2 также предлагает кроссплатформенную сборку проектов с помощью удобных абстракций.

Secure Boot

Безопасность — еще одно важное преимущество UEFI. Во времена BIOS вопросы безопасности практически не рассматривались. При передаче управления загрузчику операционной системы не было уверенности, что это действительно легитимная ОС, а не вредоносный код.

Схема работы технологии Secure Boot в UEFI
Схема работы технологии Secure Boot в UEFI

UEFI решает эту проблему с помощью технологии Secure Boot, которая проверяет цифровую подпись загрузчика или других программ, таких как Option ROM на PCIe-адаптерах. Для этого в прошивке хранятся сертификаты или открытые ключи, а файлы подписываются закрытым ключом. Перед передачей управления UEFI проверяет подпись файла. Это позволяет гибко управлять безопасностью системы: например, можно подписать загрузчик Linux своим ключом для более полного контроля над платформой. А прошивка предоставит интерфейс, чтобы сохранить внутри себя открытую часть ключа.

Аспект безопасности очень важен, поскольку в теории злоумышленники могут подменить Option ROM видеокарты или другого устройства вредоносным кодом, который будет исполняться независимо от операционной системы. Такой зловред невозможно удалить, даже переустановив ОС, так как он находится на внешнем устройстве.

Human Interface Infrastructure для создания UI

Еще одно преимущество UEFI — HII. Эта архитектура предназначена для создания пользовательских интерфейсов. UEFI поддерживает юникод «из коробки» благодаря использованию стандарта UCS-2, где каждый символ фиксированного размера занимает 2 байта. Это обеспечивает поддержку мультиязычности. 

Интерфейс сервера YADRO VEGMAN R120 G2 на русском
Интерфейс сервера YADRO VEGMAN R120 G2 на русском

Строки интерфейса хранятся в отдельных файлах, что упрощает локализацию и позволяет ее автоматизировать.

Кроме того, HII использует меню, основанное на байт-коде. Это позволяет разработчикам не задумываться о том, как нарисовать элементы интерфейса. Они просто описывают функционал кнопки, а система сборки преобразует это описание в байт-код, который исполняется во время работы прошивки.

Вендоры, которые разрабатывают Option ROM для PCIe-устройств, также могут создавать свои меню. Их легко интегрировать в BIOS, и меню будут выглядеть чужеродно. Такой подход позволяет создавать универсально выглядящие интерфейсы для настройки, например RAID-контроллеров или других устройств, без необходимости вручную прорабатывать их визуальную составляющую.

Таблица разметки GPT, модульность и переносимость

GPT — это способ разбить диск на логические разделы. Во времена BIOS использовалась разметка MBR, которую разработала IBM. Эта разметка стала стандартом де-факто, но с развитием компьютерной техники производители столкнулись с несколькими ограничениями MBR:

  • Первое — нельзя использовать больше четырех логических разделов, хотя это и можно было обойти с помощью «костылей» в виде «вторичных» разделов.

  • Второе, более фундаментальное ограничение, — MBR не умеет работать с дисками объемом более 2 ТБ. Разметка GPT, которая пришла вместе с UEFI, сняла это ограничение и позволила использовать диски объемом до 18 эксабайт со 128 разделами.

Исходя из этого можно выделить еще одно преимущество UEFI — модульность. Прошивка состоит из множества исполняемых файлов, запуск которых можно гибко настроить. Во время сборки при помощи так называемых DEPEX-выражений можно задать условия, при которых конкретный драйвер будет запускаться только после выполнения определенных задач.

Поскольку UEFI написан в основном на языке C, то его легко портировать. Отдельные драйверы можно адаптировать для работы на различных процессорных архитектурах: x86, ARM, RISC-V и других.

Архитектура UEFI

Она строится на последовательности стадий, каждая из которых выполняет свою часть работы, записывает результаты и передает управление следующей стадии. Каждая последующая стадия использует информацию, полученную на предыдущей, — вплоть до стадии Runtime, на которой уже загружена и работает операционная система. Затем большая часть сервисов BIOS завершает работу, хотя некоторая их часть продолжает функционировать на протяжении всей работы системы.

Архитектура UEFI
Архитектура UEFI

Для наглядности рассмотрим эти стадии на примере загрузки BIOS на процессорах Intel.

SEC

Первая стадия — SEC. Код BIOS хранится в энергонезависимой памяти на материнской плате, и его выполнение начинается прямо оттуда. На этом этапе система стартует в реальном режиме, как это было в 80-х годах. Однако, в отличие от старого BIOS, в UEFI практически сразу выполняется переход в 32-битный защищенный режим. Но на этом этапе оборудование еще не инициализировано: ни памяти, ни других ресурсов нет. Мы не можем использовать стек, поэтому эта стадия все еще пишется на ассемблере.

Для исправления этой ситуации используется специальный режим Cache-As-RAM, который позволяет использовать кеш процессора как временную оперативную память. Обычно кеш недоступен для прямой работы, но в этом режиме часть кеша выделяется под стек и под кучу (heap), что делает возможным работу с памятью на языке C.

PEI

Следующий шаг — поиск специального файла PEI Core, который является диспетчером стадии PEI. Эта стадия занимается базовой инициализацией оборудования, которая нужна для полноценной работы прошивки.

В начале стадии PEI-драйверы выполняются прямо с микросхемы SPI Flash, установленной на материнской плате. Так экономится память, которая на этом этапе очень ограничена — не забываем про Cache-As-RAM. Код на SPI Flash отображается на физические адреса, и к нему можно обращаться так, как если бы он находился в оперативной памяти.

Механизм XIP позволяет выполнять код прямо с SPI Flash, но с важным ограничением: в код нельзя записывать данные. Драйвер можно написать на языке C, но глобальные данные, объявленные в коде, остаются неизменяемыми. Если попытаться записывать такие данные, то ничего не произойдет, так как весь код доступен только для чтения. Нужно учитывать этот нюанс при разработке драйверов стадии PEI.

Основная задача этой стадии на процессорах Intel — инициализация оперативной памяти. Это сложный процесс, он требует больших объемов закрытого кода, из-за чего его практически невозможно реализовать самостоятельно, даже если у вас есть все необходимые спецификации.

После инициализации памяти сохраняется информация о конфигурации системы, например о типе и расположении планок памяти. 

DXE

Переходим на следующую стадию — DXE. На этом этапе выполняется переход в 64-битный защищенный режим, а управление передается диспетчеру DXE, который называется DXE Core. 

DXE — это фаза, на которой выполняется большая часть работы по инициализации оборудования.

Отмечу важную деталь, которая связана с архитектурой x86. В ней существует специальный режим работы процессора: SMM. Это наиболее привилегированный режим, который может функционировать даже во время работы операционной системы. В UEFI используются специальные SMM-драйверы, которые копируются в отдельную область памяти (SMRAM) и исполняются там. Инициализация режима SMM происходит именно на стадии DXE.

Во время стадии DXE выполняются ключевые задачи, такие как PCIe Enumeration — процесс, во время которого выделяются ресурсы для всех PCIe-устройств и резервируются ресурсы для Hotplug. Только после этого устройства становятся полностью доступными для работы.

Драйверы стадии DXE предоставляют программные интерфейсы, которые в терминологии UEFI называются протоколами. Протокол представляет собой идентификатор интерфейса и таблицу функций, которые можно вызывать.

BDS

Когда все драйверы на стадии DXE завершили работу, происходит переход к фазе BDS, и пользователь может войти в меню BIOS. Если пользователь не совершает никаких действий, происходит автоматическая загрузка с одного из доступных носителей или по сети.

Особенность UEFI — обязательная поддержка файловой системы FAT32. Для загрузки с диска или флешки в UEFI на носителе должен быть раздел FAT32 с загрузчиком. Он представляет собой исполняемый файл, аналогичный тем, которые находятся в самой прошивке.

Runtime

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

Эти драйверы предоставляют рантайм-сервисы, которые описаны в спецификации UEFI. Они выполняют роль моста между операционной системой и прошивкой. Хотя таких сервисов немного, они обеспечивают важные функции:

  • Чтение и запись настроек прошивки, а также создание загрузочных записей. Например, при установке Windows появляется загрузочная запись вида «Windows Boot Manager», которая создается установщиком операционной системы.

  • ResetSystem — функция для выключения или перезагрузки системы.

  • Функции для работы с системным временем, такие как GetTime и SetTime. Они позволяют устанавливать или получать системное время без прямого обращения к RTC. Работа с RTC выполняется драйверами прошивки, а не операционной системой.

Что такое ACPI в BIOS

ACPI — это технология для управления энергосбережением и обнаружения оборудования. Она состоит из набора аппаратных регистров и таблиц, которые создаются прошивкой и используются операционной системой. Эти таблицы написаны на специальном языке ASL и исполняются интерпретатором, встроенным в операционную систему.

Прошивка создает таблицы, а операционная система их использует. Например, для обнаружения оборудования существует таблица, в которой описано количество процессорных ядер и их распределение. Если загрузить ядро Linux с параметром acpi=off и при этом в прошивке не реализованы альтернативные методы обнаружения оборудования (например, нет поддержки режима CSM), то система покажет только одно процессорное ядро, даже если CPU многоядерный.

Режим SMM

Вернемся к режиму SMM. Его преимущества особенно заметны в серверных продуктах благодаря технологии RAS. Это набор обработчиков ошибок оборудования. В SMM работают драйверы, которые реагируют на прерывания, так как доступ в режим SMM возможен только через SMI. Из обычного кода, в том числе из кода ядра ОС, напрямую попасть в SMM нельзя.

Например, работающий сервер выдает ошибку памяти. Если система настроена правильно, то оборудование генерирует SMI, которое переводит процессор в режим SMM. В нем запускается соответствующий обработчик, который предпринимает действия для устранения или учета ошибки. 

Режим SMM также абстрагирует доступ к оборудованию, обеспечивая дополнительную безопасность. Например, при инициализации оборудования BIOS может настроить поведение системы так, что запись в определенный порт ввода-вывода вызывает прерывание SMI. В зависимости от записанных данных вызывается соответствующий обработчик.

В наших прошивках запись настроек на SPI Flash доступна только из режима SMM. Runtime-драйверы предоставляют интерфейсы, такие как GetVariable и SetVariable. Операционная система вызывает функцию SetVariable, Runtime-драйвер подготавливает данные и пишет в определенный порт ввода-вывода, что приводит к генерации SMI. Процессор переходит в режим SMM, и в соответствующем обработчике данные уже записываются на SPI Flash.

Архитектурные особенности UEFI

UEFI обладает рядом архитектурных особенностей. Код в основном однопоточный и выполняется на BSP. Это не отдельный процессор, а просто одно из ядер, которое назначается для выполнения кода.

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

Еще одной особенностью является отказ от использования прерываний, за исключением прерываний таймера. Они позволяют вызывать определенные функции, например раз в секунду. Однако вместо того, чтобы использовать прерывания в драйверах, таких как USB, применяется метод опроса (polling). Код драйвера просто циклически проверяет, не поступило ли событие от контроллера.

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

Особенности реализации UEFI

В UEFI у точки входа в приложение есть свои особенности. В отличие от операционной системы, здесь нет динамической линковки. Вместо этого используются либо статически слинкованные библиотеки, либо программные интерфейсы (протоколы).

Точка входа принимает два параметра:

  • ImageHandle — дескриптор исполняемого файла, который позволяет идентифицировать запущенный модуль,

  • System Table — таблица, содержащая функции и служебные данные.

В коде часто используются объекты gBS и gRT, которые предоставляют boot-сервисы (сервисы времени загрузки) и runtime-сервисы (сервисы времени выполнения). Boot-сервисы используются на стадии загрузки операционной системы, а runtime-сервисы доступны как в процессе работы BIOS, так и после загрузки ОС. 

Простой пример кода на UEFI выше демонстрирует вывод строки «Hello, World». Результат выполнения сохраняется в переменной Status, типа EFI_STATUS. Этот тип стандартизирован и позволяет определить, успешно ли завершилась функция или произошла ошибка. Значение EFI_SUCCESS (0) означает успех, а для ошибок используются фиксированные коды. Пользовательские коды ошибок обычно не добавляются, так как хватает стандартного набора ошибок.

Для вывода текста на экран используется программный интерфейс, который называется SimpleText Output Protocol. Указатель на этот протокол передается как первый параметр, что напоминает неявную передачу указателя this в методах C++, только в UEFI это делается явно. Обычно протоколы ищутся через функцию LocateProtocol, которая доступна через таблицу Boot Services. Однако протокол вывода текста встроен прямо в таблицу System Table, и это упрощает его использование. «Под капотом» в данном случае будет использоваться специальный драйвер ConSplitterDxe, который выведет строку на все доступные устройства, например на экран и в последовательный порт.

Формат файлов

Файлы UEFI имеют формат PE, который также используется в операционной системе Windows. Разница лишь в заголовке и некоторых деталях структуры. В Windows у подобных файлов — расширение .exe, в UEFI их структура практически такая же, но расширение файлов — .efi.

GUID

Для идентификации сущностей в UEFI используется GUID (Globally Unique Identifier). Это 16-байтовое число, которое однозначно идентифицирует объекты, такие как протоколы и другие сущности внутри прошивки. 

Как устроен UEFI «под капотом»

Давайте погрузимся во внутреннее устройство прошивки. На скриншотах ниже — прошивка нашего сервера, которая открыта в утилите UEFITool. Эта утилита разработана нашим соотечественником (@CodeRush, спасибо за утилиту

Источник: https://habr.com/ru/companies/yadro/articles/886480/


Интересные статьи

Интересные статьи

Один из пользователей нашей экосистемы – Максим Герасимов – руководитель агентства, которое помогает увеличивать прибыль компаниям, специализирующимся на изготовлении мебели и кухонь, отделке балконов...
Сегодня мы поговорим о том, как использовать вложенные транзакции в Bitrix, чтобы обеспечить целостность данных. Транзакции - это ваше надежное магическое заклинание, которое гарантирует, что ваши мн...
В первой части мы поговорили о том, что такое Кубинг (если вкратце - сборка родственников известного кубика Рубика) и какими бывают пазлы (т.е. головоломки, от англ. "puzzle"). В этой и следующих стат...
Те, кто собираются открывать интернет-магазин, предварительно начитавшись в интернете о важности уникального контента, о фильтрах, накладываемых поисковиками за копирование материалов с других ресурсо...
Основанная в 1998 году компания «Битрикс» заявила о себе в 2001 году, запустив первый в России интернет-магазин программного обеспечения Softkey.ru.