本文主要记录如何将 Alpine Linux 的 rootfs 移植到 RV1106 开发板上,重点是细节坑的处理。
手头有一个 LuckFox(幸狐)的开发板,型号是 Luckfox Pico Max,SOC 是瑞芯微的 RV1106,尺寸小巧,接口俱全,跟一个 U 盘差不多大小:
规格方面,可以看官方 Wiki 介绍,主要是如下这些:
- 瑞芯微 RV1106 Cortex A7 1.2G 单核
- 0.5 TOPS NPU
- 256MB DDR3L
- SPI NAND 256MB
- 10/100M 以太网
- 5M@30fps ISP + MIPI CS2 2-lane
- GPIO/UART/PWM/I2C/SPI/USB 等外设接口
虽然 RV1106 是为 IPC 应用设计的,但是这个价格和外设,在低端工控/物联网领域,性价比十足,这个顶配的 LuckFox
Pico Max 开发板,也才 ¥70 RMB 左右,加上 Linux 的开发效率,可以替代很多 MCU 的应用场景了。
自带的系统带有很多程序,主要是 IPC 应用。我们可以移植 Alpine Linux 的 rootfs 给这个开发板,这样就可以方面的配置系统和包管理了,而无需自己来编译移植各种程序。
Alpine Linux 的 rootfs 的移植主要参考社区的一篇文章「当幸狐来敲门」适配Alpine Linux下篇–适配Alpine Linux的详细步骤,但是实际操作起来有一些坑需要注意。
构建自己的 Alpine Linux rootfs
可以用 docker 镜像来提取 rootfs,也可以用 lxc 容器来做,这里介绍 lxc 的方法。
- 首先需要更新 lxc 的 alpine 文件模板,这个跟系统 lxc-templates 版本有关。主要是因为 alpine 的验证 keys 更新,系统 apt 安装的 lxc-templates 还未引入这个变更。
1
2
3
4
|
# 修改文件 /usr/share/lxc/templates/lxc-alpine
# 参考上游代码变更 <https://github.com/lxc/lxc-templates/commit/2163a7e4e04383696bb88a9da2f7fa61ab4781b7>
# 更新 APK_KEYS_SHA256 和 APK_KEYS_URI
sudo vim /usr/share/lxc/templates/lxc-alpine
|
- 创建一个兼容 arm A7 架构(armv7l/armhf) 的 lxc 容器,这里容器名称为 alpine-armhf:
1
2
3
4
5
|
# debian 系系统中 lxc 中 armv7 架构已经改名为 armhf
# 这里默认会安装 alpine 3.18 版本,如果想使用滚动发行版本 edge
# 则命令为
# sudo lxc-create -n alpine-armhf -t alpine -- --release edge --arch armhf
sudo lxc-create -n alpine-armhf -t alpine -- --arch armhf
|
- 创建存储 rootfs 的文件并挂载到容器中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 这部分参考文章中使用文件挂载。
# 其实在 lxc 中更为简单,因为每个容器的 rootfs 都存在系统中,可以直接访问
# /var/lib/lxc/[container-name]`/rootfs
#
# 创建临时文件,用来存储 rootfs
dd if=/dev/zero of=rootfs.ext4 bs=1M count=100
# 格式化文件,创建文件系统
sudo mkfs.ext4 rootfs.ext4
# 挂在临时文件到 /tmp/my-rootfs
mkdir /tmp/my-rootfs
sudo mount rootfs.ext4 /tmp/my-rootfs
# 编辑 alpine-armhf config 文件,映射挂载点 LXC /mnt -> HOST /tmp/my-rootfs
# 在 /var/lib/lxc/alpine-armhf/config 文件末尾添加如下行
lxc.mount.entry = /tmp/my-rootfs mnt none bind,create=file 0 0
|
- 启动 alpine-armhf 容器并登陆容器:
1
2
3
4
|
# 启动 alpine-armhf 容器
sudo lxc-start alpine-armhf
# 进入 alpine-armhf 系统
sudo lxc-attach -n alpine-armhf
|
- 进行基本的配置:
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# 注意,这里的命令都是在 alpine-armhf 容器中操作
# 使用 alpine 中科大镜像源
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
apk update
# 安装 openrc
apk add openrc
# 配置 openrc 添加必要启动服务
rc-update add devfs boot
rc-update add procfs boot
rc-update add sysfs boot
# 配置串口登陆
echo ttyFIQ0 >> /etc/securetty
# 编辑 /etc/inittab 文件
# 删除 tty1 到 tty6 的行
# 添加 ttyFIQ0 行
ttyFIQ0::respawn:/sbin/getty -L ttyFIQ0 0 vt100
# 如果需要 ttyFIQ0 自动登陆,则添加
ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100
# 并安装 agettty 包
apk add agetty
# 修改 hostname
echo "YourHostname" > /etc/hostname
# 安装 dropbear ssh server
# dropbear 比 openssh-server 更节省空间
apk add dropbear dropbear-dbclient dropbear-openrc dropbear-scp
# 添加 dropbear 自动启动
rc-update add dropbear default
# 设置密码
# 需要注意的是,这里暂时需要指定 root 密码加密方法为 md5,否则会使用 sha256
# 但是 BSP 中编译的 busybox 版本只支持 md5,BSP 版本会覆盖安装 rootfs 中的 busybox
# 会导致 ssh 登陆失败
# 另外一种解决方法是,编译 BSP 时,修改 busybox 配置
# make busybox_menuconfig
# 重新设置 root 密码
passwd -a md5
# 配置网络
# alpine 使用 ifupdown-ng 来管理网络
# 可以编辑 /etc/network/interfaces
# DHCP:
# auto eth0
# iface eth0 inet dhcp
# 固定 IP
# auto eth0
# iface eth0 inet static
# address 192.168.100.100
# netmask 255.255.255.0
# gateway 192.168.100.1
# 根据需要修改,但是不建议修改容器本身的,因为容器需要 dhcp 来接入网络更新 apk
vi /etc/network/interfaces
# 添加网络自启动
rc-update add networking default
# 设置时区
apk add tzdata
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone
# 节省空间
apk del tzdata
# 添加基本包
apk add bash bash-completion
apk add util-linux
# 添加 mtd ubi 工具
apk add mtd-ubi
# 修改默认 root 登陆 shell
# 修改 /etc/passwd 文件
# 将 root 行末尾的 /bin/ash 改为 /bin/bash
vi /etc/passwd
|
- 至此,我们需要的 rootfs 已经在容器中构建,现在需要进行打包和“最后的修改”:
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
|
# 这里仍然在 alpine-armhf 容器中操作
cd /
for d in bin etc lib root sbin usr; do tar c "$d" | tar x -C /mnt; done
for dir in dev proc run sys var; do mkdir /mnt/${dir}; done
# 进行“最后的修改”
# alpine 中程序是可以感知到在容器中运行,所以有些配置跟实际板子上不同,这里需要
# 做一些修改,但是这些修改不适合容器本身,所以在这里加上一些“最后的修改”
cd /mnt
# 以下操作均在 /mnt 进行,且文件前无 / 前缀
# 给 bbsuid rw 权限,否则 BSP 编译时会提示无法覆盖 /bin/bbsuid
chmod u+rw bin/bbsuid
# 修改 openrc 配置文件,去掉 lxc 模式
# 找到 rc_sys="lxc" 行,改为
# rc-sys=""
# 否则有些服务会在板子上不启动,比如 sysfs,会导致其他问题
vi etc/rc.conf
# NOTE: 修改 /etc/inittab 文件,加上 root 重挂载命令:
# 官方的 BSP 中也有这个配置,否则板子启动后为默认为只读 root 文件系统
# 在 ::sysinit:/sbin/openrc sysinit 行前加上一行
#
# ::sysinit:/bin/mount -o remount,rw,noatime /
# 这行是将 root 重挂载为可读写模式 noatime 可以提高性能
#
# 同时,删除或用 # 注释掉最后两行的 Main LXC console console
# # Main LXC console console
# #::respawn:/sbin/getty 38400 console
vi etc/inittab
# 打包 rootfs
cd /mnt
tar czf alpine.tar.gz *
# 此时,/mnt/alpine.tar.gz 为我们打包好的 rootfs
|
-
此时退出 alpine-armhf 容器,在主机上 /tmp/my-rootfs
目录下有一个 alpine.tar.gz 文件,将其复制到 Luckfox 官方 BSP 的 sysdrv/custom_rootfs
目录下,没有则自行创建目录。
-
关闭容器并取消挂载。
1
2
3
|
sudo lxc-stop alpine-armhf
sudo umount /tmp/my-roofs
rm rootfs.ext4
|
修改编译设置
开发板官方 BSP 搭建可以参考 SDK 环境搭建,我这里使用了 lxc 创建构建环境,没有使用虚拟机,BSP 仓库地址为 https://github.com/LuckfoxTECH/luckfox-pico.git.
- 根据官方 wiki 创建好环境后,修改 SDK 根目录的 build.sh 文件,找到 1044 行
function __PACKAGE_ROOTFS()
函数,将 build_get_sdk_version
前内容修改为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function __PACKAGE_ROOTFS()
{
local rootfs_tarball _target_dir _install_dir
if [ -f $rootfs_tarball ]; then
if [ -z $RK_CUSTOM_ROOTFS ]; then
rootfs_tarball="$RK_PROJECT_PATH_SYSDRV/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}.tar"
tar xf $rootfs_tarball -C $RK_PROJECT_OUTPUT
else
rootfs_tarball="$RK_CUSTOM_ROOTFS"
if [ ! -d $RK_PROJECT_PACKAGE_ROOTFS_DIR ]; then
mkdir $RK_PROJECT_PACKAGE_ROOTFS_DIR
fi
tar xf $rootfs_tarball -C $RK_PROJECT_PACKAGE_ROOTFS_DIR
fi
else
msg_error "Not found rootfs tarball: $rootfs_tarball"
exit 1
fi
build_get_sdk_version
|
- 修改 BSP 根目录下的 .BoardConfig.mk 文件(需要先 ./build.sh lunch 生成对应开发板配置).在文件末尾添加
1
|
export RK_CUSTOM_ROOTFS=../sysdrv/custom_rootfs/alpine.tar.gz
|
- 重新执行 ./build.sh 命令生成镜像即可。
自定义系统配置
结合了另外一篇文章 RV1103 魔改,我也对开发板的配置进行了修改,主要也是减少 CMA 内存和调整 NAND 布局,将整个 NAND 都作为根分区使用,去掉了 oem 和 userdata. 这里要修改 BSP 目录下的 .BoardConfig.mk 文件:
1
2
3
4
5
6
7
8
9
|
# BoardConfig.mk 文件的介绍可以参见 BSP 目录下的 project/cfg-all-iterms-instroction.txt 文件
# 减少 CMA 内存,默认为 66 M,也可以配置更少,比如 1M
# 这个主要影响图像视频的 AI 处理
# Config CMA size in environment
export RK_BOOTARGS_CMA_SIZE="24M"
# 配置分区布局,仅使用 rootfs 分区
export RK_PARTITION_CMD_IN_ENV="256K(env),256K@256K(idblock),512K(uboot),12M(boot),232M(rootfs)"
export RK_PARTITION_FS_TYPE_CFG=rootfs@IGNORE@ubifs
|
更大的 rootfs 分区可以安装更多的包。修改配置后重新执行 ./build.sh 构建即可。
刷机注意事项
构建好镜像后,将 image 复制到 Windows 刷机时,需要注意 rootfs.img/oem.img/userdata.img 均为链接,其大小可能为 1kb,再使用刷机工具时,这几个 img 需要选择称对应的实际文件,而非链接本身,否则不会执行刷入。
也可以在刷机工具下方选择更新固件,刷入 update.img.
如果刷机工具一直无法找到 Maskrom 设备,则有可能是用的 typec 线有问题,尽量选择全芯数据线,而不是充电线。
问题及方法
-
ssh 登陆开发板一直提示 Permission denied, please try again
,哪怕 root 密码输入是对的。
这个问题就是开发板 root 密码加密用的是 sha256/sha512 哈希算法,但是 busybox 不支持导致的。查看 rootfs
/etc/shadow
文件 root 行,如果 root:
后是 $1
开头,那么则是 md5 加密,其他数字就是其他加密方法。
要么 passwd -a md5
使用 md5 重新设置密码,要么就编译 BSP 时修改 busybox 配置,以支持这些加密方法。
-
BSP 构建时,提示无法写入 bin/bbsuid
报错。
这个问题参见 构建 rootfs 的第 6 步骤。
-
进入开发板系统后, mount
显示 ubi0:rootfs
为 ro
只读模式。
这个问题同样参见 构建 rootfs 的第 6 步骤。可以修改 /etc/inittab
添加重挂载选项。
-
进入开发板系统后,rc-status -a
显示 sysfs
不在激活序列中,且已经 rc-update add sysfs boot
添加过了。
修改 /etc/rc.conf
文件,将 rc_sys="lxc"
修改为 rc_sys=""
.