关于 uboot 的小知识

启动参数、制作 u-boot 补丁、dd 烧写等

Posted by Jerry Chen on April 26, 2021

uboot 中 bootm 和 bootz 的区别

bootm

用于加载 uImage 和 ramdisk;

uImage 必须是 mkimage 打包过的内核映像(在 zImage 前加入了 64 字节的头信息,bootm 需要解析该信息);

在 uboot 命令行上的用法:

  • 只加载 uImage:

    1
    
    bootm ${kernel_load_address}
    
  • 加载 uImage 和 ramdisk:

    1
    
    bootm ${kernel_load_address} ${ramdisk_load_address} 
    
  • 加载 uImage、ramdisk 以及 dtb:

    1
    
    bootm ${kernel_load_address} ${ramdisk_load_address} ${devicetree_load_address}
    

bootz

用于加载 zImage 和 ext4 根文件系统;

内核编译(make)后会生成两个文件:Image 和 zImage,区别在于 zImage 是压缩映像;

在 uboot 命令行上的用法:

命令示例:

tftp 80800000 zImage

tftp 83000000 imx6ull.dtb

bootz 80800000 – 83000000

1
bootz ${kernel_load_address} - ${devicetree_load_address}

uboot 中 bootargs 和 bootcmd

bootargs

类别介绍

bootargs 是记录启动参数的 uboot 环境变量,有如下类:

  • root :用来指定 rootfs 的位置

    1
    2
    3
    4
    
    root=/dev/ram0 rw         # ram
    root=/dev/nandd rw        # nand
    root=/dev/mmcblk0p7 rw    # mmc
    root=/dev/nfs             # nfs
    
  • rootfstype :根文件系统类型,如果是 ext2 可不指明

    1
    
    rootfstype=yaffs2
    
  • console :指定调试串口

    1
    2
    
    console=ttyS0,115200
    console=ttySAC0,115200n8
    
  • mem :指定内存大小,非必须

    1
    
    mem=xxxM
    
  • ramdisk_size :指定创建的 ramdisk 的 size,默认 4M

    1
    
    ramdisk_size=xxx
    
  • initrd, noinitrd :没有使用 ramdisk 启动系统的时候,需要使用 noinitrd 这个参数

    r_addr 表示 initrd 在内存中的位置,size 表示 initrd 的大小;

    1
    
    initrd=r_addr,size
    
  • init :指定进入 rootfs 后运行的第一个脚本

    如下 /init 指的是 rootfs 根目录下的 init 文件,一般情况下是一个链接;

    1
    2
    
    inti=/init
    init=/etc/preinit
    
  • mtdparts :内存技术设备

    要想这个参数起作用,内核需要勾选 mtd 驱动的支持:Device Drivers —> Memory Technology Device (MTD) support—> Command line partition table parsing

    1
    
    mtdparts=fc000000.nor_flash:1920k(linux),128k(fdt),20M(ramdisk),4M(jffs2),38272k(user),256k(env),384k(uboot)
    
  • ip :指定系统启动后网卡的 IP 地址,基于 nfs 的文件系统必须要有该参数

    which netcard 指你的网卡名称,如 eth0;

    如果为了使用 NFS 或者只为了 ping 通,那只需设置三个就行(ipaddr、serverip、网卡名),其他任意填;

    1
    2
    
    ip=ip addr
    ip=ip addr:server ip addr:gateway:netmask::which netcard:off
    
常见示例
  1. 假设文件系统是 ramdisk,且直接就在内存中

    1
    
    setenv bootargs 'initrd=0x32000000,0xa00000 root=/dev/ram0 console=ttySAC0 mem=64M init=/linuxrc'
    
  2. 假设文件系统是 ramdisk,且在 flash 中

    这种情况下需要在 bootcmd 环境变量中的 bootm 命令中指定 ramdisk 在 flash 中的地址,如 bootm kernel_addr ramdisk_addr (fdt_addr)

    1
    
    setenv bootargs 'mem=32M console=ttyS0,115200 root=/dev/ram rw init=/linuxrc'
    
  3. 假设文件系统是 jffs2 类型的,且在 flash 中

    1
    
    setenv bootargs 'mem=32M console=ttyS0,115200 noinitrd root=/dev/mtdblock2 rw rootfstype=jffs2 init=/linuxrc'
    
  4. 假设文件系统是基于 nfs 的

    1
    
    setenv bootargs 'noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5:192.168.0.3:192.168.0.3:255.255.255.0::eth0:off'
    

    或者

    1
    
    setenv bootargs ’noinitrd mem=64M console=ttySAC0 root=/dev/nfs nfsroot=192.168.0.3:/nfs ip=192.168.0.5‘
    

bootcmd

bootcmd 是记录每次启动时默认会执行的一系列命令的 uboot 环境变量;

uboot 命令行方式

比如在 uboot 命令行设置 bootcmd 环境变量,并保存:

第一次需要手动执行跳转,后续不需要;

1
2
3
4
5
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk1p2 rootwait panic=10'

setenv bootcmd 'load mmc 1:1 0x41800000 sun8i-a33-sinlinx-sina33.dtb; load mmc 1:1 0x41000000 zImage; bootz 0x41000000 - 0x41800000'

saveenv

接着重启系统或输入 boot(boot 命令会执行 bootcmd 环境变量中的指令);

文件方式
  1. 首先确保 uboot 中 include/configs/xxx.h 中存在目标定义

    1
    2
    3
    
    #define CONFIG_EXTRA_ENV_SETTINGS \
        "script=boot.scr\0" \
        ... ...
    
  2. 新建 boot.cmd

    1
    2
    3
    4
    5
    
    setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk1p2 rootwait panic=10'
       
    load mmc 1:1 0x41800000 sun8i-a33-sinlinx-sina33.dtb
    load mmc 1:1 0x41000000 zImage
    bootz 0x41000000 - 0x41800000
    
  3. 将 boot.cmd 转换为 boot.scr

    1
    
    mkimage -C none -A arm -T script -d boot.cmd boot.scr
    
  4. 将 boot.scr 放入 boot 分区,复位即可(也可想办法编译到大包中,这样烧写大包后就可用);

和主线 uboot 对比制作 uboot 补丁

补丁是什么

补丁文件是通过对比 修改文件源码文件 后得到的记录修改的文件。

  • 发布时,我们只需要分享该补丁文件;

  • 其他人收到该补丁文件,自行下载 uboot 主线源码上打上此补丁,就可以得到我们适配好的 uboot;

对比文件夹的方式制作

工作目录下有:

  • uboot 源码压缩包(u-boot-2020.07.tar.bz2)
  • 适配好的 uboot 文件夹(u-boot-2020.07)
1
2
3
4
5
6
7
8
9
10
11
# 1.进入 u-boot-2020.07 清除编译产生的文件(包括配置文件)
cd u-boot-2020.07
make distclean

# 2.返回工作目录,对适配好的 uboot 文件夹重命名,并解压源码生成原始 uboot 文件夹
cd ..
mv u-boot-2020.07 u-boot-2020.07-has-patch
tar -vxf u-boot-2020.07.tar.bz2

# 3.制作补丁 diff -urN OldDir NewDir > patch_name
diff -uNr u-boot-2020.07 u-boot-2020.07-has-patch > 0001-v2020.07.11.patch

通过 git diff 的方式制作

1
2
3
4
5
# 1.查看最近两次修改的 commit id
git log

# 2.制作补丁
git diff commitid_orginal commitid_new > test.patch

通过 git format-patch 的方式制作

1
2
3
4
5
6
7
8
9
10
11
### HEAD 后有几个 ^ 就会生成几个 patch,从最近一次开始生成
# 根据最近的一次提交生成 1 个 patch
git format-patch HEAD^
# 根据最近的两次提交生成 2 个 patch
git format-patch HEAD^^

### 根据数字生成对应数量的 patch,从最近一次开始生成
# 根据最近的一次提交生成 1 个 patch
git format-patch -1
# 根据最近的两次提交生成 2 个 patch
git format-patch -2

给主线 uboot 打补丁

补丁用了文件夹的方式的情况

1
2
3
# patch -p<数字n> < 补丁文件,其中数字 n 表示忽略前 n 级目录
# 补丁在 uboot 源码上级目录制作,这里在源码目录中打补丁,需要忽略 1 级目录
patch -Np1 < ../0001-v2020.07.11.patch

补丁用了 git diff 的方式的情况

1
git apply test.patch

补丁用了 git format-patch 的方式的情况

1
2
3
4
5
# 1.[可选]丢弃之前所有的 add 和 commit
git am --abort

# 2.打入 patch 目录中所有的补丁
git am patch/*.patch

打补丁后编译 uboot

1
2
3
make distclean
make xxx_config
make

额外的:全志 soc 使用 dd 命令快速更新固件方法

已知在 uboot 源码 /include/spare_head.h 中有宏定义:

#define BOOT0_SDMMC_START_ADDR (16)

#define UBOOT_START_SECTOR_IN_SDMMC (32800)

更新 boot0

boot0_sdcard.fex 由 boot0_sdcard_sun8iw11p1.bin 在 pack 后生成在 tools/pack/out 目录,一般大小为 32Kb;

16 来自宏定义 BOOT0_SDMMC_START_ADDR;

1
sudo dd if=boot0_sdcard.fex of=/dev/mmcblk0 bs=512 seek=16

更新 uboot

u-boot.fex 由 u-boot-sun8iw11p1.bin 在 pack 后生成在 tools/pack/out 目录,一般大小为 1024Kb;

32800 来自宏定义 UBOOT_START_SECTOR_IN_SDMM;

1
sudo dd if=u-boot.fex of=/dev/mmcblk0 bs=512 seek=32800