跳到主要内容

U-Boot

U-Boot(Universal Boot Loader) 是一款广泛应用于嵌入式系统的开源引导加载程序,主要功能包括:

  • 硬件初始化:在系统启动时负责初始化 CPU、内存以及外设,确保硬件资源就绪。
  • 加载操作系统:从闪存、eMMC、网络等介质加载操作系统内核,为系统启动提供支持。
  • 灵活配置:支持环境变量配置、命令行交互以及脚本自动化,便于定制和调试。

U-Boot 是嵌入式开发中常用的引导工具,凭借其灵活性和强大功能,成为许多设备启动过程中的关键组件。


功能描述

U-Boot 包含 USB、以太网、PCIe、I2C、GPIO、QSPI Flash、eMMC、SD 卡驱动等模块。U-Boot 支持多种启动 Linux 的方式,包括:

  • NVMe SSD 启动
  • 以太网启动
  • eMMC 启动
  • U 盘启动

以太网启动使用 TFTP 协议加载内核,可以通过 NFS 挂载根文件系统。

U-Boot 在整个启动流程中作为 BL33 阶段不存在 SPL 阶段

当前特性包括:

  • 仅支持 CPU 单核模式
  • 暂不支持中断
  • 开启一级缓存和 MMU
  • 物理地址与虚拟地址相同
  • 支持设备树方式启动
  • 支持 acpi 方式启动

配置流程

提示

在 uboot2025.04 中,修改一下内容:

  • mthreads_m1000_aimodule_defconfig 改为 m1000_aimodule_defconfig
  • CONFIG_TARGET_MTHREADS_M1000_AIMODULE 改为 CONFIG_TARGET_M1000_AIMODULE
  • mthreads-m1000-aimodule.dts 改为 m1000-aimodule.dts
  • mthreads-m1000-aimodule.dtb 改为 m1000-aimodule.dtb
  • TARGET_MTHREADS_M1000_AIMODULE 改为 TARGET_M1000_AIMODULE
  • mthreads-m1000.dtsi 改为 m1000.dtsi
  • mthreads-m1000-padc.dtsi 改为 m1000-padc.dtsi
  • CONFIG_EXTRA_ENV_SETTINGS 改为 CFG_EXTRA_ENV_SETTINGS

步骤 1:增加新的板级配置

下面假定新的板子名称为 boardx

增加新的板级配置需要建立多个文件和文件夹,建议拷贝原有的板级配置文件,这样可以避免从零开始创建新的文件。

  1. board/mthreads/ 文件夹下创建新的板级配置文件夹

    board/mthreads/ 文件夹下面是所有 m1000 芯片的板级文件。

  2. 进入目录 board/mthreads/,输入:

    cp -ra m1000_aimodule boardx

    从现有的主板配置拷贝一份,基于此进行修改。

  3. 进入 boardx 目录,修改源文件名称:

    mv m1000_aimodule.c boardx.c
  4. 修改 Kconfig 文件,如下图所示。

    E300 009 图示 其中:

    • SYS_BOARD 是板子名称
    • SYS_VENDOR 是芯片厂家名称
    • SYS_CONFIG_NAME 是 config 文件名称
  5. 修改 Makefile 如下图所示:

    E300 010 图示

  6. 建立配置头文件:

    1. 进入 include/configs 文件夹,输入:

      cp m1000_aimodule.h boardx.h
    2. 修改开始的两行和最后一行(如下图所示)

      E300 011 图示

    3. 修改最后一行,如下图所示:

    E300 012 图示

  7. 建立配置文件:

    1. 进入 configs 文件夹,输入:

      cp m1000_aimodule_defconfig boardx_defconfig
    2. 修改 boardx_defconfig 文件,将以下内容:

      CONFIG_DEFAULT_DEVICE_TREE="m1000-aimodule"
      CONFIG_TARGET_M1000_AIMODULE=y

      改为:

      CONFIG_DEFAULT_DEVICE_TREE="boardx"
      CONFIG_TARGET_MTHREADS_BOARDX=y

      如下图所示:

      E300 013 图示

  8. 建立设备树文件:

    1. 进入 arch/arm/dts 目录,输入:

      cp m1000-aimodule.dts boardx.dts
    2. 修改 Makefile 文件,在:

      dtb-$(CONFIG_TARGET_M1000_AIMODULE) += m1000-aimodule.dtb
    3. 后面增加一行:

      dtb-$(CONFIG_TARGET_MTHREADS_BOARDX) += boardx.dtb

      如下图所示:

      E300 014 图示

  9. 关联板级文件:

    1. 修改 arch/arm/mach-m1000/Kconfig 文件,在:

      config TARGET_M1000_AIMODULE

      下面增加 boardx 相关选项,如下图所示:

      E300 015 图示

    2. 并在:

      source "board/mthreads/m1000_aimodule/Kconfig"

      下面增加一行:

      source "board/mthreads/boardx/Kconfig"

      如下图所示:

      E300 016 图示

