Firefly-RK3399从Ubuntu 16.04到自定义Rootfs:手把手教你编译内核与打包固件
在嵌入式开发领域,能够自主定制系统镜像是一项极具价值的能力。Firefly-RK3399作为一款性能强大的开发板,其开放的架构为开发者提供了深度定制的可能性。本文将带你从基础的Ubuntu 16.04系统出发,逐步深入到内核编译、驱动开发、根文件系统定制,最终生成完全属于自己的固件镜像。
1. 开发环境搭建与准备
在开始定制系统之前,我们需要准备一个稳定可靠的开发环境。推荐使用Ubuntu 18.04作为宿主机系统,因为它提供了良好的兼容性和完善的工具链支持。
首先安装必要的编译工具和依赖:
sudo apt-get update sudo apt-get install -y build-essential lzop libncurses5-dev libssl-dev git对于64位系统,还需要安装32位兼容库:
sudo apt-get install -y libc6:i386接下来获取Rockchip专用的mkbootimg工具:
git clone https://github.com/neo-technologies/rockchip-mkbootimg.git cd rockchip-mkbootimg make && sudo make install关键工具版本检查表:
| 工具名称 | 最低版本要求 | 检查命令 |
|---|---|---|
| gcc | 7.4.0 | gcc --version |
| make | 4.1 | make --version |
| git | 2.17 | git --version |
| libncurses5-dev | 6.1 | dpkg -l libncurses5-dev |
提示:如果在后续步骤中遇到奇怪的编译错误,首先检查工具链版本是否符合要求。
2. 获取与配置内核源码
Firefly-RK3399的内核源码可以从官方仓库获取:
git clone https://gitlab.com/TeeFirefly/linux-kernel.git cd linux-kernel获取交叉编译工具链:
git clone https://gitlab.com/TeeFirefly/prebuilts.git export PATH=$PATH:$(pwd)/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin配置内核使用Firefly的默认配置:
make ARCH=arm64 firefly_linux_defconfig对于自定义内核配置,可以使用menuconfig界面:
make ARCH=arm64 menuconfig在menuconfig界面中,你可以:
- 启用或禁用特定内核模块
- 调整系统参数
- 添加实验性功能支持
常见配置选项参考:
| 配置项 | 推荐设置 | 说明 |
|---|---|---|
| CONFIG_LOCALVERSION | 自定义 | 设置内核版本标识 |
| CONFIG_MODULES | Y | 启用模块支持 |
| CONFIG_CMDLINE | 保留默认 | 除非有特殊启动参数需求 |
| CONFIG_DEBUG_KERNEL | N | 生产环境建议关闭 |
| CONFIG_DRM_ROCKCHIP | Y | 必须启用以支持显示输出 |
3. 开发与集成自定义驱动
让我们以一个简单的"Hello World"驱动为例,演示如何将自定义代码集成到内核中。
在drivers目录下创建新目录并添加驱动文件:
mkdir -p drivers/hello cd drivers/hello创建hello.c驱动文件:
#include <linux/init.h> #include <linux/module.h> static int __init hello_init(void) { printk(KERN_INFO "Hello World from Firefly-RK3399!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye from Firefly-RK3399!\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple Hello World driver");创建对应的Makefile:
obj-$(CONFIG_HELLO) += hello.o创建Kconfig配置文件:
config HELLO tristate "Hello World driver" help This is a simple Hello World driver example.然后需要修改上一级目录的Makefile和Kconfig,将新驱动包含进构建系统:
cd .. echo "obj-y += hello/" >> Makefile在Kconfig中添加:
source "drivers/hello/Kconfig"重新配置内核,确保新驱动被启用:
make ARCH=arm64 menuconfig在Device Drivers菜单中找到并启用"Hello World driver"选项。
4. 编译内核与生成镜像
完成所有配置后,可以开始编译内核:
make ARCH=arm64 rk3399-firefly-linux.img -j$(nproc)编译过程可能会持续10-30分钟,取决于你的主机性能。编译完成后,会在内核根目录生成以下关键文件:
- kernel.img:内核镜像
- resource.img:资源镜像(包含设备树等)
常见问题排查:
编译失败提示缺少头文件:
- 检查是否安装了所有依赖库
- 确认交叉编译工具链路径设置正确
生成的镜像无法启动:
- 检查是否使用了正确的defconfig
- 确认设备树文件(rk3399-firefly-linux.dts)是否正确
模块加载失败:
- 检查内核版本是否匹配
- 确认模块签名设置
5. 构建自定义根文件系统
我们将使用Ubuntu Base 16.04作为基础,创建自定义的根文件系统。
首先获取基础文件系统:
wget http://cdimage.ubuntu.com/ubuntu-base/releases/16.04/release/ubuntu-base-16.04.2-base-arm64.tar.gz mkdir rootfs sudo tar -xpf ubuntu-base-16.04.2-base-arm64.tar.gz -C rootfs准备chroot环境:
sudo cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/ sudo cp -b /etc/resolv.conf rootfs/etc/resolv.conf配置软件源:
sudo sh -c 'echo "deb http://ports.ubuntu.com/ubuntu-ports xenial main universe" > rootfs/etc/apt/sources.list'进入chroot环境进行定制:
sudo chroot rootfs /bin/bash在chroot环境中进行基本配置:
apt update apt upgrade -y apt install -y sudo ssh net-tools vim添加用户并设置密码:
useradd -m -s /bin/bash firefly passwd firefly usermod -aG sudo firefly passwd root退出chroot环境后,创建镜像文件:
dd if=/dev/zero of=firefly-rootfs.img bs=1M count=2048 mkfs.ext4 firefly-rootfs.img mkdir tmp-mount sudo mount firefly-rootfs.img tmp-mount sudo cp -a rootfs/* tmp-mount/ sudo umount tmp-mount优化镜像大小:
e2fsck -p -f firefly-rootfs.img resize2fs -M firefly-rootfs.img6. 打包完整固件
现在我们已经有了所有必要的组件,可以打包成完整的update.img固件了。
首先准备打包目录结构:
rockdev/ ├── Image/ │ ├── kernel.img │ ├── resource.img │ ├── MiniLoaderAll.bin │ ├── uboot.img │ ├── trust.img │ └── firefly-rootfs.img └── package-filepackage-file内容示例:
# NAME Relative path # #HWDEF HWDEF package-file package-file bootloader Image/MiniLoaderAll.bin parameter Image/parameter.txt trust Image/trust.img uboot Image/uboot.img boot Image/kernel.img rootfs Image/firefly-rootfs.img使用RK工具链打包:
./rkImageMaker -RK3399 Image/MiniLoaderAll.bin Image/trust.img Image/uboot.img Image/kernel.img Image/resource.img firefly-update.img最终生成的firefly-update.img可以通过Rockchip的工具刷写到开发板中。
7. 高级定制技巧
7.1 优化启动速度
通过分析启动过程,可以识别并优化耗时环节:
# 在内核命令行添加initcall_debug和printk.time=1 console=ttyFIQ0 root=PARTUUID=614e0000-0000 rootfstype=ext4 initcall_debug printk.time=1启动时间优化策略:
- 并行初始化:确保内核配置启用了CONFIG_HAVE_KERNEL_XZ和CONFIG_HAVE_KERNEL_LZ4等压缩选项
- 减少不必要的服务:在根文件系统中禁用不需要的systemd服务
- 预链接库文件:减少动态链接时间
7.2 安全加固
生产环境部署前应考虑以下安全措施:
- 禁用root直接登录
- 配置防火墙规则
- 启用SELinux或AppArmor
- 定期更新安全补丁
安全加固示例:
# 在chroot环境中执行 apt install -y fail2ban sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config7.3 空间优化
对于存储空间有限的场景,可以考虑:
- 使用更小的基础系统(如Debian或Buildroot)
- 移除不必要的文档和语言包
- 使用squashfs等压缩文件系统
清理无用文件的命令:
apt-get clean rm -rf /usr/share/doc/* rm -rf /usr/share/man/*8. 调试与问题解决
开发过程中难免会遇到各种问题,以下是一些实用的调试技巧:
内核日志查看:
dmesg | less # 查看内核环形缓冲区 journalctl -k # 使用systemd的系统查看内核日志常见问题及解决方案:
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 系统无法启动 | 内核与rootfs不匹配 | 检查内核配置中的rootfs参数 |
| 驱动加载失败 | 内核版本不一致 | 重新编译驱动或内核 |
| 网络不可用 | 设备树配置错误 | 检查dts文件中的网络部分 |
| 显示异常 | DRM驱动问题 | 更新或重新配置显示驱动 |
| 系统随机崩溃 | 内存或电源问题 | 检查硬件连接和电源供应 |
性能分析工具:
perf:系统级性能分析
perf top -a # 实时监控系统性能strace:系统调用跟踪
strace -f -o log.txt ./your_programvalgrind:内存调试工具
valgrind --leak-check=full ./your_program
在实际项目中,我们曾遇到一个棘手的问题:自定义驱动导致系统随机死锁。通过使用kgdb进行内核调试,最终发现是自旋锁使用不当导致的。这个经验告诉我们,即使是最简单的驱动,也需要严格的测试和验证。