Hello Embedded World — загрузка минимального Linux с Busybox на RISC-V, из исходников

В прошлый раз мы видели, как загрузить Ubuntu для RISC-V на плате QEMU virt и установить среду разработки для сборки на C и RISC-V. Это было весело и все такое, но ничто не сравнится с компиляцией собственного ядра Linux и утилит пользовательского пространства и загрузкой этого ядра на виртуальной (или физической) плате RISC-V. Поэтому мы сделаем это сегодня!

В документации по RISC-V действительно описывается процесс, как это сделать, но, к сожалению, он очень краток и пропускает много деталей, поэтому может не подойти для читателей-новичков в мире встраиваемых систем. По крайней мере, мне потребовалось много возиться, дополнительно гуглить и экспериментировать, чтобы в итоге все заработало. Таким образом, эта статья пытается восполнить пробелы в официальных документах, чтобы новичкам было проще разобраться. Поехали!

Подготовка

Подходящая среда Linux. Если вы работаете на Windows / macOS, запустите полноценную виртуальную машину Linux с гипервизором VirtualBox или VMware. WSL2 на Windows может работать или не работать, и в этой статье не будет поддерживаться.

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

Предполагается, что вы уже хорошо знакомы с командами и администрированием Linux. Если вы получите ошибку типа gpg2: command not found на полпути, от вас будут ожидать, что вы догадаетесь sudo apt install gnupg2 вместо того, чтобы жаловаться 😉

Настройка хоста, кросс-компиляция Linux и BusyBox для RISC-V

Основная статья: Запуск 64- и 32-битной RISC-V Linux на QEMU

Обновить метаданные репозитория:

$ sudo apt update
Вход в полноэкранный режим Выход из полноэкранного режима

Установите зависимости сборки для Linux и BusyBox:

$ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev 
                 gawk build-essential bison flex texinfo gperf libtool patchutils bc 
                 zlib1g-dev libexpat-dev git
Войдите в полноэкранный режим Выйти из полноэкранного режима

Вам также потребуется установить qemu-system для эмуляции платы RISC-V virt:

$ sudo apt install -y qemu-system
Войдите в полноэкранный режим Выход из полноэкранного режима

Linux

Зайдите на kernel.org и загрузите последнее стабильное ядро или любое другое достаточно свежее ядро по вашему выбору. Например, на момент написания статьи (2022-08-14) последним стабильным ядром является 5.19.1:

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.1.tar.xz
Вход в полноэкранный режим Выход из полноэкранного режима

Получите также соответствующую сигнатуру ядра. Для такой важной вещи, как ядро ОС, лучше всего проверить его подлинность и целостность:

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.19.1.tar.sign
Вход в полноэкранный режим Выйти из полноэкранного режима

Распакуйте tarball ядра (но пока не извлекайте архив):

$ unxz linux-5.19.1.tar.xz
Войдите в полноэкранный режим Выйдите из полноэкранного режима

На сайте https://kernel.org/category/signatures.html объясняется, как проверить подпись в tarball ядра. Сначала установите gnupg2:

$ sudo apt install gnupg2
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Теперь импортируйте ключи Линуса Торвальдса и Грега Кроа-Хартмана, создателя Linux и ведущего разработчика ядра (соответственно):

$ gpg2 --locate-keys torvalds@kernel.org gregkh@kernel.org
Войти в полноэкранный режим Выйти из полноэкранного режима

Доверьте импортированные ключи. Замените хэши, показанные ниже, в соответствии с результатами, полученными от предыдущей команды:

$ gpg2 --tofu-policy good 38DBBDC86092693E
$ gpg2 --tofu-policy good 79BE3E4300411886
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь проверьте:

$ gpg2 --trust-model tofu --verify linux-5.19.1.tar.sign
Enter fullscreen mode Выйти из полноэкранного режима

Ожидаемый результат:

gpg: assuming signed data in 'linux-5.19.1.tar'
gpg: Signature made Thu Aug 11 11:22:54 2022 UTC
gpg:                using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Good signature from "Greg Kroah-Hartman <gregkh@kernel.org>" [full]
gpg: gregkh@kernel.org: Verified 1 signatures in the past 0 seconds.  Encrypted
     0 messages.
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь распакуйте tarball:

$ tar xvf linux-5.19.1.tar
Войти в полноэкранный режим Выйти из полноэкранного режима

Войдите в дерево исходных текстов:

$ pushd linux-5.19.1/
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы выполнить кросс-компиляцию для RISC-V, нам нужен кросс-компилятор. Установите gcc-riscv64-linux-gnu:

$ sudo apt install -y gcc-riscv64-linux-gnu
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Теперь настройте ядро для RISC-V:

$ make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig
Войти в полноэкранный режим Выйдите из полноэкранного режима

И соберите его (это может занять некоторое время):

$ make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)
Войдите в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем покинуть дерево исходников:

$ popd
Войти в полноэкранный режим Выйти из полноэкранного режима

BusyBox

Перейдите на сайт busybox.net для получения исходного кода BusyBox. Последняя версия на момент написания статьи (2022-08-14) — 1.35.0.

Загрузите сжатый tarball:

$ wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
Войдите в полноэкранный режим Выйти из полноэкранного режима

И хэш SHA256, чтобы проверить его целостность:

$ wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2.sha256
Войдите в полноэкранный режим Выйти из полноэкранного режима

Существует также файл подписи для проверки подписи tarball, но мы не будем рассматривать его здесь.

Проверьте контрольную сумму:

$ sha256sum -c busybox-1.35.0.tar.bz2.sha256
Войдите в полноэкранный режим Выйти из полноэкранного режима

Ожидаемый результат:

busybox-1.35.0.tar.bz2: OK
Войти в полноэкранный режим Выйти из полноэкранного режима

Распакуйте архив:

$ tar xvf busybox-1.35.0.tar.bz2
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь войдите в дерево источника:

$ pushd busybox-1.35.0/
Войти в полноэкранный режим Выйдите из полноэкранного режима

Настройте и соберите для RISC-V — убедитесь, что полученный двоичный файл статически связан:

$ CROSS_COMPILE=riscv64-linux-gnu- LDFLAGS=--static make defconfig
$ CROSS_COMPILE=riscv64-linux-gnu- LDFLAGS=--static make -j$(nproc)
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь мы можем покинуть дерево исходных текстов:

$ popd
Войти в полноэкранный режим Выход из полноэкранного режима

Подготовка виртуального диска, rootfs

Прежде чем мы сможем загрузить нашу плату virt, нам нужно подготовить образ диска с корневой файловой системой (rootfs). Корневая файловая система будет в основном предоставлена BusyBox, хотя нам потребуется создать несколько дополнительных каталогов для точек монтирования, скриптов запуска и тому подобного.

Проще всего это сделать с помощью dd — давайте создадим образ виртуального диска busybox размером 1 ГБ:

$ dd if=/dev/zero of=busybox bs=1M count=1024
Вход в полноэкранный режим Выйдите из полноэкранного режима

Отформатируйте его с помощью файловой системы ext4 (или другой поддерживаемой файловой системы по вашему выбору):

$ mkfs.ext4 busybox
Войдите в полноэкранный режим Выйти из полноэкранного режима

Создайте точку монтирования rootfs:

$ mkdir -p rootfs
Войдите в полноэкранный режим Выйдите из полноэкранного режима

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

$ sudo mount busybox rootfs
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь мы можем установить Busybox на этот rootfs:

$ sudo CROSS_COMPILE=riscv64-linux-gnu- LDFLAGS=--static make -C busybox-1.35.0/ install CONFIG_PREFIX=../rootfs
Войти в полноэкранный режим Выйти из полноэкранного режима

Создайте несколько каталогов для монтирования ключевых файловых систем, таких как procfs, sysfs и devtmpfs для правильной загрузки BusyBox:

$ sudo mkdir -p rootfs/proc rootfs/sys rootfs/dev
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Убедитесь, что /etc/fstab существует, чтобы заглушить предупреждение при выключении питания:

$ sudo mkdir -p rootfs/etc
$ sudo touch rootfs/etc/fstab
Войти в полноэкранный режим Выйти из полноэкранного режима

Создайте каталог /etc/init.d для сценариев запуска:

$ sudo mkdir -p rootfs/etc/init.d
Войти в полноэкранный режим Выход из полноэкранного режима

BusyBox запускает скрипт /etc/init.d/rcS при запуске системы. Давайте заполним его и сделаем исполняемым:

$ sudo bash -c "cat > rootfs/etc/init.d/rcS" << EOF
#!/bin/sh

echo "Hello Embedded World!"
echo "Hello RISC-V World!"
mount -t proc proc /proc
mount -t sysfs sysfs /sys
ip addr add 10.0.2.15/24 dev eth0
ip link set dev eth0 up
ip route add default via 10.0.2.2 dev eth0
EOF
$ sudo chmod +x rootfs/etc/init.d/rcS
Войти в полноэкранный режим Выйти из полноэкранного режима

Размонтируйте наш виртуальный диск:

$ sudo umount rootfs
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь перейдем к самому интересному!

Загрузка нашей платы RISC-V virt

Запустим наш эмулятор:

$ qemu-system-riscv64 
    -nographic 
    -machine virt 
    -kernel linux-5.19.1/arch/riscv/boot/Image 
    -append "root=/dev/vda ro console=ttyS0" 
    -drive file=busybox,format=raw,id=hd0 
    -device virtio-blk-device,drive=hd0 
    -netdev user,id=eth0 
    -device virtio-net-device,netdev=eth0
Войти в полноэкранный режим Выйти из полноэкранного режима

Большинство опций покажутся вам знакомыми, если вы следили за нашей предыдущей статьей, поэтому мы просто расскажем о том, что нового:

Поскольку пользовательское пространство BusyBox очень легкое, он должен полностью загрузиться примерно за 1 секунду. Вот что вы должны увидеть:

...
Hello Embedded World!
Hello RISC-V World!

Please press Enter to activate this console.
Вход в полноэкранный режим Выход из полноэкранного режима

Нажмите Enter в ответ на запрос. Вы должны попасть в оболочку root.

Давайте поиграем. Просмотрите список запущенных процессов:

# ps aux
Войти в полноэкранный режим Выйти из полноэкранного режима

Пример вывода:

PID   USER     TIME  COMMAND
    1 0         0:00 init
    2 0         0:00 [kthreadd]
    3 0         0:00 [rcu_gp]
    4 0         0:00 [rcu_par_gp]
    5 0         0:00 [netns]
    6 0         0:00 [kworker/0:0-eve]
    7 0         0:00 [kworker/0:0H-ev]
    8 0         0:00 [kworker/u2:0-ev]
    9 0         0:00 [mm_percpu_wq]
   10 0         0:00 [rcu_tasks_trace]
   11 0         0:00 [ksoftirqd/0]
   12 0         0:00 [rcu_sched]
   13 0         0:00 [migration/0]
   14 0         0:00 [kworker/0:1-eve]
   15 0         0:00 [cpuhp/0]
   16 0         0:00 [kdevtmpfs]
   17 0         0:00 [inet_frag_wq]
   18 0         0:00 [khungtaskd]
   19 0         0:00 [oom_reaper]
   20 0         0:00 [writeback]
   21 0         0:00 [kcompactd0]
   22 0         0:00 [kblockd]
   23 0         0:00 [ata_sff]
   24 0         0:00 [rpciod]
   25 0         0:00 [kworker/0:1H-ev]
   26 0         0:00 [xprtiod]
   27 0         0:00 [kswapd0]
   28 0         0:00 [kworker/u2:1-ev]
   29 0         0:00 [nfsiod]
   30 0         0:00 [uas]
   31 0         0:00 [mld]
   32 0         0:00 [ipv6_addrconf]
   39 0         0:00 [jbd2/vda-8]
   40 0         0:00 [ext4-rsv-conver]
   48 0         0:00 -/bin/sh
   49 0         0:00 init
   50 0         0:00 init
   51 0         0:00 init
   52 0         0:00 ps aux
Войти в полноэкранный режим Выход из полноэкранного режима

Это действительно легкая система! Вы даже можете запустить top для просмотра процессов и загрузки процессора в реальном времени:

# top
Вход в полноэкранный режим Выход из полноэкранного режима

Нажмите q, чтобы выйти.

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

# free -m
Войти в полноэкранный режим Выйти из полноэкранного режима

Пример вывода:

              total        used        free      shared  buff/cache   available
Mem:            108          10          95           0           3          95
Swap:             0           0           0
Вход в полноэкранный режим Выход из полноэкранного режима

