U-Boot学习(6):初始化之_main函数源码分析

在上一节系统初始化之start.S源码分析详解中,我们分析了上电后的代码执行流程,实际上就是对系统特权模式、CP15、向量表等进行配置。最后一步就是进入_main函数了,这个就是U-Boot的主程序了,它完成了对系统内存、堆栈、全局结构体、外设的初始化和代码重定位的操作,这篇文章就来分析一下这个函数。

文章目录

  • 1 _main完整代码
  • 2 board_init_f_xxxxx函数
    • 2.1 board_init_f_alloc_reserve
    • 2.2 board_init_f_init_reserve
    • 2.3 board_init_f
      • 2.3.1 initcall_run_list
  • 3 代码重定位
    • 3.1 准备工作
    • 3.2 relocate_code分析
      • 3.2.1 __image_copy_start到__image_copy_end的拷贝
      • 3.2.2 __rel_dyn_start到__rel_dyn_end的拷贝
        • 3.2.2.1 ARM中的全局变量表
        • 3.2.2.1 .rel.dyn段
        • 3.2.2.3 代码分析
  • 4 relocate_vectors和c_runtime_cpu_setup
    • 4.1 relocate_vectors
    • 4.2 c_runtime_cpu_setup
  • 5 board_init_r

1 _main完整代码

函数在crt0.S中定义,这里去掉了不会执行的宏定义部分的代码,以让代码结构更清晰:
在这里插入图片描述

下面就分块分析一下上面的代码。

2 board_init_f_xxxxx函数

在这里插入图片描述

简单分析一下:
(1)首先将CONFIG_SYS_INIT_SP_ADDR的值按照8字节对齐赋值给当前的SP堆栈。

  • CONFIG_SYS_INIT_SP_ADDR值的分析,可以参考上一篇文章

(2)调用board_init_f_alloc_reserve函数,r0一般保存函数的返回值,调用完后调用mov sp, r0将函数的返回值r0赋值给sp堆栈。

(3)接着我们把R0赋值给R9。在global_data.h中,有如下的定义:

#define DECLARE_GLOBAL_DATA_PTR		register volatile gd_t *gd asm ("r9")

也就是说我们声明了一个gd_t 类型的全局数据的变量指针,然后我们希望将这个指针保存在r9寄存器中。所以在执行这行代码之前,R0的值应该是为这个全局变量分配的空间的首地址。
(4)调用board_init_f_init_reserve函数
(5)设置R0为0,然后调用board_init_f,这里R0为函数的参数。

下面就来看一下这三个board_init_f_xxxxx函数。

2.1 board_init_f_alloc_reserve

这个函数的主要目的是为全局数据(GD)和用作"globals"的保留空间从给定的顶部地址分配内存。

在这里插入图片描述

  • 函数的参数topr0的值CONFIG_SYS_INIT_SP_ADDR

(1)如果启用SYS_MALLOC_F_LEN,则减去相应长度 CONFIG_VAL(SYS_MALLOC_F_LEN)为早期分配的内存空间进行预留。
(2)为全局数据(GD)保留足够的空间,并按照16字节向下对齐。GD实际上是global_data类型的结构体。

由前面的分析可知,在此函数返回后,top的值将赋给r0spr9

2.2 board_init_f_init_reserve

在这里插入图片描述

这里大致总结一下这个函数完成的操作:

1、清零保留的空间
使用memset函数将保留的空间从 basebase + sizeof(struct global_data) - 1 清零,确保在使用之前不会包含任何旧数据。
2、 初始化内存保护
如果配置了 SYS_REPORT_STACK_F_USAGE,则调用 board_init_f_init_stack_protection_addrboard_init_f_init_stack_protection 函数,为U-Boot内部的堆栈保护机制留出一些内存。
3、更新基址
base 更新为下一个分配区域的起始地址。这个区域包括GD结构和可能的其他数据结构,并确保新的base满足16字节对齐的要求。
4、记录早期malloc区域的起始地址
如果启用了CONFIG_VAL(SYS_MALLOC_F_LEN),则记录早期malloc区域的起始地址到gd->malloc_base中。

