RK3399移植u-boot&linux内核&根文件系统

前言

  • 我们稍加查询资料,便可以知道,在ARM平台运行linux系统,需要bootloader,linux内核和根文件系统

  • 我们查询资料,也可以知道,需要交叉编译,即在PC平台上,对以上源代码进行编译获取相应的文件,烧录到嵌入式平台的相应的位置上,嵌入式平台才能正常运行。

  • 既然要编译源代码,那么我们就先要知道,从源代码中编译出什么样的文件是启动平台所必须的,Rockchip官网给出的RK3399的启动流程(Boot option - Rockchip open source Document (rock-chips.com)):
    在这里插入图片描述

    • 从图中可以看出,要从eMMC/CD启动系统,有两条路线,在图中分别由绿色箭头Boot Flow1和红色箭头BootFlow2指示,BootFlow2适用于大多数SOC,本文中我们选择BootFlow2的流程启动RK3399
    • 从图中可以看出,系统启动过程需要4个文件
      • idbloader.img
      • u-boot.itb
      • boot.img
      • rootfs.img
    • 从图中可以看出,编译出以上4个文件,每个阶段需要的文件来源不同,由颜色可以看出:
      • 编译出idbloader.img有u-boot源码就可以
      • 编译出u-boot.itb,需要u-boot源码、ARM信赖文件-Rockchip rkbin文件,即图中bl31.elf文件
      • 编译出boot.img需要linux内核源码及其他文件
      • rootfs.img也即根文件,这里选择使用busybox编译出根文件
  • 相关平台

    • Ubuntu18.04
    • 交叉编译平台aarch64-linux-gnu-
    • Firefly-RK3399开发板

准备编译环境

  • 更新
sudo apt-get update
  • 安装交叉编译工具
sudo apt-get install gcc-aarch64-linux-gnu

安装后路径/usr/aarch64-linu-gnu/

移植u-boot 2022.10

有前面描述可知,由U-boot源码和安全文件编译出两个启动文件:idbloader.img和u-boot.itb

获取安全文件

RK3399是Arm64,启动还需要ATF(Arm Trust Firmware),ATF主要负责在启动U-boot之前把CPU从安全的EL3切换到EL2,然后跳转到U-boot,并且在内核启动后负责启动其他的CPU,安全文件的名称是bl31.elf。

可以下载源代码自行编译,下载源代码执行以下命令:

git clone https://github.com/ARM-software/arm-trusted-firmware.git

也可以下载官方已经编译好的文件,下载地址https://github.com/rockchip-linux/rkbin/tree/master/bin/rk33/
文件名称为:rk3399_bl31_v1.36.elf,将该文件下载到本地
这里选择直接下载已经编译好的

获取U-boot源码

从U-boot的官方网站或代码仓库中获取U-boot 2022.10的源码:

wget https://ftp.denx.de/pub/u-boot/u-boot-2022.10.tar.bz2

解压源码包,解压后的文件夹名称为:u-boot-2022.10

tar -jxf u-boot-2022.10.tar.bz2

编译uboot

将rk3399_bl31_v1.36.elf文件移动到u-boot-2022.10文件夹内

进入u-boot-2022.10路径下,指定BL31文件

export BL31=rk3399_bl31_v1.35.elf

在u-boot-2022.10路径下编译(firefly-rk3399_defconfig文件在u-boot/configs/路径下,内容的意义参考[[RK3399_defconfig内语句的意义]]):

make firefly-rk3399_defconfig V=1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

等待编译结束,中间若报错,多半是缺少依赖,依次安装就可以
注意:uboot-2022.10使用pyhon3,Ubuntu18.04默认的python环境指向python2,注意环境适配,如果不想更改系统的环境变量,可以采用低版本的uboot,或者采用高版本的Ubuntu来规避此问题

编译完成后在u-boot-2022.10文件夹内可以找到生成的idbloader.img和u-boot.itb,这两个文件连同后面编译的内核文件boot.img和根文件系统文件一同烧录

移植kernel 4.4

下载内核源码

这里采用瑞芯微官方提供的内核源码:https://github.com/rockchip-linux/kernel/,该内核源码含有测试过的设备树文件,方便后续驱动开发

配置环境

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig

编译内核

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4

编译成功后会得到arch/arm64/boot/Image文件及arch/arm64/boot/dst/rockchip/xxx.dtb文件

准备文件

在解压的文件夹(内核源码文件夹)下,新建shell文件,输入以下内容:

#!/bin/sh
mkdir boot
cp arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dtb boot/rk3399.dtb
cp arch/arm64/boot/Image boot/
mkdir boot/extlinux
cat>boot/extlinux/extlinux.conf<<EOF
label rockchip-kernel-4.4.4
    kernel /Image
    fdt /rk3399.dtb
    append earlycon=uart8250,mmio32,0xff1a0000 console=ttyS2,1500000n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init
