RV1106移植Alpine系统

前言说明

  1. 我移植的硬件设备是:SoloLinker-A V1版本 RV1106G3 256M内存 8G储存EMMC

  2. 运行系统用的是:RV1106-SOLOLINKER-EMMC_WIFI_UBUNTU

  3. RV1106的架构为 armv7l/armhf ,本教程直接在 SoloLinker-A 上运行 Ubuntu 来获取 Alpine 系统的 ROOTFS

开始移植

  1. 获取工具
1
2
3
4
5
# Alpine官方提供了一个获取ROOTFS的脚本,如无法访问github也可解开官方alpine-make-rootfs包提取
# https://mirrors.ustc.edu.cn/alpine/v3.19/main/armhf/alpine-make-rootfs-0.7.0-r0.apk

wget https://github.com/alpinelinux/alpine-make-rootfs/raw/master/alpine-make-rootfs
chmod +x alpine-make-rootfs
  1. 运行命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 通过下面这段命令就可以获取Alpine系统的ROOTFS

APK_OPTS="--allow-untrusted" \
APK_TOOLS_URI="https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v3.0.0_pre1/armhf/apk.static" \
APK_TOOLS_SHA256="b415d215943ef71fe27fc3508c61d05f9b7a9067531b751aad9ace08d982136d" \
/root/alpine-make-rootfs \
--branch v3.19 \
--mirror-uri 'http://mirrors.ustc.edu.cn/alpine' \
--packages 'busybox-suid apk-tools openrc agetty e2fsprogs-extra findmnt openssh-server wpa_supplicant wireless-regdb bluez bluez-deprecated alsa-utils' \
--timezone 'Asia/Shanghai' \
--script-chroot \
alpine-rootfs-$(date +%Y%m%d).tar.gz - <<'SHELL'
cat > /etc/inittab << EOF
# Startup the system
::sysinit:/bin/hostname -F /etc/hostname
::sysinit:/sbin/openrc sysinit
::sysinit:/sbin/openrc boot
::wait:/sbin/openrc default
::sysinit:/bin/mount -t tmpfs tmpfs /tmp
::sysinit:/bin/mount -o remount,rw,noatime /
::sysinit:/etc/init.d/rcS

# Set up a couple of getty's
ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100

# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/openrc shutdown
EOF
echo root:root|chpasswd
chmod u+rw /bin/bbsuid
echo ttyFIQ0 > /etc/securetty
echo 'Alpine' > /etc/hostname
sed -i 's/localhost /Alpine /g' /etc/hosts
echo 'nameserver 114.114.114.114' > /etc/resolv.conf
rc-update add devfs boot
rc-update add procfs boot
rc-update add sysfs boot
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config
rc-update add sshd default
echo "Asia/Shanghai" > /etc/timezone
SHELL
  1. 命令解释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 开头三行变量APK_OPTS:允许不可信,APK_TOOLS_URI:apk工具的下载路径,APK_TOOLS_SHA256:apk工具的sha256sum值

# 指定Alpine分支为v3.19,包源地址,需要安装的包,指定时区:Asia/Shanghai

# 后面SHELL中的内容为chroot到Alpine的rootfs中执行的命令


# /sbin/init启动后会读取/etc/inittab来设置相关
# 开机自动从指定文件设置主机名
::sysinit:/bin/hostname -F /etc/hostname

# 不需要写/etc/fstab,因为procfs devfs sysfs在openrc中挂载了,而如下则在 /etc/inittab 运行时挂载了
# 根文件系统 remount要放在openrc启动后面,否则/还没挂载的话执行也不起作用
::sysinit:/bin/mount -t tmpfs tmpfs /tmp
::sysinit:/bin/mount -o remount,rw,noatime /

# 串口调试时通过ttyFIQ0自动以root用户登陆
ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100

# 设置启用busybox那样的rcS启动脚本方式
::sysinit:/etc/init.d/rcS
::shutdown:/etc/init.d/rcK


# 使用SHELL修改密码:echo username:password|chpasswd

