uboot 2021.10源码分析(启动流程)

uboot版本:2021.10

平台:armv8  rk3399  eMMC 16G  LPDDR4 4G

本文主要基于uboot的执行流程进行分析而忽略了相关细节,从uboot的基本框架结构着手,新的uboot框架是有三部分组成的:TPL SPL uboot,而且编译后产生的镜像也是有三部分构成,所以也可以认为是三个独立的程序,只不过合在了一个代码框架里面了。

1. 编译过程

编译过程主要是通过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

DeviceTree文件说明

每一个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中执行,所以要尽量的小一些,刚开始只要进行一些必须的初始化就可以了。

我在分析源码流程的时候主要是通过这三个固件源码部分来分析的

2. TPL阶段

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()

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_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阶段。

3. SPL阶段

crt0_64.s
	board_init_f()
	board_init_r()

初始化过程 board_init_f()

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()

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

4. uboot阶段

crt0_64.s
	board_init_f()
	relocate_code()//代码重映射
	board_init_r()

初始化过程 board_init_f()

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_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

  • 9
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值