赞
踩
基于2017.09版本的uboot源码分析,首先分析的是启动部分的代码。各个功能函数细节放在后续,这里以ARMv8架构的源码作为主要分析对象。
uboot.lds是一个链接脚本(linker script),用于告诉链接器如何将各个源文件(如汇编、C源文件)中的段(sections)组合成最终的可执行文件。
以armv8下的u-boot.lds为例,主要分为几个部分:
1.定义了输出格式为 ELF64 格式,指定了输出的架构为 aarch64(ARM64)
2.定义了程序的入口点 _start
3.标识了各个段的内容:
.text 段
:包含 U-Boot 的代码。先是从地址 .__image_copy_start 开始的数据,然后是 start.o 文件中的 .text* 段,最后是其他所有 .text* 段。.rodata 段
:只读数据段。.data 段
:包含已初始化的全局变量和静态变量。.u_boot_list
段:用于保留 U-Boot 链接时使用的列表信息。.efi_runtime
和 .efi_runtime_rel
段:存放 EFI 运行时环境相关的代码和数据。.image_copy_end
段:定义了 U-Boot 代码结束位置。.rel_dyn_start
、.rela.dyn
和 .rel_dyn_end
段:定义了动态重定位表相关的信息。.bss_start
、.bss
和 .bss_end
段:定义了未初始化数据(BSS)的起始地址和结束地址。4.以下各段在链接过程中会被丢弃(DISCARD),因为它们在这个链接脚本中没有被使用:
.dynsym
:动态符号表。 .dynstr*
:动态字符串表。.dynamic*
:动态段相关信息。.plt*
:过程链接表。.interp*
:动态链接器名称。.gnu*
:GNU 特定的段。#include <config.h> #include <asm/psci.h> OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") OUTPUT_ARCH(aarch64) ENTRY(_start) SECTIONS { #ifdef CONFIG_ARMV8_SECURE_BASE /DISCARD/ : { *(.rela._secure*) } #endif . = 0x00000000; . = ALIGN(8); .text : { *(.__image_copy_start) CPUDIR/start.o (.text*) *(.text*) } /* .text 区域包含 U-Boot 的代码,先从 .__image_copy_start 开始的数据, 然后是 start.o 文件中的 .text* 段,最后是其他所有 .text* 段。 */ #ifdef CONFIG_ARMV8_PSCI .__secure_start : #ifndef CONFIG_ARMV8_SECURE_BASE ALIGN(CONSTANT(COMMONPAGESIZE)) #endif { KEEP(*(.__secure_start)) } #ifndef CONFIG_ARMV8_SECURE_BASE #define CONFIG_ARMV8_SECURE_BASE #define __ARMV8_PSCI_STACK_IN_RAM #endif .secure_text CONFIG_ARMV8_SECURE_BASE : AT(ADDR(.__secure_start) + SIZEOF(.__secure_start)) { *(._secure.text) } .secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text)) { *(._secure.data) } .secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data), CONSTANT(COMMONPAGESIZE)) (NOLOAD) : #ifdef __ARMV8_PSCI_STACK_IN_RAM AT(ADDR(.secure_stack)) #else AT(LOADADDR(.secure_data) + SIZEOF(.secure_data)) #endif { KEEP(*(.__secure_stack_start)) . = . + CONFIG_ARMV8_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE; . = ALIGN(CONSTANT(COMMONPAGESIZE)); KEEP(*(.__secure_stack_end)) } #ifndef __ARMV8_PSCI_STACK_IN_RAM . = LOADADDR(.secure_stack); #endif .__secure_end : AT(ADDR(.__secure_end)) { KEEP(*(.__secure_end)) LONG(0x1d1071c); /* Must output something to reset LMA */ } #endif . = ALIGN(8); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(8); .data : { *(.data*) } . = ALIGN(8); . = .; . = ALIGN(8); .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); /*.u_boot_list 区域用于保留 U-Boot 链接时使用的列表信息。*/ } . = ALIGN(8); /*.efi_runtime 和 .efi_runtime_rel 区域用于存放 EFI 运行时环境相关的代码和数据。*/ .efi_runtime : { __efi_runtime_start = .; *(efi_runtime_text) *(efi_runtime_data) __efi_runtime_stop = .; } .efi_runtime_rel : { __efi_runtime_rel_start = .; *(.relaefi_runtime_text) *(.relaefi_runtime_data) __efi_runtime_rel_stop = .; } . = ALIGN(8); /*.image_copy_end 定义了 U-Boot 代码结束位置。*/ .image_copy_end : { *(.__image_copy_end) } . = ALIGN(8); /*.rel_dyn_start、.rela.dyn 和 .rel_dyn_end 定义了动态重定位表相关的信息。*/ .rel_dyn_start : { *(.__rel_dyn_start) } .rela.dyn : { *(.rela*) } .rel_dyn_end : { *(.__rel_dyn_end) } _end = .; . = ALIGN(8); /*.bss_start、.bss 和 .bss_end 定义了未初始化数据(BSS)的起始地址和结束地址。*/ .bss_start : { KEEP(*(.__bss_start)); } .bss : { *(.bss*) . = ALIGN(8); } .bss_end : { KEEP(*(.__bss_end)); } /DISCARD/ : { *(.dynsym) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } }
链接脚本的作用是告诉链接器如何组合各个源文件中的段,以及如何分配这些段在最终可执行文件中的内存位置。
通过lds文件知道_start
是全局入口的位置,这部分被定义在start.s代码中,这是uboot的启动代码,主要负责初始化ARMv8架构下的硬件和系统环境,然后跳转到_main
函数启动 U-Boot 的主要功能。具体内容分为以下几个部分:
而_main
函数是在crt0.S文件中定义的。这里不得不讨论两种定位的情况。
#include <asm-offsets.h> #include <config.h> #include <linux/linkage.h> #include <asm/macro.h> #include <asm/armv8/mmu.h> /************************************************************************* * * Startup Code (reset vector) * *************************************************************************/ .globl _start _start: #ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK /* * Various SoCs need something special and SoC-specific up front in * order to boot, allow them to set that in their boot0.h file and then * use it here. */ #include <asm/arch/boot0.h> #else b reset #endif .align 3 .globl _TEXT_BASE _TEXT_BASE: .quad CONFIG_SYS_TEXT_BASE /* * These are defined in the linker script. */ .globl _end_ofs _end_ofs: .quad _end - _start .globl _bss_start_ofs _bss_start_ofs: .quad __bss_start - _start .globl _bss_end_ofs _bss_end_ofs: .quad __bss_end - _start reset: /* Allow the board to save important registers */ /*保存重要的寄存器数据*/ b save_boot_params .globl save_boot_params_ret save_boot_params_ret: #ifdef CONFIG_SYS_RESET_SCTRL bl reset_sctrl #endif /* * Could be EL3/EL2/EL1, Initial State: * Little Endian, MMU Disabled, i/dCache Disabled */ /* 一系列的 switch_el 指令,用于切换执行状态到不同的异常级别(Exception Level,EL), 并设置相应的向量表(vector table)。在 ARMv8 架构中,异常级别包括 EL3、EL2 和 EL1。 根据不同的异常级别,执行相应的初始化操作,例如设置向量表地址、设置系统控制寄存器等。 */ adr x0, vectors switch_el x1, 3f, 2f, 1f 3: msr vbar_el3, x0 mrs x0, scr_el3 orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */ msr scr_el3, x0 msr cptr_el3, xzr /* Enable FP/SIMD */ #ifdef COUNTER_FREQUENCY ldr x0, =COUNTER_FREQUENCY msr cntfrq_el0, x0 /* Initialize CNTFRQ */ #endif b 0f 2: msr vbar_el2, x0 mov x0, #0x33ff msr cptr_el2, x0 /* Enable FP/SIMD */ b 0f 1: msr vbar_el1, x0 mov x0, #3 << 20 msr cpacr_el1, x0 /* Enable FP/SIMD */ 0: /* * Enable SMPEN bit for coherency. * This register is not architectural but at the moment * this bit should be set for A53/A57/A72. */ #ifdef CONFIG_ARMV8_SET_SMPEN switch_el x1, 3f, 1f, 1f 3: mrs x0, S3_1_c15_c2_1 /* cpuectlr_el1 */ orr x0, x0, #0x40 msr S3_1_c15_c2_1, x0 1: #endif /* Apply ARM core specific erratas */ /*各种 ARM 核心的错误修复(errata)的函数调用 这些错误修复函数会针对特定的硬件漏洞或设计缺陷进行处理。 */ bl apply_core_errata /* * Cache/BPB/TLB Invalidate * i-cache is invalidated before enabled in icache_enable() * tlb is invalidated before mmu is enabled in dcache_enable() * d-cache is invalidated before enabled in dcache_enable() */ /* Processor specific initialization */ /*函数执行系统的低级初始化工作, 例如初始化 GIC(Generic Interrupt Controller)等硬件。*/ bl lowlevel_init #if defined(CONFIG_ARMV8_SPIN_TABLE) && !defined(CONFIG_SPL_BUILD) branch_if_master x0, x1, master_cpu b spin_table_secondary_jump /* never return */ #elif defined(CONFIG_ARMV8_MULTIENTRY) branch_if_master x0, x1, master_cpu /* 如果系统启用了多核(multi-core)支持,并且当前核是主核(master CPU), 则执行 _main 函数,否则进入 slave_cpu 循环等待主核的信号。 */ /* * Slave CPUs */ slave_cpu: wfe ldr x1, =CPU_RELEASE_ADDR ldr x0, [x1] cbz x0, slave_cpu br x0 /* branch to the given address */ #endif /* CONFIG_ARMV8_MULTIENTRY */ master_cpu: bl _main /*_main 函数是 U-Boot 主程序的入口点,执行 U-Boot 的主要功能。*/ #ifdef CONFIG_SYS_RESET_SCTRL reset_sctrl: switch_el x1, 3f, 2f, 1f 3: mrs x0, sctlr_el3 b 0f 2: mrs x0, sctlr_el2 b 0f 1: mrs x0, sctlr_el1 0: ldr x1, =0xfdfffffa and x0, x0, x1 switch_el x1, 6f, 5f, 4f 6: msr sctlr_el3, x0 b 7f 5: msr sctlr_el2, x0 b 7f 4: msr sctlr_el1, x0 /*清除 TLB(Translation Lookaside Buffer)缓存*/ 7: dsb sy isb b __asm_invalidate_tlb_all ret #endif /*-----------------------------------------------------------------------*/ WEAK(apply_core_errata) mov x29, lr /* Save LR */ /* For now, we support Cortex-A57 specific errata only */ /* Check if we are running on a Cortex-A57 core */ branch_if_a57_core x0, apply_a57_core_errata 0: mov lr, x29 /* Restore LR */ ret apply_a57_core_errata: #ifdef CONFIG_ARM_ERRATA_828024 mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */ /* Disable non-allocate hint of w-b-n-a memory type */ orr x0, x0, #1 << 49 /* Disable write streaming no L1-allocate threshold */ orr x0, x0, #3 << 25 /* Disable write streaming no-allocate threshold */ orr x0, x0, #3 << 27 msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */ #endif #ifdef CONFIG_ARM_ERRATA_826974 mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */ /* Disable speculative load execution ahead of a DMB */ orr x0, x0, #1 << 59 msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */ #endif #ifdef CONFIG_ARM_ERRATA_833471 mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */ /* FPSCR write flush. * Note that in some cases where a flush is unnecessary this could impact performance. */ orr x0, x0, #1 << 38 msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */ #endif #ifdef CONFIG_ARM_ERRATA_829520 mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */ /* Disable Indirect Predictor bit will prevent this erratum from occurring * Note that in some cases where a flush is unnecessary this could impact performance. */ orr x0, x0, #1 << 4 msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */ #endif #ifdef CONFIG_ARM_ERRATA_833069 mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */ /* Disable Enable Invalidates of BTB bit */ and x0, x0, #0xE msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */ #endif b 0b ENDPROC(apply_core_errata) /*-----------------------------------------------------------------------*/ WEAK(lowlevel_init) mov x29, lr /* Save LR */ #if defined(CONFIG_GICV2) || defined(CONFIG_GICV3) branch_if_slave x0, 1f ldr x0, =GICD_BASE bl gic_init_secure 1: #if defined(CONFIG_GICV3) ldr x0, =GICR_BASE bl gic_init_secure_percpu #elif defined(CONFIG_GICV2) ldr x0, =GICD_BASE ldr x1, =GICC_BASE bl gic_init_secure_percpu #endif #endif #ifdef CONFIG_ARMV8_MULTIENTRY branch_if_master x0, x1, 2f /* * Slave should wait for master clearing spin table. * This sync prevent salves observing incorrect * value of spin table and jumping to wrong place. */ #if defined(CONFIG_GICV2) || defined(CONFIG_GICV3) #ifdef CONFIG_GICV2 ldr x0, =GICC_BASE #endif bl gic_wait_for_interrupt #endif /* * All slaves will enter EL2 and optionally EL1. */ adr x4, lowlevel_in_el2 ldr x5, =ES_TO_AARCH64 bl armv8_switch_to_el2 lowlevel_in_el2: #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 adr x4, lowlevel_in_el1 ldr x5, =ES_TO_AARCH64 bl armv8_switch_to_el1 lowlevel_in_el1: #endif #endif /* CONFIG_ARMV8_MULTIENTRY */ 2: mov lr, x29 /* Restore LR */ ret ENDPROC(lowlevel_init) /*这个汇编函数的作用是根据系统配置,通过 GIC 向所有的CPU核心发送软中断信号,以唤醒它们*/ WEAK(smp_kick_all_cpus) /* Kick secondary cpus up by SGI 0 interrupt */ #if defined(CONFIG_GICV2) || defined(CONFIG_GICV3) ldr x0, =GICD_BASE /*将GICD_BASE地址加载到寄存器X0中。GICD_BASE是指向GIC distributor(中断分发器)的基址*/ b gic_kick_secondary_cpus /*会向其他CPU核心发送软中断信号(SGI),以唤醒它们。*/ #endif ret /*这行指令用于返回到函数的调用者处。*/ ENDPROC(smp_kick_all_cpus) /*-----------------------------------------------------------------------*/ ENTRY(c_runtime_cpu_setup) /* Relocate vBAR */ adr x0, vectors switch_el x1, 3f, 2f, 1f 3: msr vbar_el3, x0 b 0f 2: msr vbar_el2, x0 b 0f 1: msr vbar_el1, x0 0: ret ENDPROC(c_runtime_cpu_setup) WEAK(save_boot_params) b save_boot_params_ret /* back to my caller */ ENDPROC(save_boot_params)
通过下面的条件编译宏#if ! defined(CONFIG_SPL_BUILD)
,对于正常的 U-Boot(非 SPL),调用 relocate_code()
。此函数将 U-Boot 从当前位置重定位到由board_init_f()
计算的重定位目标中。
而对于 SPL,board_init_f()
只是返回到 crt0.S
中。在 SPL 中没有代码重定位。我们的ARMv8遵循这种方式。
最后跳转到 board_init_r()
。
crt0.S
是一个必不可少且重要的,是将整个初始化过程串起来的关键。
#include <config.h> #include <asm-offsets.h> #include <linux/linkage.h> #ifdef CONFIG_CPU_V7M #include <asm/armv7m.h> #endif /* * entry point of crt0 sequence */ ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr r0, =(CONFIG_SPL_STACK) #else ldr r0, =(CONFIG_SYS_INIT_SP_ADDR) #endif bic r0, r0, #7 /* 8-byte alignment for ABI compliance */ mov sp, r0 bl board_init_f_alloc_reserve mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 bl board_init_f_init_reserve mov r0, #0 bl board_init_f #if ! defined(CONFIG_SPL_BUILD) /* * Set up intermediate environment (new sp and gd) and call * relocate_code(addr_moni). Trick here is that we'll return * 'here' but relocated. */ ldr r0, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ bic r0, r0, #7 /* 8-byte alignment for ABI compliance */ mov sp, r0 ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code here: /* * now relocate vectors */ bl relocate_vectors /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) # ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */ bl spl_relocate_stack_gd cmp r0, #0 movne sp, r0 movne r9, r0 # endif ldr r0, =__bss_start /* this is auto-relocated! */ #ifdef CONFIG_USE_ARCH_MEMSET ldr r3, =__bss_end /* this is auto-relocated! */ mov r1, #0x00000000 /* prepare zero to clear BSS */ subs r2, r3, r0 /* r2 = memset len */ bl memset #else ldr r1, =__bss_end /* this is auto-relocated! */ mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ #if defined(CONFIG_CPU_V7M) itt lo #endif strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l #endif #if ! defined(CONFIG_SPL_BUILD) bl coloured_LED_init bl red_led_on #endif /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ /* call board_init_r */ #if CONFIG_IS_ENABLED(SYS_THUMB_BUILD) ldr lr, =board_init_r /* this is auto-relocated! */ bx lr #else ldr pc, =board_init_r /* this is auto-relocated! */ #endif /* we should not return here. */ #endif ENDPROC(_main)
board_init_f
里主要运行的初始化逻辑放在了init_sequence_f
里进行初始化。而hang函数是一个死循环,永远不会从这里出去。
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags;
gd->have_console = 0;
if (initcall_run_list(init_sequence_f))
hang();
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64)
/* NOTREACHED - jump_to_copy() does not return */
hang();
#endif
}
在init_sequence_f
里定义的内容有很多,排除掉非ARM架构以及其他不重要的时序流程,这里主要的流程包括:
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, initf_bootstage, /* uses its own timer, so does not need DM */ 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_M68K) || defined(CONFIG_SH) || \ defined(CONFIG_X86) checkcpu, #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_HARD_SPI) init_func_spi, #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, #if defined(CONFIG_LOGBUFFER) reserve_logbuffer, #endif #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_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, 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, }; void board_init_f(ulong boot_flags) { gd->flags = boot_flags; gd->have_console = 0; if (initcall_run_list(init_sequence_f)) hang(); #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ !defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64) /* NOTREACHED - jump_to_copy() does not return */ hang(); #endif }
此函数为从系统 RAM(DRAM、DDR 等)执行准备硬件。由于系统RAM 可能尚不可用,因此board_init_f()
必须使用当前 GD 来存储必须传递给后续阶段的任何数据。这些数据包括重定位目标、未来的栈和未来的 GD 位置。
总结:
board_init_f
主要完成的工作是:
board_init_r()
:即初始化 SDRAM 和串行 UART。后一阶段的初始化,主要看init_sequence_r
中的定义。
包括:
static int run_main_loop(void) { #ifdef CONFIG_SANDBOX sandbox_main_loop_init(); #endif /* main_loop() can return to retry autoboot, if so just run it again */ for (;;) main_loop(); return 0; } /* * Over time we hope to remove these functions with code fragments and * stub funtcions, and instead call the relevant function directly. * * We also hope to remove most of the driver-related init and do it if/when * the driver is later used. * * TODO: perhaps reset the watchdog in the initcall function after each call? */ 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 availble * 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, 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 #if defined(CONFIG_ARM) || defined(CONFIG_NDS32) 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_LOGBUFFER initr_logbuffer, #endif #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 ressources 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_PPC initr_spi, #endif #ifdef CONFIG_CMD_NAND initr_nand, #endif #ifdef CONFIG_CMD_ONENAND initr_onenand, #endif #ifdef CONFIG_MMC initr_mmc, #endif #ifdef CONFIG_HAS_DATAFLASH initr_dataflash, #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, #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) 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) || defined(CONFIG_LOGBUFFER) initr_mem, #endif #ifdef CONFIG_PS2KBD initr_kbd, #endif run_main_loop, };
总结:
主要执行流程,位于公共代码
uboot启动流程如下:
1.设置CPU为管理模式
2.关看门狗
3.关中断
4.设置时钟频率
5.关mmu,初始化各个bank
6.进入board_init_f()函数 ,初始化时钟,SDRAM,串口等,划分内存区域
7.重定位,复制uboot,然后修改SDRAM上的uboot链接地址
8.清除bss
9.跳转到board_init_r()函数,启动流程结束
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。