2.3 board_init_f

在这里插入图片描述
在调用这个函数前,r0的值赋为了0,所以boot_flags为0。这里实际上就执行了一个initcall_run_list(init_sequence_f)函数,从函数名可以看出,这里就是根据init_sequence_f做一些初始化操作,如果初始化失败就调用hang()挂起程序。下面就来分析一下这个函数。

2.3.1 initcall_run_list

为了简介,这里把没有定义到的宏定义里的代码和不相关的代码都去掉了,函数其实很简单:
在这里插入图片描述
init_fnc_t的定义如下:

typedef int (*init_fnc_t)(void);

实际上就是从前面传的init_sequence_f函数指针数组中遍历,然后执行里面所有的初始化函数。下面来看一下这个:

static const init_fnc_t init_sequence_f[] = {
	setup_mon_len,
#ifdef CONFIG_OF_CONTROL
	fdtdec_setup,
#endif
#ifdef CONFIG_TRACE_EARLY
	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,
#if defined(CONFIG_CONSOLE_RECORD_INIT_F)
	console_record_init,
#endif
#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 */
#ifndef CONFIG_ANDROID_AUTO_SUPPORT
	serial_init,		/* serial communications setup */
#endif
	console_init_f,		/* stage 1 init of console */
	display_options,	/* say that we are here */
	display_text_info,	/* show debugging info if required */
	checkcpu,
#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 CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
	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
	setup_dest_addr,
#ifdef CONFIG_OF_BOARD_FIXUP
	fix_fdt,
#endif
#ifdef CONFIG_PRAM
	reserve_pram,
#endif
	reserve_round_4k,
	arch_reserve_mmu,
	reserve_video,
	reserve_trace,
	reserve_uboot,
	reserve_malloc,
	reserve_board,
	reserve_global_data,
	reserve_fdt,
	reserve_bootstage,
	reserve_bloblist,
	reserve_arch,
	reserve_stacks,
	dram_init_banksize,
	show_dram_config,
	INIT_FUNC_WATCHDOG_RESET
	setup_bdinfo,
	display_new_sp,
	INIT_FUNC_WATCHDOG_RESET
	reloc_fdt,
	reloc_bootstage,
	reloc_bloblist,
	setup_reloc,
	clear_bss,
	NULL,
};

这里不对每个函数进行分析了,一个个分析里面的函数的意义不大。上面的函数初始化列表涵盖了从底层硬件初始化(如设备树解析、时钟设置、CPU初始化)到高级功能(如内存初始化、环境变量配置、串口通信设置)的一系列操作,还涉及设备模型和早期的引导阶段的初始化,还有内存保护、定时器、看门狗、以及一些特定的平台或功能的初始化。

3 代码重定位

后面的代码就是完成代码重定位了:

3.1 准备工作

在跳转到代码重定位的函数之前,我们需要完成一些准备工作:
在这里插入图片描述
具体完成的操作参考上面的注释。相关的宏定义都在generic-asm-offsets.h中有声明,实际上就是global_data结构体中对应变量的偏移。

#define GD_START_ADDR_SP 72      /* offsetof(struct global_data, start_addr_sp)	@ */
#define GD_NEW_GD        80      /* offsetof(struct global_data, new_gd)	    @ */
#define GD_RELOC_OFF     76      /* offsetof(struct global_data, reloc_off)	    @ */
#define GD_RELOCADDR     56      /* offsetof(struct global_data, relocaddr)	    @ */

这里我就不详细地分析在init_sequence_f中如何计算这些代码重定位的值了,我们只要知道GD结构体中有一些变量包含绝对地址,有一些变量包含重定位的信息,这里面都帮我们计算好了重定位后的地址或重定位的相关信息了。