配置完成,编译 U-Boot 即可。


步骤 2:配置设备树

MTT E300 的设备树文件位于 arch/arm/dts 目录下,分为 .dtsi 文件和 .dts 文件。.dts 文件中包含了 .dtsi 文件。其中主要的配置在 m1000.dtsi 文件中,管脚相关的配置在 m1000-padc.dtsi 文件中。

用户自己的 .dts 文件(如上一节中的 boardx.dts 文件)中应包含这两个文件,如下图所示:

E300 017 图示

一般情况下,用户无需修改 m1000.dtsi。如果确实需要修改其中的某个配置项,不建议直接修改该文件,而是在 dts 文件中重新定义该配置项。

m1000.dtsi 中 usb0 的配置如下图所示:

E300 018 图示

其中 max_speed = <4>; 表示 usb0 使用 super speed 速度模式。如用户需要使能 super speed plus 模式,可以修改 boardx.dts,如下图所示:

E300 019 图示

&usb0 表示引用设备树包含文件(.dtsi)中的 usb0 节点。在设备树源文件(.dts)中重新定义的 max_speed = <5> 会覆盖 .dtsi 文件中的 max_speed = <4>

提示

max_speed = <5>:super speed plus 模式 max_speed = <4>:super speed 模式

下面介绍主要外设的设备树配置方法:

  1. USB

    • usb0usb3 节点代表 SoC 的 4 个 USB 控制器,默认处于 host 模式。
    • max_speed = <1> 使用全速模式。
    • max_speed = <3> 使用高速模式。
    • max_speed = <4> 使用 super speed 模式。
    • max_speed = <5> 使用 super speed plus 模式。
  2. 以太网(Ethernet)

    当前 U-Boot 仅支持 以太网 0,即 gem0 节点。 设备树中 gem0 节点的 phy-mode = "rgmii-id",表示使用 rgmii 的 phy。 若使用 rmii 的 phy,则需改为:phy-mode = "rmii";

  3. eMMC

    emmc 节点代表 eMMC 存储控制器。

    • mmc-hs200-1_8v; 表示支持 hs200 速度模式,若不使用 hs200 模式,可以屏蔽掉此项。
    • mmc-ddr-1_8v; 表示支持 ddr 速度模式。如果不使用 ddr 模式,可以屏蔽掉此项。
    • sdhci-caps = <0x0 0x40000>; 表示支持 8 线模式,若使用 4 线或 1 线模式,可以屏蔽掉这一项。
    • bus-width 表示线宽:
      • bus-width = <8>; 使用 8 线模式。
      • bus-width = <4>; 使用 4 线模式。
      • bus-width = <1>; 使用 1 线模式。
  4. SD 卡

    sd 节点代表 SD 卡。

    sd; // 表示本节点为 SD 卡

    • bus-width 表示线宽:
      • bus-width = <4>; 使用 4 线模式。
      • bus-width = <1>; 使用 1 线模式。
  5. PCIe 控制器

    pcie0pcie3 节点代表 PCIe 控制器 0–3。

    • num-lanes 表示 PCIe 使用的 lane 数量:
      • num-lanes = <1>; 表示使用 x1 模式,依此类推。
  6. QSPI

    qspi0 节点代表 QSPI,接串行 flash。

    • spi-max-frequency = <3000000>; 表示 QSPI 的总线频率为 3M。
    • spi-tx-bus-width = <4>; 表示发送支持 4 线模式。
    • spi-rx-bus-width = <4>; 表示接收支持 4 线模式。

    qspi0 节点中有一个 flash@0 节点,代表外接的串行 flash。

  7. I2C:

    i2c0 ~ i2c18 表示 I2C 的 19 个节点。用户可以根据需求在 .dts 文件中引用相应的节点,设置 status = "okay"; 即可启用该节点。

    UART:

    U-Boot 中目前支持 uart0uart7 这两个串口节点,其中 uart7 是默认调试串口,兼容 16550 标准。

    uart_clock = <100000000>; // 表示 UART 的内部总线频率为 100MHz,这里无需修改


