Пишем для UEFI BIOS в Visual Studio. Часть 1 — разворачивание среды разработки, компиляция и запуск на отладку

Введение


В этой статье будет описано, как быстро начать программировать для UEFI во фреймворке edk2 в среде Visual Studio, не тратя массу времени на настройку среды обычным способом, по оригинальным мануалам. Достаточно дать команду git clone ... в корневом каталоге диска, и это на самом деле все, среда будет полностью установлена и готова к работе. Требуются 64-разрядная Windows 7 и выше c Visual Studio 2008-2015. Эти два условия не обязательны, но тогда придется немного потрудиться над собиранием системы edk2-Visual Studio в единое целое, краткая памятка будет приведена.

Цель статьи — провести начинающего за руку по первому UEFI проекту, оставаясь в привычной ему среде. Для более опытных людей, надеюсь, будет интересным поработать в VS вместо привычной командной строки, или разобрать подход и перенести его в любимый Eclipse.

Начнем с простых вещей, вывода строки на консоль и русификации (довольно востребованная вещь, причем простая в реализации), потом будет работа с формами в HII (то, что называлось в обиходе страницами BIOS Setup), потом графика, потом Boot Manager, а потом видно будет (с).


Желающие — прошу пожаловать под кат.

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

Вначале хорошее


1) Аппаратура не понадобится от слова совсем. Никаких Evaluation Boards, никаких Intel BlueBox JTAG за $3000. Все будет отлаживаться в 32-битной виртуальной машине OVMF – портированной Интелом виртуалке qemu для отладки UEFI Firmware. Для перекомпиляции под реальную платформу достаточно потом — после отладки — переставить пару ключей в настройках компиляции, и на этом все.

2) Работать будем со всеми возможностями Visual Studio, т.е. доступны breakpoints, watch, step execution и остальное. Перекомпиляция и запуск несложного модуля занимает 8-10 секунд.

3) Файловая система виртуалки доступна на Windows-машине для записи-чтения. Очень пригодится, если надо будет редактировать скрипты UEFI Shell, после запуска посмотреть скриншоты и проанализировать логи средствами Windows.

4) Никакого ассемблера, только С/С++.

Теперь о том, что мы делать не будем


1) Мы будем работать в DXE (где уже есть UEFI Shell) и поздних фазах. В более ранние фазы не полезем, поскольку нас туда никто не пустит, по крайней мере – для Intel-процессоров. Позже будет объяснение, почему. Если хотите сделать полный цикл, от включения до загрузки ОС, причем быстро и не забивая голову кучей ненужной и неинтересной вам в данный момент информацией, а ручное конфигурирование системы вам совершенно не требуется – дальше не читайте, а наберите в Гугле «coreboot».

2) «Графического» UEFI с мышкой и кнопками, как у Dell, MSI и прочих, здесь не будет. Это платные среды, для использования в крупных компаниях. Есть, разумеется, энтузиасты, которые сами создают их своими руками, не ответив предварительно на вопрос «Зачем?», но обычно их энтузиазм заканчивается на второй форме с кнопками.

3) Мы будем работать с компилятором Visual Studio. Желающие могут настроить gcc в cygwin, или icc, но в данный момент не стоит задача получить оптимальный быстрый код, а стоит задача быстро пройти путь к началу полноценной работы.

Все, предварительные танцы закончены, кто надо – воодушевлен, кто надо – напуган.

Переходим к делу


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

Итак, у кого на машине есть git в командной строке, выполняют команду в cmd окне (или в Far Commander, не суть) из корневого каталога:

git clone https://github.com/ProgrammingInUEFI/FW

а те, у кого нет, идут по ссылке на github, скачивают zip-файл и раскрывают его в каталог с:/FW.

Как было ранее обещано, приведем подсказку из Интеловского тренинга по использованию другой конфигурации, отличной от указанной в начале статьи. Ищите свое сочетание в табличке, если предлагаемое по каким-то причинам не подходит:



Конфигурирование среды для версии, отличной от VS2010


Открываем файл c:\FW\edk2\Conf\target.txt и в строчке
TOOL_CHAIN_TAG = VS2010x86