实际上这些变量的值都在init_sequence_f一系列的函数执行完后,设置相关变量为代码重定位后对应的地址的值,而我们将把他们重定位到DDR内存的末尾。由前几篇文章的分析可知,U-Boot被链接到0x87800000处运行,那为什么我们要将0x87800000开始的变量和代码链接到整个内存的末尾呢?

  • 0x80000000开始我们后面要放内核,防止内核大小超过0x7800000大小而覆盖U-Boot内核。

那为什么我们不把U-Boot直接在u-boot.lds中就链接到DDR的末尾呢?
因为U-Boot需要兼容不同的平台,而且编译出来的U-Boot的大小也不确定,所以我们一般就把U-Boot链接到DDR的中间位置,然后在U-Boot程序中计算程序的大小,然后变量和代码重定位到DDR内存的最后,而不浪费更多的内存。防止后续内核的代码覆盖掉U-Boot的代码,导致U-Boot启动Linux内核的时候出错。

3.2 relocate_code分析

代码如下:
在这里插入图片描述
在u-boot.lds链接脚本分析中,我们知道__image_copy_start__image_copy_end在链接脚本中声明了,其中__image_copy_start实际上是0x87800000,__image_copy_end实际上就是加上中间那些段的大小后的内存地址,我们需要拷贝这一块内存区域。同样还声明了__rel_dyn_start__rel_dyn_end

relocate.S文件最后还声明了_ofs结尾的变量,我们在relocate_code中用的正是这些变量。这些值都声明为其在链接脚本中对应的变量减去relocate_code函数的首地址。

_image_copy_start_ofs:
	.word	__image_copy_start - relocate_code
_image_copy_end_ofs:
	.word	__image_copy_end - relocate_code
_rel_dyn_start_ofs:
	.word	__rel_dyn_start - relocate_code
_rel_dyn_end_ofs:
	.word	__rel_dyn_end - relocate_code

3.2.1 __image_copy_start到__image_copy_end的拷贝

现在我们来分析第一部分的代码:
在这里插入图片描述
在调用函数之前r0已经设置为了GD结构体中的relocaddr变量,即我们重定位要拷贝数据的目标地址。

同时,我们发现我们引用xxx_ofs变量后,又加上了relocate_code标号的地址,实际上就是引用原来链接脚本中的地址。这样的设计可能是为了方便处理和维护代码,如果在多个地方需要使用相同的偏移值,只需引用相应的标签即可,而不必在每次使用时硬编码偏移值。

下面就是代码的拷贝操作了,将_image_copy_start_image_copy_end部分的代码,拷贝到gd->relocaddr地址处。
在这里插入图片描述

3.2.2 __rel_dyn_start到__rel_dyn_end的拷贝

我们的代码重定位实际上把代码链接到了另一个位置,但是我们编译的时候这个链接地址就已经固定了,那在我们重定位代码之后,对一些变量的访问也就会出错。那有没有什么解决方法呢?请参考我的这篇文章:位置无关码PIC详解:原理、动态链接库、代码重定位,虽然我使用的不是ARM的编译器,但是在实现上也大同小异。

总得来说,就是如果我们加上了-pie等位置无关的编译器选项后:

  • 函数:函数之间的跳转都会使用相对的跳转指令,如bl,而不是绝对的跳转,如ldr pc,=0x12345678。这就保证了函数代码段在重定位后的正常访问。
  • 变量:全局变量就会使用一个GOT全局变量表来存储每个变量的地址,如果我们代码重定位的话,只需要修改这个表中的地址即可。
3.2.2.1 ARM中的全局变量表

前面说了,我那篇文章分析的是Linux平台GCC编译器的重定位,那ARM中实现肯定类似,但我们必须知道里面的细节才能实现重定位,所以现在就来看看反汇编代码分析一下。

首先我们可以看到我们U-Boot的编译是有-pie选项的:
在这里插入图片描述
首先我们反汇编一下u-boot的源码:

arm-linux-gnueabihf-objdump -D -m arm u-boot > u-boot.dis