步骤 3:配置 config 头文件

include/configs 文件夹下是与 U-Boot 配置相关的头文件,其中 m1000.h 是 msoc 的公共 config 头文件,需要被每块板子对应的 config 头文件包含。

以下以 boardx.h 为例介绍如何客制化 config 头文件:

  1. CONFIG_EXTRA_ENV_SETTINGS

    这个宏定义是 U-Boot 扩展的环境变量。 在 U-Boot 中,环境变量主要用于存储系统启动时的各种参数和配置信息,其作用包括定义启动命令、传递内核参数等。 CONFIG_EXTRA_ENV_SETTINGS 被定义为一系列拼接起来的字符串,每个字符串之间用 \0 分隔。例如:

    "bootdelay=2\0"

    等号前是环境变量名称,右边是环境变量的值。

    文件开始部分有大量宏定义,这些宏多数用于方便 CONFIG_EXTRA_ENV_SETTINGS 的定义。

下面介绍 CONFIG_EXTRA_ENV_SETTINGS 中各参数的含义:

网络和启动参数
  • bootdelay: U-Boot 启动 Linux 之前会等待若干秒。在此期间,用户可通过串口按下空格键进入 U-Boot 命令行。bootdelay 表示该等待时间(秒数)。
  • tftppath: 通过以太网 TFTP 协议加载内核时,指定内核镜像在 TFTP 服务器上的路径。
  • nfspath: 通过以太网将 rootfs 挂载至 NFS 服务器的某个文件夹时,指定该文件夹路径。
  • ipaddr: 板子的 IP 地址。
  • gatewayip: 网关地址。若板子与服务器在同一网段,该值可设置为空:"gatewayip=\0"
  • netmask: 子网掩码。
  • serverip: TFTP 服务器 IP 地址。
  • nfsserver: NFS 服务器 IP 地址。
内存地址参数
  • dtbaddr: Linux 设备树在 DDR 中的内存地址。
  • kerneladdr: Linux 内核在 DDR 中的内存地址。
文件名参数
  • kernel: Linux 内核镜像(不包含 ramfs)文件名。
  • kernelinitramfs: Linux 内核镜像(包含 ramfs)文件名。
  • dtb: Linux 设备树文件名.
启动配置参数
  • udiskpart: 表示 U 盘的分区号,例如 udiskpart=1 表示第 1 分区。主要用于 U 盘启动,从该分区查找内核镜像。
  • compara: 传递给 Linux 内核的通用参数。
  • mmcparam: 从 eMMC 启动内核时传递的参数。
  • nvmeparam: 从 NVMe SSD 启动内核时传递的参数.
  • netparam: 从以太网启动内核时传递的参数.
启动命令