EOF
  • 执行以上shell脚本,在内核源码文件夹下新增boot文件夹,其内含有:extlinxu Image rk3399.dtb

创建ext2格式的镜像

genext2fs -b 32768 -B $((64*1024*1024/32768)) -d boot/ -i 8192 -U boot.img

其中:

  • -b :表示文件系统块的数量;
  • -B : 表示文件系统块的大小,单位为字节;
  • -d :从指定目录开始创建文件系统
  • -i 8192;每个inode使用的字节数;
  • -U:选项表示创建一个唯一的文件系统UUID

这里我们设置了32768个块,每个块大小为64*1024*1024/32768,所以文件系统总大小为64MB,足够放下boot文件夹内的所有文件。

  • 执行成功后得到boot.img

busybox制作根文件系统

准备

  • 下载busybox1.36.0源代码:
wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
  • 将下载的busybox源代码复制到指定的文件加下,本文这里是~/rk3399/rootfs/
  • 切换到root权限:
gump@gump:~/rk3399/rootfs$ sudo su
  • 解压:
root@gump:/home/gump/rk3399/rootfs# tar -jxf busybox-1.36.0.tar.bz2

编译busybox

构建根文件在管理员权限下进行下面操作

  • 进入解压路径下:
root@gump:/home/gump/rk3399/rootfs# cd busybox-1.36.0
  • 使用默认配置:(注意交叉编译工具要与编译内核的相同)
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# make defconfig
  • 然后进入图像化配置页面(此配置会跳出参数配置窗口,保持终端界面尽量大):
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# make menuconfig
  • 进入Busybox Settings,配置如下参数(用键盘操作):
Settings --->
    [ ] Build static binary (no shared libs)

编译方式有两种:
第一种是以静态方式编译,即生成的busybox不需要动态库的支持就能运行。这样做我们就不需要部署动态库了,缺点就是自己写的程序在这个根文件系统中是不能运行的,因为缺少动态库库的支持
第二种方式使用动态编译,这样的话我们就需要部署动态库了,在linux动态库文件是以so为后缀,而windows下文件是以dll为后缀。这里我们使用动态编译,下面指定编译工具链

Settings --->
    () Cross compile prefix

这里是配置交叉编译工具链,这里要填上ubuntu上使用的交叉编译工具链前缀,笔者使用的交叉编译链前缀是aarch64-linux-gnu-,位于/usr/bin/路径下,如图
在这里插入图片描述

Settings --->    
    ( -march=armv8-a) additional CFLAGS 

这里是给编译器传递一个标志,rk3399是ARMv8架构,因此在这里添加此标志

Settings --->
    [*] Build Shared libbusybox
  • 继续配置:
Coreutils --->
    [] sync
Linux System Utilities --->
    [*] hexdump
    [] nsenter

按Esc键退出时保存当前更改

  • 编译安装
    依次执行以下命令:
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# make 
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# make install

make install的目的就是将编译生成的可执行程序及其依赖的库文件、配置文件、头文件安装到当前系统中指定(一般都可以自己指定安装到哪个目录下,如果不指定一般都有个默认目录)的目录下,默认被安装到_install 目录下,里面有5个文件:bin、sbin、usr这三个目录里都是二进制命令工具,lib里面是库文件,
如果要构成一个可用的根文件系统,还必须进行其它完善工作

构建根文件系统

  • 准备文件夹
    新建一个目录用来存放制作的根文件系统,可以命名为busybox_install。将利用BusyBox生成的二进制文件及目录,即_install目录下的所有文件及目录复制到busybox_install目录下:
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# mkdir ../busybox_install
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# cp -a _install/* ../busybox_install/
  • 添加库文件
    切换到busybox_install路径下:
root@gump:/home/gump/rk3399/rootfs/busybox-1.36.0# cd ../busybox_install/

找到交叉编译工具里的动态库复制到lib目录下:

root@gump:/home/gump/rk3399/rootfs/busybox_install# cp -a /usr/aarch64-linux-gnu/lib/*so* ./lib

-a保留权限,复制软链接本身,递归复制。

  • 构建etc目录
    初始化配置脚本放在/etc目录下,用于系统启动所需的初始化配置脚本。
    (BusyBox提供了一些初始化范例脚本,在/busybox-1.36.0/examples/bootfloppy/etc/目录下,直接复制就可以)
    在busybox_install目录下,创建etc文件夹:
root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir etc

将配置文件复制到新制作的根文件系统etc目录下:

root@gump:/home/gump/rk3399/rootfs/busybox_install# cp -a ../busybox-1.36.0/examples/bootfloppy/etc/* ./etc/
  • 修改etc/inittab文件
    etc/inittab文件是init进程解析的配置文件,通过这个配置文件决定执行哪个进程,何时执行:
root@gump:/home/gump/rk3399/rootfs/busybox_install/etc# vim inittab 

将文件修改为:

# 系统启动时
::sysinit:/etc/init.d/rcS
 
# 系统启动按下Enter键时
::askfirst:-/bin/sh
 
# 按下Ctrl+Alt+Del键时
::ctrlaltdel:/sbin/reboot
 
# 系统关机时
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
 
# 系统重启时
::restart:/sbin/init

以上内容定义了系统启动时,关机时,重启时,按下Ctrl+Alt+Del键时等操作执行的进程。

  • 修改etc/fstab
    etc/fstab文件存放的是文件系统信息。在系统启动后执行/etc/init.d/rcS文件里/bin/mount -a命令时,自动挂载这些文件系统。
root@gump:/home/gump/rk3399/rootfs/busybox_install/etc# vim fstab

修改为:

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>     
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0

这里我挂载的文件系统有三个proc、sysfs和tmpfs,在内核中proc和sysfs默认都支持,而tmpfs是没有支持的,我需要添加tmpfs的支持

  • 修改/etc/profile文件
    etc/profile文件作用是设置环境变量,每个用户登录时都会运行它,将文件内容修改:
root@gump:/home/gump/rk3399/rootfs/busybox_install/etc# vim profile

修改为:

# 主机名
export HOSTNAME=gump_rk3399
 
# 用户名
export USER=gump
 
# 用户目录
export HOME=/gump
 
# 终端默认提示符
export PS1="[$USER@$HOSTNAME:\$PWD]\# "    
 
# 环境变量
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
 
# 动态库路径
export LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
  • 新建S40network
    S40network脚本主要用于配置网络接口的参数,如IP地址、子网掩码、网关等。这些配置确保开发板能够正确连接到网络,打开:
root@gump:/home/gump/rk3399/rootfs/busybox_install/etc# vim init.d/S40network

输入以下内容:

#!/bin/sh
#
# Start the network....
#

# Debian ifupdown needs the /run/network lock directory
mkdir -p /run/network

case "$1" in
  start)
        printf "Starting network: "
        /sbin/ifup -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  stop)
        printf "Stopping network: "
        /sbin/ifdown -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  restart|reload)
        "$0" stop
        "$0" start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?
  • udhcpc配置
    新建udhcpc文件夹
root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir -p ./usr/share/udhcpc

拷贝文件simple.script到根文件系统的/usr/share/udhcpc/目录下,并更名为default.script

root@gump:/home/gump/rk3399/rootfs/busybox_install# cp ../busybox-1.36.0/examples/udhcp/simple.script ./usr/share/udhcpc/
root@gump:/home/gump/rk3399/rootfs/busybox_install# mv ./usr/share/udhcpc/simple.script ./usr/share/udhcpc/default.script

将default.script中RESOLV_CONF=”/etc/resolv.conf”更改为RESOLV_CONF=”/tmp/resolv.conf”
配置一下网络DHCP,系统启动以后就会自动设置好网络:
新建文件夹root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir -p etc/network/interfaces.d
新建文件root@gump:/home/gump/rk3399/rootfs/busybox_install# vim etc/network/interfaces在文件内输入以下内容:

# interface file auto-generated by buildroot

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

新建文件夹root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir etc/network/if-down.d
新建脚本文件root@gump:/home/gump/rk3399/rootfs/busybox_install# vim etc/network/if-down.d/dhcp-down.sh并输入以下内容

#!/bin/sh

# This file is executed by ifupdown in pre-up, post-up, pre-down and
# post-down phases of network interface configuration.

# run this script only for interfaces which have dhcp-down option
[ -z "$IF_DHCP_DOWN" ] && exit 0

$IF_DHCP_DOWN $IFACE

exit 0

新建文件夹root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir etc/network/if-up.d
新建脚本文件root@gump:/home/gump/rk3399/rootfs/busybox_install# vim etc/network/if-up.d/dhcp-up.sh并输入以下内容

#!/bin/sh

# This file is executed by ifupdown in pre-up, post-up, pre-down and
# post-down phases of network interface configuration.

# run this script only for interfaces which have dhcp-up option
[ -z "$IF_DHCP_UP" ] && exit 0

$IF_DHCP_UP $IFACE&

exit 0
  • 修改etc/init.d/rcS文件:root@gump:/home/gump/rk3399/rootfs/busybox_install# vim etc/init.d/rcS,输入以下内容:
#!/bin/sh

# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a

# 挂载虚拟的devpts文件系统用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# 扫描并创建节点
/sbin/mdev -s

# 加载网卡驱动
/sbin/modprobe r8125

# 配置网络
/sbin/udhcpc

# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do

     # Ignore dangling symlinks (if any).
     [ ! -f "$i" ] && continue

     case "$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set start
                . $i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            $i start
            ;;
    esac
done
  • 修改init.d文件权限:
root@gump:/home/gump/rk3399/rootfs/busybox_install# chmod -R 777 etc/init.d/*
  • 构建dev目录
    在busybox_install目录下,创建dev文件夹:
root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir dev

创建终端文件:

root@gump:/home/gump/rk3399/rootfs/busybox_install# mknod dev/console c 5 1
root@gump:/home/gump/rk3399/rootfs/busybox_install# mknod dev/null c 1 3
  • 构建其他文件夹
    在busybos_install路径下
root@gump:/home/gump/rk3399/rootfs/busybox_install# mkdir mnt proc tmp sys

制作ext4根文件系统镜像

建立根文件系统挂载点busybox_rootfs:

root@gump:/home/gump/rk3399/rootfs# mkdir busybox_rootfs

创建空镜像文件busybox_ext4_rootfs.img,大小300m,具体需要的大小可以通过du build -h查看我们根文件系统大小调整,在busybox_install同级目录下:

root@gump:/home/gump/rk3399/rootfs# dd if=/dev/zero of=busybox_ext4_rootfs.img bs=1M count=300

上面命令返回的结果:

300+0 records in
300+0 records out
314572800 bytes (315 MB, 300 MiB) copied, 0.14087 s, 2.2 GB/s

查看文件:root@gump:/home/gump/rk3399/rootfs# ll busybox_ext4_rootfs.img
上面命令返回结果:
-rw-rw-r-- 1 gump gump 314572800 12月 7 19:55 busybox_ext4_rootfs.img
创建文件系统:root@gump:/home/gump/rk3399/rootfs# mkfs.ext4 busybox_ext4_rootfs.img
上面命令返回结果:

mke2fs 1.44.1 (24-Mar-2018)
Discarding device blocks: done                            
Creating filesystem with 307200 1k blocks and 76912 inodes
Filesystem UUID: 46fe5c81-413b-4187-a69d-600c3a05d646
Superblock backups stored on blocks: 
    8193, 24577, 40961, 57345, 73729, 204801, 221185

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done 

将该镜像文件挂载到busybox_rootfs:

root@gump:/home/gump/rk3399/rootfs# mount busybox_ext4_rootfs.img busybox_rootfs

将busybox_install的文件复制到该空文件夹中:

root@gump:/home/gump/rk3399/rootfs# cp ./busybox_install/* ./busybox_rootfs/ -af

使用df命令可以查看是否已经挂载:

root@gump:/home/gump/rk3399/rootfs$ df busybox_rootfs/

上面命令返回结果:

Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/loop18       287237 12705    255076   5% /home/gump/rk3399/rootfs/busybox_rootfs

将之前挂载的卸载掉:

root@gump:/home/gump/rk3399/rootfs# umount busybox_rootfs

用e2fsck修复及检测镜像文件系统,resize2fs减小镜像文件的大小;

e2fsck -p -f busybox_ext4_rootfs.img
resize2fs -M busybox_ext4_rootfs.img
du -sh  busybox_ext4_rootfs.img

最终得到的ext4根文件系统镜像busybox_ext4_rootfs.img,本文这里根文件大小是42.9M

烧录验证

将上面编译好的四个文件放到一个文件夹内,准备烧录
本文这里是在windows平台下烧录,利用瑞芯微官方提供的AndroidTool 2.65版本
连接好烧录线和串口线,本文这里使用MobaXterm作为串口通信的上位机,串口通信速度是1500000,如图
在这里插入图片描述

打开AndroidTool并导入上述四个文件,按照第一个图中所描述的地址对应好,同时使开发板进入loader模式,点击Run进行烧录,如图
在这里插入图片描述

系统启动后,MobaXterm将接收到系统启动的信息,如图u-boot正常启动:
在这里插入图片描述

内核正常启动
在这里插入图片描述

根文件系统正常
在这里插入图片描述

注意:

  • 开发板启动时,网络一定要连接到路由器,由路由器分配IP地址,否则会一直卡在DHCPC
  • 此根文件系统没有内核模块安装功能,若要安装内核模块,可以使用命令:
sudo apt-get install kmod

若uboot总是反复重启,可在计时至0前打断重启,如图
在这里插入图片描述

重写gpt分区:
gpt write mmc 0 $partitions
保存环境saveenv后重启开发板

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值