我们以mem.c中的do_mem_base函数为例:
在这里插入图片描述
来看一下这个函数的反汇编:
在这里插入图片描述

  • 首先我们可以注意到这里的函数跳转确实都是使用的相对跳转指令blble

我们现在重点关注这段汇编如何获取base_address这个全局变量的,很容易看出ldr r4, [pc, #24]这个操作就是把base_address全局变量的地址加载到r4的。而PC+0x24的值为0x8790a800,这个地址在整个函数的最后,这个地址的值为0x87950974,我们看一下这个地址保存的是什么:
在这里插入图片描述
在BSS段中(base_address初始值为0)发现了,实际上这个地址指向的就是base_address的地址。

总结:ARM中函数使用到的全局变量,会在每个函数的最后留一段内存空间来保存使用到的变量的地址。如果我们要让重定位后的代码可以正常访问全局变量的话,我们只需要修改函数最后这段内存空间里对应的全局变量的地址,给这个地址加上重定位偏移即可。

3.2.2.1 .rel.dyn段

前面我们总结出了,需要把每个函数最后的指向全局变量的内存空间加上一个偏移,但是我们怎么获取每个函数的这段内存空间呢?我们发现重定位代码中有一个.rel.dyn段,从代码实现来看,我们可以猜测所有函数最后的指示全局变量的内存应该就在这个段里,我们搜一下0x8790a800
在这里插入图片描述
果然在.rel.dyn段中,我们发现这个段中的内存分布大致就是:一行全局变量的地址,一行0x17。实际上这个0x17是一个标识符,指示前4字节的内存为某个函数最后的保存其要使用的全局变量的地址。知道了这个特性,我们就可以继续往下分析代码了。

3.2.2.3 代码分析

在这里插入图片描述
一开始我们将r2赋值为rel_dyn段的起始地址,将r3赋值为rel_dyn段的结束地址。然后从r2读取两个32字节到r0r1中,再取出r1的低8位,判断它是否等于23(0x17):

  • 不等于23:跳转到fixnext,判断是否检查到rel_dyn结尾了,否则继续往下拷贝。
  • 等于23:r0所保存的地址为某个函数中用到的标号,我们需要重定位这个标号所指向的地址,就继续往下执行代码

我们分析一下等于23时,执行的那四行代码。

add	r0, r0, r4
ldr	r1, [r0]
add	r1, r1, r4
str	r1, [r0]

3.2.1中,我们知道r4就是重定位的偏移。所以我们给r0加上这个偏移然后保存到r0,现在r0即重定位后的标号的地址。这个地址所指向的值即为实际变量的地址,但这个地址还没有重定位。所以将变量的地址加载到r1中,然后加上偏移r4,再保存回标号重定位后的地址r0

这样就完成了代码的重定位。最后我们bx lr,我们知道lr在调用relocate_code前已经保存为_main函数中here标号了。也就是最后几行代码了:
在这里插入图片描述

4 relocate_vectors和c_runtime_cpu_setup

4.1 relocate_vectors

为了简洁,我去掉了不执行的代码分支,实际上就执行以下语句。
在这里插入图片描述
实际上就是设置协处理器中的向量表首地址为GD中的relocaddr,即前面我们提到的重定位要拷贝数据的目标地址,也是我们向量表所保存的地址。然后由于前面bl relocate_vectors,所以LR寄存器即为_main中下一条指令的地址,我们的程序就跳回去了。

4.2 c_runtime_cpu_setup

这个函数也很简单,就是如果使能了ICache,则将缓存中的所有有效数据标记为无效,这意味着下次访问这些数据时,系统将不会从缓存中读取。
在这里插入图片描述

5 board_init_r

最后就是执行board_init_r函数了:
在这里插入图片描述
这个函数接受两个参数,第一个参数为GD全局数据,第二个参数为重定位后的地址。这里删减掉不会执行的分支,实际上board_init_rboard_init_f一样,也是执行一系列的初始化函数:
在这里插入图片描述
init_sequence_r的定义如下:

static init_fnc_t init_sequence_r[] = {
	initr_trace,
	initr_reloc,
	/* TODO: could x86/PPC have this also perhaps? */
#if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
	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 */
#if defined(CONFIG_CONSOLE_RECORD)
	console_record_init,
#endif
#ifdef CONFIG_SYS_NONCACHED_MEMORY
	noncached_init,
#endif
	initr_of_live,
#ifdef CONFIG_DM
	initr_dm,
#endif
#ifdef CONFIG_ADDR_MAP
	init_addr_map,
#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
	initr_binman,
#ifdef CONFIG_FSP_VERSION2
	arch_fsp_init_r,
#endif
	initr_dm_devices,
	stdio_init_tables,
	serial_initialize,
	initr_announce,
	dm_announce,
#if CONFIG_IS_ENABLED(WDT)
	initr_watchdog,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_NEEDS_MANUAL_RELOC) && defined(CONFIG_BLOCK_CACHE)
	blkcache_init,
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	initr_manual_reloc_cmdtable,
#endif
	arch_initr_trap,
#if defined(CONFIG_BOARD_EARLY_INIT_R)
	board_early_init_r,
#endif
	INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_POST
	post_output_backlog,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI_INIT_R) && 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.
	 */
	pci_init,