# 给 bbsuid rw 权限,否则SDK编译时会因无法覆盖/bin/bbsuid而导致没有mount命令挂载失败
chmod u+rw /bin/bbsuid

# 配置串口登陆
echo ttyFIQ0 > /etc/securetty

# 修改默认主机名
echo 'Alpine' > /etc/hostname
sed -i 's/localhost /Alpine /g' /etc/hosts

# 设置DNS解析
echo 'nameserver 114.114.114.114' > /etc/resolv.conf

# 通过openrc挂载procfs devfs sysfs系统
rc-update add devfs boot
rc-update add procfs boot
rc-update add sysfs boot

# 由于Alpine默认是关闭root用户ssh登录的,这里设置允许root登录并启用开机运行
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config
rc-update add sshd default

# 设置时区为Shanghai
echo "Asia/Shanghai" > /etc/timezone
  1. 通过上面的步骤,可以获取到 alpine-rootfs-20240401.tar.gz 根文件系统。后期如果想修改或添加其他则可以参考如下操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 解包后直接操作文件或chroot执行里面的命令
mkdir /root/alpine-rootfs && cd /root/alpine-rootfs
tar -xzf /root/alpine-rootfs-20240401.tar.gz

---------------------------------------------------------------------------------
# 以将准备好的文件拷贝到rootfs为例
SRC_PATH=/root/temp
TAR_PATH=/root/alpine-rootfs

cp $SRC_PATH/etc/init.d/rcS $TAR_PATH/etc/init.d/
cp $SRC_PATH/etc/init.d/rcK $TAR_PATH/etc/init.d/

# 开启ADB支持
cp $SRC_PATH/usr/bin/adbd $TAR_PATH/usr/bin/
cp $SRC_PATH/etc/init.d/S50usbdevice $TAR_PATH/etc/init.d/

# 磁盘扩容脚本
cp $SRC_PATH/usr/bin/disk-helper $TAR_PATH/usr/bin/
cp $SRC_PATH/usr/bin/resize-helper $TAR_PATH/usr/bin/
cp $SRC_PATH/etc/init.d/S99_First_Resize_Disk $TAR_PATH/etc/init.d/T_S99_First_Resize_Disk

# 拷贝无线连接模板
cp $SRC_PATH/etc/init.d/S55_Start_Wlan_BT $TAR_PATH/etc/init.d/T_S55_Start_Wlan_BT
cp $SRC_PATH/etc/init.d/S80_Connect_Wifi $TAR_PATH/etc/init.d/T_S80_Connect_Wifi
cp $SRC_PATH/etc/wpa_supplicant/wpa_supplicant.conf $TAR_PATH/etc/wpa_supplicant/wpa_supplicant.conf

# 拷贝LVGL的DEMO及运行库
cp $SRC_PATH/opt/lvgl_app $TAR_PATH/opt/
cp $SRC_PATH/lib/ld-uClibc.so.0 $TAR_PATH/lib/
cp $SRC_PATH/lib/libc.so.0 $TAR_PATH/lib/
---------------------------------------------------------------------------------

# 重新打包成
tar -czf /root/rootfs_alpine.tar.gz .

相关脚本

  1. 启动时执行的脚本:/etc/init.d/rcS 和关闭时执行的脚本:/etc/init.d/rcK
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# BusyBox init 会在启动后读取/etc/inittab文件,开机后会执行字段为 sysinit 的语句中的命令,关机前先执行字段为 shutdown 的语句中的命令
::sysinit:/etc/init.d/rcS
::shutdown:/etc/init.d/rcK

# 从上可知 sysinit 系列的语句最后一条会去执行 /etc/init.d/rcS 脚本
# 即开机时其它工作完成后会执行 rcS 这个脚本,同样的会在关机时先执行 /etc/init.d/ 目录下的 rcK 脚本

# 从 /etc/init.d/rcS 的内容可知该脚本会依据文件名排序依次读取 /etc/init.d/ 目录下名字为 S??* 格式的脚本文件,并执行其中的 start 方法
# 同样的 rcK 脚本会依据文件名逆排序读取 /etc/init.d/ 目录下名字为 S??* 格式的脚本文件,并执行其中的 stop 方法

