U-boot 21.10 启动流程梳理(草稿状态,完成度 45%)

背景

  • 设备:MilkV Duo S

  • 版本:U-boot 2021.10

  • 编译命令

    # Milkv-DuoS SD卡版本,对应[board]与[config]分别为:
    # milkv-duo	 cv1800b_milkv_duo_sd
    # milkv-duos cv1813h_milkv_duos_sd
    # 拆解如下
    CHIP=cv1813h
    VENDOR=milkv
    BOARD=duos
    BOOT=sd
    
    # 得到板级目录与配置文件
    BOARD_FULL=${CHIP}-${VENDOR}-${BOARD}-${BOOT}
    CONFIG_FULL=${CHIP}_${VENDOR}_${BOARD}_${BOOT}
    
    # 加载板级配置与基础环境变量
    source device/${BOARD_FULL}/boardconfig.sh
    source build/milkvsetup.sh
    
    # 加载板级配置
    defconfig ${CONFIG_FULL}
    
    # 编译U-boot
    build_uboot
    

平台启动

入口确认

查看Kbuild生成的链接脚本u-boot-2021.10/build/cv1813h_milkv_duos_sd/u-boot.lds,内容如下:

OUTPUT_ARCH("riscv")
ENTRY(_start)
SECTIONS
{
	. = ALIGN(4);
	.text : {							// .text,代码段
 		arch/riscv/cpu/start.o (.text)	// 入口所在源码文件
	...
	.data : {							// .data,数据段
		*(.data*)						 	
	...
	. = ALIGN(4);
	_end = .;							// GCC定义的末尾,此位置到	

	.bss : {							// .bss, BSSP段
		__bss_start = .;
		*(.bss*)
		. = ALIGN(8);
		__bss_end = .;
	}
}		
...

其中,关键信息攫取:

  • OUTPUT_ARCH("riscv"),目标平台为riscv;
  • ENTRY(_start),入口为_start;
  • arch/riscv/cpu/start.o (.text),入口可能存在于汇编文件arch/riscv/cpu/start.S中;
  • __end,指示代码段的结束位置,一般在这后面跟着的是堆栈内容;
  • .bss,BSS段是ELF文件折最后一个段,有两个位置信息:__bss_start BSS段起始位置;__bss_end,BSS段末尾;

而生成该u-boot.lds脚本的命令可以参考文件u-boot-2021.10/build/cv1813h_milkv_duos_sd/.u-boot.lds.cmd,内容如下:

cmd_u-boot.lds := riscv64-unknown-linux-musl-gcc -E -Wp,-MD,./.u-boot.lds.d -D__KERNEL__ -D__UBOOT__ -DCONFIG_SKIP_RAMDISK=y -DCONFIG_USE_DEFAULT_ENV=y -DCVICHIP=cv1813h -DCVIBOARD=milkv_duos_sd -DCV1813H_MILKV_DUOS_SD -DMIPI_PANEL_HX8394 -ffixed-gp -fpic -fno-common -gdwarf-2 -ffunction-sections -fdata-sections -pipe -march=rv64imac -mabi=lp64 -mcmodel=medlow -Iinclude  -I/home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include  -I/home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include/cvitek  -I/home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/arch/riscv/include -include /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include/linux/kconfig.h -nostdinc -isystem /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/host-tools/gcc/riscv64-linux-musl-x86_64/bin/../lib/gcc/riscv64-unknown-linux-musl/10.2.0/include -ansi -include /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include/u-boot/u-boot.lds.h -DCPUDIR=arch/riscv/cpu/generic  -D__ASSEMBLY__ -x assembler-with-cpp -std=c99 -P -o u-boot.lds /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/arch/riscv/cpu/u-boot.lds

source_u-boot.lds := /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/arch/riscv/cpu/u-boot.lds

deps_u-boot.lds := \
  /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include/linux/kconfig.h \
    $(wildcard include/config/booger.h) \
    $(wildcard include/config/foo.h) \
    $(wildcard include/config/spl/.h) \
    $(wildcard include/config/tpl/build.h) \
    $(wildcard include/config/spl/build.h) \
    $(wildcard include/config/spl/foo.h) \
    $(wildcard include/config/tpl/foo.h) \
    $(wildcard include/config/option.h) \
    $(wildcard include/config/acme.h) \
    $(wildcard include/config/spl/acme.h) \
    $(wildcard include/config/tpl/acme.h) \
  /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/u-boot-2021.10/include/u-boot/u-boot.lds.h \

u-boot.lds: $(deps_u-boot.lds)

其中,关键信息:

  • cmd_u-boot.lds,u-boot.lds文件生成命令,其中有参数:
    • riscv64-unknown-linux-musl-gcc,使用GCC工具;
    • .../u-boot-2021.10/arch/riscv/cpu/u-boot.lds,源文件为arch/riscv/cpu/u-boot.lds
    • -o u-boot.lds,编译输出为u-boot.lds

查看相似文件,可以获知程序入口。

查看u-boot编译命令文件u-boot-2021.10/build/cv1813h_milkv_duos_sd/.u-boot.cmd,有如下内容:

cmd_u-boot := riscv64-unknown-linux-musl-ld.bfd -m elf64lriscv --gc-sections -static -pie -Bstatic  --no-dynamic-linker --build-id=none -Ttext 0x80200000 -o u-boot -T u-boot.lds arch/riscv/cpu/start.o --whole-archive  arch/riscv/cpu/built-in.o  arch/riscv/cpu/generic/built-in.o  arch/riscv/lib/built-in.o  board/cvitek/cv181x/built-in.o  cmd/built-in.o  common/built-in.o  disk/built-in.o  drivers/built-in.o  drivers/cvi_usb/built-in.o  drivers/dma/built-in.o  drivers/gpio/built-in.o  drivers/net/built-in.o  drivers/net/phy/built-in.o  drivers/power/built-in.o  drivers/power/battery/built-in.o  drivers/power/domain/built-in.o  drivers/power/fuel_gauge/built-in.o  drivers/power/mfd/built-in.o  drivers/power/pmic/built-in.o  drivers/power/regulator/built-in.o  drivers/serial/built-in.o  drivers/spi/built-in.o  drivers/usb/cdns3/built-in.o  drivers/usb/common/built-in.o  drivers/usb/dwc3/built-in.o  drivers/usb/emul/built-in.o  drivers/usb/eth/built-in.o  drivers/usb/host/built-in.o  drivers/usb/mtu3/built-in.o  drivers/usb/musb-new/built-in.o  drivers/usb/musb/built-in.o  drivers/usb/phy/built-in.o  drivers/usb/ulpi/built-in.o  env/built-in.o  fs/built-in.o  lib/built-in.o  net/built-in.o --no-whole-archive -L /home/gaoyang3513/Source/10-CV1800/01-MilkDuo/02-Project/CV180x_Milkv_Baseline/host-tools/gcc/riscv64-linux-musl-x86_64/bin/../lib/gcc/riscv64-unknown-linux-musl/10.2.0/lib64/lp64 -lgcc -Map u-boot.map;  true

其中,有关键信息:

  • -Ttext 0x80200000,入口地址为0x80200000,在DDR内存起始偏移128KB的位置;
  • -o u-boot -T u-boot.lds,依据链接脚本u-boot.lds编译输出u-boot文件;

启动源码

DuoS_SG2000_RISCV

查找入口_start,有如下:

$ grep -wrn "_start" u-boot-2021.10/
...
u-boot-2021.10/arch/x86/cpu/start.S:65:_start:
...
u-boot-2021.10/arch/arm/cpu/armv8/start.S:20:_start:
...
u-boot-2021.10/arch/riscv/cpu/start.S:41:_start:
...
u-boot-2021.10/arch/mips/mach-jz47xx/start.S:20:_start:
...

有多个平台的汇编入口,由上一章节链接脚本的生成过程中可知,汇编实现源文件为u-boot-2021.10/arch/riscv/cpu/start.S,内容如下:

.globl _start
_start:
	...
	mv	a0, zero		/* a0 <-- boot_flags = 0 */
	la	t5, board_init_f
	jalr	t5			/* jump to board_init_f() */			// 调用 board_init_f
	...
/*
 * void relocate_code(addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
.globl relocate_code											// 定义函数 relocate_code
relocate_code:
	mv	s2, a0			/* save addr_sp */
	mv	s3, a1			/* save addr of gd */
	mv	s4, a2			/* save addr of destination */			
...
call_board_init_r:
	jal	invalidate_icache_all
	jal	flush_dcache_all
	la	t0, board_init_r    /* offset of board_init_r() */
	add	t4, t0, t6			/* real address of board_init_r() */
/*
 * setup parameters for board_init_r
 */
	mv	a0, s3			/* gd_t */								
	mv	a1, s4			/* dest_addr */

/*
 * jump to it ...
 */
	jr	t4			/* jump to board_init_r() */				// 调用 board_init_r

其中,关键信息:

  • 初始化工作前段(front)由C语言实现的函数board_init_f完成,主要完成如下工作:

    • 完成栈SP、BSS等的初始化,保证由汇编环境顺利跳转C运行时环境;
    • DRAM初始化;
    • 内存相关初始化;
    • 其他;
  • .globl relocate_code,全局符号relocate_code,用于实现U-boot重定向。调用时机在board_init_f完成后,board_init_r执行前;

    重定向功能是U-boot的一个重要功能,需要另开章节说明,此处简单带过。

  • 初始化工作后段(rear)由函数board_init_r完成,主要完成如下工作:

    • 板级初始化;
    • Flash、网卡等重要外设初始化;
    • 自动引导;
    • 命令行交互,不再退出;

可与ARM平台进入board_init_f的流程做一个对比。

Lubancat2_RK3568_ARM
#---- u-boot/arch/arm/cpu/armv8/start.S
...
master_cpu:
	bl	_main
	
#---- u-boot/arch/arm/lib/crt0_64.S
ENTRY(_main)
...
	bl	board_init_f
	...
	b	relocate_code				// 调用relocate_code,所属文件:u-boot/arch/arm/lib/relocate_64.S
	...
	b	board_init_r				/* PC relative jump */
	/* NOTREACHED - board_init_r() does not return */
ENDPROC(_main)

初始化流程

board_init_f

# common/board_f.c

void board_init_f(ulong boot_flags)
{
	if (initcall_run_list(init_sequence_f))
		hang();
  
static const init_fnc_t init_sequence_f[] = {
	setup_mon_len,
#ifdef CONFIG_OF_CONTROL
	fdtdec_setup,
#endif
#ifdef CONFIG_TRACE
	trace_early_init,
#endif
	initf_malloc,
	log_init,
	initf_bootstage,	/* uses its own timer, so does not need DM */
#ifdef CONFIG_BLOBLIST
	bloblist_init,
#endif
	setup_spl_handoff,
	initf_console_record,
#if defined(CONFIG_HAVE_FSP)
	arch_fsp_init,
#endif
	arch_cpu_init,		/* basic arch cpu dependent setup */
	mach_cpu_init,		/* SoC/machine dependent CPU setup */
	initf_dm,
	arch_cpu_init_dm,
#if defined(CONFIG_BOARD_EARLY_INIT_F)
	board_early_init_f,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
	/* get CPU and bus clocks according to the environment variable */
	get_clocks,		/* get CPU and bus clocks (etc.) */
#endif
#if !defined(CONFIG_M68K)
	timer_init,		/* initialize timer */
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
	board_postclk_init,
#endif
	env_init,		/* initialize environment */
	init_baud_rate,		/* initialze baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,		/* stage 1 init of console */
	display_options,	/* say that we are here */
	display_text_info,	/* show debugging info if required */
#if defined(CONFIG_PPC) || defined(CONFIG_SH) || defined(CONFIG_X86)
	checkcpu,
#endif
#if defined(CONFIG_SYSRESET)
	print_resetinfo,
#endif
#if defined(CONFIG_DISPLAY_CPUINFO)
	print_cpuinfo,		/* display cpu info (and speed) */
#endif
#if defined(CONFIG_DTB_RESELECT)
	embedded_dtb_select,
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
	show_board_info,
#endif
	INIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)
	misc_init_f,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_I2C)
	init_func_i2c,
#endif
#if defined(CONFIG_VID) && !defined(CONFIG_SPL)
	init_func_vid,
#endif
	announce_dram_init,
	dram_init,		/* configure available RAM banks */
#ifdef CONFIG_POST
	post_init_f,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)
	testdram,
#endif /* CONFIG_SYS_DRAM_TEST */
	INIT_FUNC_WATCHDOG_RESET

#ifdef CONFIG_POST
	init_post,
#endif
	INIT_FUNC_WATCHDOG_RESET
	/*
	 * Now that we have DRAM mapped and working, we can
	 * relocate the code and continue running from DRAM.
	 *
	 * Reserve memory at end of RAM for (top down in that order):
	 *  - area that won't get touched by U-Boot and Linux (optional)
	 *  - kernel log buffer
	 *  - protected RAM
	 *  - LCD framebuffer
	 *  - monitor code
	 *  - board info struct
	 */
	setup_dest_addr,
#ifdef CONFIG_PRAM
	reserve_pram,
#endif
	reserve_round_4k,
#ifdef CONFIG_ARM
	reserve_mmu,
#endif
	reserve_video,
	reserve_trace,
	reserve_uboot,
	reserve_malloc,
	reserve_board,
	setup_machine,
	reserve_global_data,
	reserve_fdt,
	reserve_bootstage,
	reserve_bloblist,
	reserve_arch,
	reserve_stacks,
	dram_init_banksize,
	show_dram_config,
#if defined(CONFIG_M68K) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
	defined(CONFIG_SH)
	setup_board_part1,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
	INIT_FUNC_WATCHDOG_RESET
	setup_board_part2,
#endif
	display_new_sp,
#ifdef CONFIG_OF_BOARD_FIXUP
	fix_fdt,
#endif
	INIT_FUNC_WATCHDOG_RESET
	reloc_fdt,
	reloc_bootstage,
	reloc_bloblist,
	setup_reloc,
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
	copy_uboot_to_ram,
	do_elf_reloc_fixups,
	clear_bss,
#endif
#if defined(CONFIG_XTENSA)
	clear_bss,
#endif
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
		!CONFIG_IS_ENABLED(X86_64)
	jump_to_copy,
#endif
	NULL,							// 以NULL结尾,结束initcall_run_list初始化序列,退出board_init_f阶段
};   