Опять же, очень легкий — VM имеет около 128MB памяти, а мы использовали только 10MB. Сравните это с сервером Ubuntu 22.04 на RISC-V, который не загружается с 512MB памяти из-за недостаточного количества памяти 😉

Проверьте, сколько дискового пространства используется / доступно:

# df -Th
Войти в полноэкранный режим Выйти из полноэкранного режима

Пример вывода:

Filesystem           Type            Size      Used Available Use% Mounted on
/dev/root            ext4          973.4M      1.7M    904.5M   0% /
devtmpfs             devtmpfs       53.0M         0     53.0M   0% /dev
Войти в полноэкранный режим Выход из полноэкранного режима

Мы выделили 1 ГБ для нашего виртуального диска, а корневая файловая система BusyBox занимает менее 2 МБ.

Давайте также протестируем сеть. Попробуйте пинговать хост:

# ping -c5 10.0.2.2
Войти в полноэкранный режим Выйти из полноэкранного режима

Пример вывода:

PING 10.0.2.2 (10.0.2.2): 56 data bytes
64 bytes from 10.0.2.2: seq=0 ttl=255 time=16.085 ms
64 bytes from 10.0.2.2: seq=1 ttl=255 time=3.775 ms
64 bytes from 10.0.2.2: seq=2 ttl=255 time=3.810 ms
64 bytes from 10.0.2.2: seq=3 ttl=255 time=3.770 ms
64 bytes from 10.0.2.2: seq=4 ttl=255 time=4.322 ms

--- 10.0.2.2 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 3.770/6.352/16.085 ms
Войти в полноэкранный режим Выход из полноэкранного режима

Отлично! Давайте посмотрим, можем ли мы связаться с серверами из Интернета. Вот публичный IP-адрес для google.com: 142.250.204.46:

# ping -c5 142.250.204.46
Вход в полноэкранный режим Выход из полноэкранного режима

Пример вывода:

PING 142.250.204.46 (142.250.204.46): 56 data bytes
64 bytes from 142.250.204.46: seq=0 ttl=255 time=15.262 ms
64 bytes from 142.250.204.46: seq=1 ttl=255 time=8.313 ms
64 bytes from 142.250.204.46: seq=2 ttl=255 time=8.136 ms
64 bytes from 142.250.204.46: seq=3 ttl=255 time=11.132 ms
64 bytes from 142.250.204.46: seq=4 ttl=255 time=8.752 ms

--- 142.250.204.46 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 8.136/10.319/15.262 ms
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте также посмотрим информацию о процессоре через procfs:

# cat /proc/cpuinfo
Войти в полноэкранный режим Выход из полноэкранного режима

Выход:

processor   : 0
hart        : 0
isa     : rv64imafdc
mmu     : sv57

Войти в полноэкранный режим Выход из полноэкранного режима

Итак, наша эмулируемая плата имеет одно процессорное ядро RISC-V с одним аппаратным потоком (hart), и процессорное ядро поддерживает спецификацию RV64IMAFDC ISA, где «IMAFD» можно упростить до просто «G», чтобы получить RV64GC («G» для общих расширений, «C» для сжатых инструкций). Вы можете прочитать больше о спецификации RISC-V ISA на GitHub, которая является очень модульной с минимальной базовой ISA плюс множество дополнительных расширений.

Поиграйте еще немного, затем выключите питание платы:

# poweroff
Войти в полноэкранный режим Выход из полноэкранного режима

Вот и все — поздравляем! Вы успешно скомпилировали собственное ядро Linux и минимальное пользовательское пространство BusyBox, и загрузили его на виртуальной плате RISC-V virt с QEMU.

Следующие шаги

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

  • Если вы немного поиграли, вы могли заметить, что разрешение DNS не работает внутри платы. Попробуйте выяснить причину и устранить ее
  • Попробуйте следовать этой статье (или официальной документации по RISC-V) с реальной, физической RISC-V SoC. В конце концов, физическое оборудование — это реальная вещь.
  • Если вам удастся загрузить свой собственный встроенный Linux на физическом оборудовании RISC-V, попробуйте сделать с ним что-нибудь полезное, например, превратить его в IoT-проект как часть умного дома.

Оцените статью
devanswers.ru
Добавить комментарий