# 注意:目录 /etc/init.d/ 下S前缀的脚本执行顺序是从 0 到 9,从 a 到 z。后续这些脚本都需要赋予可执行文件权限,否则会无法运行!!!

# 脚本 /etc/init.d/rcS 中的内容如下:
#!/bin/sh
for i in /etc/init.d/S??* ;do
[ ! -f "$i" ] && continue

case "$i" in
*.sh)
(
trap - INT QUIT TSTP
set start
. $i
)
;;
*)
$i start
;;
esac
done

-------------------------------------------------

# 脚本 /etc/init.d/rcK 中的内容如下:
#!/bin/sh
for i in $(ls /etc/init.d/S??*) ;do
[ ! -f "$i" ] && continue
case "$i" in
*.sh)
(
trap - INT QUIT TSTP
set stop
. $i
)
;;
*)
$i stop
;;
esac
done

# 也可用Alpine系统的OpenRC服务来实现开机自动运行
# 若要在开机时启动 /etc/local.d/ 下的脚本,请将其添加到默认运行级别:rc-update add local default
# 这样系统启动时会执行 /etc/local.d/ 下以 .start 结尾的脚本,系统关闭时则会运行以 .stop 结尾的脚本
  1. 添加通过 ADB 连接到 Alpine 系统的功能
1
2
3
4
5
6
# 下面这两个文件自己去 sololinker/sysdrv/tools/board/rootfs_ubuntu.tar.gz 中提取放到Alpine根文件系统对应位置
# 用USB连接电脑与RV1106设备,上电后片刻电脑即可看到有Android设备接入,电脑端通过adb shell可与Alpine系统交互
# 注意:未短接4,6针脚接入电脑才能看到有Android设备接入,下面两个提取的文件拷贝到Alpine后一定要赋予执行权限

/usr/bin/adbd # ADB 服务器
/etc/init.d/S50usbdevice # 开机自启脚本
  1. 通过 rcS 方式启动和连接WiFi的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 设置硬件资源和加载无线网卡驱动(V2版本请改4为6)
cat > /etc/init.d/S50_Start_Wlan_BT << "EOF"
#!/bin/sh

GPIO_PIN=4
GPIO_PATH="/sys/class/gpio/gpio$GPIO_PIN"

case $1 in
start)
echo $GPIO_PIN > /sys/class/gpio/export
echo out > $GPIO_PATH/direction
echo 1 > $GPIO_PATH/value
echo host > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode
insmod /lib/modules/5.10.160/aic_load_fw.ko
insmod /lib/modules/5.10.160/aic8800_fdrv.ko
insmod /lib/modules/5.10.160/aic_btusb.ko
;;
stop)
echo 0 > $GPIO_PATH/value
echo $GPIO_PIN > /sys/class/gpio/unexport
echo otg > /sys/devices/platform/ff3e0000.usb2-phy/otg_mode
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
EOF

-------------------------------------------------------------------------------
# 连接到设置好的WiFi
cat > /etc/init.d/S80_Connect_Wifi << "EOF"
#!/bin/sh

case $1 in
start)
sleep 10
ifconfig wlan0 up
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
udhcpc -i wlan0
;;
stop)
pkill -f "udhcpc -i wlan0"
pkill -f "wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf"
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
EOF

-------------------------------------------------------------------------------
# 上面的无线配置文件 /etc/wpa_supplicant/wpa_supplicant.conf 既可使用如下命令自动写入
wpa_passphrase "SSID" "password" > /etc/wpa_supplicant/wpa_supplicant.conf

# 也可以手动将如下内容写入配置文件:/etc/wpa_supplicant/wpa_supplicant.conf
cat > /etc/wpa_supplicant/wpa_supplicant.conf << EOF
network={
ssid="RV1106"
psk="1234567890"
priority=9
}
EOF
  1. 刷入后让首次运行执行扩容脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 如果不存在 /var/lib/misc/firstrun 文件则运行 /usr/bin/resize-helper 后创建不存在的文件