relocate_code

DuoS_SG2000_RISCV
.globl _start
_start:
	...
/*
 * Set stackpointer in internal/ex RAM to call board_init_f
 */
call_board_init_f:
	li	t0, -16
	...
	li	t1, CONFIG_SYS_INIT_SP_ADDR								// 栈指针
	and	sp, t1, t0		/* force 16 byte alignment */			// 栈寄存器初始化,16字节对齐
	...
	/* Enable cache */
	jal	icache_enable											// I-Cache 使能
	jal	dcache_enable											// D-Cache 使能
	...
	mv	a0, zero		/* a0 <-- boot_flags = 0 */
	la	t5, board_init_f
	jalr	t5			/* jump to board_init_f() */			// 调用 board_init_f
	...
/*
 * void relocate_code(addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
.globl relocate_code											// 定义函数 relocate_code
relocate_code:
	mv	s2, a0			/* save addr_sp */
	mv	s3, a1			/* save addr of gd */
	mv	s4, a2			/* save addr of destination */			// 重定向的目标地址

/*
 *Set up the stack
 */
stack_setup:													// 栈初始化
#if CONFIG_IS_ENABLED(SMP)
	...
#else
	mv	sp, s2
#endif

	la	t0, _start												// 计算重定向需要的偏移量,_start是入口地址,即:0x8020000;s4是重定向的目标地址
	sub	t6, s4, t0			/* t6 <- relocation offset */		// 重定向偏移量(t6) = 目标地址(s4, addr_moni) - 入口地址(t0,_start)
	beq	t0, s4, clear_bss	/* skip relocation */				// 如果目标地址即是入口地址,即不需要搬移,跳转到BSS清零阶段运行

	mv	t1, s4			/* t1 <- scratch for copy_loop */
	la	t3, __bss_start
	sub	t3, t3, t0		/* t3 <- __bss_start_ofs */				// BSS段偏移量(t3) = 入口地址(t0,_start) - BSS地址(s3, __bss_start)
	add	t2, t0, t3		/* t2 <- source end address */			// 代码段末尾(t2) = 入口地址(t0,_start) + BSS段偏移量(t3)

