本文主要记录如何将 Alpine Linux 的 rootfs 移植到 RV1106 开发板上,重点是细节坑的处理。

手头有一个 LuckFox(幸狐)的开发板,型号是 Luckfox Pico Max,SOC 是瑞芯微的 RV1106,尺寸小巧,接口俱全,跟一个 U 盘差不多大小:

Luckfox Pico Max

规格方面,可以看官方 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 的方法。

  1. 首先需要更新 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
  1. 创建一个兼容 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
  1. 创建存储 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
  1. 启动 alpine-armhf 容器并登陆容器:
1
2
3
4
# 启动 alpine-armhf 容器
sudo lxc-start alpine-armhf
# 进入 alpine-armhf 系统
sudo lxc-attach -n alpine-armhf
  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
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
  1. 至此,我们需要的 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
  1. 此时退出 alpine-armhf 容器,在主机上 /tmp/my-rootfs 目录下有一个 alpine.tar.gz 文件,将其复制到 Luckfox 官方 BSP 的 sysdrv/custom_rootfs 目录下,没有则自行创建目录。

  2. 关闭容器并取消挂载。

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.

  1. 根据官方 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
  1. 修改 BSP 根目录下的 .BoardConfig.mk 文件(需要先 ./build.sh lunch 生成对应开发板配置).在文件末尾添加
1
export RK_CUSTOM_ROOTFS=../sysdrv/custom_rootfs/alpine.tar.gz
  1. 重新执行 ./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 线有问题,尽量选择全芯数据线,而不是充电线。

问题及方法

  1. ssh 登陆开发板一直提示 Permission denied, please try again,哪怕 root 密码输入是对的。

    这个问题就是开发板 root 密码加密用的是 sha256/sha512 哈希算法,但是 busybox 不支持导致的。查看 rootfs /etc/shadow 文件 root 行,如果 root: 后是 $1 开头,那么则是 md5 加密,其他数字就是其他加密方法。 要么 passwd -a md5 使用 md5 重新设置密码,要么就编译 BSP 时修改 busybox 配置,以支持这些加密方法。

  2. BSP 构建时,提示无法写入 bin/bbsuid 报错。

    这个问题参见 构建 rootfs 的第 6 步骤。

  3. 进入开发板系统后, mount 显示 ubi0:rootfsro 只读模式。

    这个问题同样参见 构建 rootfs 的第 6 步骤。可以修改 /etc/inittab 添加重挂载选项。

  4. 进入开发板系统后,rc-status -a 显示 sysfs 不在激活序列中,且已经 rc-update add sysfs boot 添加过了。

    修改 /etc/rc.conf 文件,将 rc_sys="lxc" 修改为 rc_sys="".