На чем программируется BIOS: полный разбор технологического стека

При попытке разобрать прошивку материнской платы в дизассемблере IDA Pro вы почти гарантированно увидите не читаемый высокоуровневый код, а перемешанные инструкции x86 с фрагментами таблиц и данных. Это не случайность: подавляющее большинство современных UEFI-прошивок написано на языке C с вкраплениями ассемблера, а вот устаревшие Legacy BIOS эпохи 90-х и начала 2000-х собирались преимущественно на ассемблере. Если вам нужно модифицировать BIOS, понять логику его работы или просто оценить сложность разработки — знание реального стека технологий критически важно.

Разработка прошивки — это не написание приложения под Windows. Здесь действуют жёсткие ограничения: отсутствие операционной системы, прямой доступ к железным регистрам и необходимость работать в 16-битном реальном режиме на этапе инициализации. Именно эти факторы определили выбор языков и инструментов, которые используются в индустрии уже несколько десятилетий.

Историческая эволюция: от ассемблера к C и EDK II

Первые BIOS-прошивки для IBM PC/XT писались исключительно на ассемблере. В 16-битном реальном режиме с его сегментной моделью памяти и ограниченным адресным пространством в 1 МБ высокоуровневые языки были практически неприменимы. Каждый байт кода имел значение, а компиляторы того времени генерировали слишком много лишних инструкций.

Переломный момент наступил в конце 90-х, когда Intel начала продвигать спецификацию EFI (Extensible Firmware Interface), позже ставшую UEFI. Новая архитектура предполагала работу в 32-битном и 64-битном режимах, что открыло дорогу для использования языка C. Сегодня практически все прошивки для материнских плат, ноутбуков и серверов пишутся именно на C.

  • 🖥️ Legacy BIOS (до ~2005 года) — ассемблер, реже PL/M (язык от Intel для микроконтроллеров)
  • ⚙️ Переходный период (2000-2010) — гибридные решения: ассемблер для ранней инициализации, C для POST-логики
  • 🔧 Современный UEFI (с 2010 года) — C с использованием фреймворка EDK II от Intel
  • 🧩 Специфичные модули — вставки на ассемблере для критичных по скорости участков

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

⚠️ Внимание: если вы планируете изучать исходный код BIOS для модификации, начинайте с C и EDK II. Ассемблерные вставки встречаются точечно, но основной объём логики написан именно на C.

Язык C — фундамент современного UEFI

Спецификация UEFI требует, чтобы прошивка была написана на C без использования стандартной библиотеки. Вместо привычных printf и malloc разработчики используют собственные функции из UEFI API: Print, AllocatePool, AllocatePages. Компилятор для таких проектов — обычно Intel C Compiler или GCC с набором специфичных флагов.

Ключевая особенность — отсутствие глобальных переменных в привычном понимании. UEFI-приложения и драйверы используют механизм System Table и Boot Services, которые передаются в каждую функцию в качестве аргумента. Это делает код модульным и переносимым между различными реализациями прошивок.

Стандарт ANSI C соблюдается не полностью. Например, структуры выравниваются специфичным образом, а работа с указателями требует особой осторожности из-за различий между физической и виртуальной адресацией. Ошибка в управлении памятью в прошивке приводит не к «синему экрану», а к полному отказу системы загружаться.

Ассемблер: где он действительно нужен

Даже в новейших UEFI-прошивках есть участки, написанные на ассемблере. Это так называемый SEC (Security Phase) — самый первый этап загрузки, который выполняется сразу после включения питания. На этом этапе ещё нет стека, нет глобальных дескрипторных таблиц, и код должен работать в чистом 16-битном реальном режиме.

Конкретные задачи ассемблера в BIOS:

  • 🔌 Переключение процессора из реального режима в защищённый и обратно
  • ⚡ Настройка базовых регистров и таблицы дескрипторов (GDT, IDT)
  • 🧮 Критичные по производительности операции копирования памяти
  • 🛠️ Обработка прерываний на самых ранних этапах загрузки

Для ассемблерных вставок используется синтаксис Intel или AT&T в зависимости от компилятора. В EDK II применяется NASM — популярный ассемблер с открытым исходным кодом, который поддерживает оба синтаксиса.

⚠️ Внимание: модификация ассемблерных участков BIOS без глубокого понимания архитектуры x86 почти гарантированно приведёт к нерабочей прошивке. Даже одна неверная инструкция на этапе SEC может заблокировать запуск системы.

Стек EDK II и инструменты разработки

EDK II (EFI Development Kit) — это официальный фреймворк от Intel с открытым исходным кодом, на базе которого строятся коммерческие прошивки от AMI, Insyde и Phoenix. Он включает в себя компиляторы, библиотеки, драйверы и утилиты для сборки. Исходный код EDK II доступен на GitHub и сопровождается документацией для разработчиков.

Процесс сборки прошивки автоматизирован: используются make-файлы, скрипты на Python и специальные инструменты вроде GenFw и GenFds. Компиляция происходит в несколько этапов: сначала C-код компилируется в объектные файлы, затем линкуется в PE/COFF-образ, который упаковывается в FFS-файлы (Firmware File System).

КомпонентНазначениеЯзык
SEC PhaseПервичная инициализация CPU и памятиАссемблер
PEI PhaseИнициализация чипсета и базовых контроллеровC
DXE PhaseЗагрузка драйверов и служб UEFIC
BDS PhaseВыбор загрузочного устройстваC
RuntimeСлужбы, доступные ОС после загрузкиC