copy_loop:
	LREG	t5, 0(t0)
	addi	t0, t0, REGBYTES
	SREG	t5, 0(t1)
	addi	t1, t1, REGBYTES
	blt	t0, t2, copy_loop										// 循环进制拷贝,直至“代码段末尾(t2)”,即源码(代码段+数据段)都拷贝完成
...
clear_bss:
	la	t0, __bss_start		/* t0 <- rel __bss_start in FLASH */
	add	t0, t0, t6		/* t0 <- rel __bss_start in RAM */
	la	t1, __bss_end		/* t1 <- rel __bss_end in FLASH */
	add	t1, t1, t6		/* t1 <- rel __bss_end in RAM */
	beq	t0, t1, relocate_secondary_harts
...
relocate_secondary_harts:
#if CONFIG_IS_ENABLED(SMP)
	...					// Duo S 单核,SMP内容忽略
...
/*
 * We are done. Do not return, instead branch to second part of board
 * initialization, now running from RAM.
 */
call_board_init_r:
	jal	invalidate_icache_all
	jal	flush_dcache_all
	la	t0, board_init_r    /* offset of board_init_r() */
	add	t4, t0, t6			/* real address of board_init_r() */		// board_init_r重定向后的地址 = 原地址 + 重定向偏移量(t6)
