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

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

Пришло время перейти от теории к практике!

Оглавление

3. Знакомство с Buildroot

3.1. Подготовка

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

3.1.1. Необходимое оборудование

Список оборудования довольно скромный:

  • Одноплатный компьютер с SoC на борту

  • Карта MicroSD

  • Переходник USB-MicroSD

  • Переходник USB-UART

  • Провода для соединения переходника и одноплатника

Поскольку мы только знакомимся с технологией, лучше всего взять плату с поддержкой Buildroot «из коробки». В моём случае это OrangePi PC от компании Sunxi на базе SoC Allwinner H3 архитектуры ARM.

3.1.2. Рабочее окружение

Собирать EL можно почти где угодно, но я рекомендую использовать ПК с ОС Linux, где рабочее окружение Buildroot будет развёрнуто на файловой системе ext4. В моём случае это Ubuntu 22.04 LTS.

Чтобы не засорять основную систему, сборку Embedded Linux мы будем выполнять в Docker-контейнере.

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

В процессе работы мы будем «общаться» с нашим одноплатником по UART, поэтому ставим на свою ОС какую-нибудь подходящую утилиту. В моём случае, это minicom:

sudo apt install -y minicom;    \
sudo usermod -aG dialout $USER; \
newgrp dialout

Также желательно сразу всё оформить в виде репозитория, поэтому нужно установить git:

sudo apt install -y git

Теперь создадим рабочую директорию, необходимые поддиректории, обернем всё это в git-репозиторий и скачаем Buildroot:

mkdir -p "$HOME"/buildroot-builder/{docker,output};                               \
cd "$HOME"/buildroot-builder;                                                     \
git init -b master;                                                               \
git submodule add --depth=1 https://github.com/buildroot/buildroot.git buildroot; \
git -C buildroot fetch --depth=1 origin tag 2025.05;                              \
git -C buildroot checkout 2025.05;                                                \
cat > .gitignore << 'EOF'
/output
/buildroot/*
*.old
EOF
git add buildroot docker .gitignore .gitmodules

Последний релиз Buildroot на июль 2025 года

В целях последующего ознакомления со структурой компонентов и их настройки, скачаем ещё и U-Boot с Linux:

git clone --depth=1 -b v2025.07 https://github.com/u-boot/u-boot.git  u-boot; \
git clone --depth=1 -b v6.15    https://github.com/torvalds/linux.git linux

Используемые версии — последние стабильные релизы Linux и U-Boot на июль 2025 года

Создадим Docker-образ. В моём случае основой для него служит образ Debian 11:

cat > docker/Dockerfile << 'EOF'
FROM debian:11

ARG UID=1000
ARG GID=1000
ARG USERNAME=builder

RUN apt update &&                                              \
    apt install -y sudo git &&                                 \
    groupadd -g ${GID} ${USERNAME} &&                          \
    useradd -m -u ${UID} -g ${GID} -s /bin/bash ${USERNAME} && \
    echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

USER ${USERNAME}

WORKDIR /host
EOF
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) --tag buildroot-builder-image -f docker/Dockerfile docker

Запускаем контейнер:

docker run --name buildroot-builder -v /home/$USER/buildroot-builder:/host -it buildroot-builder-image /bin/bash
Для любителей графики

Если хочется пробросить в контейнер графику (например, для make gconfig или make xconfig):

echo "xhost +SI:localuser:$USER" >> ~/.xprofile; \
source ~/.xprofile;                              \
docker run --name buildroot-builder -v /home/$USER/buildroot-builder:/host -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -it buildroot-builder-image /bin/bash

На Host-системе должен быть запущен X-сервер!

Сноска для незнакомых с Docker:

После вызова docker run вы попадаете в консоль контейнера.

Чтобы выйти в Host-систему, используйте exit.

Чтобы снова зайти в контейнер, используйте docker start -i buildroot-builder

Установим в контейнер несколько базовых утилит:

sudo apt update && sudo apt install -y mc nano
  • mc — для удобной навигации по директориям

  • nano — для изменения текстовых файлов (да простят меня любители Vim)

Несмотря на обширный список зависимостей U-Boot, Linux и Buildroot, вручную нужно устанавливать только зависимости для последнего — остальное он загрузит, соберёт и установит автоматически.

Поскольку сначала мы будем практиковаться с настройкой U-Boot и Linux отдельно от Buildroot, часть их зависимостей придётся установить вручную:

sudo apt install -y flex bison

Установка минимального набора пакетов:

sudo apt install -y build-essential libncurses-dev debianutils pkg-config \
diffutils findutils binutils patch bzip2 unzip rsync make bash            \
gzip perl cpio file gawk wget sed gcc g++ tar bc

Установка опциональных пакетов:

sudo apt install -y openssh-client default-jdk python3-pip subversion \
mercurial graphviz python3 dblatex curl cvs git w3m;                  \
pip install matplotlib asciidoc argparse aiohttp bazaar

Для тех, кто пробросил в контейнер графику и планирует использовать make gconfig или make xconfig, нужно доставить следующие пакеты:

sudo apt install -y qtbase5-dev-tools libqt5widgets5 libglib2.0-dev \
libgtk2.0-dev libglade2-dev qtbase5-dev libqt5gui5 qt5-qmake

Также зададим переменную окружения BR2_EXTERNAL, чтобы Buildroot знал об использовании внешнего слоя. О данном механизме модификации Buildroot поговорим чуть позже, а пока просто создадим минимальную структуру внешнего слоя в директории external:

echo "export BR2_EXTERNAL=/host/external" >> ~/.bashrc;  \
source ~/.bashrc;                                        \
mkdir -p "$BR2_EXTERNAL"/{board/test,configs};           \
cd "$BR2_EXTERNAL";                                      \
touch external.mk;                                       \
cat > external.desc << 'EOF' 
name: TEST_EXTERNAL_LAYER
desc: Test external layer for Buildroot practice
EOF
cat > Config.in << 'EOF' 
menu "Test external layer options"
endmenu
EOF

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

3.2. Структура компонентов

Если вы уже заглянули в содержимое u-boot, linux или buildroot, то наверняка удивились обилию директорий и файлов в них. Не переживайте, необходимости досконально разбирать структуры компонентов нет (по крайней мере, на текущем этапе), но узнать про ключевые директории и файлы необходимо.

3.2.1. U-Boot

  • arch — «архитектурозависимый» код, расположенный в соответствующей поддиректории (arm, x86 и т.д.). Внутри:

    • dts — файлы описания оборудования Device Tree (*.dts, *.dtsi)

  • board — «платозависимый» код. Директории названы по вендору (sunxi, nvidia и т.п.). Внутри — специфический для конкретных плат код начальной инициализации, настройки питания и прочего

  • configs — типовые конфигурации U-Boot (*_defconfig)

  • doc — официальная документация. Онлайн-версия

  • drivers — драйверы различных периферийных устройств (UART, SPI и т.д.)

3.2.2. Linux

  • Documentation — официальная документация. Онлайн-версия

  • arch — «архитектурозависимый» код, расположенный в соответствующей поддиректории (arm, x86 и т.д.). Внутри:

    • boot/dts — файлы описания оборудования Device Tree (*.dts, *.dtsi)

    • configs — типовые конфигурации Linux (*_defconfig)

  • drivers — драйверы различных устройств (сеть, шины, графика, звук и т.д.)

3.2.3. Buildroot

  • board — скрипты, патчи и настройки под конкретные платы

  • configs — типовые конфигурации Buildroot (*_defconfig)

  • docs — официальная документация. Онлайн-версия

  • package — пакеты, доступные для сборки через Buildroot

3.2.4. External Layer

Как вы понимаете, любые изменения, внесённые непосредственно в u-boot, linux или buildroot, просто затеряются в обилии файлов и директорий. Да и тащить в свой репозиторий всё содержимое исходников — не самое удачное решение.

Для решения этой проблемы в Buildroot есть решение — External Tree, или же Внешний слой.

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

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

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

Каждый из компонентов, будь то Buildroot, Linux или U-Boot, может быть настроен посредством переменных make. Эти переменные хранятся в файлах, которые можно разделить на несколько видов:

  • Настройки по умолчанию — распределены по различным файлам компонента

  • Текущие настройки — хранятся в файле .config

  • Файлы *_defconfig — настройки, отличные от настроек по умолчанию

Для создания последних двух существует набор правил make:

  • make board_defconfig — создание .config файла из указанного *_defconfig

  • make savedefconfig — создание *_defconfig файла из .config

Если вы не знакомы с make, не переживайте. Подробно мы разберём его устройство позже. Пока просто учтите: вызывать make нужно из корня соответствующего компонента.

Для изменения .config файла компонента существуют различные интерактивные меню:

  • make menuconfig — текстовый интерфейс на базе curses.

  • make nconfig — текстовый интерфейс на базе ncurses.

  • make gconfig — графический интерфейс на базе GTK+.

  • make xconfig — графический интерфейс на базе Qt.

Я буду использовать menuconfig как самый универсальный и лёгкий в использовании вариант.

Давайте попробуем создать .config файл компонента и поменять его конфигурацию при помощи menuconfig.

3.3.1. Знакомство с menuconfig

Предлагаю начать с u-boot. Как я упоминал ранее, моя плата от компании Sunxi называется OrangePi PC с SoC архитектуры ARM на борту.

Переходим в u-boot и создаём .config файл:

cd /host/u-boot; \
make orangepi_pc_defconfig

Теперь можно вызвать меню конфигурации:

make menuconfig

Перед нами открылся интерфейс с различными группами настроек. Управление довольно простое:

  • Стрелки вверх и вниз — перемещение между пунктами.

  • Стрелки влево и вправо или Tab — переключение между меню и кнопками снизу (Select, Exit, Help и т.д.).

  • Enter — выбор пункта или подтверждение действия.

  • Двойное нажатие Esc — возврат на уровень выше или выход из меню.

Внесения изменений в конфигурацию не будут зафиксированы, пока вы не нажмёте кнопку Save и не укажете файл для записи настроек.

Это меню одинаково для всех компонентов, поэтому можете поэкспериментировать: полистайте меню, измените несколько настроек, загляните в Help как в главном меню, так и в каком-либо из подменю, попробуйте воспользоваться функцией поиска, сохраните результат в /host/u-boot/.config, откройте меню ещё раз и убедитесь, что настройки действительно изменились.

Закончили? Тогда вот небольшая задачка для закрепления материала: включите в состав Linux поддержку сетевого USB драйвера Realtek RTL8152/RTL8153 как built-in. У Linux есть особенность: при вызове make нужно указать архитектуру:

cd /host/linux; \
make ARCH=arm sunxi_defconfig; \
make ARCH=arm menuconfig

Задача выполнена? В таком случае можно смело удалять директории u-boot и linux — они больше не понадобятся:

cd /host; \
rm -rf linux u-boot

Вслед за ними удаляем их зависимости:

sudo apt remove -y flex bison && sudo apt autoremove -y

А как в дальнейшем настраивать Linux и U-Boot? Через Buildroot, конечно же.

3.3.2. Настройка через Buildroot

Переходим в buildroot и создаём копию нужного нам *_defconfig файла во внешнем слое, после чего создаём .config файл:

cd /host/buildroot;                                                  \
cp configs/orangepi_pc_defconfig ../external/configs/test_defconfig; \
make O=../output test_defconfig

Параметр O= задаёт выходную директорию сборки Buildroot

Теперь нам доступны правила make для настройки компонентов:

  • make linux-menuconfig — вызов меню конфигурации Linux

  • make uboot-menuconfig — вызов меню конфигурации U-Boot

Убедитесь, что соответствующий компонент включён в конфигурацию Buildroot. Иначе вызов команды завершится с ошибкой.

При первом вызове этих правил придётся дождаться загрузки компонента и настройки его рабочего окружения.

Перед сборкой предлагаю изменить по одному параметру в каждом компоненте:

  • u-boot

    make O=../output uboot-menuconfig
    • Boot options

      • Autoboot options

        • (5) delay in seconds before automatically booting

        • [*] Stop autobooting via specific input key / string

        • (Bye U-Boot in %d...\n) Autoboot stop prompt

        • [*] Enable Ctrl-C autoboot interruption

    Сохраните конфигурацию в файл /host/external/board/test/u-boot.config

  • linux

    make O=../output linux-menuconfig
    • General setup

      • (HelloLinux) Local version - append to kernel release

    Сохраните конфигурацию в файл /host/external/board/test/linux.config

  • buildroot

    make O=../output menuconfig
    • System configuration

      • (Hello Buildroot!) System banner

    • Kernel

      • Kernel configuration (Using a custom (def)config file)

      • ($(BR2_EXTERNAL)/board/test/linux.config) Configuration file path

    • Bootloaders

      • U-Boot

        • U-Boot configuration (Using a custom (def)config file)

        • ($(BR2_EXTERNAL)/board/test/u-boot.config) Configuration file path

    Сохраните конфигурацию в файл /host/output/.config

Компоненты настроены — теперь можно обновить *_defconfig файл Buildroot во внешнем слое:

make O=../output savedefconfig

Настройка завершена. Переходим к следующему этапу — сборке.

4. Первый шаг

4.1. От make до образа

Думаю на примере make uboot-menuconfig и make linux-menuconfig вы уже успели оценить подход Buildroot: всё необходимое было скачано, настроено, собрано и установлено автоматически.

Для сборки всех компонентов достаточно одной команды — и немного терпения. Этой командой является уже знакомый нам make.

4.1.1. Сборка системы

Итак, запустим сборку системы:

cd /host/buildroot; \
make O=../output

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

Конечно, не всегда всё проходит гладко. Иногда могут возникнуть ошибки сборки из-за нестабильной версии какого-либо пакета, проблем с конфигурацией или неожиданного поведения сторонних утилит. У Buildroot есть несколько инструментов для устранения подобных проблем — мы рассмотрим их позже.

Что касается времени сборки, оно зависит от множества факторов: скорости интернет-соединения, объёма оперативной памяти, частоты работы процессора, скорости диска и количества ядер. Особенно долго сборка проходит в первый раз — Buildroot скачивает и компилирует все пакеты с нуля.

Сборка успешно завершена? В таком случае, предлагаю сделать первый коммит в наш репозиторий:

cd "$HOME"/buildroot-builder; \
git add external;             \
git commit -m "First step";   \
git tag FirstStep

А теперь давайте взглянем на содержимое выходной директории.

4.1.2. Структура выходной директории

В ней можно найти несколько ключевых директорий:

  • build — скачанные исходники и промежуточные файлы сборки всех пакетов

  • host — изолированная среда для программ, необходимых Host-системе во время сборки. В ней расположены тулчейн, утилиты, библиотеки и заголовочные файлы

  • target — корневая файловая система Target-системы. Всё, что находится в этой директории, будет доступно на целевом устройстве

  • images — директория с артефактами сборки

Именно images нас интересует больше всего.

4.1.3. Артефакты сборки

Список артефактов зависит от конфигурации компонентов, но в случае OrangePi PC с базовыми настройками он выглядит так:

  • genimage.cfg — конфигурационный файл для сборки sdcard.img

  • rootfs.* — корневая файловая система

  • sdcard.img — образ для записи на SD-карту

  • sun8i-h3-orangepi-pc.dtb — скомпилированный Device Tree

  • u-boot-sunxi-with-spl.bin — исполняемый файл U-Boot с SPL

  • u-boot.bin — исполняемый файл U-Boot

  • zImage — сжатое ядро Linux

Подробнее о том, какой файл за что отвечает, поговорим потом. Пока что нас интересует только sdcard.img.

4.2. Подготовка к запуску

До запуска осталось всего 2 шага: запись образа на SD-карту и подключение платы к ПК.

4.2.1. Запись на SD-карту

Обычно команда для записи образа на SD-карту находится в readme.txt файле по пути buildroot/board/<плата>.

В случае OrangePi PC это файл buildroot/board/orangepi/orangepi-pc/readme.txt. Команда в нем выглядит так:

sudo dd if=output/images/sdcard.img of=/dev/sdX

Давайте подключим SD-карту к нашему ПК при помощи переходника MicroSD-USB и откроем в основной ОС еще один терминал. Вызовите lsblk и найдите в списке свою SD-карту. В моем случае, это /dev/sdb.

Убедитесь, что найденный диск именно SD-карта! В противном случае вы можете повредить данные на другом диске!

Получается следующая команда:

sudo umount /dev/sdb*;                                                                            \
sudo dd  of=/dev/sdb if="$HOME"/buildroot-builder/output/images/sdcard.img bs=1M status=progress; \
sudo sync

bs — количество данных, записываемых единовременно.

status=progess — вывод процесса записи

Можно отключать SD-карту от ПК и подключать её к плате.

4.2.2. Подключение платы к ПК

Подключиться к первому попавшемуся UART на плате не получится — нужно будет найти правильный. Обычно, на платах он выведен отдельной группой пинов: RX, TX и GND. На OrangePi PC они находятся между HDMI и разъемом питания.

Обычно, UART работает на скорости 115200 бод, 8 бит данных, 1 стоп-бит, без протокола контроля четности и без управления потоком данных.

Все параметры эти параметры UART и его пины можно найти в файлах *.dts и *.dtsi. Device Tree — сложная тема, поэтому детальнее мы разберем её позднее.

Соединяем плату с UART-USB переходником: RX платы с TX переходника и наоборот. GND соединяем 1 к 1.

Выполните на ПК команду:

watch -n 1 -t ls /dev/ttyUSB* /dev/ttyACM*

Подключите переходник к ПК. Запущенная команда выведет путь до переходника. В моем случае, это /dev/ttyUSB0.

Для завершения работы команды watch нажмите Ctrl+C

Ранее мы устанавливали minicom. Вызываем его, передав путь до найденного устройства и скорость UART:

minicom -D /dev/ttyUSB0 -b 115200

Теперь нам надо настроить наш порт:

  • Нажмите Ctrl+A, O

  • Перейдите во вкладку Serial Port Setup

  • Настройте порт при помощи нажатия на клавиатуре соответствующей буквы. Настройки должны быть следующими:

    • E - Bps/Par/Bits: 115200 8N1

    • F - Hardware Flow Control : No

    • G - Software Flow Control: No

  • Сохраните настройки как конфигурацию по умолчанию, выбрав Save setup as dlf

  • Закройте меню конфигурации, выбрав Exit

Всё готово для запуска платы.

4.3. Первый запуск

Подайте на плату питание. Если всё настроено и подключено правильно, вы увидите в терминале:

  1. Процесс загрузки U-Boot

  2. Сообщение Bye U-Boot in 5...

  3. Процесс загрузки ядра

  4. Появление нашего приветственного баннера системы Hello Buildroot!

Предлагаю войти в систему. Пользователь — root, пароль не требуется.

Осталось проверить последнюю настройку. Давайте выведем версию ядра:

uname -r

Рядом с версией ядра вы увидите HelloLinux — это означает, что все наши настройки применились корректно.

Для выхода из minicom нажмите Ctrl+A, X.

Поздравляю: вы настроили, собрали и запустили собственную Embedded Linux-систему!

Первый шаг на этом нелёгком пути сделан. Дальше — больше.

Итог

Итак, мы подготовили рабочее окружение при помощи Docker, разобрали структуру U-Boot, Linux, Buildroot и его внешнего слоя, настроили, собрали и запустили Embedded Linux на своей плате.

Прежде чем вы начнёте играть со своей новой «игрушкой», скажу пару слов о следующей главе.

Если коротко — мы пойдём вглубь. Нас ждёт подробный разбор основ, на которых строится Buildroot: bash, make и KConfig. Что это такое, как это работает, зачем нужно и какие возможности это открывает.

Теории будет много — но без неё в Embedded никуда.

А пока — оставляю вас наедине с вашей платой. Поиграйте с ней: изучите структуру файловой системы, попробуйте разные команды, загляните в системные директории — например, в /proc.

Спасибо за уделённое время. Ещё увидимся!

Комментарии (0)