При попытке обновить прошивку материнской платы через утилиту 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 используют специализированный набор инструментов, который сильно отличается от типичной среды программирования. Основной инструмент — 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
Как посмотреть версию 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, так и при решении проблем с загрузкой системы.