/*
 * setup parameters for board_init_r
 */
	mv	a0, s3			/* gd_t */										// 参数{gd_t, dest_addr}
	mv	a1, s4			/* dest_addr */

/*
 * jump to it ...
 */
	jr	t4			/* jump to board_init_r() */						// 调用board_init_r(重定向后)

其中,关键信息:

  • 初始化工作前段(front)由C语言实现的函数board_init_f完成,所以由汇编环境进入C运行时环境需要先完成栈SP、BSS的初始化;

  • CONFIG_SYS_INIT_SP_ADDR,C语言需要的栈指针SP的值,Duo S单板设定的值为:

    #---- u-boot-2021.10/include/configs/cv180x-fpga.h
    #define CONFIG_SYS_INIT_SP_ADDR		CVIMMAP_CONFIG_SYS_INIT_SP_ADDR
    
    #---- u-boot-2021.10/include/cvi_board_memmap.h
    #define CVIMMAP_CONFIG_SYS_INIT_SP_ADDR 0x82800000  /* offset 40.0MiB */
    

    其中已知,由SG2000手册可知DDR内存物理地址为0x80000000,如图

    在这里插入图片描述

    依据ELF文件的内存设计,如图

    在这里插入图片描述

    结合章节<入口确认>获知的信息(BSS段是最后一个段)且入口地址为0x8020_0000,设置栈底地址CONFIG_SYS_INIT_SP_ADDR0x8280_0000,也就可认为U-boot的有效内容为:
    U b o o t _ S i z e = _ _ b s s _ e n d − _ _ s t a r t Uboot\_Size = \_\_bss\_end - \_\_start Uboot_Size=__bss_end__start
    代入后得到值0x260_0000,即38MB。

  • jal icache_enablejal dcache_enable,调用I-Cache与D-Cache使能实现;

    I-Cache(指令缓存)和D-Cache(数据缓存)都是处理器中的缓存,开启后可以提高处理器对指令和数据的访问速度和效率。

    该函数定义为弱符号内容为空,可以hart(硬件线程,risc-v的概念,等效于CPU)厂商实现,但实际没有。之所以可以为空,可能是因:U-boot属于BL33,在BL2中已经完成I-Cache、D-cache的使能,所以不需要重复使能。

  • jalr t5,无条件跳转到符号board_init_f处运行,等效于调用函数board_init_f,其中还有前置实现:

    • mv a0, zero,通用寄存器a0为ABI名称(在RISC-V下,对应通用寄存器x10),即函数参数0。其值设置为0,即形参boot_flags值为0;
    • la t5, board_init_f,将符号board_init_f地址存入寄存器t5中;
    • 此处使用伪指令jalr而不使用jal的因为在于:
      1. 符号icache_enabledcache_enable都是弱符号,平台可能不实现;
      2. board_init_f为强符号;
  • .globl relocate_code,全局符号relocate_code,用于实现U-boot重定向。调用时机在board_init_f完成后,board_init_r执行前;

    重定向功能是U-boot的一个重要功能,需要另开章节说明,此处简单带过。

  • 执行前置的(front)初始化操作,调用board_init_f接口

    • 清除BBS段
    • 执行relocation操作
  • 执行后置的(rear)初始化操作,调用board_init_r接口

