Собираем и устанавливаем свою Linux-систему на микроконтроллер STM32MP1
Примечание переводчика: эта статья уже выходила в блоге, но из-за мисклика вышла как оригинальная статья. Выпускаю ее снова, указав автора оригинала и ссылку на него
В этой статье мы автоматизируем процесс сборки и установки Linux-системы на микроконтроллер STM32MP157-DK2. ОС будет обладать минимальной функциональностью, но зато мы соберём из исходников собственную систему. А поможет нам в этом Buildroot — система сборки Linux-дистрибутивов.
Что такое Buildroot?
- Загрузчик (обычно для архитектуры ARM это U-Boot): выполняет инициализацию HW, загружает ядро Linux и стартует его.
- Собственно, само ядро, управляющее процессами и памятью, содержащее планировщик, файловые системы, сетевой стек и, конечно, все необходимые драйвера для вашей аппаратной платформы.
- Использовать готовый бинарный дистрибутив, например от Debian, Ubuntu или Fedora.
Некоторые из этих дистрибутивов поддерживают архитектуру ARMv7. Основное преимущество этого решения в том, что оно простое: эти бинарные дистрибутивы знакомы большинству пользователей Linux-систем, они имеют красивую и простую в использовании систему управления пакетами, все пакеты предварительно скомпилированы, поэтому нашей цели можно достичь очень быстро. Однако собранные таким образом системы обычно сложно кастомизировать (компоненты уже созданы, поэтому вы не можете легко изменить их конфигурацию в соответствии с вашими потребностями) и сложно оптимизировать (с точки зрения объёма занимаемой памяти или времени загрузки).
Buildroot — это набор make-файлов и скриптов, которые автоматизируют загрузку исходного кода различных компонентов, их извлечение, настройку, сборку и установку. В конечном итоге он генерирует образ системы, готовый к прошивке и обычно содержащий загрузчик, файл-образ ядра Linux и корневую файловую систему.
Важно отметить, что Buildroot не поставляется с исходными кодами Linux, с U-Boot или с другими компонентами. Он всего лишь содержит набор скриптов и инструкций, описывающих, какой исходный код загружать и какие настройки использовать при сборке.
Как собрать Linux-систему с Buildroot?
Начнём с установки самой системы Buildroot, а потом перейдём к её настройке:
git clone git://git.buildroot.net/buildroot cd buildroot
Обычно настройка Buildroot делается с помощью команды make menuconfig, которая позволяет указать необходимые опции для вашей системы. Но мы вместо этого используем свою конфигурацию, которую мы создали заранее — специально для STM32MP157-DK2. Она находится в моём репозитории.
git remote add tpetazzoni https://github.com/tpetazzoni/buildroot.git git fetch tpetazzoni git checkout -b stm32mp157-dk2 tpetazzoni/2019.02/stm32mp157-dk А теперь дадим Buildroot’у команду использовать нашу конфигурацию. make stm32mp157_dk_defconfig
Мы могли бы сразу приступить к сборке, так как эта конфигурация работает нормально. Но здесь я хочу показать, как можно изменить конфигурацию и ускорить сборку. Мы изменим всего один параметр. Для этого запустим утилиту menuconfig (она встроена в Buildroot). Если кто-то из вас уже настраивал ядро Linux, этот инструмент должен быть вам интуитивно понятен, поскольку это просто утилита для настройки.
Если команда не сможет работать из-за отсутствия библиотеки ncurses, установите пакет libncurses-dev или ncurses-devel (точное название пакета будет зависеть от версии Linux ОС, на которой вы запускаете Buildroot). Библиотека ncurses предназначена для управления вводом-выводом на терминал.
Успешно выполнив menuconfig, перейдём в подменю Toolchain. По умолчанию в Toolchain Type выбрана опция . Нужно изменить её на , нажав ENTER.
Дело в том, что по умолчанию Buildroot использует собственный кросс-компилятор. Выбрав опцию , мы можем использовать более шустрый кросс-компилятор, специально заточенный под архитектуру ARMv7.
Выходим из menuconfig и сохраняем изменения. Теперь пришло время поработать с командой make. Я люблю всё логировать, поэтому она будет выглядеть вот так:
На этом этапе система сборки Buildroot проверит наличие всех необходимых пакетов. Если чего-то не обнаружит, то выполнение команды прервётся. Если это произойдёт, на сайте Buildroot посмотрите раздел System requirements > Mandatory packages и установите все необходимые зависимости. После этого можно запускать команду заново.
На моей машине команда make работала 10 минут. После сборки появится набор каталогов и файлов (самое интересное лежит в output/images):
- output/images/zImage: здесь лежит ядро Linux;
Прошивка и тестирование системы
Запишем sdcard.img на карту microSD:
sudo dd if=output/images/sdcard.img of=/dev/mmcblk0 bs=1M conv=fdatasync status=progress
Не забудьте проверить, что в вашей системе карта microSD определяется как /dev/mmcblk0 (и на всякий случай предупреждаю: после того, вы запишете туда образ, вся информация на этой карточке будет затёрта)!
Подключите карту к микроконтроллеру.
Соедините USB-кабелем ваш компьютер и micro-USB разъём с надписью ST-LINK CN11 на плате. Ваша машина должна распознать устройство с именем /dev/ttyACM0, через которое вы сможете получить доступ к последовательному порту платы. Установите на свой компьютер и запустите программу для общения с последовательным портом. Лично мне очень нравится picocom:
picocom -b 115200 /dev/ttyACM0
Он подходит для embedded-систем, так как занимает минимальный объём памяти (менее 20 КБ) и имеет подробную документацию.
Наконец, включите плату, воткнув кабель USB-C в разъём PWR_IN CN6. Затем на последовательный порт начнут приходить сообщения. Нам важно, что в конце появится приглашение залогиниться в системе Buildroot. Можно войти в систему с пользователем root, пароль вводить не нужно.
Этапы загрузки системы и вход
Рассмотрим основные этапы процесса загрузки, изучая сообщения, которые приходят на последовательный порт:
U-Boot SPL 2018.11-stm32mp-r2.1 (Apr 24 2019 — 10:37:17 +0200)
Это сообщение от загрузчика первой стадии: код, содержащимся в файле u-boot-spl.stm32 скомпилирован как часть загрузчика U-Boot. Его непосредственно загружает STM32MP157. Загрузчик первой стадии должен быть достаточно маленьким, чтобы поместиться во внутреннюю память STM32MP157.
U-Boot 2018.11-stm32mp-r2.1 (Apr 24 2019 — 10:37:17 +0200)
Это сообщение от загрузчика второй стадии, который был выгружен из внутренней памяти устройства во внешнюю память загрузчиком первой стадии. Загрузчик второй стадии — это файл u-boot.img, который также является частью загрузчика U-Boot.
Retrieving file: /boot/zImage Retrieving file: /boot/stm32mp157c-dk2.dtb
Эти сообщения печатает загрузчик второй стадии: мы видим, что он загрузил образ ядра Linux (файл zImage) и блоб дерева устройств (файл stm32mp157c-dk2.dtb), описывающий нашу аппаратную платформу. Хорошо, U-Boot загрузил оба файла в память: теперь он готов к запуску ядра Linux.
Starting kernel . Это последнее сообщение U-Boot, после этого управление передаётся ядру. [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 4.19.26 (thomas@windsurf) (gcc version 8.2.1 20180802 (GNU Toolchain for the A-profile Architecture 8.2-2018.11 (arm-rel-8.26))) #1 SMP PREEMPT Wed Apr 24 10:38:00 CEST 2019
И сразу появляются первые сообщения ядра Linux, показывающие версию Linux и дату/время сборки. Далее идут другие, не слишком интересные сообщения… Нам нужно дождаться вот этого:
[ 3.248315] VFS: Mounted root (ext4 filesystem) readonly on device 179:4.
Это сообщение указывает на то, что ядро смонтировало корневую файловую систему. После этого ядро запустит первый пользовательский процесс. Поэтому следующие сообщения будут связаны с инициализацией пользовательских служб:
Starting syslogd: OK [. ] Welcome to Buildroot buildroot login:
И вот, наконец, появляется то самое сообщение от Buildroot с просьбой залогиниться.
После входа в систему вы получите доступ к командной оболочке Linux. Введя команду ps, можно посмотреть список процессов, команда ls/ покажет содержимое корневой файловой системы и так далее.
Также можно немного поиграться с платой — например, включить и выключить один из светодиодов:
echo 255 > /sys/class/leds/heartbeat/brightness echo 0 > /sys/class/leds/heartbeat/brightness
Как сделать это «с нуля»?
Углубляемся в основы конфигурирования Buildroot
В начале статьи мы говорили про настройку и оптимизацию конфигурации Buildroot. По идее, для этого нужно сначала изучить основы конфигурирования в этой системе. Поэтому вернёмся в прошлое к команде make menuconfig.
В меню Target options выбрана архитектура ARM Little Endian, а в Target Architecture Variant указан Cortex-A7. На этом процессоре как раз построен наш микроконтроллер.
В меню Build options используем все значения по умолчанию.
Как я писал выше, в меню Toolchain вместо кросс-компилятора по умолчанию был выбран пункт External toolchain>.
В меню System configuration мы произвели следующие изменения:
- Оверлей-каталоги корневой файловой системы определены как board/stmicroelectronics/stm32mp157-dk/overlay/. Эта опция сообщает Buildroot, что содержимое этого каталога должно быть скопировано в корневую файловую систему в конце сборки. Такой подход позволяет добавлять собственные файлы в корневую файловую систему.
- Мы загрузили исходники ядра Linux с Github с помощью макроса Buildroot под названием github. В соответствии с выбранной версией (v4.19-stm32mp-r1.2), Buildroot пошёл в репозиторий (https://github.com/STMicroelectronics/linux/) и загрузил оттуда ядро.
В меню Filesystem images активировали ext2/3/4root filesystem и выбрали ext4. Эта файловая система отлично подходит для SD-карт.
Теперь в меню Bootloaders активируем U-Boot, для которого выполняем следующий набор действий:
- Загружаем U-Boot из репозитория https://github.com/STMicroelectronics/u-boot.git с Git-тегом v2018.11-stm32mp-r2.1
Углубляемся в процесс сборки Buildroot
Надеюсь, после изучения основ конфигурирования стало понятнее, как происходит сборка (для простоты я опустил несколько промежуточных шагов):
- Загрузка и установка компилятора с веб-сайта ARM и установка библиотек C и C ++ на нашу корневую файловую систему.
image sdcard.img < hdimage < gpt = «true» >partition fsbl1 < image = «u-boot-spl.stm32» >partition fsbl2 < image = «u-boot-spl.stm32» >partition uboot < image = «u-boot.img» >partition rootfs < image = «rootfs.ext4» partition-type = 0x83 bootable = «yes» size = 256M >>
Какие конкретно указания даны в этом скрипте:
Файл extlinux.conf находится внутри оверлей-каталога нашей файловой системы (board/stmicroelectronics/stm32mp157-dk/overlay/boot/extlinux/extlinux.conf), в корневой файловой системе он будет определяться как /boot/extlinux/extlinux.conf и U-Boot легко найдёт его.
Вот что внутри этого файла:
label stm32mp15-buildroot kernel /boot/zImage devicetree /boot/stm32mp157c-dk2.dtb append root=/dev/mmcblk0p4 rootwait
Таким образом мы говорим U-Boot, чтобы он загружал образ ядра из /boot/zImage, дерево устройств — из /boot/stm32mp157c-dk2.dtb. А строка root=/dev/mmcblk0p4 rootwait должна быть передана ядру Linux во время загрузки. Именно в этом выражении (root=/dev/mmcblk0p4) хранится информация о том, где находится корневая файловая система.
Итак, сформулируем этапы загрузки собранной Linux-системы на нашей аппаратной платформе — с учётом новых подробностей:
- Встроенный в STM32MP157 ROM ищет разделы GPT, чьи имена начинаются с fsbl. Если успешно, то загружает их содержимое во внутреннюю память STM32 и запускает загрузчик первой стадии.
Облачные серверы от Маклауд быстрые и безопасные.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!