转载-rk3399主线uboot/Linux/Debian根文件——02

link: https://mp.weixin.qq.com/s/00UGiwSf3G4IlGhTNbneWw

原创 zxc ZxcWorld 2025-05-15 20:50

接着上篇的,接下来移植内核和构建根文件。

1.Linux内核

1.1编译内核

进入docker环境,在Linux目录下。

在arm下有很多板子的defconfig,和arm不同,在arm64下的configs中只有一个defconfig,这个里面已经包含了所有的arm64架构的:

但我不需要全部的,所以我拷贝了rk官方库的defconfig,命名为
zxc_rk3399_defconfig:

kernel/arch/arm64/configs/rockchip_linux_defconfig at develop-6.1 · rockchip-linux/kernel · GitHub

然后运行:

make O=rk3399 ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- zxc_rk3399_defconfig

这里指定了一个输出目录 O=,使用输出目录比较方便查找,而且可以编译多个而不会覆盖掉之前的,推荐使用。

然后编译内核:

make O=rk3399 ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- 

编译完成后获得Image镜像。

1.2烧录内核

像uboot这种启动一般是放在指定地址,而内核和根文件则一般放在文件系统中。

可以使用tftp的方式加载,也可以打包成img烧录,在这里我使用uboot的一个功能指令 ums(偶然发现),使用ums可以将mmc以块设备的方式暴露给系统,然后可以在虚拟机上,当成正常的存储设备使用,比如使用gpartd进行分区。

ums启用见编译uboot。

先将板子进入uboot,运行uboot命令,这样就变成了一个块设备:

ums 0 mmc 0

然后就可以在虚拟机上看见设备,使用gpartd分一下区就行了。前面uboot其实需要的是固定地址,不需要文件系统,不要分区也可以,最好不要分区,否则会影响后面的启动脚本,后面两个用于存储内核和根文件,必须是文件系统,我这里将内核的文件系统设置为ext2,rootfs设置为ext4,注意地址偏移(我偏移了20M),不要将前面3个数据给覆盖了。

挂载后,将内核镜像和dtb拷贝进去就行了,initramfs是一个临时的存于ram的根文件系统,它就是一个根文件,只不过存在于ram中,可以在真正根文件之前做一些初始化,校验等工作,不是必须的,后面会写一下如何通过initramfs运行一个脚本。

2.构建根文件

2.1构建debian

在docker中:

运行脚本(这个过程是官方给出的固定的,所以我直接写成脚本,脚本见第一篇开头),但是有一个很重要,在x86上运行arm64的文件系统,需要QEMU,这个在使用chroot切换是很重要。

./build-rootfs.sh

运行后,生成 rootfs-debian,这已经是一个根文件了。

可以使用chroot切过去,如上所说,必须保证qemu是开启的。

可以使用下面命令查看:

update-binfmts --display qemu-aarch64