可用的启动命令包括:

# ACPI 方式启动
netbootacpi // 从以太网启动内核(使用 ramfs 作为根文件系统)
mmcbootacpi // 从 eMMC 启动内核
usbbootacpi // 从 U 盘启动内核
nvmebootacpi // 从 NVMe SSD 启动内核

# 设备树方式启动
netbootdts // 从以太网启动内核(使用 ramfs 作为根文件系统)
mmcbootdts // 从 eMMC 启动内核
usbbootdts // 从 U 盘启动内核
nvmebootdts // 从 NVMe SSD 启动内核
自适应启动环境变量
  • bootmedia: 设置环境变量默认值为 auto。

    • 若配置为 auto,则优先从 mmc 启动,mmc 启动失败则从 nvme 启动,nvme 启动失败则启动失败,返回错误
    • 若环境变量的值为非 auto,设置为 mmc 就从 mmc 启动,设置为 nvme 就从 nvme 启动,启动失败则直接报错
  • bootdts:

    • 设置为 acpi 表示通过 acpi 方式启动 linux
    • 设置为 dts 表示通过设备树方式启动 linux
  • bootdev: 表示从第几个设备启动内核(0 表示第一个,1 表示第二个...)

  • bootextraarg: 用户需要加入 bootargs 的参数,会放到 bootargs 的最后部分

  • udiskupdate: 是否支持 u 盘更新文件系统功能(0 表示不支持,1 表示支持)

  1. CONFIG_BOOTCOMMAND

CONFIG_BOOTCOMMAND 是 U-Boot 启动 Linux 所需要的命令,被定义为一个字符串。U-Boot 启动 Linux 过程中会运行这个字符串代表的命令来启动 Linux 内核。

CONFIG_BOOTCOMMAND 默认为 autoboot,这是一个自适应启动的命令,默认以设备树方式启动内核。

自定义启动行为
  • 如果希望以 acpi 方式启动内核,可以将环境变量 bootmode 改为 acpi
  • 如果希望以设备树方式启动内核,可以将环境变量 bootmode 改为 dts
  • 如果希望从第二个 nvme 卡启动内核,可以将环境变量 bootdev 改为 1,以此类推
  • 如果希望从 u 盘启动并更新文件系统,可以将环境变量 udiskupdate 设置为 1
U 盘启动逻辑

udiskupdate=1 时,系统会先检测是否插入 u 盘:

  • 若插入了一个 u 盘(注意只能插入 1 个 u 盘),u 盘根目录中有 .boot 文件,且文件中的字符为 usb_bootusb_update,则从 u 盘启动
  • 如果没有,则从 nvme 卡或 emmc 启动

如果想从 u 盘启动,需要在 u 盘根目录中创建 boot 文件夹,文件夹中放入以下三个文件:

  • Image
  • m1000-aimodule.dtb
  • mt-minimal-image-initramfs-aimodule.cpio
根文件系统挂载

内核启动后根文件系统默认挂载在第一个分区(标签一般为 root)上。如果第二个分区(标签为 recovery)的根目录存在名字为 .recovery 的文件,就会挂载在第二个(recovery)分区上,这是为了支持 recovery 功能。


步骤 4:U-Boot 系统配置选项

U-Boot 的系统配置选项可通过 menuconfig 命令进行配置。使用 Yocto 环境时,配置流程相对复杂。下面以 boardx_defconfig 作为系统配置文件举例说明:

在编译环境中,输入:
bitbake u-boot-mt -c devshell # 进入 devshell

输入:
cd oe-workdir/u-boot-mt-2025.04/boardx_defconfig/ # 进入 uboot 编译文件夹

输入:
make menuconfig # 进入配置界面,配置好后保存退出

运行:
make savedefconfig # 保存配置为 defconfig 文件

将该文件拷贝到方便访问的文件夹:
cp defconfig ~/tmp/

输入:
Exit # 退出 devshell