Lubancat2_RK3568_ARM
#---- u-boot/arch/arm/cpu/armv8/start.S
...
master_cpu:
	bl	_main
	
#---- u-boot/arch/arm/lib/crt0_64.S
ENTRY(_main)
...
	bl	board_init_f
	...
	adr	lr, relocation_return														// 返回地址,重定向前
	/* Add in link-vs-runtime offset */
	adr	x0, _start		/* x0 <- Runtime value of _start */
	ldr	x9, _TEXT_BASE	/* x9 <- Linked value of _start */
	sub	x9, x9, x0		/* x9 <- Run-vs-link offset */
	add	lr, lr, x9

	/* Add in link-vs-relocation offset */
	ldr	x9, [x18, #GD_RELOC_OFF]	/* x9 <- gd->reloc_off */
	add	lr, lr, x9					/* new return address after relocation */		// 计算重定向后的lr地址;
	ldr	x0, [x18, #GD_RELOCADDR]	/* x0 <- gd->relocaddr */    
	b	relocate_code					// 调用relocate_code,所属文件:u-boot/arch/arm/lib/relocate_64.S
	...
	b	board_init_r				/* PC relative jump */
	/* NOTREACHED - board_init_r() does not return */
ENDPROC(_main)

#---- u-boot/arch/arm/lib/relocate_64.S
...
ENTRY(relocate_code)
	...
copy_loop:
	ldp	x10, x11, [x1], #16	/* copy from source address [x1] */
	stp	x10, x11, [x0], #16	/* copy to   target address [x0] */
	cmp	x1, x2			/* until source end address [x2] */
	b.lo	copy_loop
	str	x0, [sp, #24]
	...
	ret									// 返回重定向后的lr地址;
ENDPROC(relocate_code)	

ARM32使用r9保存gd指针;ARM64 使用x18保存gd指针。

board_init_r

# common/board_r.c

static init_fnc_t init_sequence_r[] = {
	initr_trace,
	initr_reloc,
	/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
	initr_caches,
	/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
	 *	 A temporary mapping of IFC high region is since removed,
	 *	 so environmental variables in NOR flash is not available
	 *	 until board_init() is called below to remap IFC to high
	 *	 region.
	 */
#endif
	initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
	initr_unlock_ram_in_cache,
#endif
	initr_barrier,
	initr_malloc,
	log_init,
	initr_bootstage,	/* Needs malloc() but has its own timer */
	initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
	initr_noncached,
#endif
	bootstage_relocate,
#ifdef CONFIG_OF_LIVE
	initr_of_live,
#endif
#ifdef CONFIG_DM
	initr_dm,
#endif
#ifdef CONFIG_BSP_SPEC
	initr_func_bsp,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) || \
	defined(CONFIG_SANDBOX)
	board_init,	/* Setup chipselects */			// 板级初始化
#endif
	/*
	 * TODO: printing of the clock inforamtion of the board is now
	 * implemented as part of bdinfo command. Currently only support for
	 * davinci SOC's is added. Remove this check once all the board
	 * implement this.
	 */
#ifdef CONFIG_CLOCKS
	set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
	efi_memory_init,
#endif
	stdio_init_tables,
	initr_serial,
	initr_announce,
	INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
	initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
	initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
	board_early_init_r,
#endif
	INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_POST
	initr_post_backlog,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
	/*
	 * Do early PCI configuration _before_ the flash gets initialised,
	 * because PCU resources are crucial for flash access on some boards.
	 */
	initr_pci,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
	arch_early_init_r,
#endif
	power_init_board,
#ifdef CONFIG_MTD_NOR_FLASH
	initr_flash,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86)
	/* initialize higher level parts of CPU like time base and timers */
	cpu_init_r,
#endif
#ifdef CONFIG_CMD_NAND
	initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
	initr_onenand,
#endif
#ifdef CONFIG_MMC
	initr_mmc,
#endif
	initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
	initr_malloc_bootparams,
#endif
	INIT_FUNC_WATCHDOG_RESET
	initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
	mac_read_from_eeprom,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
	/*
	 * Do pci configuration
	 */
	initr_pci,
#endif
	stdio_add_devices,
	initr_jumptable,				// standalone 相关跳转表
#ifdef CONFIG_API
	initr_api,
#endif
	console_init_r,		/* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
	console_announce_r,
	show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
	arch_misc_init,		/* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
	misc_init_r,		/* miscellaneous platform-dependent init */
#endif
	INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
	initr_kgdb,
#endif
	interrupt_init,
#ifdef CONFIG_ARM
	initr_enable_interrupts,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_M68K)
	timer_init,		/* initialize timer */
#endif
#if defined(CONFIG_LED_STATUS)
	initr_status_led,
#endif
	/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
	initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
	board_late_init,
#endif
#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
	INIT_FUNC_WATCHDOG_RESET
	initr_scsi,
#endif
#ifdef CONFIG_BITBANGMII
	initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
	INIT_FUNC_WATCHDOG_RESET
	initr_net,
#endif
#ifdef CONFIG_POST
	initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_IDE)
	initr_pcmcia,
