Здраствуйте меня зовут Дмитрий. Как-то я купил OrangePI i96, но к сожалению производитель давно забыл об этой плате, прошивки для неё построены на ядре 3.10.62(актуальная версия на момент написания статьи 6.5.1). Поэтому я решил собрать собственную прошивку на актуальной версии ядра. Сборка будет проходить полностью из исходников.

Для начала хочу отметить что для сборки прошивки я использовал WSL (Windows Subsystem for Linux) второй версии, а в качестве гостевой системы была установлена Ubuntu, в принципе все собирается прекрасно но были некоторые проблемы, о которых будет сказано позже. Также хочу отметить что несмотря на то что во время работы над проектом, мне пришлось переустановить Windows я не потерял свои файлы. Если по каким-то причинам WSL потеряет образ, то систему можно восстановить из ext4.vhdx файла при помощи команды:

wsl --import-in-place <new_distro_name> <path_to_ext4.vhdx>

Главное не потеряйте ext4.vhdx файл и все будет в порядке.

Компиляция uboot

Сначала создадим папку в которой будем проводить сборку:

mkdir OrangePI
cd OrangePI

Поскольку Uboot идущий по умолчанию все прекрасно запускает, то менять его нет никакого смысла. Поэтому будем делать все по инструкции от авторов OrangePI i96:

wget https://github.com/orangepi-xunlong/toolchain/archive/refs/heads/arm-linux-gnueabi-1.13.1.zip
unzip arm-linux-gnueabi-1.13.1.zip

Хочу заметить, что этот тулчайн мы будем использовать только для компиляции uboot.

wget https://github.com/orangepi-xunlong/OrangePiRDA_uboot/archive/refs/heads/master.zip
unzip master.zip
cd OrangePiRDA_uboot-master

Создаем конфиг и компилируем. Путь к тулчайну у вас может отличатся. Цифра после j равна количеству потоков вашего процессора.

make CROSS_COMPILE=/home/dmitry/OrangePI/toolchain-arm-linux-gnueabi-1.13.1/gcc-linaro-1.13.1-2012.02-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi- rda8810_config
make CROSS_COMPILE=/home/dmitry/OrangePI/toolchain-arm-linux-gnueabi-1.13.1/gcc-linaro-1.13.1-2012.02-x86_64_arm-linux-gnueabi/bin/arm-linux-gnueabi- -j16

У меня во время компиляции возникла ошибка (error while loading shared libraries: libz.so.1). Я преодолел её командой:

sudo apt-get install lib32z1

После компиляции должен появится файл u-boot.rda следует его запомнить он понадобится нам в будущем.

Создание корневой директории

Cоздадим директорию rootfs, это будет образ файловой системы нашего устройства. Для корректной работы устройства в этой директории должны находится определенные папки, в которые в последствии будут примонтированы необходимые для работы системные каталоги.

mkdir rootfs
cd rootfs
mkdir -p dev root sys proc run tmp var/run var/log

Компиляция ядра

Сначала нужно определится с тулчайном. Чип RDA8810PL использует набор команд ARMv7 с аппаратной поддержкой команд с плавающей запятой. Я использовал gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf (hf значит аппаратная поддержка команд с плавающей запятой)

wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz

Потом надо скачать исходники ядра с сайта kernel.org. На момент создания статьи актуальная версия 6.5.1, с ней и будем работать.

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.5.1.tar.xz
tar -xvf linux-6.5.1.tar.xz
cd linux-6.5.1

Необходимо отметить что Opange PI i96 построена на основе чипа RDA8810PL, для работы которого необходимы драйверы которых нету в стандартном ядре. И хотя у нас есть исходники старого ядра от разработчиков . Просто взять файлы драйверов из старого ядра и положить их в новое нельзя, потому что с тех пор изменилась архитектура ядра. Современное ядро использует архитектуру которая называется Device Tree.

Раньше само ядро инициализировало устройства. А поскольку у каждого одноплатника свой набор устройств, то ядро у каждого было свое. Теперь список устройств задается в файле Device Tree(который имеет расширение .dts , .dtb после компиляции), а ядро уже считывает устройства из этого файла. Благодаря этому ядро у всех одноплатников одно и тоже отличается только файл Device Tree.