Кроме EDK II, существуют альтернативные фреймворки. Например, coreboot — проект с открытым исходным кодом, который пишется на C с минимальными ассемблерными вставками. Coreboot используется в Chromebook и некоторых серверных платформах, а его кодовая база значительно меньше и понятнее, чем у коммерческих аналогов.

Компиляторы и специфические ограничения

Выбор компилятора для BIOS-разработки — нетривиальная задача. Стандартный GCC не подходит из-за особенностей генерации кода: необходимо отключить использование глобальных переменных, запретить некоторые оптимизации и обеспечить совместимость с EFI calling convention.

На практике используются:

  • 🧰 Intel C Compiler — предпочтительный выбор для коммерческих прошивок, отличная оптимизация под x86
  • 🐧 GCC с патчами EDK II — бесплатная альтернатива, используется в coreboot и open-source проектах
  • 🔬 Clang/LLVM — набирает популярность, хорошо работает с EDK II
  • ⚙️ NASM — для ассемблерных модулей

Отдельного упоминания заслуживает компилятор Open Watcom, который использовался для сборки многих Legacy BIOS. Он поддерживает 16-битный режим и специфичные для DOS расширения, что делало его незаменимым в эпоху ассемблерных прошивок.

⚠️ Внимание: не пытайтесь компилировать BIOS-код обычным компилятором без специальных флагов. Результат будет либо неработоспособным, либо вообще не соберётся из-за отсутствия стандартной библиотеки и особенностей линковки.

C|Ассемблер|Python|Ни один, BIOS — закрытая система-->

Скриптовые языки и утилиты в разработке BIOS

Хотя сама прошивка пишется на C и ассемблере, процесс её разработки и настройки не обходится без скриптовых языков. Python используется для автоматизации сборки, генерации конфигурационных файлов и анализа логов. В EDK II есть набор Python-скриптов для управления процессом компиляции.

Для настройки параметров прошивки применяются специальные утилиты, которые работают с бинарными образами. Например, UEFITool позволяет извлекать и заменять отдельные модули в BIOS-файле, а IFR Extractor декодирует строки настроек. Эти инструменты написаны на C++ и Python, но для их использования знание языков не требуется — достаточно графического интерфейса.

В коммерческих компаниях, таких как AMI и Insyde, разработчики используют внутренние инструменты на базе C# и Java для генерации кода драйверов. Это позволяет ускорить разработку типовых модулей, хотя итоговый код всё равно компилируется в C или ассемблер.

Практические советы: как начать изучение

Если вы хотите разобраться в устройстве BIOS на уровне исходного кода, начните с изучения документации EDK II и примеров из репозитория. Проект coreboot — отличная отправная точка, поскольку его кодовая база компактнее и лучше документирована, чем у коммерческих аналогов.

Попробуйте собрать минимальную прошивку для эмулятора QEMU. Это безопасный способ изучить процесс компиляции и структуру образа без риска повредить реальное железо. Для этого вам понадобятся: Linux-окружение, установленный GCC, NASM и исходники EDK II или coreboot.

Изучение ассемблера x86 — обязательный этап, если вы хотите понять, что происходит на самых ранних этапах загрузки. Не обязательно становиться экспертом, но базовое понимание инструкций mov, jmp, call и особенностей реального режима существенно облегчит чтение кода.

Установить Linux и GCC|Скачать исходники EDK II или coreboot|Изучить основы ассемблера x86|Настроить QEMU для тестирования|Разобрать структуру FFS-файлов-->

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

Перспективы: что будет после C

Индустрия движется в сторону использования языка Rust для разработки прошивок. Уже существуют экспериментальные проекты по написанию UEFI-драйверов на Rust, а TianoCore (разработчик EDK II) официально добавил поддержку этого языка в experimental-ветку. Rust предоставляет безопасность памяти на уровне компиляции, что критически важно для кода, работающего без операционной системы.

Однако полный переход на Rust в ближайшие годы маловероятен. Слишком много существующего кода написано на C, а переход на новый язык требует переписывания тысяч драйверов и библиотек. Скорее всего, мы увидим гибридный подход, где новые модули пишутся на Rust, а старые остаются на C.

Для конечного пользователя эти изменения незаметны: прошивка продолжит работать так же, как и раньше. Но для разработчиков и энтузиастов, которые модифицируют BIOS, знание C останется необходимым ещё как минимум десятилетие.

Частые вопросы о языках программирования BIOS

Можно ли написать BIOS на Python или Java?

Технически нет. Python и Java требуют интерпретатора или виртуальной машины, которые сами должны загружаться в память. На этапе инициализации BIOS нет ни того, ни другого. Исключение — скрипты для автоматизации сборки и анализа, но не сама прошивка.

Почему нельзя использовать обычный компилятор C для BIOS?

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

Сколько кода в современных прошивках написано на ассемблере?

Обычно не более 5-10% от общего объёма. Это самые ранние этапы инициализации и критичные по производительности функции. Основная часть — C, а в перспективе — Rust.

Что такое EDK II и обязательно ли его использовать?

EDK II — это фреймворк от Intel для разработки UEFI-прошивок. Он не обязателен, но используется в подавляющем большинстве коммерческих продуктов. Альтернативы — coreboot, U-Boot (для ARM-устройств) и собственные разработки компаний.

Можно ли модифицировать BIOS без знания языков программирования?

Для простых модификаций (изменение логотипа, настройка скрытых параметров) языки не нужны — достаточно специальных утилит. Но для изменения логики работы, добавления драйверов или исправления ошибок потребуется знание C и ассемблера.

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