#endif
#if defined(CONFIG_IDE) && !defined(CONFIG_BLK)
	initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
	INIT_FUNC_WATCHDOG_RESET
	/*
	 * Some parts can be only initialized if all others (like
	 * Interrupts) are up and running (i.e. the PC-style ISA
	 * keyboard).
	 */
	last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
	INIT_FUNC_WATCHDOG_RESET
	initr_bedbug,
#endif
#if defined(CONFIG_PRAM)
	initr_mem,
#endif
	run_main_loop,
};

扩展

  • 参考下面的代码,解答:为什么调用board_init_f时使用的是jalr而不是jarl?

    	/* Enable cache */
    	jal	icache_enable
    	jal	dcache_enable
    
    #ifdef CONFIG_DEBUG_UART
    	jal	debug_uart_init
    #endif
    
    	mv	a0, zero		/* a0 <-- boot_flags = 0 */
    	la	t5, board_init_f
    	jalr	t5			/* jump to board_init_f() */
    

    ​ 在RISC-V架构中,jal指令用于进行无条件跳转,并将当前指令地址+4存储到目标寄存器,同时延迟槽中的指令会执行。jalr指令也用于进行无条件跳转,但它是通过寄存器来指定跳转的目标地址,并且不需要考虑分支延迟槽问题。在上述代码中,使用jalr而不是jal的原因可能是为了更加灵活地跳转到board_init_f函数。

    ​ 在RISC-V架构中,伪指令jarl实际上不是有效的指令,因此在实际的RISC-V汇编代码中,我们需要使用jalr来执行从寄存器中读取的跳转地址。因此,使用jalr来调用board_init_f函数是正确的做法。

  • “延迟槽”是什么?

    ​ 在RISC-V架构和许多其他计算机架构中,分支指令可能造成延迟槽问题。延迟槽是指分支指令之后的下一条指令(延迟槽指令)可能会被执行,即使分支指令的条件未满足或尚未确定。延迟槽的存在是由于在进行分支操作时,计算机需要在跳转前执行下一条指令,以充分利用处理器资源。

    ​ 在RISC-V架构中,jal(Jump and Link)指令会在跳转之前执行当前指令的下一条指令,这就是延迟槽的概念。如果分支成功,延迟槽中的指令会被执行;如果分支失败,延迟槽中的指令也会被执行。因此,在编写代码时需要注意延迟槽的存在,确保延迟槽中的指令不会对程序逻辑造成影响。

参考

  1. u-boot启动流程分析(2)_板级(board)部分
  2. 内存分布
posted @ 2024-04-27 17:45  0欧姆  阅读(101)  评论(0)    收藏  举报  来源