#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
#ifdef CONFIG_XEN
	xen_init,
#endif
#ifdef CONFIG_PVBLOCK
	initr_pvblock,
#endif
	initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
	initr_malloc_bootparams,
#endif
	INIT_FUNC_WATCHDOG_RESET
	cpu_secondary_init_r,
#if defined(CONFIG_ID_EEPROM)
	mac_read_from_eeprom,
#endif
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI_INIT_R) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
	/*
	 * Do pci configuration
	 */
	pci_init,
#endif
	stdio_add_devices,
	jumptable_init,
#ifdef CONFIG_API
	api_init,
#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
	kgdb_init,
#endif
	interrupt_init,
#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
#if defined(CONFIG_GPIO_HOG)
	gpio_hog_probe_all,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
	board_late_init,
#endif
#ifdef CONFIG_FSL_FASTBOOT
	initr_fastboot_setup,
#endif
#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
	INIT_FUNC_WATCHDOG_RESET
	initr_scsi,
#endif
#ifdef CONFIG_BITBANGMII
	bb_miiphy_init,
#endif
#ifdef CONFIG_PCI_ENDPOINT
	pci_ep_init,
#endif
#ifdef CONFIG_CMD_NET
	INIT_FUNC_WATCHDOG_RESET
	initr_net,
#endif
#ifdef CONFIG_POST
	initr_post,
#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
#if defined(CONFIG_PRAM)
	initr_mem,
#endif
#ifdef CONFIG_EFI_SETUP_EARLY
	(init_fnc_t)efi_init_obj_list,
#endif
#if defined(AVB_RPMB) && !defined(CONFIG_SPL)
	initr_avbkey,
#endif
#ifdef CONFIG_IMX_TRUSTY_OS
	initr_tee_setup,
#endif
#ifdef CONFIG_FSL_FASTBOOT
	initr_check_fastboot,
#endif
#ifdef CONFIG_DUAL_BOOTLOADER
	initr_check_spl_recovery,
#endif
	run_main_loop,
};

