
Привет, Хабр. Меня зовут Роман, я разработчик встраиваемых систем в Dannie и мы тут делаем умные камеры. По долгу службы, мне потребовалось завести эмуляцию прошивки для чипа из семейства MIPS. В рамках разработки проекта мы обозначили для себя задачу получения быстрой обратной связи при разработке ПО и прошивки. Для этого начали выстраивать CI/CD-цепочку с проверкой прошивки в эмуляторе. Одной из требуемых функций являлась возможность манипулировать окружением загрузчика (u-boot environment). В статье я расскажу что получилось и как из говна и палок завести авто-тесты прошивки в CI.
Постановка задачи
Предмет эмуляции
Не столь критичная информация для топика в целом, но стоит упомянуть “что же такое эмулируется?”. А эмулируется прошивка для чипа Ingenic T40. Это SoC, базирующийся на MIPS архитектуре и имеющий NPU на борту.
Итак имеем:
-
Эмулируемая платформа MIPS 32R2 Little Endian
-
Docker-контейнер, с ubuntu 20.04 x86_64 внутри
Компоненты
Для эмуляции был выбран QEMU, в своем составе он имеет группу утилит для развертывания виртуальной машины qemu-system-mips(el,64,64el).
Эмуляторам этой группы для запуска необходимо указать либо BIOS (-bios), либо ядро (-kernel).
$ qemu-system-mipsel Unable to init server: Could not connect: Connection refused qemu-system-mipsel: Could not load MIPS bios 'mipsel_bios.bin', and no -kernel argument was specified
Как говорилось вначале, нам нужна возможность манипуляции окружением u-boot. По-этому первым компонентом будет загрузчик u-boot.
Вторым компонентом, очевидно, выступает сама прошивка в которую входят ядро Linux и файловая система (rootfs) с тестируемым ПО.
Прошивка
Тестовая прошивка выполнена в виде образа памяти, со следующей схемой разделов.

Она немного отличается от той, что устанавливается в конечное устройство, но для данной статьи и общей проверки ПО этого будет достаточно.
boot — раздел, содержащий ядро Linux c заголовком для чтения u-boot (uImage)
rootfs — раздел с утилитами, драйверами и тестируемым ПО. Здесь стоит помнить о том, что некоторые драйвера не могут функционировать в среде эмулятора, так как “по-честному” эмулируется не целевая плата, а одна из “коробочного” состава QEMU. Для упрощения я использую плату Malta. В идеальном случае нужно добавлять периферию платы самостоятельно, но

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

