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

При попытке обновить прошивку материнской платы через утилиту AFUWIN или UEFITool пользователь видит десятки файлов с расширениями .bin, .fd и .rom — и почти никогда не задумывается, на чем пишется BIOS. На самом деле базовая система ввода-вывода — это не монолитная программа, а сложный комплекс модулей, написанных на разных языках. Современный UEFI BIOS содержит код на C, ассемблере, а также интерпретируемые скрипты EDK II, и понимание этой архитектуры критически важно при диагностике ошибок прошивки или попытке модификации загрузчика.

Если вы открывали дамп прошивки в HEX-редакторе, то могли заметить характерные сигнатуры EFI и блоки с GUID-идентификаторами. Это следы компиляции кода на C, который лежит в основе 90% современных UEFI-реализаций. Однако в legacy BIOS, который использовался до 2010-х годов, преобладал ассемблер — именно поэтому старые прошивки весили всего 1-2 мегабайта, а современные UEFI-образы достигают 32 МБ и более.

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

Первые BIOS для IBM PC 1981 года были написаны полностью на ассемблере x86. Разработчики компании Phoenix Technologies и AMI использовали исключительно низкоуровневые инструкции, поскольку объем микросхемы ПЗУ составлял всего 8-16 КБ. Каждый байт был на счету, а компиляторы C того времени генерировали слишком объемный код.

Переломный момент наступил в конце 1990-х, когда Intel представила спецификацию EFI (Extensible Firmware Interface). Она предполагала использование языка C и объектно-ориентированной модели. С 2005 года развитие продолжилось в рамках проекта EDK II (EFI Development Kit II) — именно на этом фреймворке сегодня пишутся все современные UEFI BIOS.

В 2026-2026 годах практически все новые материнские платы от ASUS, MSI и Gigabyte используют прошивки на основе EDK II. Код на C занимает около 70-80% объема, остальное — ассемблерные вставки для критически важных операций и скрипты на языках описания.

⚠️ Внимание: не путайте язык программирования BIOS с языком написания настроек CMOS. Хранение параметров конфигурации в энергозависимой памяти не связано с кодом прошивки — это просто структура данных, которую читает BIOS при каждом включении.

Основные языки программирования в современном UEFI

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

  • 🔧 C (C99) — основной язык для реализации драйверов, протоколов и сервисов UEFI. Весь runtime-код, который исполняется при загрузке, написан именно на C.
  • ⚙️ Ассемблер x86/x64 — используется для самых критичных участков: переход в защищенный режим, настройка таблиц страниц памяти, обработка прерываний. В EDK II это файлы с расширением .asm или .nasm.
  • 📜 EDK II DSC/DEC-скрипты — декларативные файлы описания платформы. Они определяют, какие модули войдут в прошивку и как они будут скомпонованы. Формально это не язык программирования, но без них сборка невозможна.
  • 🧩 VFR (Visual Forms Representation) — специализированный язык для описания графического интерфейса BIOS Setup. На нем размечаются меню, вкладки и поля настроек.

В некоторых случаях применяется и C++ — например, в прошивках для серверных платформ от Insyde. Однако стандарт UEFI требует C-совместимого ABI, поэтому C++ используется ограниченно и только для внутренних модулей.

Компонент BIOS Язык Доля в прошивке
Ядро UEFI (SEC, PEI, DXE) C + ассемблер ~40%
Драйверы устройств C ~30%
Интерфейс Setup VFR + C ~15%
Скрипты и конфигурация DSC/DEC/FDF ~10%
Служебные данные Бинарные GUID ~5%

Важно понимать: несмотря на разнообразие языков, конечный результат всегда компилируется в машинный код или байт-код EFI. На материнской плате нет интерпретатора C — весь код уже преобразован в исполняемые инструкции процессора.

Структура EDK II: как организован код прошивки

Фреймворк EDK II — это не просто компилятор, а целая экосистема с модульной архитектурой. Исходный код разбит на пакеты (packages), каждый из которых содержит библиотеки, драйверы и определения протоколов. Например, пакет MdePkg содержит базовые определения типов, а MdeModulePkg — универсальные драйверы для работы с памятью и PCI.

Сборка прошивки происходит в несколько этапов. Сначала компилятор GCC или MSVC превращает C-файлы в объектные модули. Затем линковщик собирает их в PE32+ образы, которые соответствуют формату исполняемых файлов Windows. После этого специальный инструмент GenFw конвертирует образы в формат TE (Terse Executable), используемый в UEFI.

Для платформы Intel процесс сборки обычно выглядит так: загрузка набора модулей в соответствии с файлом Platform.dsc, генерация таблиц GUID и протоколов, затем создание финального образа Platform.fd. Именно этот файл и загружается в микросхему SPI Flash.

# Пример фрагмента DSC-файла для включения модуля

[Components]

MdeModulePkg/Universal/PcatDxe/PcatDxe.inf

MdeModulePkg/Universal/Setup/Setup.inf

⚠️ Внимание: попытка редактировать исходники EDK II без понимания архитектуры может привести к нерабочей прошивке. Даже небольшое изменение в DSC-файле способно исключить критически важный драйвер, и система перестанет загружаться.

Почему ассемблер не исчез полностью

Несмотря на доминирование C, ассемблерные вставки остаются обязательными в трех ключевых местах. Во-первых, это фаза SEC (Security Phase) — самый первый код, который выполняется после сброса процессора. На этом этапе еще нет стека, нет оперативной памяти, и все операции выполняются через регистры. Писать такую логику на C невозможно.

