source analysis =============== :download:`res/uboot-world.pdf` 目录结构 -------- :: board: 开发板相关目录,平台依赖 arch: CPU相关目录,平台依赖 include: 头文件目录,开发板的配置文件也在其中,configs存放开发板配置文件 common: 通用多功能函数实现 cmd: cmd命令实现 drivers: 通用设备驱动程序,包括以太网,flash驱动等 fs: 文件系统支持 lib: 通用库函数实现 net: 网络相关工具,如tftp,nfs tool: 用于创建u-boot s-record和bin镜像文件的工具 script: 脚本 u-boot目录可分为3类: 1) 处理器体系结构或开发板硬件直接相关的 2) 通用函数或者驱动程序 3) u-boot应用程序、工具或者文档 重要文件 -------- - include/configs/holo_ark_v3.h 开发板配置文件,包含spl loader配置信息,环境变量配置信息,地址配置信息等 - arch/arm/cpu/armv8/u-boot.lds 程序链接脚本,定义程序入口entry(_start),并安排各程序段衔接位置,程序首先运行start.s文件。 - arch/arm/cpu/armv8/start.S 程序首先运行的文件,程序刚开始运行的汇编代码,完成基本初始化。 - arch/arm/cpu/armv8/lowlevel_init.S 内存初始化,clock、SDRAM初始化代码(bootstrap中已完成,不执行) - board/holomatic/holo_ark_v3/board.c C语言入口start - common/main.c main_loop()定义于此。 - arch/arm/lib/interrupts.c 中断相关函数 main_loop()在没有字符输入的情况下。执行autoboot_command函数,boot kernel u-boot 实际主要运行的三个位置:: _start----->start_armboot-------->main_loop 生成文件 -------- :: System.map: uboot映像符号表,它包含了uboot全局变量和函数的地址信息。可供用户查看或由外部程序调试使用 uboot.bin: uboot映像原始二进制文件。 uboot: uboot映像elf格式文件。 uboot.srec: uboot映像的s_record格式。 注:uboot和uboot.srec格式都自带定位信息 tool/mkimge可转换为其他格式印象 u-boot启动流程 -------------- 嵌入式系统启动的基本流程是这样的: RoomBoot--->SPL--->u-boot--->linux kernel--->file system------>start application 1) RoomBoot 是固化在芯片内部的代码,负责从各种外(sdcard、mmc、flash、)中加载spl到芯片内部的SRAM 2) SPL的主要工作是初始化板载的DDRAM,然后将u-boot搬运到DDRAM中 3) u-boot最主要的功能就是加载启动linux kernel spl一般是地址无关的,设计成地址无关的主要目的是为了保证SPL被搬运到任何地方都能运行,这样设计是因为我们不知道spl会被放到 哪个地址运行。 位置无关码究其原因,主要是编译生成的汇编代码都是相对地址。 启动流程图如下,不同平台之间略有差别 .. image:: res/uboot.jpg SPL启动流程分析 ^^^^^^^^^^^^^^^ spl的入口代码是在arch/arm/lib/vector.S中的 ``_start`` 函数 - _start :: .macro ARM_VECTORS #ifdef CONFIG_ARCH_K3 ldr pc, _reset #else b reset #endif ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq .endm _start: #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG .word CONFIG_SYS_DV_NOR_BOOT_CFG #endif ARM_VECTORS #endif /* !defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) */ 1) _start中直接执行reset 2) ldr pc, _xx定义的是中断的处理方式,类似中断向量表 .. note:: spl阶段是不允许中断的,u-boot可以 - reset 代码路径:arch/arm/cpu/armv8/start.S :: reset: /* Allow the board to save important registers */ b save_boot_params .globl save_boot_params_ret save_boot_params_ret: #if CONFIG_POSITION_INDEPENDENT /* * Fix .rela.dyn relocations. This allows U-Boot to be loaded to and * executed at a different address than it was linked at. */ pie_fixup: adr x0, _start /* x0 <- Runtime value of _start */ ldr x1, _TEXT_BASE /* x1 <- Linked value of _start */ sub x9, x0, x1 /* x9 <- Run-vs-link offset */ adr x2, __rel_dyn_start /* x2 <- Runtime &__rel_dyn_start */ adr x3, __rel_dyn_end /* x3 <- Runtime &__rel_dyn_end */ pie_fix_loop: ldp x0, x1, [x2], #16 /* (x0, x1) <- (Link location, fixup) */ ldr x4, [x2], #8 /* x4 <- addend */ cmp w1, #1027 /* relative fixup? */ bne pie_skip_reloc /* relative fix: store addend plus offset at dest location */ add x0, x0, x9 add x4, x4, x9 str x4, [x0] pie_skip_reloc: cmp x2, x3 b.lo pie_fix_loop pie_fixup_done: #endif #ifdef CONFIG_SYS_RESET_SCTRL bl reset_sctrl #endif #if defined(CONFIG_ARMV8_SPL_EXCEPTION_VECTORS) || !defined(CONFIG_SPL_BUILD) .macro set_vbar, regname, reg msr \regname, \reg .endm adr x0, vectors #else .macro set_vbar, regname, reg .endm #endif /* * Could be EL3/EL2/EL1, Initial State: * Little Endian, MMU Disabled, i/dCache Disabled */ switch_el x1, 3f, 2f, 1f 3: set_vbar 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: set_vbar vbar_el2, x0 mov x0, #0x33ff msr cptr_el2, x0 /* Enable FP/SIMD */ b 0f 1: set_vbar 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 */ 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 */ 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 /* * 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 主要设置CPU的工作模式,禁用FIQ,IRQ。然后跳转到 ``lowlevel_init`` 中,然后跳转到 ``_main`` - lowlevel_init 代码路径:arch/arm/cpu/armv8/lowlevel_init.S :: ENTRY(lowlevel_init) /* * Setup a temporary stack. Global data is not available yet. */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr w0, =CONFIG_SPL_STACK #else ldr w0, =CONFIG_SYS_INIT_SP_ADDR #endif bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ /* * Save the old LR(passed in x29) and the current LR to stack */ stp x29, x30, [sp, #-16]! /* * Call the very early init function. This should do only the * absolute bare minimum to get started. It should not: * * - set up DRAM * - use global_data * - clear BSS * - try to start a console * * For boards with SPL this should be empty since SPL can do all of * this init in the SPL board_init_f() function which is called * immediately after this. */ bl s_init ldp x29, x30, [sp] ret ENDPROC(lowlevel_init) 1) 设置栈指针 2) 确保sp 16字节对齐 3) 跳转到s_init中 - _main 代码路径: arch/arm/lib/crt0_64.S :: ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). */ #if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK) ldr x0, =(CONFIG_TPL_STACK) #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr x0, =(CONFIG_SPL_STACK) #elif defined(CONFIG_INIT_SP_RELATIVE) adr x0, __bss_start add x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET #else ldr x0, =(CONFIG_SYS_INIT_SP_ADDR) #endif bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ mov x0, sp bl board_init_f_alloc_reserve mov sp, x0 /* set up gd here, outside any C code */ mov x18, x0 bl board_init_f_init_reserve mov x0, #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 x0, [x18, #GD_START_ADDR_SP] /* x0 <- gd->start_addr_sp */ bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ ldr x18, [x18, #GD_NEW_GD] /* x18 <- gd->new_gd */ adr lr, relocation_return #if CONFIG_POSITION_INDEPENDENT /* 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 #endif /* 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 */ ldr x0, [x18, #GD_RELOCADDR] /* x0 <- gd->relocaddr */ b relocate_code relocation_return: /* * Set up final (full) environment */ bl c_runtime_cpu_setup /* still call old routine */ #endif /* !CONFIG_SPL_BUILD */ #if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK) #if defined(CONFIG_SPL_BUILD) bl spl_relocate_stack_gd /* may return NULL */ /* set up gd here, outside any C code, if new stack is returned */ cmp x0, #0 csel x18, x0, x18, ne /* * Perform 'sp = (x0 != NULL) ? x0 : sp' while working * around the constraint that conditional moves can not * have 'sp' as an operand */ mov x1, sp cmp x0, #0 csel x0, x0, x1, ne mov sp, x0 #endif /* * Clear BSS section */ ldr x0, =__bss_start /* this is auto-relocated! */ ldr x1, =__bss_end /* this is auto-relocated! */ clear_loop: str xzr, [x0], #8 cmp x0, x1 b.lo clear_loop /* call board_init_r(gd_t *id, ulong dest_addr) */ mov x0, x18 /* gd_t */ ldr x1, [x18, #GD_RELOCADDR] /* dest_addr */ b board_init_r /* PC relative jump */ /* NOTREACHED - board_init_r() does not return */ #endif ENDPROC(_main) _main 所做的工作都是为调用C函数做前期的准备,这个C函数就是 ``board_init_f`` 1) 重新对sp赋值,确认是16字节对齐 2) board_init_f_alloc_reserve board_init_f_init_reserve C函数在栈顶保留一个global_data的大小,这个global_data是u-boot里面的一个全局数据 很多地方都会用到,俗称gd_t 3) 跳转到board_init_r - board_init_r 代码路径: common/spl/spl.c :: void board_init_r(gd_t *dummy1, ulong dummy2) { int ret; u32 spl_boot_list[] = { BOOT_DEVICE_NONE, BOOT_DEVICE_NONE, BOOT_DEVICE_NONE, BOOT_DEVICE_NONE, BOOT_DEVICE_NONE, }; struct spl_image_info spl_image; debug(">>" SPL_TPL_PROMPT "board_init_r()\n"); spl_set_bd(); #if defined(CONFIG_SYS_SPL_MALLOC_START) mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, CONFIG_SYS_SPL_MALLOC_SIZE); gd->flags |= GD_FLG_FULL_MALLOC_INIT; #endif if (!(gd->flags & GD_FLG_SPL_INIT)) { if (spl_init()) { hang(); } } #if !defined(CONFIG_PPC) && !defined(CONFIG_ARCH_MX6) timer_init(); #endif if (CONFIG_IS_ENABLED(BLOBLIST)) { ret = bloblist_init(); if (ret) { debug("%s: Failed to set up bloblist: ret=%d\n", __func__, ret); puts(SPL_TPL_PROMPT "Cannot set up bloblist\n"); hang(); } } if (CONFIG_IS_ENABLED(HANDOFF)) { ret = setup_spl_handoff(); if (ret) { puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n"); hang(); } } #if CONFIG_IS_ENABLED(BOARD_INIT) spl_board_init(); #endif #if defined(CONFIG_SPL_WATCHDOG_SUPPORT) && CONFIG_IS_ENABLED(WDT) initr_watchdog(); #endif if (IS_ENABLED(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF)) { dram_init_banksize(); } bootcount_inc(); memset(&spl_image, '\0', sizeof(spl_image)); #ifdef CONFIG_SYS_SPL_ARGS_ADDR spl_image.arg = (void *)CONFIG_SYS_SPL_ARGS_ADDR; #endif spl_image.boot_device = BOOT_DEVICE_NONE; board_boot_order(spl_boot_list); if (boot_from_devices(&spl_image, spl_boot_list, ARRAY_SIZE(spl_boot_list))) { puts(SPL_TPL_PROMPT "failed to boot from all boot devices\n"); hang(); } spl_perform_fixups(&spl_image); if (CONFIG_IS_ENABLED(HANDOFF)) { ret = write_spl_handoff(); if (ret) { printf(SPL_TPL_PROMPT "SPL hand-off write failed (err=%d)\n", ret); } } if (CONFIG_IS_ENABLED(BLOBLIST)) { ret = bloblist_finish(); if (ret) { printf("Warning: Failed to finish bloblist (ret=%d)\n", ret); } } #ifdef CONFIG_CPU_V7M spl_image.entry_point |= 0x1; #endif switch (spl_image.os) { case IH_OS_U_BOOT: debug("Jumping to U-Boot\n"); break; #if CONFIG_IS_ENABLED(ATF) case IH_OS_ARM_TRUSTED_FIRMWARE: debug("Jumping to U-Boot via ARM Trusted Firmware\n"); spl_invoke_atf(&spl_image); break; #endif #if CONFIG_IS_ENABLED(OPTEE) case IH_OS_TEE: debug("Jumping to U-Boot via OP-TEE\n"); spl_optee_entry(NULL, NULL, spl_image.fdt_addr, (void *)spl_image.entry_point); break; #endif #if CONFIG_IS_ENABLED(OPENSBI) case IH_OS_OPENSBI: debug("Jumping to U-Boot via RISC-V OpenSBI\n"); spl_invoke_opensbi(&spl_image); break; #endif #ifdef CONFIG_SPL_OS_BOOT case IH_OS_LINUX: debug("Jumping to Linux\n"); spl_fixup_fdt(); spl_board_prepare_for_linux(); jump_to_image_linux(&spl_image); #endif default: debug("Unsupported OS image.. Jumping nevertheless..\n"); } #if CONFIG_VAL(SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE) debug("SPL malloc() used 0x%lx bytes (%ld KB)\n", gd->malloc_ptr, gd->malloc_ptr / 1024); #endif bootstage_mark_name(spl_phase() == PHASE_TPL ? BOOTSTAGE_ID_END_TPL : BOOTSTAGE_ID_END_SPL, "end " SPL_TPL_NAME); #ifdef CONFIG_BOOTSTAGE_STASH ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR, CONFIG_BOOTSTAGE_STASH_SIZE); if (ret) { debug("Failed to stash bootstage: err=%d\n", ret); } #endif debug("loaded - jumping to U-Boot...\n"); spl_board_prepare_for_boot(); jump_to_image_no_args(&spl_image); } 1) mem_malloc_init 进行memory的malloc池初始化,以后调用malloc就在这个池子中分配内存 2) spl_init 包括fdt log等前期初始化工作 3) timer_init 定时器初始化 4) spl_board_init 根据配置选项完成相应的spl阶段外设初始化,包括console i2c misc watchdog 5) boot_from_devices 设置从哪个外部设备启动(NAND SDCARD NOR) 6) 将image从外部设备load到ram中 7) 判断image类型,如果是uboot则break,去运行u-boot。如果是linux则启动linux(说明:spl可以直接启动linux) 至此,SPL结束它的生命,控制权交给uboot或者linux u-boot 启动流程分析 ^^^^^^^^^^^^^^^^^^^ 从编译系统可知,u-boo.bin的入口代码是arch/arm/lib/vectors.S中的_start函数 - _start 代码路径: arch/arm/lib/vectors.S 与SPL的执行流程基本一直,不同的地方是u-boot.bin阶段会负责处理异常中断。_start会跳转到reset - reset 代码路径 arch/arm/cpu/armv8/start.S 与spl的reset执行流程一致。reset会跳转到_main - _main 代码路径: arch/arm/lib/crt0_64.S 前一部分与spl的执行基本一致,board_init_f函数的调用会有不同。两个阶段调用的函数名虽然都是一样的,但实现的文件是不同的。 spl的board_init_f是在arch/arm/lib/spl.c中实现的,而u-boot.bin阶段是在arch/arm/mach-k3/j721e_init.c中实现的,这个不同是由 编译阶段决定的。 第二部分主要的事情是 ``relocate_code`` u-boot.bin 的链接地址是在编译阶段决定的,假设这个链接地址是0x20000000,SPL在load uboot.bin的时候,需要把它load到这个地址,然后 jump到这个地址运行。 注意:u-boot.bin是地址相关的,只有link address,load address , run address这三者一致才可正常运行。当代码运行到 ``b relocate_code`` 这个位置时,代表u-boot.bin已经被加约定地址。 relocate_code意思时把u-boot.bin余下部分的code全部搬运到另外一个地址运行。 relocate_code 代码在arch/arm/lib/relocate_64.S中实现 第三部分是 ``board_init_r``,该函数的实现在/common/board_r.c 中。 - board_init_f 代码路径: arch/arm/mach-k3/j721e_init.c :: void board_init_f(ulong dummy) { #if defined(CONFIG_K3_J721E_DDRSS) || defined(CONFIG_K3_LOAD_SYSFW) int ret; struct udevice *dev; #endif store_boot_index_from_rom(); ctrl_mmr_unlock(); #ifdef CONFIG_CPU_V7R disable_linefill_optimization(); setup_k3_mpu_regions(); #endif spl_early_init(); #ifdef CONFIG_K3_LOAD_SYSFW ret = uclass_find_device_by_seq(UCLASS_SERIAL, 0, true, &dev); if (!ret) { pinctrl_select_state(dev, "default"); } /* * Load, start up, and configure system controller firmware. Provide * the U-Boot console init function to the SYSFW post-PM configuration * callback hook, effectively switching on (or over) the console * output. */ k3_sysfw_loader(k3_mmc_stop_clock, k3_mmc_restart_clock); /* Prepare console output */ preloader_console_init(); /* Disable ROM configured firewalls right after loading sysfw */ #ifdef CONFIG_TI_SECURE_DEVICE remove_fwl_configs(cbass_hc_cfg0_fwls, ARRAY_SIZE(cbass_hc_cfg0_fwls)); remove_fwl_configs(cbass_hc0_fwls, ARRAY_SIZE(cbass_hc0_fwls)); remove_fwl_configs(cbass_rc_cfg0_fwls, ARRAY_SIZE(cbass_rc_cfg0_fwls)); remove_fwl_configs(cbass_rc0_fwls, ARRAY_SIZE(cbass_rc0_fwls)); remove_fwl_configs(infra_cbass0_fwls, ARRAY_SIZE(infra_cbass0_fwls)); remove_fwl_configs(mcu_cbass0_fwls, ARRAY_SIZE(mcu_cbass0_fwls)); remove_fwl_configs(wkup_cbass0_fwls, ARRAY_SIZE(wkup_cbass0_fwls)); #endif #else /* Prepare console output */ preloader_console_init(); #endif #if defined(CONFIG_TARGET_J721E_A72_EVM) || defined(CONFIG_TARGET_J721E_R5_EVM) /* Perform EEPROM-based board detection */ do_board_detect(); #endif #if defined(CONFIG_CPU_V7R) && defined(CONFIG_K3_AVS0) ret = uclass_get_device_by_driver(UCLASS_MISC, DM_GET_DRIVER(k3_avs), &dev); if (ret) { printf("AVS init failed: %d\n", ret); } #endif #if defined(CONFIG_K3_J721E_DDRSS) ret = uclass_get_device(UCLASS_RAM, 0, &dev); if (ret) { panic("DRAM init failed: %d\n", ret); } #endif } 1) 设置CPU工作状态 2) load u-boot.bin,可以从mmc DFU或者UART获取到 3) console初始化 - board_init_r 代码路径: common/board_r.c :: void board_init_r(gd_t *new_gd, ulong dest_addr) { /* * Set up the new global data pointer. So far only x86 does this * here. * TODO(sjg@chromium.org): Consider doing this for all archs, or * dropping the new_gd parameter. */ #if CONFIG_IS_ENABLED(X86_64) arch_setup_gd(new_gd); #endif #ifdef CONFIG_NEEDS_MANUAL_RELOC int i; #endif #if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) gd = new_gd; #endif gd->flags &= ~GD_FLG_LOG_READY; #ifdef CONFIG_NEEDS_MANUAL_RELOC for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++) init_sequence_r[i] += gd->reloc_off; #endif if (initcall_run_list(init_sequence_r)) hang(); /* NOTREACHED - run_main_loop() does not return */ hang(); } board_init_r中一个重要的函数是 ``initcall_run_list(init_sequrnce_r)`` ,此函数会顺序调用 ``init_sequence_r`` 中的 函数列表。如下 :: 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 #ifdef CONFIG_OF_LIVE initr_of_live, #endif #ifdef CONFIG_DM initr_dm, #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, initr_serial, initr_announce, #if CONFIG_IS_ENABLED(WDT) initr_watchdog, #endif 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, #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 #if defined(CONFIG_GPIO_HOG) gpio_hog_probe_all, #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_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 #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE) blkcache_init, #endif run_main_loop, }; 此函数列表囊括了C语言实现的u-boot 几乎所有功能。通过函数名可大概了解到实现的功能 最后一个函数run_main_loop会跳转到main_loop中执行。 - main_loop 代码路径: common/main.c :: /* We come here after U-Boot is initialised and ready to process commands */ void main_loop(void) { const char *s; bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); if (IS_ENABLED(CONFIG_VERSION_VARIABLE)) env_set("ver", version_string); /* set version variable */ cli_init(); if (IS_ENABLED(CONFIG_USE_PREBOOT)) run_preboot_environment_command(); if (IS_ENABLED(CONFIG_UPDATE_TFTP)) update_tftp(0UL, NULL, NULL); s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); autoboot_command(s); cli_loop(); panic("No CLI available"); } 1) bootstage_mark_name设置当前状态为main_loop 2) 设置环境变量env 3) run_preboot_environment_command 运行环境变量preboot所定义的命令 4) 如果定义了CONFIG_UPDATE_TFTP则通过tftp下在filename到某个地址然后将其烧录到flash中 5) bootdelay_process 从环境变量bootdelay获取需要等待多少时间,超时时间内没有字符键入则 从环境变量bootcmd中获取对应的命令然后执行 至此u-boot结束它的生命周期,控制权交由kernel 移植u-boot ^^^^^^^^^^ 1. 安装依赖库 :: sudo apt-get install build-essential sudo apt-get install libncurses5-dev 2. 添加开发板对应的板级文件 A) copy板级文件目录 :: cp board/ti cp board/holo_ark B) 修改目录下的.c文件命名 :: mv evm.c board.c C) 修改目录下对应的Makefile :: obj-y += board.o D) 修改目录下对应Kconfig 例如: :: if TARGET_X3 config SYS_BOARD ##SYS_BOARD对应board目录下的板子类型 default "x3" config SYS_VENDOR ##SYS_VENDOR代表board下的文件夹,例如holomatic,则在编译的时候会选择board/holomatic文件夹 default "holomatic" config SYS_SOC ##SYS_SOC代表SOC类型 default "x3" config SYS_CONFIG_NAME ##SYS_CONFIG_NAME代表include/configs目录下的头文件 default "holo_j3" endif 修改Kconfig可改变图形界面选项,以及对应的配置信息 E) 修改arch/arm/Kconfig :: source "board/holomatic/x3/Kconfig" 3. 移植配置文件 :: cd configs cp j721e_evm_a72_defconfig holo_ark_a72_defconfig #修改XX_defconfig文件 CONFIG_ARM=y CONFIG_ENV_SIZE=0x20000 CONFIG_ENV_OFFSET=0x680000 ... 4. 修改开发板对应的头文件 :: cd include/configs cp j721e_evm.h holo_ark_v3.h #修改.h的重复检测宏 #ifndef __HOLO_ARK_V3_H #define __HOLO_ARK_V3_H #define CONFIG_SYS_SPL_MALLOC_START 0x84000000 #define CONFIG_SYS_SPL_MALLOC_SIZE SZ_16M #define EXTRA_ENV_J721E_BOARD_SETTINGS ... 5. 驱动修改 6. bootcmd :: 环境变量的默认值定义在include/env_default.h中 "bootcmd=" CONFIG_BOOTCOMMAND CONFIG_BOOTCOMMAND 在include/environment/ti/boot.h中定义 u-boot命令总结 ^^^^^^^^^^^^^^ - 环境变量相关 :: printenv setenv saveenv - 内存操作相关 :: md[.b,.w,.l] address nm[.b,.w,.l] address mm[.b,.w,.l] address mw[.b,.w,.l] address value [count] cp[.b,.w,.l] source target count cmp[.b,.w,.l] addr1 addr2 count - 网络相关 :: setenv ipaddr 192.168.1.2 setenv ethaddr 20:21:01:08:17:33 setenv gatewayip 192.168.1.1 setenv netmask 255.255.255.0 setenv serverip 192.168.1.11 saveenv ping 192.168.1.11 dhcp nfs [loadAddress] [[hostIpaddr:]bootfilename] nfs 80800000 192.168.1.11:/home/zImage tftpboot [loadAddress] [[hostIpaddr:]bootfilename] ftftp 80800000 zImage - mmc 操作命令 :: mmc info mmc rescan mmc list mmc dev [dev] [part] mmc part 更详细的命令可以通过 ``help`` 查看