cat > /etc/init.d/S90_First_Resize_Disk << "EOF"
#!/bin/sh
PATH=/usr/sbin:/usr/bin:/sbin:/bin

case "$1" in
start)
if [ ! -e /var/lib/misc/firstrun ]; then
/usr/bin/resize-helper
mkdir -p /var/lib/misc
touch /var/lib/misc/firstrun
fi
;;
stop)
;;
restart|reload)
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac

exit $?

EOF

# 下面这两个脚本文件由于太长了自己去 sololinker/sysdrv/tools/board/rootfs_ubuntu.tar.gz 中提取
/usr/bin/resize-helper
/usr/bin/disk-helper

其他说明

  1. Alpine使用apk作为包管理工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 换其他源示例
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

apk update # 更新最新的本地镜像源
apk upgrade # 升级所有已装的软件包
apk add alpine-baselayout # 安装包
apk del wpa_supplicant # 卸载包

apk info # 列出所有已装的软件包
apk info -a zlib # 显示完整的软件包信息
apk info --who-owns /bin/su # 显示指定文件属于的包
apk search alpine-base # 查找所有可用的软件包

# 一些其它包在Alpine中的包名
tzdata openntpd bridge-utils apk-tools busybox-suid mount umount
openrc openssh-server wpa_supplicant util-linux bash-completion
  1. Alpine使用OpenRC的启动服务相关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Alpine使用OpenRC管理服务启动、停止、查看状态及加入启动项,但它默认没有安装
# 所有可用的服务都在 /etc/init.d/ 目录下,常见的命令有以下几个
rc-status # 查看默认级别的运行服务
rc-update add # 添加服务到运行级别服务清单
rc-update del # 从对应运行级别服务清单删除服务
rc-service # 管理服务启停

rc-update add procfs boot # 添加个boot级别的服务
rc-update add nginx default # 添加个default级别的服务
rc-update del nginx # 删除nginx服务

rc-service networking restart # 手动重启网络服务

# OpenRC在/etc/init.d维护每个service的控制脚本,它的配置文件是:/etc/rc.conf
# 通过 /etc/init.d/<service> <start|stop|restart|zap> 的方式控制服务开启、关闭、重启
# 或者 rc-service <service> <start|stop|restart|zap> 的方式控制服务开启、关闭、重启

# 若要在系统启动时执行 /etc/local.d/ 下以 .start 结尾的脚本,系统关闭时则会运行以 .stop 结尾的脚本
# 请将其添加到默认运行级别:rc-update add local default
# 但这些脚本会延缓你的开机和关机进程。所以尽量不要把需要大量时间运行的脚本放在该目录
  1. Alpine的有线网络管理
1
2
3
4
5
6
7
8
9
10
# 如果不需要通过eth0联网,则可跳过这步。因为如果没有插入网线则会在 udhcpc: broadcasting discover 处卡一会

# Alpine使用 ifupdown-ng 来管理网络,可以编辑 /etc/network/interfaces 来配置它
cat > /etc/network/interfaces << EOF
auto eth0
iface eth0 inet dhcp
EOF

# 设置开机启动后主机名会自动设置,会自动运行udhcpc为eth0服务
rc-update add networking default
  1. Ubuntu系统中的cat /etc/fstab 的内容
1
2
3
4
5
6
7
8
9
10
# <file system>		<mount pt>		<type>		<options>		<dump>	<pass>
/dev/root / ext2 rw,noauto 0 1
proc /proc proc defaults 0 0
devpts /dev/pts devpts defaults,gid=5,mode=620 0 0
tmpfs /dev/shm tmpfs mode=0777 0 0
tmpfs /tmp tmpfs mode=1777 0 0
tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
sysfs /sys sysfs defaults 0 0
debug /sys/kernel/debug debugfs defaults 0 0
pstore /sys/fs/pstore pstore defaults 0 0

构建镜像

  1. 提供两种方式将我们移植的Alpine系统构建成可刷入的镜像

  2. 方法一:替换根文件系统后开始编译