Заменяем VS2010x86 на тэг установленной у вас версии Visual Studio. Для Visual Studio 2010 тэг останется как есть, для других версий VS – смотрите картинку выше, или список в начале файла c:\FW\edk2\Conf\tools_def.txt

Собственно, среда разработки edk2 развернута полностью и в ней можно работать из командной строки. Некоторые так и работают всю жизнь («угорать по хардкору, поддерживать дух старой школы и всё такое» — (с) CodeRush в своей ставшей классической статье). Но мы все же пойдем дальше, пересаживать человека из MSVS обратно в командную строку — негуманно, особенно в 2017.

Настраиваем проект в Visual Studio


Открываем Visual Studio, в нем открываем Solution NT32.sln из каталога C:\FW\VS\NT32. В целях уменьшения времени входа в тему, в solution уже создан одноименный проект NT32, в котором уже сделаны описанные ниже настройки. Это если не получится создать самим – чтобы иметь гарантированно рабочие настройки проекта. Такой подход сильно сократит время поиска источника проблем, в случае их появления. Тем не менее, лучше пройти описанный ниже путь самим, и понять смысл настроек – это облегчит настройку следующих проектов.

Небольшой совет для тех, кто загорелся всерьез
Полезно будет сразу в Tools->Options настроить рабочий каталог на c:\FW\VS, но если в VS ведется другой, рабочий проект, то так делать не надо:



Итак, по шагам:

Создание проекта


Создаем в Solution NT32 новый проект для Visual C++ (правой клавишей на Solution NT32, Add->New Project, выбираем опцию Makefile Project), и назовем его MyFirstUEFIProject (или как угодно еще). Жмем Finish.



Выбираем в Solution проект NT32, выбираем из контекстного меню Project->Properties и производим настройки проекта.

Настройка NMake опций


Выбираем в окне слева строку Configurarion Properties → NMake, в окне справа — строку Build Command Line



Жмем Edit… и в открывшемся текстовом окне вводим:

set NASM_PREFIX=C:\FW\NASM\
call c:\FW\edk2\edksetup.bat --nt32
build

Сейчас стоит немного объяснить, что мы делаем. По сути, мы пишем в этом окне обычный пакетный bat-файл вместо makefile.

В первой строке устанавливается переменная окружения ассемблера NASM_PREFIX в том виде, как ее понимает edk2, то есть путь, по которому лежит файл nasm.exe. На ассемблере мы сами писать не будем, но нашей системе сборки ассемблер нужен обязательно.

Во второй строке вызывается скрипт настройки среды edk2 и настраиваются переменные окружения для данного сеанса компиляции и запуска (вне VS эти переменные не отражаются). Ключ –nt32 указывает системе сборки, что компилировать исходники надо для пакета (package) Nt32Pkg, расположенного в C:\FW\edk2\Nt32Pkg. Этих пакетов там много, мы их рассмотрим, но не сейчас.

В третьей строке мы даем команду на компиляцию в только что настроенной среде (build.exe лежит в C:\FW\edk2\BaseTools\Bin\Win32, этот путь прописывается в предыдущей строчке, в edksetup.bat)

Итак, вот что у нас должно появиться в итоге в текстовом окне Build Command Line:



Затем вводим в следующей строке Rebuild Command Line в открывшееся по Edit … окно следующий текст

set NASM_PREFIX=C:\FW\NASM\
call c:\FW\edk2\edksetup.bat --nt32 
build clean
build

Команда build clean означает то самое, что вы предполагаете. Она делает полную перестройку проекта с перекомпиляцией всех модулей.

Что мы вводим в окне из Clean Command Line, наверное, все уже догадались:

set NASM_PREFIX=C:\FW\NASM\
call  c:\FW\edk2\edksetup.bat --nt32 
build clean

Честно говоря, особо эта опция не нужна, в 99% случаев хватит Rebuild, но пускай будет – например, очистить среду для ее переноса в другое место или заливки на github.
В итоге, у нас должно получиться вот такое окно:



Все с настройкой NMake.

Настройка опции Debugging


Итак, открываем строчку Debugging и вводим:
В строчке Command:

C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\SecMain.exe