这里面的初始化太多了,就不具体地一个个分析了,简单地说明一下:

  1. initr_trace:初始化追踪功能。在调试或跟踪执行流程时可能会用到。
  2. initr_reloc:初始化重定位过程。在U-Boot启动时,可能需要将U-Boot的二进制代码从加载地址移动到实际运行地址。
  3. initr_caches:如果是ARM或RISC-V架构,初始化缓存。
  4. initr_reloc_global_data:初始化全局数据的重定位。
  5. initr_unlock_ram_in_cache:在E500平台上,解锁缓存中的RAM。
  6. initr_barrier:执行一些同步屏障操作。
  7. initr_malloc:初始化内存分配器。
  8. log_init:初始化日志系统。
  9. initr_bootstage:初始化引导阶段信息,可能涉及内存分配等。
  10. console_record_init:如果启用了控制台记录功能,进行相关初始化。
  11. noncached_init:如果配置了非缓存内存,进行初始化。
  12. initr_of_live:初始化设备树(DeviceTree)。
  13. initr_dm:如果启用了设备模型(DeviceModel),进行相关初始化。
  14. init_addr_map:如果配置了地址映射,进行相关初始化。
  15. board_init:针对特定板级硬件的初始化,例如设置芯片选择器(chipselects)。
  16. set_cpu_clk_info:如果启用了时钟信息功能,设置CPU时钟信息。
  17. efi_memory_init:如果启用了EFILoader,初始化内存。
  18. initr_binman:初始化二进制管理。
  19. arch_fsp_init_r:如果启用了FSP(FirmwareSupportPackage),进行相关初始化。
  20. initr_dm_devices:初始化设备模型中的设备。
  21. stdio_init_tables:初始化标准输入输出的数据结构。
  22. serial_initialize:初始化串口。
  23. initr_announce:在启动期间宣告初始化。
  24. dm_announce:宣告设备模型的初始化。
  25. initr_watchdog:初始化看门狗。
  26. blkcache_init:如果需要手动重定位并启用块缓存,进行相关初始化。
  27. initr_manual_reloc_cmdtable:如果需要手动重定位,初始化命令表。
  28. arch_initr_trap:初始化陷阱。
  29. board_early_init_r:在板级初始化的早期阶段进行初始化。
  30. post_output_backlog:如果启用了POST,输出POST的日志。
  31. pci_init:初始化PCI。
  32. arch_early_init_r:在体系结构早期初始化阶段进行初始化。
  33. power_init_board:板级电源初始化。
  34. initr_flash:如果启用了NORFlash,进行相关初始化。
  35. cpu_init_r:初始化CPU的高级部分,如时间基准和定时器。
  36. initr_nand:如果启用了NANDFlash,进行相关初始化。
  37. initr_onenand:如果启用了OneNANDFlash,进行相关初始化。
  38. initr_mmc:如果启用了MMC(多媒体卡),进行相关初始化。
  39. xen_init:如果启用了Xen,进行相关初始化。
  40. initr_pvblock:初始化pvblock。
  41. initr_env:初始化环境变量。
  42. initr_malloc_bootparams:初始化引导参数的内存分配。
  43. cpu_secondary_init_r:初始化辅助CPU。
  44. mac_read_from_eeprom:从EEPROM读取MAC地址。
  45. pci_init:初始化PCI。
  46. stdio_add_devices:添加标准输入输出设备。
  47. jumptable_init:初始化跳转表。
  48. api_init:初始化API。
  49. console_init_r:完全初始化控制台作为设备。
  50. console_announce_r:如果启用了显示板信息的延迟配置,进行相关初始化。
  51. arch_misc_init:初始化与体系结构相关的杂项功能。
  52. misc_init_r:初始化与平台相关的杂项功能。
  53. kgdb_init:如果启用了KGDB(内核调试器),进行相关初始化。
  54. interrupt_init:初始化中断。
  55. timer_init:初始化定时器。
  56. initr_status_led:如果启用了LED状态指示功能,进行相关初始化。
  57. initr_ethaddr:初始化以太网地址。
  58. gpio_hog_probe_all:如果启用了GPIOHOG,进行相关初始化。
  59. board_late_init:板级后期初始化。
  60. initr_fastboot_setup:如果启用了快速启动(Fastboot),进行相关初始化。
  61. initr_scsi:如果启用了SCSI,进行相关初始化。
  62. initr_ide:如果启用了IDE,进行相关初始化。
  63. last_stage_init:最后阶段初始化。
  64. initr_mem:初始化内存。
  65. efi_init_obj_list:如果启用了EFI设置早期初始化,进行相关初始化。
  66. initr_avbkey:如果启用了AVB(AndroidVerifiedBoot),进行相关初始化。
  67. initr_tee_setup:如果启用了TrustyOS,进行相关初始化。
  68. initr_check_fastboot:如果启用了快速启动,进行相关检查。
  69. initr_check_spl_recovery:如果启用了双引导加载器,进行相关检查。
  70. run_main_loop:运行主循环。