1
2
3
4
5
6
7
8
9
# 先备份原来SDK中的Ubuntu根文件系统,然后拷贝alpine-rootfs-20240401.tar.gz到该目录与Ubuntu的同名
mv sololinker/sysdrv/tools/board/rootfs_ubuntu.tar.gz sololinker/sysdrv/tools/board/bak_rootfs_ubuntu.tar.gz
cp /mnt/d/alpine-rootfs-20240401.tar.gz sololinker/sysdrv/tools/board/rootfs_ubuntu.tar.gz

# 开始编译
cd sololinker/
./build.sh lunch # 选择编译的方案,我这选的是 0. BoardConfig_IPC/BoardConfig-EMMC-NONE-Hinlink-sololinker-A-Ubuntu.mk
./build.sh clean # 如出错误先清理
./build.sh # 开始编译
  1. 方法二:通过SDK提供的工具来构建镜像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 拷贝SDK的内核源码和uboot源码(以下用8G的EMMC版本为例)
cp -r /home/leux/sololinker/sysdrv/source/uboot/rkbin /home/leux/alpine/
cp -r /home/leux/sololinker/sysdrv/source/uboot/u-boot /home/leux/alpine/
cp -r /home/leux/sololinker/sysdrv/source/kernel/ /home/leux/alpine/

# 设置交叉编译工具链
export PATH=$PATH:/home/leux/sololinker/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin

# 创建ENV镜像文件(注意:EMMC和NAND的.env.txt内容不相同,project/build.sh中有定义,如果想从SD卡启动请将mmcblk0改为mmcblk1)
cat > /home/leux/alpine/out/.env.txt << EOF
blkdevparts=mmcblk0:32K(env),512K@32K(idblock),256K(uboot),32M(boot),7G(rootfs)
sys_bootargs= root=/dev/mmcblk0p5 rootfstype=ext4 rk_dma_heap_cma=32M
sd_parts=mmcblk0:16K@512(env),512K@32K(idblock),4M(uboot)
EOF

cd /home/leux/alpine/out
/home/leux/sololinker/sysdrv/out/bin/pc/mkenvimage -s 0x8000 -p 0x0 -o /home/leux/alpine/out/env.img /home/leux/alpine/out/.env.txt


# 编译u-boot及拷贝生成的镜像
cd /home/leux/alpine/u-boot
make rv1106_defconfig rk-emmc.config
./make.sh --spl-new CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf-
cp /home/leux/alpine/u-boot/rv1106_download_v1.13.107.bin /home/leux/alpine/out/download.bin
cp /home/leux/alpine/u-boot/rv1106_idblock_v1.13.101.img /home/leux/alpine/out/idblock.img
cp /home/leux/alpine/u-boot/uboot.img /home/leux/alpine/out/uboot.img

# 编译内核及拷贝内核镜像
cd /home/leux/alpine/kernel/
make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- rv1106_linux_sololinker_defconfig
make ARCH=arm CROSS_COMPILE=arm-rockchip830-linux-uclibcgnueabihf- BOOT_ITS=boot.its rv1106g-hinlink-sololinker-a.img -j8
cp /home/leux/alpine/kernel/boot.img /home/leux/alpine/out

# 用移植的Alpine系统构建根文件镜像(注意根文件系统的所有者权限)
tar -xvf /home/leux/alpine/rootfs_alpine.tar.gz -C /home/leux/alpine/rootfs
sudo chown -R root:root /home/leux/alpine/rootfs
sudo mkfs.ext4 -d /home/leux/alpine/rootfs -r 1 -N 0 -m 5 -L "" -O ^64bit,^huge_file /home/leux/alpine/out/rootfs.img "7168M"
sudo resize2fs -M /home/leux/alpine/out/rootfs.img
sudo e2fsck -fy /home/leux/alpine/out/rootfs.img
sudo tune2fs -m 5 /home/leux/alpine/out/rootfs.img
sudo resize2fs -M /home/leux/alpine/out/rootfs.img