В строчке “Working Directory”:

C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\

Пара комментариев:

SecMain.exe – объяснять сейчас, что это такое – долго, если очень кратко и упрощенно – то это аналог bootloader-a, который запускает все остальное.

Рабочий каталог – сюда будут помещаться все успешно созданные модули, и доступны они будут все сразу из командной строки.

Итак, вот что мы должны получить после настроек в этом окне:



На этом все с настройками проекта.

Построение проекта


Вызываем Build Solution, смотрим на экран примерно минуту, в течение которой есть наибольший риск быть обруганным компилятором, и идем пить кофе – создаваться это все будет 10-15 мин, в зависимости от ресурсов вашего компьютера. Ничто нудное не вечно, и в конце концов мы получаем сообщение:

========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Если же вместо этого получено что-то иное, смотрите, правильно ли вы прошли все шаги. Наиболее тяжелый случай – получить:

LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt

это баг среды VS2010 и означает, что VS2010 установлен без SP1. Поставьте SP1, или ищите способы затыкания этой ошибки в инете.

Если же получили ошибку и из сообщений компилятора не понятно, что это такое – переставьте дефолтный проект на NT32 и запустите его на компиляцию с отладкой. Если и там ошибка – проверьте еще раз соответствие TOOL_CHAIN_TAG предопределенным значениям, описанным в tools_def.txt. Больше ничего там упираться не может, разве что сам Visual Studio установлен, хм, не вполне стандартно, или использует сторонний компилятор.

Работа в UEFI Shell


Итак, все скомпилялось хорошо, и вы читаете эти строки. Теперь жмем на любимую F5 и после примерно минуты работы с диском (чуть позже сократим и это время) получаем вот такую требуемую картинку:



Собственно, это и есть UEFI Shell. Как в нем работать – написана куча руководств, посмотрите в Гугле, а мы пока сделаем в нем несколько вещей.

1. Смотрим, что мы там накомпиляли за эти 10 минут. Вводим fs0: (UEFI Shell нечувствителен к регистру) и затем ls –b, где опция –b означает ожидание нажатия Enter для прокрутки страницы, ибо список там большой, не на один экран.

Теперь стало понятно, что означал параметр “Working Directory” в настройке опций проекта Visual Studio — C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\. Там этот же самый список файлов, и лучше его смотреть (и редактировать скрипты) через развитую оболочку Far Commander (или Total Commander), чем с командной строки в UEFI Shell.

2. В UEFI Shell и набираем “hel”, жмем Tab и видим на экране Helloworld.efi. Не то, чтобы мы совсем не догадывались, что будет, если нажать после этого Enter, но проверить-то надо! Жмем и получаем троекратное UEFI Hello World!. Число повторений – это конфигурируемый в настройках среды (а не в исходниках) внешний параметр и мы будем эту конфигурацию потом разбирать.

3. Набираем exit и попадаем в наше любимое и знакомое окно:



Ну вот, можно любоваться на плоды своих трудов. После этого стрелками гоним фокус на Reset и виртуалка закрывается, возвращая нас в знакомое окно MSVC.

Выводим свою строку


Создание полностью своего приложения потребует достаточно много настроек, которые лучше будет рассмотреть в следующей статье — иначе получится большой объем, а длинные простыни никто не читает. Однако же в заголовке этой статьи написано «Программирование», а мы пока занимались только настройкой. Чтобы сдержать обещание, давайте в этой статье сделаем очень простую модификацию приложения HelloWorld, используя его имеющийся набор файлов, а в следующей статье создадим свой, при помощи Интеловской утилиты UEFI Driver Wizard, поскольку прогонять начинающих по полному циклу создания набора файлов для UEFI драйвера (или приложения) — это нечеловеколюбиво, дико рутинно и несет риск потери 90% аудитории. Если человека зацепит — он сам к этому придет со временем, а если хочется просто поиграться — нет смысла тратить на это кучу времени, благо многое давно уже делается автоматически через UEFI Driver Wizard, причем по фэн-шуй, чего от новичка ждать наивно.

Итак, открываем в Visual Studio файл
C:\FW\edk2\MdeModulePkg\Application\HelloWorld\HelloWorld.c