然后用 defconfig 覆盖 configs/boardx_defconfig 文件:
cp ~/tmp/defconfig configs/boardx_defconfig

至此,完成了一次配置流程。

下面介绍部分配置项:

  • Architecture select (ARM architecture):配置 CPU 架构,m1000 使用 ARM 架构。

  • ARM architecture 菜单中包含架构相关的选项:

    • ARM Generic Timer support: 需要选中以支持 ARM 的定时器
    • Support for booting from TF-A: 需要选中以支持从 TF-A 启动 U-Boot
    • m1000 board select: 用于选择板子,例如:m1000 boardx board
    • m1000 uart console select: 用于选择调试串口,建议选择 uart ns16550 for console(使用兼容 16550 的 uart7)
  • Command line interface 菜单中包含大量 U-Boot 命令,可选择需要的命令:

    • Boot commands: 启动不同格式内核镜像的命令。booti 可启动未压缩的内核镜像,是默认启动命令
    • Environment commands: 操作环境变量的命令
    • Memory commands: 内存操作命令。md 查看内存内容,mm 修改内存内容
    • Device access commands: 访问设备命令,包括 gpio、i2c、gpt、mmc、nvme、pci、pinmux、sf(操作 flash)、usb 等
    • Network commands: 网络相关命令。tftpboot 加载镜像,ping 测试网络连接
    • Misc commands: 各类其他命令。icache/dcache 可开关缓存
    • Filesystem commands: 文件系统支持。ext4/fat/filesystem 通用命令支持
  • Device Tree Control 中包含设备树相关选项:

    • Default Device Tree for DT control: 默认设备树文件(如 boardx)
  • Environment 中包含环境变量相关选项:

    • Enable overwriting environment: 允许修改以太网和串口环境变量
    • Environment is in SPI flash: 环境变量存储在 qspi flash 中
    • Environment address: 环境变量在内存中的地址
    • Environment offset: 环境变量在 flash 中的偏移地址
    • Environment Size: 环境变量占用空间大小
  • Networking support 包含网络相关选项:

    • Random ethaddr if unset: 未设置 MAC 地址时自动生成随机 MAC
    • Support IP datagram reassembly: 用于加速 TFTP 网络传输
  • Device Drivers 菜单中含有多种驱动,可以在其中选择需要的驱动:

    • GPIO support: GPIO 驱动支持
    • I2C support: I2C 驱动支持。mthread m1000 I2C driver 表示支持 m1000 SoC I2C 驱动
    • MMC Host controller Support: eMMC 和 SD 卡驱动支持
    • MTD Support: Flash 驱动框架支持
    • Ethernet PHY support: 以太网 PHY 驱动支持
    • Network device support: 网络设备驱动支持,包括以太网
    • NVM Express device support: NVMe SSD 驱动支持
    • PCI support: PCIe 驱动支持
    • Serial: 串口驱动支持
    • SPI Support: SPI 驱动支持,包括 QSPI
    • USB support: USB 驱动支持
  • File systems 文件系统相关支持:

    • Enable ext4 filesystem support: ext4 文件系统支持
    • Enable FAT filesystem support: FAT 文件系统支持

开发参考

开发定制的命令

U-Boot 提供了大量命令,有时需定制自定义命令。

命令代码在 cmd/ 文件夹中。可以在此文件夹中新建文件,如 commandx.c,并在 Makefile 中添加一行:

obj-y += commandx.o

commandx.c 中,要包含如下头文件:

E300 020 图示

在文件末尾,需要加入如下代码(假定命令为 commandx):

E300 021 图示

U_BOOT_CMD 是定义命令的一个特殊宏。

  • commandx 是指命令本身的字符串表示;
  • 第一个 1 是指命令参数的最大数量(包括命令本身),因此 1 表示该命令不接受额外参数;
  • 第二个 1 表示该命令是否可以被重复执行;
  • do_commandx 是执行该命令时所调用的函数名;
  • 最后的两个字符串分别用于解释该命令及其用法说明,通常会显示在 help 命令输出中。