К счастью я подправил файлы драйверов чтобы они могли работать с новым ядром. Мой репозиторий. Вам нужно распаковать его в паку с исходниками ядра. Хочу заметить что я сначала положил оригинальные файлы а потом уже модифицированные, так что можно посмотреть что мне пришлось подправить.

Если вы вдруг на компилируете что-то не то всегда можно ввести команду:

make distclean

И она вернет все к первоначальному виду.

Приступим к компиляции. Задаем настройки по умолчанию. Путь к тулчайну у вас может быть другой.

make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- defconfig
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- menuconfig

Заходим в меню и отмечаем опции:

  • System type-->RDA Micro SoCs

  • Device drivers-->Character devices-->Enable TTY-->Serial Drivers-->RDA Micro serial port support-->Console on RDA Micr serial port

  • Device drivers-->GPIO Support-->Memory Mapped GPIO drivers-->RDA Micro GPIO controller support

  • Device drivers-->RDA support

RDA support это не опция а подменю. В него нужно войти и выбрать все опции в нем. После этого выходим и сохраняемся.

Когда вы попытаетесь включить опцию "RdaMicro IEEE802.11 emdedded FullMac WLAN driver". То система скажет вам что она зависит от другой опции которая включена как модуль и поэтому эта опция тоже будет включена как модуль. К сожалению если включить эту опцию как модуль то не произойдет инициализации сети при загрузке (я проверял). Поэтому надо найти опцию:

Networking support-->Wireless-->cfg80211-wireless configuration API

И изменить её тип с m на просто включено. После этого удастся нормально включить опцию "RdaMicro IEEE802.11 emdedded FullMac WLAN driver".

Компиляция j равно количеству потоков вашего процессора.

make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16

Также можно компилировать ядро по частям:

make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 zImage
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 dtbs
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16 modules

Если вы используете WSL и копировали файлы драйверов через проводник Windows то все папки будут иметь владельца root и вы получите ошибку permission denay. Чтобы это избежать нужно ввести команду:

sudo chown -R "имя вашего акаунта":"имя вашего акаунта" drivers/rda 

После компиляции. Устанавливаем модули ядра в нашу папку rootfs.

make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=../rootfs

Также создаем папку sysroot в которой будут хранится заголовки библиотек и устанавливаем их.

mkdir -p ../sysroot/usr
make ARCH=arm CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- INSTALL_HDR_PATH=../sysroot/usr headers_install

Ядро мы объединяем с файлом Device tree командой cat. Тут может возникнуть вопрос а зачем это нужно ведь мы положим файл Device tree в образ. Здесь я сам не понимаю до конца (возможно это вызвано тем что u-boot у нас старой версии и он ни знает ничего о Device tree), но при загрузке будет использоваться тот файл который мы присоединим к ядру.

cat /home/dmitry/OrangePI/linux-6.5.1/arch/arm/boot/zImage /home/dmitry/OrangePI/linux-6.5.1/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dtb > zImage+dtb

Компиляция glibc

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

Для кроcс-компиляции необходимо задать несколько переменных среды. Эти переменные будут сохранятся до перезагрузки консоли.

export PATH=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
export CHOST=arm-linux-gnueabihf
export CC=arm-linux-gnueabihf-gcc
export AR=arm-linux-gnueabihf-ar
export CXX=arm-linux-gnueabihf-g++

Итак приступим:

wget https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.gz
tar -xzf glibc-2.38.tar.gz
cd glibc-2.38
mkdir build
cd build
../configure --prefix=/usr --host=arm-linux-gnueabihf CFLAGS="--sysroot=/home/dmitry/OrangePI/sysroot -O2" CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make DESTDIR=`pwd`/glibcBuild install

Я на всякий случай передаю переменные CC AR CXX, хотя этого можно и не делать.

Также во время компиляции возникали ошибки.

fatal error: gmp.h: No such file or directory

решение:

sudo apt-get install libgmp3-dev libmpfr-dev
fatal error: mpc.h: No such file or directory

решение:

sudo apt-get install libmpc-dev

Теперь можно зайти в папку glibcBuild и рассортировать полученные файлы.

В папку rootfs:

etc
lib
sbin
usr/bin
usr/lib/*.so
usr/libexec
usr/sbin
usr/share

Здесь и далее в папку rootfs мы будем класть только файлы *.so это динамические библиотеки файлы с расширениями *.o *.a нас не интересуют.

В папку sysroot:

lib
usr/include
usr/lib

В sysroot кладем все подряд эту папку мы будем использовать для компиляции программ.

Компиляции busybox

Busybox это легковесный набор утилит для встраиваемой электроники. Самое то для одноплатника.

wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvjf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- defconfig
make CROSS_COMPILE=/home/dmitry/OrangePI/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf- -j16

Busybox ставится в папку _install. Все содержимое этой папки надо перенести в rootfs.

Компиляция библиотеки libxcrypt

wget https://github.com/besser82/libxcrypt/releases/download/v4.4.36/libxcrypt-4.4.36.tar.xz
tar -xvf libxcrypt-4.4.36.tar.xz
cd libxcrypt-4.4.36
mkdir build
./configure --prefix=/home/dmitry/OrangePI/libxcrypt-4.4.36/build --host=arm-linux-gnueabihf
make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make install

Содержимое папки build/lib/*.so в rootfs всё остальное в sysroot.

Компиляция DropBear

Поскольку Orange PI i96 не имеет ни каких способов вывода изображения, то единственный способ взаимодействия с ним это через терминал. Поэтому будем ставить ssh сервер DropBear.

wget https://matt.ucc.asn.au/dropbear/releases/dropbear-2022.83.tar.bz2
tar -xjf dropbear-2022.83.tar.bz2
cd dropbear-2022.83
./configure --prefix=/home/dmitry/OrangePI/rootfs --disable-zlib --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld
make install 

Для корректной работы DropBear необходимо сгенерировать ключи.

cd ../rootfs
mkdir etc/dropbear
dropbearkey -t dss -f etc/dropbear/dropbear_dss_host_key
dropbearkey -t rsa -f etc/dropbear/dropbear_rsa_host_key

Компиляция библиотеки libnl-3

wget http://www.infradead.org/~tgr/libnl/files/libnl-3.2.25.tar.gz
tar -xzf libnl-3.2.25.tar.gz
cd libnl-3.2.25
mkdir build
./configure --host=arm-linux-gnueabihf --prefix=/home/dmitry/OrangePI/libnl-3.2.25/build
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ 
make install
cd include
make install

Из папки build копируем в rootfs все кроме include. А в sysroot все папки.

Компиляция iw

Чтобы воспользоваться терминалом нам понадобится сеть, iw это утилита для настройки wi-fi соединения.

Перед тем как мы начнем нужно задать переменную PKG_CONFIG_PATH и установить пакет pkg-config если его нет.

export PKG_CONFIG_PATH=/home/dmitry/OrangePI/sysroot/lib/pkgconfig
sudo apt-get install -y pkg-config

wget https://www.kernel.org/pub/software/network/iw/iw-3.15.tar.gz
tar -xzf iw-3.15.tar.gz
cd iw-3.15
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
cp iw ../rootfs/sbin/

Компиляция библиотеки OpenSSL

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

wget https://www.openssl.org/source/openssl-1.1.1h.tar.gz
tar -xzf openssl-1.1.1h.tar.gz
cd openssl-1.1.1h
mkdir build
./Configure linux-generic32 --prefix=/home/dmitry/OrangePI/openssl-1.1.1h/build
make -j16 CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++
make install

Как всегда в rootfs папка lib\libcrypto.so и lib\libssl.so. В sysroot все остальное.

Компиляция wpa_supplicant

При помощи iw мы можем устанавливать соединение только с точками доступа на которых не используется шифрование. Чтобы установить соединение с шифрованием нам понадобится пакет wpa_supplicant.

wget https://w1.fi/releases/wpa_supplicant-2.10.tar.gz
tar -xzf wpa_supplicant-2.10.tar.gz
cd wpa_supplicant-2.10/wpa_supplicant
cp defconfig .config

Мне пришлось внести некоторые изменения в файл .config. Во первых я раскоментировал строки с путями к OpenSSL и подправил эти пути:

CFLAGS += -I/home/dmitry/OrangePI/sysroot/include
CFLAGS += -I/home/dmitry/OrangePI/sysroot/include/libnl3
LIBS += -L/home/dmitry/OrangePI/sysroot/lib

После закомментировал следующие строки:

#CONFIG_DRIVER_MACSEC_LINUX=y
#CONFIG_MACSEC=y
#CONFIG_CTRL_IFACE_DBUS_INTRO=y
#CONFIG_CTRL_IFACE_DBUS_NEW=y

Компиляция:

make CC=arm-linux-gnueabihf-gcc AR=arm-linux-gnueabihf-ar CXX=arm-linux-gnueabihf-g++ -j16
make install DESTDIR=/home/dmitry/OrangePI/rootfs

Создание конфигурационных файловой

Создаем файл с пользователем root у которого домашняя папка /. То есть при входе мы будем видеть корневой каталог.

cd rootfs/etc
cat >> passwd
root:x:0:0:root:/:/bin/sh
^D

Символ ^D означает что надо нажать сочетание клавиш CTRL+D, чтобы закончить редактирование.

В файле shadow содержатся пароли в зашифрованном виде. Здесь зашифрован пароль root.

cat >> shadow
root:LlNBpbQmj8p7g:1:0:99999:7:::
^D

Также создадим файл group. Группа wheel нужна для wpa_supplicant.

cat >> group
root:x:0:root
wheel:x:10:root
^D

В файле wpa_supplicant.conf хранится информация о том к какой сети мы будем подключатся. Также хранится пароль в виде шестнадцатеричного числа. Чтобы получить это число нужно установить wpa_supplicant на компьютер и воспользоваться утилитой wpa_passphrase:

wpa_passphrase i96 123456789

Здесь имя сети i96 пароль 123456789

cat >> wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
network={
       ssid="i96"
        psk=c96a7f97cfee8b9f9ce9aae88c4cc6c5f1962f6dca48ea7444d80a70a300b5b1
}
^D

Файл resolv.conf содержит адреса DNS серверов:

cat >> resolv.conf 
nameserver 1.1.1.1
nameserver 1.0.0.1
^D

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

cat >> hosts
127.0.0.1	localhost
^D
cat >> hostname
OrangePI
^D
cat >> host.conf
order hosts, bind
multi on
^D
cat >> networks
loopback	127.0.0.0
localnet	127.0.0.0
^D

Дальше пойдут файлы которые слишком большие для ввода c помощью cat. Ссылка на эти файлы будет приведена в конце статьи.

Файл profile содержит начальное значение переменной PATH. Это полезно, например wap_suplicant устанавливается не в /usr/sbin а в /usr/local/sbin. Также я добавил пути к каталогу opt.

Скрипты инициализации системы

Файл inittab отвечает за такие вещи как загрузка, выключение а также нажатие AltCtrlDel. Справка.

cat >> inittab
::sysinit:/etc/rc.d/rc.S
::respawn:-/bin/sh -l
::ctrlaltdel:/sbin/reboot 
::shutdown:/etc/rc.d/rc.0
^D

Создадим папку для скриптов:

mkdir rc.d
cd rc.d

Скрипт rc.0 размонтировывает диски при выключении.

cat >> rc.0
#!/bin/sh
sync
/sbin/umount -a -r > /dev/null 2>&1
^D

Дальше файл rc.S который выполняется при загрузке:

Hidden text
#!/bin/sh
# Первым делом смонтируем procfs. Без нее ничего не полетит
/bin/mount -v proc /proc -t proc 1> /dev/null

/bin/mkdir /dev/pts
/bin/mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts 

PATH=/bin:/usr/bin:/sbin:/usr/sbin
# Мы не будем насиловать карточку временным файлами, будем держать их в пямяти
/bin/mount -t tmpfs none -o size=150M,nodev,nosuid,noatime /tmp
/bin/mount -t tmpfs none -o size=10M,nodev,nosuid,noatime /var/log
/bin/mount -t tmpfs none -o size=5M,nodev,nosuid,noatime /var/run
/bin/mount -t tmpfs none -o size=5M,nodev,nosuid,noatime /run

# Удалим (если он был) файл о смонтированных ранее ФС и создадим пустой
/bin/rm -f /etc/mtab{,~,.tmp} && /bin/touch /etc/mtab

# Смонтируем sysfs и запустим ldconfig для обновления информации о библиотеках
/bin/mount -v sysfs /sys -t sysfs 1> /dev/null
if [ -x /sbin/ldconfig ]; then
  /sbin/ldconfig 1> /dev/null
fi

# Запустим системный логгер и логгер ядра
/sbin/syslogd 2> /dev/null
sleep 1
/sbin/klogd -c 3 1> /dev/null

modprobe loop 1> /dev/null 2> /dev/null

if [ -x /etc/rc.d/rc.modules ]; then
. /etc/rc.d/rc.modules
fi

if [ -x /etc/rc.d/rc.network ]; then
. /etc/rc.d/rc.network start
fi

# Очистим экран
/bin/setterm -blank 0 2>/dev/null

# Создадим файл приглашений
echo > /etc/motd
echo "`/bin/uname -a | /usr/bin/cut -d\  -f1,3`." >> /etc/motd
echo >> /etc/motd

# Выдадим прилашение для входа в консоль
echo "Press <Enter>..."

Скрипт инициализации сети rc.network:

Hidden text
#!/bin/sh

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin

net_start() {
    
    ifconfig wlan0 up
	iw dev wlan0 scan > /dev/null 
	wpa_supplicant -B -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf
	sleep 2
	ifconfig lo up 127.0.0.1
    route add -net 127.0.0.0 netmask 255.255.255.0 lo
	ifconfig wlan0 192.168.1.252 netmask 255.255.255.0
	route add default gw 192.168.1.1 wlan0
	dropbear
	route 
}

net_stop() {
    if [ -f /var/run/udhcpc.pid ]; then
      kill -9 `cat /var/run/udhcpc.pid`
      rm /var/run/udhcpc.pid
    fi
    ip set link wlan0 down
}

case "$1" in
'start')
    net_start
    ;;
'stop')
    net_stop
    ;;
'restart')
    net_stop
    net_start
    ;;
*)
    echo "Usage $0 start|stop|restart"
esac

Скрипт сканирует точки доступа и подключается к точки доступа параметры которой записаны в файле wpa_supplicant.conf. После этого назначается IP адрес. Если вы хотите подключится к незащищённой точки доступа (хотя я бы не советовал создавать такие точки доступа) то вместо строчки wap_suplicant нужно ввести строку iw dev wlan0 connect "имя незащищённой точки доступа". А если вы просто хотите узнать статус соединения можно использовать команду iw dev wlan0 link. Команда ifconfig -a выводит список сетевых интерфейсов

Здесь надо пояснить. У меня скрипт устроен так что IP адрес всегда выбирается 192.168.1.252. А IP адрес роутера с которым связывается одноплатник равен 192.168.1.1. Но если вы хотите чтобы при инициализации система сама находила адрес роутера, а также присваивала себе незанятый IP. Нужно строчки:

ifconfig wlan0 192.168.1.252 netmask 255.255.255.0
route add default gw 192.168.1.1 wlan0

Заменить на:

udhcpc -i wlan0 -p /var/run/udhcpc.pid -s /etc/scripts/udhcp.sh 

И добавить в папку /etc/scripts скрипт udhcp.sh

Hidden text
#!/bin/sh

ACTION="$1"

case "${ACTION}" in
"renew"|"bound")
    /sbin/ifconfig ${interface} ${ip} netmask ${subnet}
    /sbin/route del default > /dev/null 2>&1
    for i in "${router}"; do
        /sbin/route add default gw $i dev ${interface}
    done
    ;;
esac

Не забываем разрешить выполнение всех скриптов.

chmod u+x rc.0
chmod u+x rc.S
chmod u+x rc.network

Создание файла uInitrd

Чтобы загрузка системы считалась успешной. Ядро должно выполнить две вещи. Это смонтировать корневую директорию и выполнить скрипт инициализации. Но чтобы смонтировать корневую директорию на раздел SD карты например нужно загрузить драйверы SD карты, но чтобы их загрузить нужно уже иметь корневую директорию из которой мы подгрузим драйверы. Отсюда возникает дилемма чтобы смонтировать корневую директорию нам нужно уже иметь корневую директорию. Чтобы разорвать этот порочный круг создается образ uInitrd, в котором содержатся драйверы для дальнейшей работы. Этот образ разворачивается в оперативной памяти.

Кстати если в образ uInitrd положить загрузочный скрипт например такой файл init

Hidden text
#! /bin/sh
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mount -t devtmpfs udev /dev
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts 
/sbin/syslogd
sleep 1
/sbin/klogd -c 3
. /etc/rc.d/rc.network start
dropbear
sysctl -w kernel.printk="2 4 1 7"
/bin/sh
poweroff -f

То система выполнит его и на этом остановится и мы получим Linux с корневой системой смонтированной в оперативной памяти, любые изменения в которой после перезагрузки исчезают.

Итак приступим к созданию uInitrd. Сначала мы архивируем папку rootfs и потом сжимаем её утилитой gzip.

cd rootfs
find . | cpio -H newc -ov --owner root:root > ../initramfs.cpio
cd ..
gzip initramfs.cpio

Утилита mkimage добавляет заголовок необходимый для u-boot.

mkimage -A arm -T ramdisk -n uInitrd -d initramfs.cpio.gz uInitrd

Создание файла boot.scr

boot.scr это сценарий загрузки системы. Этот сценарий предназначается для u-boot.

Создаем файл boot.cmd

setenv bootargs "earlycon initcall_debug console=ttyRDA2,921600n8 root="/dev/mmcblk0p2" rootfstype="ext4" rootwait rw"
ext2load mmc 0:1 ${kernel_addr} zImage+dtb
ext2load mmc 0:1 ${script_addr} rda8810pl-orangepi-i96.dtb
ext2load mmc 0:1 ${initrd_addr} uInitrd
ext2load mmc 0:1 ${modem_addr} modem.bin
mdcom_loadm ${modem_addr}
mdcom_check 1
bootz ${kernel_addr} ${initrd_addr}:${filesize} ${script_addr}

И cкомпилируем его утилитой mkimage:

mkimage -C none -A arm -T script -d boot.cmd boot.scr

Файл modem.bin необходим, без него ядро не стартует. Кроме того он нужен если вы хотите передать файл по UART в u-boot. Например когда я редактировал файлы драйверов мне постоянно приходилось загружать новые ядра но перезаписывать их на SD карту очень долго. Поэтому я создал образ без ядра системы. В результате чего u-boot загружался но загрузить ядро он не мог и загрузка останавливалась. После этого я вводил команду loady в консоль u-boot это переводило u-boot в режим получения файла (как раз эта команда не работает без modem.bin) и в TerTetm меню File->Transfert->YMODEM->send после пересылки ядра загружал его командой:

0x82000000 ${initrd_addr}:${filesize} ${script_addr}

Ссылка на modem.bin в конце статьи.

Сборка образа

Ну собственно все приготовления закончились дальше будет быстрее.

Сначала создадим заголовок размером 2 мегабайта:

dd if=/dev/zero bs=1M count=2 of="NewImg.img"

Копируем u-boot по смещению 256. 256 это не 256 байт а 256 блоков (dd это утилита блочного копирования) по умолчанию один блок равен 512 байт.

dd if=OrangePiRDA_uboot-master/u-boot.rda conv=notrunc seek=256 of="NewImg.img"

Создадим раздел для загрузки. И раздел для корневой директории здесь я установил размер 300Мб но по желанию можно увеличить:

dd if=/dev/zero bs=1M count=50 of="NewImg.img"1
dd if=/dev/zero bs=1M count=300 of="NewImg.img"2

Создаем файловые системы на этих разделах.

mkfs.ext2 -L BOOT "NewImg.img"1
mkfs.ext4 -O ^metadata_csum -F -b 4096 -E stride=2,stripe-width=1024 -L rootfs "NewImg.img"2

Монтируем загрузочную директорию и копируем в неё ядро системы device tree файл и сценарий загрузки:

mkdir -p /tmp/tmp
sudo mount "NewImg.img"1 /tmp/tmp
sudo cp -rf linux-6.5.1/zImage+dtb /tmp/tmp
sudo cp -rf linux-6.5.1/arch/arm/boot/dts/unisoc/rda8810pl-orangepi-i96.dtb /tmp/tmp
sudo cp -rf uInitrd /tmp/tmp
sudo cp -rf boot.cmd /tmp/tmp
sudo cp -rf boot.scr /tmp/tmp
sync
sudo umount /tmp/tmp

Монтируем вторую директорию и копируем в неё содержимое папки rootfs:

sudo mount "NewImg.img"2 /tmp/tmp
sudo cp -rT rootfs /tmp/tmp
sync
sudo umount /tmp/tmp

Объединяем все части в одну:

dd if="NewImg.img"1 conv=notrunc oflag=append bs=1M seek=2 of="NewImg.img"
dd if="NewImg.img"2 conv=notrunc oflag=append bs=1M seek=52 of="NewImg.img

Создание таблицы разделов

Пока что образ нерабочий поскольку нет таблицы разделов. Таблица разделов создается при помощи программы fdisk.

fdisk "NewImg.img"
o
n
p
1
4096
+50M

n
p
2
106496

w

После начала редактирования мы вводим команду "o" чтобы создать пустую таблицу разделов MBR. Затем командой "n" создаем новый раздел. Выбираем тип номер и первый сектор. Дело в том что размер сектора равен 512 байт, то есть один килобайт равен двум секторам. Размер заголовка 2 мегабайта то есть 2048 килобайт или 4096 сектора. Размер раздела можно указать в мегабайтах. Тоже самое делаем со вторым разделом с той лишь разницей что программа автоматически предложит последний сектор и останется только с ней согласится. Выход командой w.

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

sudo parted NewImg.img unit s print

Можно также монтировать эти разделы только смещение нужно указывать не в секторах а в байтах (2097152 = 4096 * 512) и (54525952 = 106496 * 512).

sudo mount -o loop,offset=2097152 NewImg.img /tmp/tmp 
sudo mount -o loop,offset=54525952 NewImg.img /tmp/tmp 

Всё осталось только записать образ на SD карту например при помощи Win32DiskImager.

Запуск

Как я уже говорил нужно на роутере настроить точку доступа с именем i96 и паролем 123456789, у роутера IP должен быть 192.168.1.1. Параметры сети настраиваются в файле /etc/wpa_supplicant.conf а IP в файле /etc/rc.d/rc.network. После включения нужно подождать некоторое время i96 не самый быстрый одноплатник. Вход root root.

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

Заходим в папку root и скачиваем установочный скрипт.

cd /root
wget http://bin.entware.net/armv7sf-k3.2/installer/generic.sh
chmod u+x generic.sh
./generic.sh

После этого мы получим целую корневую директорию в папке opt. Также у нас появится менеджер пакетов opkg. Давайте например установим sftp сервер он позволит перебрасывать файлы на одноплатник при помощи приложения WinSCP:

opkg update
opkg install openssh-sftp-server

Здесь есть маленький момент. Дело в том что DropBear мы компилировали из исходников и он расположен в корневой директории поэтому он ищет sftp сервер в директории /usr/libexec/sftp-server. А sftp сервер устанавливается в директорию opt. Чтобы они смогли найти друг друга надо создать символьную ссылку командой.

ln -s /opt/lib/sftp-server /usr/libexec/sftp-server

После перезагрузки sftp сервер начнет работать. Также можно установить текстовый редактор nano. Midnight commander я пытался установить, но у меня не работали стрелки и функциональные клавиши, поэтому я не смог даже из него выйти. На этом все.

Файлы modem.bin, profile, protocols, services, termcap. (Если ссылка не действует, то файлы можно достать из готового образа)

Мой репозиторий с файлами ядра.

Собранный образ. (В принципе он должен работать и на Orange PI 2g-iot но у меня нет этой платы, проверить не могу.)

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


  1. Zuy
    28.09.2023 04:03

    А не пробовали воспользоваться buildroot? Он может все это автоматически сделать и на выходе выдать уже образ для записи на карту.


    1. NutsUnderline
      28.09.2023 04:03
      +2

      он сможет сделать если его этому научили и подсунули нужные файлы ядра (которые еще нужно пропатчить как оказалось)


  1. vin2809
    28.09.2023 04:03
    +12

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

    Большое спасибо Автору.


  1. SamOwaR
    28.09.2023 04:03
    +1

    Класс! Я как-то заморачивался подобным для платы на i.MX6 процессоре.

    Но там был и SATA и mini PCI-E, и HDMI, т.е. много чего можно из него слепить.

    А тут непонятно, вход для камеры есть, а видео выхода нет. Какие сценарии использования?


    1. NutsUnderline
      28.09.2023 04:03

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

      А вот распознавание образов сейчас делают даже на ESP32, так что этой штуки вполне должно хватить на распознавание чего нибуть..

      А вообще так чипсет вроде телефонный? Тогда экран планировался, фоточки... На борту есть целый GPU и скриншот Android на странице продукта

      Дисплеи в общем то и на SPI есть только медленнее


  1. NutsUnderline
    28.09.2023 04:03
    +1

    Автору честь и хвала за то что ядро пропатчил. И все расписал.

    Мне любопытно стало - а если использовать /gcc-linaro-7.5.0-2019.12-i686-mingw32_arm-linux-gnueabihf.tar.xz так ведь наверное получиться собрать бинарники и без WSL хотя дальше будут приключения с правами доступа в файловой системе (образе)


  1. Johan_Palych
    28.09.2023 04:03
    +3

    The official build system for Orange Pi, support RDA8810(OrangePi I96, OrangePi 2G-IOT)
    https://github.com/orangepi-xunlong/OrangePi_Build
    Linux kernel release 3.x - старое ядро
    OrangePiRDA https://github.com/orangepi-xunlong/OrangePiRDA_kernel

    Здесь чел собирает на Debian Bullseye готовые образы:
    https://github.com/TheRemote/Legendary-OrangePi-i96
    https://github.com/TheRemote/Legendary-OrangePi-i96/releases/download/1.37/Legendary_OrangePi_i96_debian_bullseye_server_v1.37.tar.xz
    Его блог.
    https://jamesachambers.com/orange-pi-i96-getting-started-guide/


    1. NutsUnderline
      28.09.2023 04:03
      +1

      Ядро апгрейдить он похоже не осилил, хотя там и для ядра 3. всякие патчи


      1. Johan_Palych
        28.09.2023 04:03
        +1

        По конфигу не осилил. Да с патчами он плотно поработал.
        https://github.com/TheRemote/Legendary-OrangePi-i96/blob/main/OrangePiRDA/kernel/arch/arm/configs/i96_linux_defconfig
        Проверил:

        sudo kpartx -av Legendary_OrangePi_i96_debian_bullseye_server_v1.37.img
        sudo parted -l
        /dev/mapper/loop0p1: 52,4MB
        /dev/mapper/loop0p2: 1375MB
        mount | grep loop0p
        /dev/mapper/loop0p1 on /media/user/BOOT type ext2 (rw,nosuid,nodev,relatime,uhelper=udisks2)
        /dev/mapper/loop0p2 on /media/user/rootfs type ext4 (rw,nosuid,nodev,relatime,stripe=1024,uhelper=udisks2)

        Да. Не осилил.


        1. NutsUnderline
          28.09.2023 04:03

          включил он в ядро поддержку кое-какого железа включая USB и интерфейс SPI (а на нем CAN) плюс какие то еще настройки ...


          1. Johan_Palych
            28.09.2023 04:03
            +1

            Чип RDA8810PL использует набор команд ARMv7 с аппаратной поддержкой команд с плавающей запятой.

            Можно попробовать подсунуть alpine-minirootfs вместо busybox. Больше возможностей по софту.
            https://mirror.yandex.ru/mirrors/alpine/latest-stable/releases/armhf/alpine-minirootfs-3.18.4-armhf.tar.gz
            просто ARMv7
            https://mirror.yandex.ru/mirrors/alpine/latest-stable/releases/armv7/alpine-minirootfs-3.18.4-armv7.tar.gz


            1. NutsUnderline
              28.09.2023 04:03

              Я могу ошибаться но выглядит это не совсем так. busybox это по большому счету не rootfs а только набор утилит командной строки, которые в rootfs еще надо разместить, в общем то, по своему усмотрению, что, собтвенно, здесь и показано. Я встречал заявления что busybox - нечто большее, но пока я не вижу полноценной законченной среды.

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

              alpine-minirootfs - сущность такого же рода, только система пакетов уже другая и и используется библиотека musl вместо glibc, а это может потянут за собой очень существенные изменения.

              С учетом характера платы я считаю более разумным прикрутить к ней OpenWrt, это тоже вполне полноценный linux расчитанный на малые ресурсы. Я даже не одинок в этом мнении https://forum.openwrt.org/t/support-for-the-rda8810pl/148380


              1. deema35 Автор
                28.09.2023 04:03

                OpenWrt хорош если разработчики поддержали плату которая тебе нужна. А если её поддержки нет проще будет собрать прошивку из исходников вручную, чем пытаться как-то экспериментировать с OpenWrt.