И добавим свою простую строчку, сразу после объявления переменных в единственной функции:

Print (L"I did it in UEFI!\r\n");

Разумеется, текст можно заменить на что угодно, только английскими буквами — русский шрифт edk2 еще не понимает, мы добавим его в следующих статьях. Можете поставить на эту строку обычный breakpoint и посмотреть, как себя поведет Visual Studio.

Жмем F5, после компиляции и устранения ошибок вводим «fs0:», HelloWorld.efi и получим такой вывод:



На этом все. В следующей статье мы немного поработаем в UEFI Shell, чтобы освоиться, и разберем немного теории.
Как выделение жирным шрифтом ключевых слов в статье влияет на ее восприятие?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 16
  • +9

    Приятно видеть статьи про UEFI на Хабре, спасибо за материал.
    Главный недостаток дефолтного OVMF — 32х-битность и не совсем честная эмуляция оборудования, но если не пробовать пробрасывать туда железо и отлаживать там VT-d, SGX и подобные вещи, хорошая эмуляция которых будет доступна еще не скоро, то такой подход намного лучше, чем "голый" EDK2.
    Заодно порекомендую проект VisualUEFI товарища Ионеску, преследующий те же цели, да еще и 64х-битный.

    • +5
      Прежде всего — спасибо Вам, Николай. Это ведь Ваша статья «Пишем DXE-драйвер» подвигла меня в данном направлении.
      Если про VisualUEFI, которую Вы также упоминали в статье про DXE-драйвер — то, конечно, я ее попробовал, перед тем как творить что-то свое (хотя какое там свое — по сути, я соединил два компонента в единое целое так, как не видел в инете, и получилось, на удивление, очень удобно). Дело в другом…
      Видимо, я пересидел в проектных менеджерах :) Задача статьи — быстрый ввод новичка в тему, в проект. То, что предложил я — скачать папку одной командой git, поправить в ней один-единственный файл в одном-единственном месте и все. Дальше новичку доступна (почти) вся огромная гамма примеров в фреймворке edk2 (в крайнем случае — добавить/раскомментировать единственную строку в соответствующем dsc файле), просто щелкнув по проекту NT32 правой кнопкой, выбрав Add Files и затащив в проект мышкой все файлы из каталога проекта. Это на самом деле все, дальше можно добавлять свой код, брякпойнты и делать все те фокусы, к которым привык, скажем, человек, пишущий на C#. Если мы говорим про свой, новый проект, то inf-файлы и все остальное генерирутся автоматом, через Intel UEFI Driver Wizard — я покажу это в следующей статье, которая уже готова, надо только оформить.
      У Ионеску это, при всем к нему уважении — новый фреймворк. Он совместим, в принципе, с edk2, но только в одну сторону — в свою. Обратной дороги нет, и той, что предложена, придется учиться некоторое время. Примеров там — с гулькин нос. А человеку потом переучиваться на edk2, если он выберет тернистую дорогу UEFI и пойдет снова работать к какому-нибудь IBV (впрочем, я в IBV не работал, не знаю реально, как у них фреймворки отличаются от edk2)
      • +3

        Пожалуйста, рад что кому-то моя статья помогла.
        По поводу нового фреймворка — соглашусь, но новички перестанут быть новичками, и изучать сборочную систему EDK2 им все равно придется, неважно, с чего они при этом начинали, с NT32Pkg, c VisualUEFI, или с какой-нибудь IDE от IBV (я раньше работал с Visual eBIOS, которая сделана из Eclipse, сейчас работаю с Xcode). Визарды — это отлично, конечно, но их быстро перестанет хватать.
        Не могу сказать, что лучше начинать прямо с EDK2, как это сделано в моей статье, но точно могу сказать, что работать с EDK2 в любом случае придется, рано или поздно, так или иначе.

    • +1
      Объясните, пожалуйста. Буквально вчера начал изучать эту тему, ещё не разобрался, а тут такая статья удачная. Так вот: я хочу написать «загружаемое приложение». То есть я хочу писать код, который работает без ОС. Например, как Memtest86+, или как загрузочный носитель каждой уважающей себя утилиты резервного копирования и восстановления: записываем что-то на флэшку, в UEFI / BIOS выбираём её загрузочным устройством при запуске системы, грузимся туда, и оно как-то работает.
      В этой статье, вроде бы, написано, как расширять сам UEFI. UEFI — это современный аналог BIOS. То, что мне нужно, на уровень выше BIOS и только лишь пользуется его возможностями. Значит, описанное здесь — не то, что мне нужно?

      В идеале, я бы хотел найти фреймворк для написания таких приложений, что-то вроде микроОС, которая бы прятала всякие сложные для понимания вещи вроде работы с экраном, инициализации памяти, или организации многопоточности / синхронизации между потоками.
      • +1
        Я думаю, Вам стоит посмотреть вначале вот этот цикл статей, он гораздо ближе к Вашему запросу:
        habrahabr.ru/company/neobit/blog/173263
      • +1

        для таких историй вроде принято WinPE юзать? Или это слишком громоздкая "микроОС" для вашей утилиты?

        • +1
          Э-э-э… Не знаю, не пользовал. Наверно, можно и WinPE прикрутить, но зачем, если уже есть готовая OVMF?
          UEFI BIOS — это про то, что до загрузки ОС. Когда начинает загружаться ОС, UEFI BIOS уходит «в глубокое подполье», большинство ее функций становится недоступно, по принципу разделения ответственности между ОС и BIOS. Сама цель UEFI BIOS — минимально проинициализировать аппаратуру, процессор и чипсет, до той степени, чтобы максимально быстро стала возможной загрузка ОС. Т.е. проверить секьюрити, сделать доступными диски и консоль вывода (на предмет диагностики ошибок), и передать управление загрузчику. Все, что можно перенести из BIOS в ОС — переносится без разговоров. Все эти красивые окошечки с управлением от мышки в MSI BIOS Setup и прочих — конфигурационные, на скорость обычной загрузки они не влияют никак.
          Только статья не про утилиту, это учебник, с набором уже настроенных конфигов. Утилиту я для этого не писал, используется все готовое :)
          • 0
            Похоже, я ветку перепутал. Извиняюсь.
        • +1

          Это оно и есть.
          На носителе создаётся партишн EFI, форматируется в FAT (-12, -16 или -32 — зависит обычно от размера партишна). В нём в корне лежит папка EFI, а в ней — либо прямо, либо в подпапках — приложения с расширением .efi
          Как раз процесс создания одного из них описан в статье.
          Дальше уже дело настроек.
          Для сменных носителей (флешки) этот партишн может сканироваться и создаваться на лету меню с доступными опциями.
          Для постоянных — можно либо так же, либо прописать меню в ROM.
          На убунте это делается (из-под-рута) командой efibootmgr. Например, можно прямо в EFI положить ядро, и прописать в ROM (упс… теперь это называется NVRAM. По смыслу то же, но дьявол в деталях). Но обычно туда кладут GRUB. Или тот же memcheck.
          В общем — воспринимайте это как старый-добрый DOS. Который при этом умеет работать с разными носителями, но понимает только партишны EFI. И видит там только программы (.efi).


          Отдельный момент (а иногда и головняк) — если в NVRAM прописан конкретный плагин на конкретном диске — а диск вдруг поменяли. Иногда получается довольно весело.

          • 0
            Для решения задачи рекомендую Вам посмотреть фреймворк от QNX:
            Глава 3 в Manual QNX
            и Глава 13 Manual IDE
            Соответственно, вы запускаете не ОС, а вашу программу. Или см. в сторону FreeRTOS.
            • 0
              Можете ещё в сторону BareMetal OS посмотреть.
              • 0
                Вам для таких целей вполне подойдет стандартное ядро linux + собранный вами бинарник функционала «слинкованый» статически.
              • +1

                Ну, вроде полезно, спасибо, покопаюсь.


                ПС:
                Visual Studio 2008-2016 — есть разве такая? (2016)

                • 0
                  Точно, 2015. Спасибо.
                  Это я к тому, что 2017 в edk2 еще не поддерживается, точнее — не поддерживалась, когда я писал эту статью месяц назад
                  • 0
                    Поправил

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.