因此,需要定义 do_commandx 函数:

static int do_commandx(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
...

return CMD_RET_SUCCESS;
}

argc 是参数的数量,包括命令本身;

argv 是一个字符串数组,包含每个参数的字符串表示;

... 部分就是该命令需要实现的功能代码。

在函数内部,其实也可以调用其他 U-Boot 命令,使用如下函数:

run_command(const char *cmd, int flag);

例如:

run_command("pci emu", 0);

其中 flag 一般设置为 0 即可。


调试方法说明

U-Boot 主要通过串口进行调试,默认使用 uart7(兼容 16550 标准)作为调试串口,波特率默认为 115200


U-Boot 启动 log 分析

U-Boot 启动 log 如下图所示:

E300 022 图示

  • U-Boot 2025.04-g40d1c4934c9b-dirty (Aug 01 2025 - 04:46:14 +0000):表示 U-Boot 的版本号为 2025.04,括号中是编译的时间戳。
  • SoC: m1000 with ARM Cortex-A78 x 12 @ 1.2GHz:表示使用的 SoC 为 m1000,核心为 ARM Cortex-A78,支持 12 核。目前 U-Boot 仅支持单核模式。
  • DRAM: 16 GiB:表示 DDR 的内存容量为 16GB。
  • MMC: mmc@738000: 0mmc@738000: 0 表示 eMMC。
  • Loading Environment from SPIFlash...:表示从 QSPI Flash 中加载 U-Boot 的环境变量到 DDR 中。
  • In: serial@47000;Out: serial@47000;Err: serial@47000:表示输入输出及错误输出均为串口 serial@47000
  • Net: ethernet voltage is 1.8V;eth0: ethernet@734000:表示网络配置,默认使用 RGMII 接口,因此 PHY 电压为 1.8V。
  • Press <SPACE> key to stop autoboot in 2s:U-Boot 启动 Linux 内核前会等待若干秒(此处为 2 秒,可配置)。如果此时按下空格键,可打断启动流程并进入 U-Boot shell。

U-Boot shell 调试

在 U-Boot shell 中,有很多命令可以用于调试,主要分为以下几类:

环境变量命令

pri # 打印所有环境变量。
setenv # 设置环境变量的值。
eraseenv # 清空 QSPI Flash 中所有的环境变量。
saveenv # 将内存中的环境变量保存到 QSPI Flash 中。

内存命令

md <addr> # 打印指定内存地址内容,例如 md 0x900000000

E300 023 图示

mm <addr> # 修改内存内容。例如:
# mm 0x900000000
# 在 ? 后输入新值按回车,连续修改,按 q 退出。

E300 024 图示

MMC(eMMC/SD 卡)命令

mmc list # 列出所有 MMC 设备。
mmc info # 打印当前 MMC 设备的信息。
mmc read # 从 MMC 读取数据到内存中。
mmc write # 将内存中的数据写入到 MMC 中。
mmc dev <id> # 切换当前操作的设备(eMMC 或 SD 卡)。

USB 命令

usb start # 开始枚举 USB 设备。
usb stop # 停止 USB 访问(重新 start 前需要先 stop)。
usb info # 查看 USB 设备信息。

PCI 命令

pci emu # 枚举 PCIe 设备。

NVMe SSD 命令

nvme scan # 扫描并初始化 NVMe SSD。
nvme info # 查看 NVMe SSD 信息。
nvme read # 从 NVMe 读取数据到内存。
nvme write # 将内存中的数据写入到 NVMe。

QSPI Flash 命令

sf probe # 初始化 QSPI Flash(使用前必须运行)。
sf read # 从 QSPI 读取数据到内存。
sf write # 将内存数据写入到 QSPI。
sf erase # 擦除 QSPI Flash 数据。

网络命令