Во-вторых, ассемблер нужен для переключения процессора из 16-битного реального режима в 32-битный защищенный, а затем в 64-битный длинный режим. Эти переходы требуют точного контроля над флагами и таблицами дескрипторов — здесь C-компилятор может внести нежелательные оптимизации.

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

В итоге в современном UEFI BIOS на ассемблер приходится около 5-10% кода, но это самые ответственные участки. Ошибка в них приводит к полной неработоспособности платы, которую невозможно исправить программно.

📊 Какой тип BIOS у вашей материнской платы?
Legacy BIOS
UEFI BIOS
Не знаю
У меня ноутбук, не смотрел

Инструменты разработки и отладки прошивки

Разработчики BIOS используют специализированный набор инструментов, который сильно отличается от типичной среды программирования. Основной инструмент — EDK II Build System, автоматизирующий процесс компиляции. Для отладки применяются аппаратные средства: внутрисхемные эмуляторы JTAG и логические анализаторы.

Популярный компилятор для сборки UEFI — это либо GCC с кросс-компиляцией под x86_64, либо Intel C++ Compiler. Интересно, что Microsoft Visual C++ тоже поддерживает сборку EDK II, но требует специальных настроек и используется реже.

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

  • 🛠️ EDK II Build — основная система сборки, аналог Makefile с поддержкой платформенных конфигураций
  • 💻 JTAG-отладчики — аппаратные устройства для пошагового исполнения кода прямо на материнской плате
  • 📊 Serial Debug — вывод отладочной информации через COM-порт, используемый на ранних этапах загрузки
  • 🔍 UEFITool — графическая утилита для просмотра структуры BIOS-образа и извлечения модулей

Практическое применение знаний о языках BIOS

Знание того, на чем пишется BIOS, помогает в нескольких реальных сценариях. Например, при диагностике ошибки 0xE в POST-кодах вы можете определить, какой модуль не загрузился. Код 0xE относится к фазе DXE, где исполняются драйверы на C — значит, проблема в инициализации конкретного контроллера, а не в аппаратной части.

Если вы планируете модифицировать BIOS для добавления NVMe-драйвера в старую систему, вам понадобится понимание формата PE32+ и структуры модулей. Инструменты вроде MMTool позволяют заменять драйверы в образе, но требуют знания, какие модули совместимы с вашей платформой.

Для обычного пользователя эти знания полезны при выборе утилиты обновления. Если производитель предлагает обновление через Windows, значит, в прошивке есть специальный модуль Shell.efi, написанный на C. Если же требуется загрузка с флешки — прошивка использует модуль FAT-драйвера, который тоже написан на C в рамках EDK II.

☑️ Чек-лист для безопасного обновления BIOS

Выполнено: 0 / 5
Как посмотреть версию BIOS в Windows

Нажмите Win+R и введите msinfo32. В открывшемся окне найдите строку «Версия BIOS» — там будет указана дата и версия прошивки. Альтернативный способ — команда wmic bios get smbiosbiosversion в командной строке.

Будущее: что придет на смену EDK II

С 2020 года Intel и AMD активно продвигают проект NVIDIA UEFI и LinuxBoot — альтернативные загрузчики, написанные на Rust и Go. LinuxBoot заменяет большую часть DXE-драйверов на ядро Linux, что сокращает время загрузки с 15 секунд до 2-3 секунд на серверных платформах.

Тем не менее, на потребительском рынке EDK II останется стандартом еще минимум 5-7 лет. Производители материнских плат вложили огромные ресурсы в разработку на C и вряд ли откажутся от этой базы. Rust-компоненты появляются только в отдельных модулях, например, в драйверах безопасности.

Интересная тенденция — использование интерпретируемых языков для конфигурации. Некоторые прошивки от Insyde поддерживают скрипты на Python для автоматизации тестирования, но в production-коде они не используются из-за требований к производительности и надежности.

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

Можно ли написать свой BIOS с нуля?

Технически да, но практически это крайне сложная задача. Проект EDK II открыт, и вы можете собрать собственную прошивку для тестовой платформы. Однако для реальной материнской платы потребуется доступ к закрытым описаниям чипсета и референсным кодам от Intel или AMD, которые не публикуются.

Почему в BIOS используется C, а не C++?

Стандарт UEFI определяет ABI (Application Binary Interface), совместимый с языком C. C++ добавляет перегрузку функций, шаблоны и исключения, которые усложняют компоновку модулей и увеличивают размер кода. Для прошивки, где каждый байт важен, C остается оптимальным выбором.

Влияет ли язык программирования BIOS на скорость загрузки Windows?

Косвенно — да. Оптимизированный код на C с правильно настроенными драйверами сокращает время инициализации оборудования. Однако основной вклад в скорость загрузки вносит не язык, а количество инициализируемых устройств и алгоритмы управления питанием.

Как узнать, какой язык используется в моей прошивке?

Практически невозможно определить язык по внешним признакам. Если ваша плата выпущена после 2012 года и поддерживает UEFI, можно с уверенностью сказать, что в основе лежит C. Для точного анализа нужно извлечь образ прошивки и изучить его в UEFITool — наличие модулей с расширением .efi подтвердит использование EDK II.

Безопасно ли редактировать BIOS-образ на уровне кода?

Любое вмешательство в прошивку несет риск выхода устройства из строя. Даже опытные специалисты всегда имеют при себе программатор SPI Flash для восстановления. Если вы не уверены в своих действиях, используйте официальные утилиты производителя и не пытайтесь модифицировать модули вручную.

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