我们注意到,在最后会进入run_main_loop函数中执行,实际上这就是U-Boot的命令行解析函数,也就是我们上电后看到的倒计时,然后可以输入一些U-Boot命令以执行一些特定的操作。这部分代码都是C语言实现的,比较简单,就不分析了。

最后当然就是U-Boot跳转内核了,U-boot是如何跳转到内核的呢,需不需要传递一些参数给内核呢?我们下一节分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/357326.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TensorFlow2实战-系列教程7:TFRecords数据源制作1

🧡💛💚TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、TFRecords 在训练过程中,基本都是使用GPU来计算,但是取一个…

防火墙知识普及详解,使用TOR Router把TOR作为默认网关,增加隐私/匿名性

防火墙知识普及详解,使用TOR Router把TOR作为默认网关,增加隐私/匿名性。 #################### 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习,切勿用于非法犯罪活动,对于恶意使用该工具造成的损失,和本人及开发者…

【ArcGIS微课1000例】0099:土地利用变化分析

本实验讲述在ArcGIS软件中基于两期土地利用数据,做土地利用变化分析。 文章目录 一、实验描述二、实验过程三、注意事项一、实验描述 对城市土地利用情况进行分析时,需要考虑不同时期土地利用图层在空间上的差异性,如农用地转建筑用地的空间变化。而该变化过程表现为各时期…

Glide完全解读

一,概述 glide作为android流行的图片加载框架,笔者认为有必要对此完全解读。glide提供了三级缓存、生命周期Destroy后自动移除缓存、自动适配ImageView,以及提供了各种对图片修饰的操作,如剪裁等。本文通过最简单的使用&#xff…

Spring Boot通过配置文件支持数据库自定义表名

直接上干货: 例如一个叫xxx的项目,yml文件里加上这段 xxxproject:db:xxxTable: xxx_dbname #自定义的数据库表名创一个Configuration类放表名和Mapper // XxxProjectAutoConfiguration.javaConfiguration MapperScan(basePackages "cn.com.xxxp…

PageHelper分页插件-以三层架构模型开发为例

文章目录 1、简介2、使用2.1、导入2.1.1、SpringBoot2.1.2、非SpringBoot 2.2、controller2.3、service2.4、mapper ​🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博主,专注于Java领域学习,擅长web应用开发、…

Spring Boot导出EXCEL 文件

主要功能:实现java导出excel到本地 JDK版本&#xff1a;openJDK 20.0.1 依赖pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchem…

详细讲解Java中的Properties类

目录 前言1. 基本知识2. 代码示例3. Demo 前言 使用Properties出现中文乱码可看我这篇文章&#xff1a;properties出现中文乱码解决方法&#xff08;万能&#xff09; 1. 基本知识 Properties 类是 Java 中用于处理配置文件的工具类&#xff0c;它继承自 Hashtable 类&#…

计算机网络——IP协议

前言 网络层的主要负责地址分配和路由选择,ip负责在网络中进行数据包的路由和传输。 IPv4报文组成&#xff08;了解&#xff09; IPv4首部&#xff1a;IPv4首部包含了用于路由和传输数据的控制信息&#xff0c;其长度为20个字节&#xff08;固定长度&#xff09;。 版本&#…

Mysql 插入数据

1 为表的所有字段插入数据 使用基本的INSERT语句插入数据要求指定表名称和插入到新记录中的值。基本语法格式为&#xff1a; INSERT INTO table_name (column_list) VALUES (value_list); 使用INSERT插入数据时&#xff0c;允许列名称列表column_list为空&#xff0c;此时&…