Запуск эмулятора
Подготовка
Для упрощения процесса настройки сборочного окружения и подготовки финальных артефактов, я воспользовался системой сборки buildroot версии 2021.11.1.
Из постановки задачи следует, что нужно сформировать следующие компоненты для тестирования платформы в QEMU:
-
Загрузчик u-boot
-
Ядро Linux
-
Файловую систему
u-boot
Все дальнейшие действия я выполняю с u-boot версии 2021.7 (однако, справедливы и для многих более ранних версий).
В проекте u-boot из коробки поддерживается платформа maltael, ее конфигурацию можно использовать для сборки загрузчика, но для моих целей её потребовалось немного модифицировать. Ниже основные изменения (полная конфигурация):
CONFIG_USE_BOOTARGS=y CONFIG_BOOTARGS="console=ttyS0,38400n8r loglevel=8 mem=128M@0x0 rmem=128M@0x8000000 mtdparts=physmap-flash.0:128k@3968k(u-boot-env) init=/sbin/init root=/dev/hda2 rootfstype=ext4 rw rootwait" CONFIG_USE_BOOTCOMMAND=y CONFIG_BOOTCOMMAND="saveenv; fatload ide 0 0x81000000 uImage; printenv bootargs; bootm 0x81000000" CONFIG_CMD_FAT=y # Так же стоит запомнить следующие опции, они влияют на остальную систему CONFIG_ENV_SIZE=0x20000 CONFIG_ENV_SECT_SIZE=0x20000 CONFIG_ENV_IS_IN_FLASH=y CONFIG_ENV_ADDR=0xBE3E0000 CONFIG_MTD_NOR_FLASH=y
И здесь я бы хотел оставить пару слов о команде загрузки “по-умолчанию” CONFIG_BOOTCOMMAND. Вызов saveenv происходит лишь с той целью, чтобы проинициализировать память с окружением при первом запуске (а для эмулятора, каждый запуск — первый).
Ядро Linux
В виду того, что ядро для платы не содержит модулей для запуска в режиме гостя и может содержать проприетарные вещи, я собираю “ванильное” ядро, но той же версии что и в камере 4.4.94. В общем же случае можно воспользоваться и последними релизами.
По аналогии с u-boot, вместо “коробочной” конфигурации для maltael, я использовал свою (полная конфигурация), ниже основные моменты:
CONFIG_MTD=y CONFIG_MTD_BLOCK=y CONFIG_MTD_BLOCK2MTD=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_ADV_OPTIONS=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_STAA=y CONFIG_MTD_PHYSMAP=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_GLUEBI=y # Здесь стоит выделить следующую опцию, без нее будет сложно предоставить возможность записи в MTD CONFIG_MTD_CMDLINE_PARTS=y
Помните параметры запуска ядра по-умолчанию из конфигурации u-boot? Здесь и раскрывается настройка u-boot environment. Cреди параметров нас интересует настройка mtdparts:
mtdparts=physmap-flash.0:128k@3968k(u-boot-env)
Здесь мы говорим ядру, что у нас есть FLASH-память physmap-flash.0 и в ней нас интересует раздел размером 0x20000 (128Kb), со смещением 0x3e0000 (3968Kb). Эти значения можно взять из конфигурации платформы, здесь же можно и отредактировать состав mtd-разделов и их размеры, например так.
Файловая система
Для работы с u-boot environment существует открытый проект libubootenv. Он предоставляет утилиты fw_printenv и fw_setenv. Чтобы эти утилиты смогли работать с нашим окружением, необходимо указать тип окружения и параметры в файле fw_env.config:
/dev/mtd0 0x0000 0x20000 0x20000
Проверка работы
После запуска эмулятора, проверить работоспособность u-boot environment можно следующим образом:
# Отобразим текущее состояние u-boot env u-boot $ fw_printenv baudrate=115200 bootargs=console=ttyS0,38400n8r loglevel=8 mem=128M@0x0 rmem=128M@0x8000000 mtdparts=physmap-flash.0:128k@3968k(u-boot-env) init=/sbin/init root=/dev/hda2 rootfstype=ext4 rw rootwait bootcmd=saveenv; fatload ide 0 0x81000000 uImage; printenv bootargs; bootm 0x81000000 bootdelay=1 fdtcontroladdr=8ff7fcc0 stderr=serial@3f8 stdin=serial@3f8 stdout=serial@3f8 В качестве примера, увеличиваем время перед выполнение команды автозагрузки linux $ fw_setenv bootdelay 10 linux $ fw_printenv baudrate=115200 bootargs=console=ttyS0,38400n8r loglevel=8 mem=128M@0x0 rmem=128M@0x8000000 mtdparts=physmap-flash.0:128k@3968k(u-boot-env) init=/sbin/init root=/dev/hda2 rootfstype=ext4 rw rootwait bootcmd=saveenv; fatload ide 0 0x81000000 uImage; printenv bootargs; bootm 0x81000000 fdtcontroladdr=8ff7fcc0 stderr=serial@3f8 stdin=serial@3f8 stdout=serial@3f8 bootdelay=10 linux $ reboot U-Boot 2021.07 (Feb 09 2022 - 13:07:51 +0000) Board: MIPS Malta CoreLV DRAM: 256 MiB Flash: 4 MiB Loading Environment from Flash... OK In: serial@3f8 Out: serial@3f8 Err: serial@3f8 Net: No ethernet found. IDE: Bus 0: OK Device 0: Model: QEMU HARDDISK Firm: 2.5+ Ser#: QM00001 Type: Hard Disk Capacity: 2256.0 MB = 2.2 GB (4620289 x 512) Device 1: not available Hit any key to stop autoboot: 10 u-boot $ printenv baudrate=115200 bootargs=console=ttyS0,38400n8r loglevel=8 mem=128M@0x0 rmem=128M@0x8000000 mtdparts=physmap-flash.0:128k@3968k(u-boot-env) init=/sbin/init root=/dev/hda2 rootfstype=ext4 rw rootwait bootcmd=saveenv; fatload ide 0 0x81000000 uImage; printenv bootargs; bootm 0x81000000 bootdelay=10 fdtcontroladdr=8ff7fcc0 stderr=serial@3f8 stdin=serial@3f8 stdout=serial@3f8 Environment size: 376/131068 bytes
Здесь я изменил значение задержки bootdelay до 10 секунд и отобразил значение этого поля внутри командной строки u-boot.
Автоматизация тестов
В конечном итоге, решая исходную задачу, можно использовать командную оболочку expect:
#!/usr/bin/expect -f set timeout 10 spawn qemu-system-mipsel -cpu 24Kc -M malta -m 1024 -nodefaults -nographic -serial stdio -bios env(IMAGE),format=raw -net nic,model=pcnet -net user expect "login: " send "root\r" expect "Password: " send "root\r" expect "# " send "halt\r"
Здесь скрипт ожидает окончания загрузки прошивки, производит попытку авторизации пользователем root и выключает эмулятор.
Вывод
В итоге я получил необходимую цепочку загрузки прошивки и возможность разрабатывать тестовые сценарии для проверки функций, не завязанных напрямую на железо.
Исходники проекта с конфигурациями, описанными в статье можно скачать здесь.
ссылка на оригинал статьи https://habr.com/ru/post/652757/
Добавить комментарий