uboot版本:2021.10
平台:armv8 rk3399 eMMC 16G LPDDR4 4G
本文主要基于uboot的执行流程进行分析而忽略了相关细节,从uboot的基本框架结构着手,新的uboot框架是有三部分组成的:TPL SPL uboot,而且编译后产生的镜像也是有三部分构成,所以也可以认为是三个独立的程序,只不过合在了一个代码框架里面了。
编译过程主要是通过CONFIG_TPL_BUILD CONFIG_SPL_BUILD这两个宏来指定,三个阶段开关打开情况:
TPL | CONFIG_TPL_BUILD=EN CONFIG_SPL_BUILD=EN |
SPL | CONFIG_TPL_BUILD=DIS CONFIG_SPL_BUILD=EN |
uboot | CONFIG_TPL_BUILD=DIS CONFIG_SPL_BUILD=DIS |
新版本在编译完成之后会在源码目录下生成tpl和spl文件夹,这两个文件夹就是用来保存编译后的object文件
可以看出整个uboot是由三部分固件组成:u-boot-tpl.bin u-boot-spl.bin u-boot.bin
每一个bin文件其实都包含了对应的dtb文件,dts/dt.dtb是uboot阶段使用的完整的dtb文件,而实际上u-boot-tpl.dtb和u-boot-spl.dtb是同一个文件,都是缩小版的dtb,在doc/README.tpl中有说明:
work@ubuntu:~/uboot/u-boot-2021.10$ grep "u-boot-tpl.dtb" -r ./
./tpl/.u-boot-tpl.dtb.cmd:cmd_tpl/u-boot-tpl.dtb := cp tpl/dts/dt-tpl.dtb tpl/u-boot-tpl.dtb
./tpl/.u-boot-tpl.bin.cmd:cmd_tpl/u-boot-tpl.bin := cp tpl/u-boot-tpl-dtb.bin tpl/u-boot-tpl.bin
./tpl/.u-boot-tpl-dtb.bin.cmd:cmd_tpl/u-boot-tpl-dtb.bin := cat tpl/u-boot-tpl-nodtb.bin tpl/u-boot-tpl.dtb > tpl/u-boot-tpl-dtb.bin
work@ubuntu:~/uboot/u-boot-2021.10$ grep "dt-tpl.dtb" -r ./
./tpl/.u-boot-tpl.dtb.cmd:cmd_tpl/u-boot-tpl.dtb := cp tpl/dts/dt-tpl.dtb tpl/u-boot-tpl.dtb
./tpl/dts/.dt-tpl.dtb.cmd:cmd_tpl/dts/dt-tpl.dtb := ./tools/fdtgrep -b u-boot,dm-pre-reloc -b u-boot,dm-tpl -RT dts/dt.dtb -n /chosen -n /config -O dtb | ./tools/fdtgrep -r -O dtb - -o tpl/dts/dt-tpl.dtb -P u-boot,dm-pre-reloc -P u-boot,dm-spl -P u-boot,dm-tpl -P pinctrl-0 -P pinctrl-names -P clock-names -P interrupt-parent -P assigned-clocks -P assigned-clock-rates -P assigned-clock-parents
通过查找发现u-boot-tpl.dtb的生成是通过./tools/fdtgrep来实现的,这个工具的作用是从dt.dtb文件中把需要的devicetree提取出来。
这样做的原因是因为cpu刚启动的时候ddr还没有被初始化,tpl阶段要在cpu内部的sram中执行,所以要尽量的小一些,刚开始只要进行一些必须的初始化就可以了。
我在分析源码流程的时候主要是通过这三个固件源码部分来分析的
start.s //第一个执行的文件
reset:
save_boot_params() bootrom.c
setjmp(brom_ctx) ret 0 //这里用于跳转到SPL阶段
save_boot_params_ret:
reset
lowlevel_init: //底层初始化 关Cache/BPB/TLB等
_main() crt0_64.s
board_init_f()//forward 前级舒适化 arch/tpl.c
board_init_r()//rear 后级初始化
上电第一个执行的文件是start.s,然后主要是board_init_f()和board_init_r()这两个函数
board_init_f() arch/tpl.c
spl_early_init()//早期初始化 在common/spl/spl.c文件中 跟SPL阶段共用
spl_common_init()
dm_init_and_scan()
dm_scan()
dm_extended_scan()
dm_scan_fdt()
dm_scan_fdt_node() //若status=disable则不执行lists_bind_fdt()
ofnode_is_enabled()
fdtdec_get_is_enabled()
fdt_getprop()
fdt_getprop_namelen()
fdt_get_property_namelen_()
lists_bind_fdt()
driver_check_compatible()
device_bind_with_driver_data()
device_bind_common()
uclass_get()
drv->bind(dev)->rk3399_syscon() ...
这里主要是在device_bind_common()中进行相应设备的驱动程序绑定,TPL阶段在编译的时候使用的devicetree是u-boot-tpl.dtb,dm_scan_fdt()这个函数相当于搜索dts中的设备并是否是okay状态,是的话就为其挂载驱动并初始化。
这个时候只绑定了CPU相关的基础驱动 因为MMC等这些驱动是在spl.bin里面,这时候没有被加载,这时候驱动的地址是在0xff8xxxxx 以上,运行在cpu的SRAM中,只加载了tpl.bin 里面没有emmc等设备驱动。
将lists_bin_fdt的调试信息打开:
[TPL阶段]
U-Boot TPL 2021.10 (Dec 06 2021 - 18:14:33)
lists_bind_fdt - found match at 'ns16550_serial': 'ns16550' matches 'snps,dw-apb-uart' devname 'serial@ff1a0000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmu' devname 'power-management@ff310000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmugrf' devname 'syscon@ff320000'
lists_bind_fdt - found match at 'rockchip_rk3399_pmucru': 'rockchip,rk3399-pmucru' matches 'rockchip,rk3399-pmucru' devname 'pmu-clock-controller@ff750000'
lists_bind_fdt - found match at 'rockchip_rk3399_cru': 'rockchip,rk3399-cru' matches 'rockchip,rk3399-cru' devname 'clock-controller@ff760000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-grf' devname 'syscon@ff770000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-cic' devname 'syscon@ff620000'
lists_bind_fdt - found match at 'rockchip_rk3399_dmc': 'rockchip,rk3399-dmc' matches 'rockchip,rk3399-dmc' devname 'dmc'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmusgrf' devname 'syscon@ff330000'
Channel 0: LPDDR4, 50MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
Channel 1: LPDDR4, 50MHz
BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB
256B stride
lpddr4_set_rate: change freq to 400000000 mhz 0, 1
lpddr4_set_rate: change freq to 800000000 mhz 1, 0
Trying to boot from BOOTROM
Returning to boot ROM...
[SPL阶段]
lists_bind_fdt - found match at 'rockchip_rk3288_dw_mshc': 'rockchip,rk2928-dw-mshc' matches 'rockchip,rk3288-dw-mshc' devname 'mmc@fe320000'
lists_bind_fdt - found match at 'rockchip_sdhci_5_1': 'arasan,sdhci-5.1' matches 'arasan,sdhci-5.1' devname 'mmc@fe330000'
lists_bind_fdt - found match at 'ns16550_serial': 'ns16550' matches 'snps,dw-apb-uart' devname 'serial@ff1a0000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmu' devname 'power-management@ff310000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmugrf' devname 'syscon@ff320000'
lists_bind_fdt - found match at 'rockchip_rk3399_pmucru': 'rockchip,rk3399-pmucru' matches 'rockchip,rk3399-pmucru' devname 'pmu-clock-controller@ff750000'
lists_bind_fdt - found match at 'rockchip_rk3399_cru': 'rockchip,rk3399-cru' matches 'rockchip,rk3399-cru' devname 'clock-controller@ff760000'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-grf' devname 'syscon@ff770000'
lists_bind_fdt - found match at 'rockchip_rk3399_pinctrl': 'rockchip,rk3399-pinctrl' matches 'rockchip,rk3399-pinctrl' devname 'pinctrl'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-cic' devname 'syscon@ff620000'
lists_bind_fdt - found match at 'rockchip_rk3399_dmc': 'rockchip,rk3399-dmc' matches 'rockchip,rk3399-dmc' devname 'dmc'
lists_bind_fdt - found match at 'rk3399_syscon': 'rockchip,rk3399-grf' matches 'rockchip,rk3399-pmusgrf' devname 'syscon@ff330000'
U-Boot SPL 2021.10 (Dec 06 2021 - 18:14:33 -0800)
Trying to boot from MMC2
可以发现在TPL阶段并没有找到mmc@fe330000设备的驱动程序,因为这个阶段驱动并没有加载,这个阶段初始化的设备是serial,syscon,pmu,dmc这些基本必须设备。
board_init_r()
board_boot_order() //arch/../spl-boot-order.c
fdt_path_offset()
spl_node_to_boot_device()
uclass_get_device_by_of_offset()
uclass_find_device_by_of_offset()
uclass_get_device_tail()
device_probe() //若找到设备,则执行相应的probe
boot_from_devices()
loader->load_image()->spl_return_to_bootrom()
board_return_to_bootrom()
back_to_bootrom()
_back_to_bootrom()
longjmp(brom_ctx, brom_cmd); 跳转到SPL
setjmp(brom_ctx)--save_boot_params() ret>0
因为在上一阶段中加载了一个BOOTROM设备的驱动程序,在这里执行相应的初始化程序(probe),如syscon, ddr等都在这个阶段进行初始化工作。
这里找到了一个SPL_LOAD_IMAGE_METHOD类型的引导设备BOOTROM ,对应的执行了spl_return_to_bootrom函数,在执行longjmp之后相当于又跳转到start.s中进行reset,然后进入SPL阶段。
crt0_64.s
board_init_f()
board_init_r()
board_init_f() arch/spl.c
spl_early_init()
spl_common_init()
dm_init_and_scan()
dm_scan()
dm_extended_scan()
dm_scan_fdt()
dm_scan_fdt()
dm_scan_fdt_node() //若status=disable则不执行lists_bind_fdt()
ofnode_is_enabled()
fdtdec_get_is_enabled()
fdt_getprop()
fdt_getprop_namelen()
fdt_get_property_namelen_()
lists_bind_fdt()//这里比较dts中设备的名称和驱动的名称是否一致
driver_check_compatible()
device_bind_with_driver_data()
device_bind_common()//在这里完成设备驱动的绑定
uclass_get()
drv->bind(dev)->rockchip_sdhci_bind() ... //这里以初始化eMMC为例
sdhci_bind()
mmc_bind()
mmc_bind()
blk_create_devicef()
blk_create_device()
device_bind_driver()
device_bind_driver_to_node()
device_bind_with_driver_data()
board_init_r() //common/spl.c
board_boot_order() //arch/../spl-boot-order.c
fdt_path_offset()
spl_node_to_boot_device()
uclass_get_device_by_of_offset()
uclass_find_device_by_of_offset()
uclass_get_device_tail()
device_probe() //若找到设备,则执行相应的probe
boot_from_devices()
spl_ll_find_loader()
spl_load_image()
loader->load_image()->spl_mmc_load_image()
spl_mmc_load()
spl_mmc_find_device()
mmc_init_device()
mmc_start_init()
mmc_get_op_cond()
mmc_power_init()
mmc_load_image_raw_sector()
spl_load_simple_fit()
spl_simple_fit_read()
spl_fit_get_image_node("loadables")
spl_load_fit_image()
fit_image_get_load()
fit_image_get_address()--spl_image->load_addr=0x200000
spl_fit_append_fdt()
spl_load_fit_image()
->entry_point=load_addr
jump_to_image_no_args(&spl_image)
spl_image->entry_point() 跳转到uboot
crt0_64.s
board_init_f()
relocate_code()//代码重映射
board_init_r()
board_init_f() board_f.c
initcall_run_list()->init_sequence_f[]() //执行这个数组中的函数进行初始化
...
initf_dm() //初始化设备驱动
dm_init_and_scan()
...
display_options() //打印起始信息如:U-Boot 2021.04 (Dec 03 2021 - 17:19:14 +0800)
display_options_get_banner()
display_options_get_banner_priv()
board_init_r() board_r.c
initcall_run_list()->init_sequence_r[]() //执行数组中的函数
run_main_loop()
main_loop()
bootdelay_process()//在环境变量env中获取bootcmd的值(本例为run distro_bootcmd)
autoboot_command("run distro_bootcmd")//检测是否有ctl+c按下,若按下则退出,否则执行自动脚本不会退出
abortboot()
run_command_list()
cli_simple_run_command_list()
cli_simple_run_command()
cmd_process()
cli_loop()//进入命令行,等待输入命令
parse_file_outer()
parse_stream_outer()
run_list()
run_list_real()
run_pipe_real()
cmd_process()
find_cmd()
cmd_call()
cmdtp->cmd_rep() -> do_mmcops()...
do_mmcops() argc=3 argv[0]=mmc argv[1]=dev //以mmc命令为例
find_cmd_tbl()
cp->cmd()
do_mmc_dev()
init_mmc_device()
__init_mmc_device()
mmc_init()
mmc_start_init()
mmc_get_op_cond()
run distro_bootcmd(env distro_bootcmd)->
run bootcmd_${target}-> //target= mmc1 mmc0 usb0 pxe dhcp
run bootcmd_mmc0(env bootcmd_mmc0)...->
run mmc_boot(env mmc_boot)->
run scan_dev_for_boot_part(env scan_dev_for_boot_part) fstype() ->
run scan_dev_for_boot(env scan_dev_for_boot)->
run scan_dev_for_extlinux(env scan_dev_for_extlinux)...->
run boot_extlinux(env boot_extlinux)->
sysboot()
下面是我使用的开发板的环境变量
altbootcmd=setenv boot_syslinux_conf extlinux/extlinux-rollback.conf;run distro_bootcmd
arch=arm
baudrate=1500000
board=evb_rk3399
board_name=evb_rk3399
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootaa64.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_efi_bootmgr=if fdt addr ${fdt_addr_r}; then bootefi bootmgr ${fdt_addr_r};else bootefi bootmgr;fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}
boot_net_usb_start=usb start
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_syslinux_conf=extlinux/extlinux.conf
boot_targets=mmc1 mmc0 usb0 pxe dhcp sf0
bootcmd=run distro_bootcmd
bootcmd_dhcp=devtype=dhcp; run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00011:UNDI:003000;setenv bootp_arch 0xb;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=devnum=0; run mmc_boot
bootcmd_mmc1=devnum=1; run mmc_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_sf0=busnum=0; run sf_boot
bootcmd_usb0=devnum=0; run usb_boot
bootdelay=2
cpu=armv8
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
ethact=ethernet@fe300000
fdt_addr_r=0x01f00000
fdtcontroladdr=f5f28700
fdtfile=rockchip/rk3399-khadas-edge.dtb
fdtoverlay_addr_r=0x02000000
kernel_addr_r=0x02080000
kernel_comp_addr_r=0x08000000
kernel_comp_size=0x2000000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
partitions=uuid_disk=${uuid_gpt_disk};name=loader1,start=32K,size=4000K,uuid=${uuid_gpt_loader1};name=loader2,start=8MB,size=4MB,uuid=${uuid_gpt_loader2};name=trust,size=4M,uuid=${uuid_gpt_atf};name=boot,size=112M,bootable,uuid=${uuid_gpt_boot};name=rootfs,size=-,uuid=B921B045-1DF0-41C3-AF44-4C6F280D3FAE;
pxefile_addr_r=0x00600000
ramdisk_addr_r=0x06000000
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done; setenv devplist
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;run boot_efi_bootmgr;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootaa64.efi; then echo Found EFI removable media binary efi/boot/bootaa64.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_conf}; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scan_sf_for_scripts=${devtype} read ${scriptaddr} ${script_offset_f} ${script_size_f}; source ${scriptaddr}; echo SCRIPT FAILED: continuing...
script_offset_f=0xffe000
script_size_f=0x2000
scriptaddr=0x00500000
sf_boot=if sf probe ${busnum}; then devtype=sf; run scan_sf_for_scripts; fi
soc=rk3399
stderr=serial@ff1a0000
stdin=serial@ff1a0000
stdout=serial@ff1a0000
usb_boot=usb start; if usb dev ${devnum}; then devtype=usb; run scan_dev_for_boot_part; fi
vendor=rockchip