选型 之 工业相机篇

一、概述 23年24年行情不会好&#xff0c;公司各种想办法裁员&#xff0c;在大陆这个大熔炉中只能不断地提炼。我个人主要是在工业领域做2D图像算法和3D算法&#xff0c;但是现在出去都需要全能人才 方案、算法、运动控制等&#xff0c;我目前最大的短板就是方案&#xff0c;在…

Ubuntu本地部署Nextcloud并结合内网穿透实现远程访问搭建个人云盘

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访…

USB-C显示器:未来显示技术的革新者

随着科技的不断发展&#xff0c;显示技术也在不断进步&#xff0c;而USB-C显示器作为最新的显示技术&#xff0c;正在引领着显示行业的发展潮流。USB-C显示器具有许多优点&#xff0c;如高速传输、便捷连接、节能环保等&#xff0c;使其成为未来显示技术的革新者。 一、USB-C显…

leetcode hot100跳跃游戏Ⅱ

本题和上一题还是有不一样的地方&#xff0c;这个题中&#xff0c;我们需要记录我们跳跃的步数并尽可能的满足最小的跳跃步数到达终点。 那么我们还是采用覆盖范围的概念&#xff0c;但是我们需要两个&#xff0c;一个是在当前位置的覆盖范围&#xff0c;另一个是下一步的覆盖…

12.从项目经理的生存哲学到适配器模式(Adapter Pattern)

如果这个世界没有了项目经理&#xff0c;事情的发展可能并不会如同想象中一样美好&#xff0c;相反&#xff0c;对于开发人员来说可能是噩梦的开始。 比如&#xff1a; 客户因为几个需求的具体实现大发雷霆&#xff0c;甚至开始恶语相向&#xff0c;一通含ma量极高的“斯伯坦语…

多种协议转IEC104网关BE115

随着电力系统信息化建设和数字化转型的进程不断加速&#xff0c;对电力能源的智能化需求也日趋增强。健全稳定的智慧电力系统能够为工业生产、基础设施建设以及国防建设提供稳定的能源支持。在此背景下&#xff0c;高性能的工业电力数据传输解决方案——协议转换网关应运而生&a…

centos7上安装mysql5.7并自定义数据目录路径

1、卸载mariadb rpm -qa |grep mariadb #查出来的结果是mariadb-libs-5.5.68-1.el7.x86_64 rpm -e mariadb-libs-5.5.68-1.el7.x86_64 --nodeps #卸载查到的结果 2、官网下载响应的tar.gz包&#xff0c;比如mysql-5.7.38-el7-x86_64.tar.gz &…

回归预测 | Matlab实现CPO-LSTM【24年新算法】冠豪猪优化长短期记忆神经网络多变量回归预测

回归预测 | Matlab实现CPO-LSTM【24年新算法】冠豪猪优化长短期记忆神经网络多变量回归预测 目录 回归预测 | Matlab实现CPO-LSTM【24年新算法】冠豪猪优化长短期记忆神经网络多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-LSTM【24年新算…

js实现填涂画板

文章目录 1实现效果2 实现代码 凑个数&#xff0c;存粹是好玩儿&#xff0c;哈哈... 1实现效果 最上方一栏&#xff1a; 左侧是颜色按钮&#xff0c;点击选中颜色&#xff0c; 中间是功能按钮&#xff0c;重置颜色、清空画板、回退、涂改液&#xff08;填涂色置为白色&#xff…

数字人的不足

在数字化环境中对于事物或情况的感知能力存在缺陷。这可能涉及到人类对数字化信息的理解、利用和应对能力方面的问题&#xff0c;也可能涉及到数字化系统或技术本身的局限性。在当今数字化时代&#xff0c;人们对数字化信息的感知和理解至关重要&#xff0c;因为数字化技术已经…