![](https://img2024.cnblogs.com/blog/2679005/202505/2679005-20250521095228506-1013267959.png ""=500x)

没有开启,则需要开启,我的脚本已经执行了这个命令,如果是执行脚本后直接切换说没有问题的,以后再次进入可能会用到:

update-binfmts --enable qemu-aarch64

切换:

chroot rootfs-debian/

可以改下用户名:

echo "zxc" > ./rootfs-debian/etc/hostname

可以改下密码:

password

可以使用apt安装东西:

apt update
apt install ...

简而言之就是一个正常Linux根文件。

然后有模块可以使用make install安装。

可以将设备树拷贝到/boot/dts/中。

将.config拷贝到/boot中,名称为config-{内核版本}。

设备树和.config等在基于debian构建initramfs中可能会用到,其实我下面的initramfs是没用的,因为功能过于简单。

拷贝比较麻烦,我构建了一个python脚本,直接运行这个脚本就可以:

import os
import shutil
import argparse
import subprocess

def get_kernel_version(kernel_out):
    try:
        result = subprocess.run(
            ['make', 'kernelrelease'],
            stdout=subprocess.PIPE, #获取标准输入
            stderr=subprocess.PIPE, #
            text=True, #文本格式
            check=True, #错误抛出异常
            cwd=kernel_out #在kernel_out中运行make
        )
        version = result.stdout.strip()
        print(f"内核版本: {version}")
        return version
    except Exception as e:
        print(f"获取内核版本失败: {e}")
        return None


def copy_file(src, dst):
    if os.path.exists(src):
        os.makedirs(os.path.dirname(dst), exist_ok=True)
        shutil.copy2(src, dst)
        print(f"拷贝成功: {src} -> {dst}")
    else:
        print(f"Warning: {src} 不存在,跳过")

def install_kernel_modules(kernel_out_dir, rootfs_dir, arch="arm64", cross_compile="aarch64-none-linux-gnu-"):
    if not os.path.isdir(kernel_out_dir):
        raise FileNotFoundError(f"内核输出目录没有找到: {kernel_out_dir}")
    if not os.path.isdir(rootfs_dir):
        raise FileNotFoundError(f"根文件目录没有找到: {rootfs_dir}")

    cmd = [
        "make",
        # f"ARCH={arch}",
        # f"CROSS_COMPILE={cross_compile}",
        f"INSTALL_MOD_PATH={os.path.abspath(rootfs_dir)}",
        "modules_install"
    ]
    print(f"Running: {' '.join(cmd)} in {kernel_out_dir}")
    # 切换到内核输出目录执行
    try:
        subprocess.run(cmd, cwd=kernel_out_dir, check=True)
    except Exception as e:
        print("{cmd} 运行失败")
    print("模块安装成功.")
def main():
    parser = argparse.ArgumentParser(description="拷贝内核数据到debian根文件")
    parser.add_argument("--kernel-out", required=True, help="内核编译后输出目录")
    parser.add_argument("--rootfs", required=True, help="debian根文件目录")
    parser.add_argument("--ARCH",default="arm64", help="架构(暂时无用)")
    parser.add_argument("--CROSS_COMPILE",default="aarch64-none-linux-gnu-", help="编译前缀(暂时无用)")

    args = parser.parse_args()
    version= get_kernel_version(args.kernel_out)
    if version is None:
        print("ERROR: 获取内核版本失败,操作失败")
        return
    kernel_out_dir = args.kernel_out
    rootfs_dir = args.rootfs
    config_src = os.path.join(kernel_out_dir, ".config")
    config_dst = os.path.join(rootfs_dir, f"boot/config-{version}")
    copy_file(config_src, config_dst)
    install_kernel_modules(kernel_out_dir,rootfs_dir,args.ARCH,args.CROSS_COMPILE)


if __name__ == "__main__":
    main()

运行:

python3 ./copy_linux_config.py --kernel-out=内核目录 --rootfs=根文件目录

最后将根文件上传到mmc:

如内核烧录一样使用uboot的ums命令,这个将根文件拷贝到第2个分区,挂载后,使用rsync拷贝过去:

sudo rsync -aAX ../rootfs-debian/ /mnt/rootfs/

2.2构建initramfs

initramfs就是一个根文件,所以说可以使用buildroot之类的构建一个根文件,然后打包成镜像使用,这里直接使用debian的
initramfs-tools来构建。

在docker环境中。

切到上面制作debian根文件:

sudo update-binfmts --enable qemu-aarch64
sudo chroot rootfs-debian /bin/bash

安装工具:

apt install initramfs-tools -y
apt install u-boot-tools -y

添加ls指令,其实本身自带ls,不需要添加,举个例子,可以在这里添加工具,囧。

cat > /etc/initramfs-tools/hooks/fsck <<'EOF'
#!/bin/sh
. /usr/share/initramfs-tools/scripts/functions
. /usr/share/initramfs-tools/hook-functions
copy_exec /bin/ls
EOF

修改执行权限:

chmod +x /etc/initramfs-tools/hooks/fsck

创建脚本,这个脚本主要就是这句 ls -l /dev/mmc*,显示mmc设备:

cat > /etc/initramfs-tools/scripts/init-top/checkmmc << 'EOF'
#!/bin/sh
PREREQ=""

prereqs() { echo "$PREREQ"; }

case $1 in
  prereqs) prereqs; exit 0 ;;
esac

echo "[*] Listing mmc devices..."
ls -l /dev/mmc* || echo "No mmc devices found"
sleep 3
EOF

添加执行权限:

chmod +x /etc/initramfs-tools/scripts/init-top/checkmmc

生成镜像,参数 6.12.16-dirty是内核版本:

mkinitramfs -c gzip -o /boot/initramfs.gz 6.12.16-dirty
mkimage -A arm64 -O linux -T ramdisk -C none -a 0 -e 0 -n initramfs -d /boot/initramfs.gz /boot/initramfs.img

如果执行了上面的python代码,则会在/boot下生成这个文件,后缀即内核版本,就该示例而言,不要这个是可以,因为我只有一个ls指令,不需要加载其他。

运行后生成如下,
将这个也拷贝到内核启动目录中:

3.启动

可以在uboot中手动加载:

ext2load mmc 0:1 0x4000000 /Image
ext2load mmc 0:1 0x1000000 /rk3399-zxc.dtb
ext2load mmc 0:1 0x2000000 /initramfs.img
setenv bootargs console=ttyS2,1500000n8 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw
booti 0x4000000 0x2000000 0x1000000

如果不指定root路径,或者直接不设置
bootargs,则会进入到那个initramfs中,这时可以体验到那就是一个简单的根文件。

然后在启动中会打印上面那个ls脚本:

接下来可以构建要给启动脚本,
uboot启动脚本有两种方式extlinux.confboot.scr(这个需要编译,我在v3s中使用的这个,这里不再使用)。

确保uboot开启了这个: CONFIG_BOOTMETH_DISTRO

可以uboot下查看是否支持extlinux启动:

bootmeth list

创建 extlinux.conf

DEFAULT linux
TIMEOUT 50
LABEL linux
    MENU LABEL Boot with initramfs
    KERNEL /Image
    FDT /rk3399-zxc.dtb
    INITRD /initramfs.img
    APPEND console=ttyS2,1500000n8 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw
LABEL no_ramfs
    MENU LABEL Boot without initramfs
    KERNEL /Image
    FDT /rk3399-zxc.dtb
    APPEND console=ttyS2,1500000n8 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw

然后在启动分区文件系统中创建一个文件夹/boot/extlinux

extlinux.conf拷贝到文件夹中。

然后上电后就会正常启动,进入debian:

进入后就是一个比较完整的Linux,比我那个v3s中使用buildroot构建的好用多了,没办法,谁让v3s只有64M RAM呢,只能说比单片机强点,我感觉称它为大号单片机更加合适。

至此完成。

posted @ 2025-05-21 09:54  LiYanbin  阅读(7)  评论(0)    收藏  举报