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

news2024/11/18 3:48:01

在上一节系统初始化之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是如何跳转到内核的呢,需不需要传递一些参数给内核呢?我们下一节分析。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

非常好用的高效率截图工具-Snipaste

官网:https://zh.snipaste.com/ 帮助文档: https://docs.snipaste.com/zh-cn/

已实现:vue、h5项目如何使用echarts实现雷达图、六边形图表

说实话,要说图表里,最强的应该属于echarts了,不管是接入难度上,还是样式多样性上,还有社区庞大程度上,都是首屈一指的,反观有的人习惯用chart.js了,这个无可厚非,但是如果…

Glide完全解读

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

Vim实战:使用 Vim实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度,DP多卡,EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

光学3D表面轮廓仪服务超精密抛光技术发展

随着技术的不断进步,精密制造领域对材料表面的处理要求越来越高,超精密抛光技术作为当下表面处理的尖端技术,对各种高精密产品的生产起到了至关重要的作用,已广泛应用于集成电路制造、医疗器械、航空航天、3C电子、汽车、精密模具…

Django知识随笔

目录 1.如何再ajax中传输post数据? 2.在form表单中使用jquery序列化,input框过多。 1.如何再ajax中传输post数据? 在ajax传递的那个网址,会调用你路由的视图函数,在视图函数上面加一句 csrf_exempt 。写上之后会有提…

Docker私有仓库搭建

目录 搭建本地私有仓库 Docker--harbor私有仓库部署与管理 Harbor 简介 什么是Harbor Harbor的特性 Harbor的构成 Harbor 部署 部署 Docker-Compose 服务 ​编辑部署 Harbor 服务 启动 Harbor 进入浏览器http://192.168.20.10进入harbor的客户端 搭建本地私有仓库 …

Shell中sed编辑器

1.简介 sed是一种流编辑器,流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。 sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个 命令文本文件中。 2.sed编辑器的工作流程 sed…

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

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

【EEG信号处理】ERP相关

ERP,全称为event-related potential,中文是事件相关电位。 首先要明确的一点是,ERP是根据脑电图EEG得到的,他是EEG的一部分,是最常用的时域分析方法 可能有一部分是介绍不到的,望谅解 在维基百科中给的定义…

Vue3的v-model

目录 基本用法 底层机制 v-model 的参数 多个 v-model 绑定 处理 v-model 修饰符 带参数的 v-model 修饰符 例子 总结:Vue 3 中的 v-model 指令与 Vue 2 中的 v-model 相比有一些变化和改进。最显著的变化是,在 Vue 3 中,v-model 可以…

matlab appdesigner系列-仪器仪表4-开关、开关(切换)、开关(翘板)

开关、开关(切换)、开关(翘板),可进行On和Off两种状态切换 示例:开关开启时,可通过滑块调整表盘数值,并有提示框提示 开关关闭时,滑块、表盘数值清零,并有提…

【DDD】学习笔记-理解上下文映射

一个软件系统通常被分为多个限界上下文,这是运用“分而治之”思想来降低业务复杂度的有效手段,设计的难题往往会停留在“如何分”,然而限界上下文之间的“怎么合”问题同样值得关注,分与合遵循的还是软件设计的最高原则——高内聚…

系列五十、idea父子项目忽略部分文件

一、idea父子项目忽略部分文件 **/mvnw **/mvnw.cmd **/.mvn **/target/ .idea **/.gitignore

一进一出模拟量信号隔离变送器

一进一出模拟量信号隔离变送器 捷晟达科技推出一进一出模拟量信号隔离变送器 深圳捷晟达科技推出一款具有隔离,放大,转换保护功能的一进一出的小型隔离变送器设备,该设备可以把模拟量(4-20mA/0-10V等)标准信号转换用户需要的信号,该产品具有抗EMC干扰,可以有效的保护后级设备安…

【MySQL】双写、重做日志对宕机时脏页数据落盘的作用的疑问及浅析

众所周知,双写机制、重做日志文件是mysql的InnoDB引擎的几个重要特性之二。其中两者的作用都是什么,很多文章都有分析,如,双写机制(Double Write)是mysql在crash后恢复的机制,而重做日志文件&am…

网安文件包含漏洞

文件包含概念: 开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程一般被称为包含。为了使代码更加灵活,通常会将被包含的文件设置为变…

docker+jekins+maven+ssh 持续集成交付部署 jar包

一. docker环境搭建,此处略过。 二. docker部署jekins 2.1 拉取镜像,挂载工作目录,xxxx为宿主机指定工作目录 docker pull jenkins/jenkins docker run -d -p 8080:8080 -p 50000:50000 --name jenkins --privilegedtrue -v xxxxxxxxxx:/var/jenkins…

源码安装nginx并提供服务脚本

一、下载nginx ①官网复制下载链接 ②在Linux中下载 [rootopenEuler2 ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz 二、解压并指定路径 [rootopenEuler2 ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/ 三、安装依赖 dnf install -y gcc gcc-c mak…

使用plotly dash 画3d圆柱(Python)

plotly3D (3d charts in Python)可以画3维图形 在做圆柱的3D装箱项目,需要装箱的可视化,但是Mesh (3d mesh plots in Python)只能画三角形,所以需要用多个三角形拼成一个圆柱(想做立…