net list:列举当前可用的以太网设备。
ping:测试网络是否连通。
tftpboot:通过以太网和 tftp 协议从 tftp 服务器获取文件到内存中。

文件系统通用命令(支持 FAT 和 ext4)

ls <dev>:<part>[/path] # 查看指定路径下文件。
load <dev>:<part> <addr> <file> # 加载文件到内存。

FAT 文件系统专用命令

fatrm # 删除文件。
fatwrite # 写入内存数据到文件。
fatmkdir # 创建目录。

ext4 文件系统命令

ext4write # 将内存中的数据写入文件。

启动内核命令

booti # 启动未压缩内核镜像。
bootz # 启动 gzip 压缩格式的内核镜像。
bootefi # 启动 EFI 格式的内核镜像。

U-Boot 启动ISO镜像

1.4.1 U-Boot已支持启动安装ISO镜像的nvme硬盘,不需要输入命令,默认以ACPI方式启动。

DTS方式启动

setenv bootmode dts # 设置启动模式为 DTS。
load nvme 0:2 ${dtbaddr} boot/m1000-aimodule.dtb # 加载 DTS 文件到内存,路径根据实际路径修改。
load nvme 0:1 ${kerneladdr} /EFI/BOOT/BOOTAA64.EFI # 加载内核镜像到内存,路径根据实际路径修改。
bootefi ${kerneladdr} ${dtbaddr} # 启动内核镜像。

ACPI方式启动

setenv bootmode acpi # 设置启动模式为 ACPI。
load nvme 0:1 ${kerneladdr} /EFI/BOOT/BOOTAA64.EFI # 加载内核镜像到内存,路径根据实际路径修改。
bootefi ${kerneladdr} # 启动内核镜像。

ISO安装盘安装系统

# 安装ISO镜像到U盘。
sudo dd if=m1000-release-oem-2026051908.iso of=/dev/sda bs=4M status=progress conv=fsync
# 进入U-Boot shell。
setenv bootmode acpi # 设置启动模式为 ACPI。
usb start # 开始枚举 USB 设备。
load usb 0:1 ${kerneladdr} /EFI/BOOT/BOOTAA64.EFI # 加载内核镜像到内存,路径根据实际路径修改。
bootefi ${kerneladdr}

常见问题

Q:为什么没有 SPL 阶段?

A:MTT E300 SoC 使用 ARM64 架构,ARM32 传统架构中的 SPL 阶段被 TF-A 软件中的 BL2、BL31 阶段取代,U-Boot 在整个启动链条中处于 BL33 阶段,所以不再需要 SPL 阶段。

Q:运行启动命令后,看到了 Starting kernel ... 的打印,然后就没有打印信息了,是不是表示卡死在内核里面了?

A:不是的,Starting kernel ... 是指开始进行启动内核的流程,并没有完成流程,所以仍然可能卡死在 U-Boot 中。

Q:如何判断是卡死在 U-Boot 最后阶段还是卡死在内核的早期阶段?

A:可以在内核较早启动的汇编代码中加入简单的打印信息,例如在 arch/arm64/kernel/head.S 文件中。如果能看到这些打印,说明已经进入内核;如果没有看到,则说明仍然卡在 U-Boot 阶段。

Q:U-Boot 最开始运行的代码在哪?

A:U-Boot 最早期运行的代码在 arch/arm/cpu/armv8/start.S 文件中,_start 是程序开始运行的入口。

Q:调试驱动代码时,发现有时 CPU 已经将数据写入到 DDR 中,为什么设备看不到 DDR 中的数据?

A:有可能是数据还在缓存中。U-Boot 中开启了一级缓存,写入数据后需要运行 Cache Clean 相关函数才可保证数据真正写入 DDR。可使用 flush_dcache_range 函数。 同样地,从 DDR 中读取数据前也需运行 Cache Invalidate 相关函数,保证数据从 DDR 重新加载到 Cache,可使用 invalidate_dcache_range 函数。