一般的芯片啓動流程都是Romcode –> BL1 –> BL2 –> Kernel –> User Image,
TI的芯片也不例外,Romcode –> ML0(SPL) –> uboot.img
整個boot階段被分爲三部分,
第一部分是芯片固化的Romcode,上電自動執行,一般支持flash,sd,uart,usb等啓動方式,引導加載spl至片內ram運行;
第二部分是uboot spl,這裏被稱爲ML0,是uboot的第一階段,主要是初始化必要的硬件外設,關閉看門狗,關中斷,配置時鐘,初始化外部RAM,Flash控制器等。然後從對應啓動方式中獲取uboot.img,也就是uboot的第二階段,加載到片外sdram中運行;
第三部分是uboot.img,主要是板級初始化,用來引導加載內核。
下面針對Romcode –> ML0(SPL) –> uboot.img這個流程做一個說明:
1.Romcode
芯片的Boot Rom,存放在總計176KB的ROM當中,如下圖:
該地址區域存放了芯片的引導加載程序,板子上電後會自動執行這些代碼。
2. ML0(SPL)
系統上電後根據啓動方式,到對應的地方把MLO加載到芯片內部的RAM中運行。
圖中的Downloaded Image區域是用來保存 MLO(SPL)文件的,其最大可達到 109 KB
Downloaded Image = 0X4030B800 – 0X402F0400 = 0X1B400 = 109KB
MLO(SPL)的內存分佈:
3.uboot.img
該階段的代碼搬到了芯片外的SDRAM中運行:(物理起始地址爲0x8000000)
uboot.img的內存分佈:
在對以上概念有一個整體的瞭解之後,下面開始來分析uboot的SPL階段:
一、SPL的簡介:
SPL(Secondary programloader)是uboot第一階段執行的代碼。主要負責搬移uboot第二階段的代碼到片外內存中運行。SPL是由固化在芯片內部的ROM引導的。我們知道很多芯片廠商固化的ROM支持從nandflash、SD卡等外部介質啓動。所謂啓動,就是從這些外部介質中搬移一段固定大小(4K/8K/16K等)的代碼到內部RAM中運行。這裏搬移的就是SPL。在最新版本的uboot中,可以看到SPL也支持nandflash,SD卡等多種啓動方式。當SPL本身被搬移到內部RAM中運行時,它會從nandflash、SD卡等外部介質中搬移uboot第二階段的代碼到片外內存中。
二、SPL的功能:
1.Basic Arm Initialization
2.UART console initialization
3.Clocks and DPLL Locking(minimal)
4.SDRAM initialization
5.Mux(minimal)
6.Boot Device Initialization, based on where we are booting from MMC1, or MMC2,or Nand, or Onenand
7.Bootloading real u-boot from the Boot Device and passing control to it.
三、SPL鏈接文件U-boot-spl.lds
鏈接文件決定一個可執行程序的各個段的存儲(加載)地址,以及運行(鏈接)地址。下面來看看SPL的鏈接文件U-boot-spl.lds:
從該鏈接文件中可以看出,程序執行是從_start標號開始的。
_start位於vectors.S (arch/arm/lib)
- _start:
-
- #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
- .word CONFIG_SYS_DV_NOR_BOOT_CFG
- #endif
- /*
- 跳轉到reset之後就不會返回了。不然就是沒關中斷出錯了,
- 然後跳轉到下面7種狀態之一:死循環。
- */
- b reset
- 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
-
- /*
- *************************************************************************
- *
- * Indirect vectors table
- *
- * Symbols referenced here must be defined somewhere else
- *
- *************************************************************************
- */
-
- .globl _undefined_instruction
- .globl _software_interrupt
- .globl _prefetch_abort
- .globl _data_abort
- .globl _not_used
- .globl _irq
- .globl _fiq
-
- _undefined_instruction: .word undefined_instruction
- _software_interrupt: .word software_interrupt
- _prefetch_abort: .word prefetch_abort
- _data_abort: .word data_abort
- _not_used: .word not_used
- _irq: .word irq
- _fiq: .word fiq
- /*
- 前面佔據了(8 + 7) * 4 = 60字節 ,不是16的倍數,所以要
- 填充0xdeadbeef字段,變成64字節。
- */
- .balignl 16,0xdeadbeef
-
- /*
- *************************************************************************
- *
- * Interrupt handling
- *
- *************************************************************************
- */
-
- /* SPL interrupt handling: just hang */
-
- #ifdef CONFIG_SPL_BUILD
-
- .align 5
- undefined_instruction:
- software_interrupt:
- prefetch_abort:
- data_abort:
- not_used:
- irq:
- fiq:
-
- 1:
- bl 1b /* hang and never return */
reset: 位於start.S (arch/arm/cpu/armv7)
- .globl reset
- .globl save_boot_params_ret
-
- reset:
- /* Allow the board to save important registers */
- b save_boot_params
- save_boot_params_ret:
- /*
- * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
- * except if in HYP mode already
- */
- mrs r0, cpsr
- and r1, r0, #0x1f @ mask mode bits
- teq r1, #0x1a @ test for HYP mode
- bicne r0, r0, #0x1f @ clear all mode bits
- orrne r0, r0, #0x13 @ set SVC mode
- orr r0, r0, #0xc0 @ disable FIQ and IRQ
- msr cpsr,r0
-
- /*
- * Setup vector:
- * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
- * Continue to use ROM code vector only in OMAP4 spl)
- */
- #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
- /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
- mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
- bic r0, #CR_V @ V = 0
- mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
-
- /* Set vector address in CP15 VBAR register */
- ldr r0, =_start
- mcr p15, 0, r0, c12, c0, 0 @Set VBAR
- #endif
-
- /* the mask ROM code should have PLL and others stable */
- #ifndef CONFIG_SKIP_LOWLEVEL_INIT
- bl cpu_init_cp15
- bl cpu_init_crit
- #endif
-
- bl _main
reset部分做了如下動作:
關閉中斷,設置cpu爲SVC32模式(管理模式)。
cpu_init_cp15初始化了協處理器:關閉cathe以及mmu。
cpu_init_crit準備臨時堆棧,初始化鎖相環以及內存。
- ENTRY(cpu_init_crit)
- /*
- * Jump to board specific initialization...
- * The Mask ROM will have already initialized
- * basic memory. Go here to bump up clock rate and handle
- * wake up conditions.
- */
- b lowlevel_init @ go setup pll,mux,memory
- ENDPROC(cpu_init_crit)
跳轉至lowlevel_init :位於lowlevel_init.S (arch/arm/cpu/armv7)
- ENTRY(lowlevel_init)
- /*
- * Setup a temporary stack. Global data is not available yet.
- */
- ldr sp, =CONFIG_SYS_INIT_SP_ADDR
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- #ifdef CONFIG_SPL_DM
- mov r9, #0
- #else
- /*
- * Set up global data for boards that still need it. This will be
- * removed soon.
- */
- #ifdef CONFIG_SPL_BUILD
- ldr r9, =gdata
- #else
- sub sp, sp, #GD_SIZE
- bic sp, sp, #7
- mov r9, sp
- #endif
- #endif
- /*
- * Save the old lr(passed in ip) and the current lr to stack
- */
- push {ip, lr}
-
- /*
- * 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
- pop {ip, pc}
- ENDPROC(lowlevel_init)
爲調用c函數準備一個臨時堆棧,這個堆棧在cpu的片上內存
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
跟蹤代碼,找到CONFIG_SYS_INIT_SP_ADDR的定義:
ti_armv7_common.h (include/configs)
#define CONFIG_SYS_INIT_SP_ADDR (NON_SECURE_SRAM_END - GENERATED_GBL_DATA_SIZE)
#define NON_SECURE_SRAM_START 0x402F0400
#define NON_SECURE_SRAM_END 0x40310000
#define SRAM_SCRATCH_SPACE_ADDR 0x4030B800
#define GENERATED_GBL_DATA_SIZE 224 /* (sizeof(struct global_data) + 15) & ~15 @ */
所以算出CONFIG_SYS_INIT_SP_ADDR = 0x40310000 - 224 = 0x4030FF20
(反彙編u-boot.spl,查看dump文件,發現的確是0x4030FF20)
繼續跳轉至s_init: board.c (arch/arm/cpu/armv7/am33xx)
- void s_init(void)
- {
- #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_RTC_ONLY_SUPPORT)
- rtc_only();
- #endif
- /*
- * The ROM will only have set up sufficient pinmux to allow for the
- * first 4KiB NOR to be read, we must finish doing what we know of
- * the NOR mux in this space in order to continue.
- */
- #ifdef CONFIG_NOR_BOOT
- enable_norboot_pin_mux();
- #endif
- watchdog_disable();
- set_uart_mux_conf();
- setup_clocks_for_console();
- uart_soft_reset();
- #if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
- /* Enable RTC32K clock */
- rtc32k_enable();
- #endif
- }
主要工作:關閉看門狗、設置串口的引腳以及時鐘、使能RTC時鐘信號
最後返回至reset代碼,跳轉到_main執行:crt0.S (arch/arm/lib)
- ENTRY(_main)
-
- /*
- * Set up initial C runtime environment and call board_init_f(0).
- */
-
- #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
- ldr sp, =(CONFIG_SPL_STACK)
- #else
- ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
- #endif
- #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
- mov r3, sp
- bic r3, r3, #7
- mov sp, r3
- #else
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- #endif
- mov r0, sp //r0 = sp,之後調用board_init_f_alloc_reserve函數,r0作爲形參
- bl board_init_f_alloc_reserve
- //返回之後,r0裏面存放的是global_data的地址
- //注意,同時也是堆棧地址,因爲堆棧是向下增長的,所以不必擔心和global_data衝突的問題
- mov sp, r0 //sp = board_init_f_alloc_reserve函數返回的top,也就是global_data的地址
- /* set up gd here, outside any C code */
- mov r9, r0 //r9 = r0 = global_data的起始地址
- //調用board_init_f_init_reserve對global_data進行初始化
- 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 sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
- #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
- mov r3, sp
- bic r3, r3, #7
- mov sp, r3
- #else
- bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
- #endif
- 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 //new_gd指針存入sp
- movne r9, r0 //new_gd指針存入r9
- # 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 */
- /*
- r0 = __bss_start
- r1 = __bss_end
- r2 = 0
- */
- clbss_l:cmp r0, r1 /* while not at end of BSS */
- #if defined(CONFIG_CPU_V7M)
- itt lo
- #endif
- //HS - Higher or Same, LO - Lower
- //__bss_start比__bss_end小,則把r2的值加載到以r0地址的存儲器中
- strlo r2, [r0] /* clear 32-bit BSS word */
- //r0 = r0 + 4
- 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 */
- //r1 = r9 + GD_RELOCADDR
- ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
- /* call board_init_r */
- #if defined(CONFIG_SYS_THUMB_BUILD)
- //r0以及r1作爲形參傳遞
- ldr lr, =board_init_r /* this is auto-relocated! */
- //BX指令跳轉到指令中所指定的目標地址,若目標地址的bit[0]爲0,
- //則跳轉時自動將CPSR中的標誌位T復位,即把目標地址的代碼解釋
- //爲ARM代碼;若目標地址的bit[0]爲1,則跳轉時自動將CPSR中的標誌位
- //T置位,即把目標地址的代碼解釋爲Thumb代碼。
- bx lr
- #else
- ldr pc, =board_init_r /* this is auto-relocated! */
- #endif
- /* we should not return here. */
- #endif
-
- ENDPROC(_main)
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
設置棧,上面已經計算出來:
sp = CONFIG_SYS_INIT_SP_ADDR = 0x4030FF20
mov r0, sp //r0 = sp,之後調用board_init_f_alloc_reserve函數,r0作爲形參
- // 這個函數用於給global_data分配空間,在relocation之前調用
- // 傳入的參數是頂部地址,但是不一定是要內存頂部的地址,
- //可以自己進行規劃,後面_main函數會說明
- ulong board_init_f_alloc_reserve(ulong top)
- {
- /* Reserve early malloc arena */
- #if defined(CONFIG_SYS_MALLOC_F)
- // 先從頂部向下分配一塊CONFIG_SYS_MALLOC_F_LEN大小的空間給early malloc使用
- // 關於CONFIG_SYS_MALLOC_F_LEN可以參考README
- // 這塊內存是用於在relocation前用於給malloc函數提供內存池。
- top -= CONFIG_SYS_MALLOC_F_LEN; //top = top - CONFIG_SYS_MALLOC_F_LEN = r0(sp)-CONFIG_SYS_MALLOC_F_LEN
- #endif
- /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
- // 繼續向下分配sizeof(struct global_data)大小的內存給global_data使用,向下16byte對齊
- // 這時候得到的地址就是global_data的地址。
- top = rounddown(top-sizeof(struct global_data), 16);
- // 將top,也就是global_data的地址返回
- return top;
- }
mov sp, r0 //sp = board_init_f_alloc_reserve函數返回的top,也就是global_data的地址。注意,同時也是堆棧地址,因爲堆棧是向下增長的,所以不必擔心和global_data衝突的問題
mov r9, r0 //r9 = r0 = global_data的起始地址
跳轉至board_init_f_init_reserve函數,r0作爲形參:board_init.c (common/init)
- // 這個函數用於對global_data區域進行初始化,也就是清空global_data區域
- // 傳入的參數就是global_data的基地址
- void board_init_f_init_reserve(ulong base)
- {
- struct global_data *gd_ptr;
- #ifndef _USE_MEMCPY
- int *ptr;
- #endif
-
- /*
- * clear GD entirely and set it up.
- * Use gd_ptr, as gd may not be properly set yet.
- */
-
- gd_ptr = (struct global_data *)base;
- /* zero the area */
- #ifdef _USE_MEMCPY
- // 先通過memset函數對global_data數據結構進行清零
- memset(gd_ptr, '\0', sizeof(*gd));
- #else
- for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
- *ptr++ = 0;
- #endif
- /* set GD unless architecture did it already */
- #if !defined(CONFIG_ARM)
- arch_setup_gd(gd_ptr);
- #endif
- /* next alloc will be higher by one GD plus 16-byte alignment */
- // 因爲global_data區域是16Byte對齊的,對齊後,後面的地址就是early malloc
- //的內存池的地址,具體參考上述board_init_f_alloc_reserve
- // 所以這裏就獲取了early malloc的內存池的地址
- base += roundup(sizeof(struct global_data), 16);
-
- /*
- * record early malloc arena start.
- * Use gd as it is now properly set for all architectures.
- */
-
- #if defined(CONFIG_SYS_MALLOC_F)
- /* go down one 'early malloc arena' */
- // 將內存池的地址寫入到gd->malloc_base中
- gd->malloc_base = base;
- /* next alloc will be higher by one 'early malloc arena' size */
- //加上CONFIG_SYS_MALLOC_F_LEN,獲取early malloc的內存池的末尾地址,
- //這裏並沒有什麼作用,是爲了以後在early malloc的內存池後面多
- //加一個區域時的修改方便。
- base += CONFIG_SYS_MALLOC_F_LEN;
- #endif
- }
這裏首先爲調用board_init_f準備一個臨時堆棧。然後將堆棧初始的地址保存在r9,所以r9就是gd的起始地址,後面需要靠r9訪問gd中的成員。然後將r0賦值成0,r0就是要調用的board_init_f函數的第一個參數。
global_data內存分佈如下:
———————- CONFIG_SYS_INIT_SP_ADDR —————————–高地址
…………………………….. early malloc 內存池
————————-early malloc 內存池基地址 —————————
………………………………… global_data區域
—————-global_data基地址(r9), 也是堆棧的起始地址————-
………………………………………堆棧空間
————————————–堆棧結束—————————————-低地址
跳轉至board_init_f 函數:board.c (arch/arm/cpu/armv7/am33xx)
- #ifdef CONFIG_SPL_BUILD
- void board_init_f(ulong dummy)
- {
- board_early_init_f();
- sdram_init();
- }
- #endif
該函數主要功能是初始化clock、dpll、timer、IO引腳(mmc、i2c、cpsw、nand)、ddr。
然後跳轉至spl_relocate_stack_gd: spl.c (common/spl)
- /**
- * spl_relocate_stack_gd() - Relocate stack ready for board_init_r() execution
- *
- * Sometimes board_init_f() runs with a stack in SRAM but we want to use SDRAM
- * for the main board_init_r() execution. This is typically because we need
- * more stack space for things like the MMC sub-system.
- *
- * This function calculates the stack position, copies the global_data into
- * place, sets the new gd (except for ARM, for which setting GD within a C
- * function may not always work) and returns the new stack position. The
- * caller is responsible for setting up the sp register and, in the case
- * of ARM, setting up gd.
- *
- * All of this is done using the same layout and alignments as done in
- * board_init_f_init_reserve() / board_init_f_alloc_reserve().
- *
- * @return new stack location, or 0 to use the same stack
- */
- ulong spl_relocate_stack_gd(void)
- {
- #ifdef CONFIG_SPL_STACK_R
- gd_t *new_gd;
- ulong ptr = CONFIG_SPL_STACK_R_ADDR; //0x82000000, SDRAM的地址
-
- #ifdef CONFIG_SPL_SYS_MALLOC_SIMPLE
- if (CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN) {
- if (!(gd->flags & GD_FLG_SPL_INIT))
- panic_str("spl_init must be called before heap reloc");
-
- ptr -= CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
- gd->malloc_base = ptr;
- gd->malloc_limit = CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
- gd->malloc_ptr = 0;
- }
- #endif
- /* Get stack position: use 8-byte alignment for ABI compliance */
- //roundup(x, y)類似於一個數學函數,它總是嘗試找到大於x並接近x的可以整
- //除y的那個數,也即向上圓整。
- ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16);
- //將之前在片內內存中的global_data對象重定位到SDRAM中(new_gd)
- new_gd = (gd_t *)ptr;
- memcpy(new_gd, (void *)gd, sizeof(gd_t));
- #if !defined(CONFIG_ARM)
- gd = new_gd;
- #endif
- //返回新的棧指針
- return ptr;
- #else
- return 0;
- #endif
- }
功能在註釋那裏說的很清楚了:
This function calculates the stack position, copies the global_data into place, sets the new gd (except for ARM, for which setting GD within a C function may not always work) and returns the new stack position.
返回至_main代碼中,繼續運行(r0存放了新的gd指針,同時也是新的棧指針):
cmp r0, #0
movne sp, r0
movne r9, r0
cmp命令是比較r0和0的大小(第一個操作數減去第二個操作數,但不影響第兩個操作數的值,最後結果影響CPSR狀態寄存器):
EQ和NE條件碼:當兩個數據相等時,條件碼EQ有效,否則條件碼NE有效。
最後把new_gd指針放在了r9寄存器裏面。
然後繼續往下走,主要功能是清除bss段:(存儲未初始化的全局變量和未初始化的static變量)
- 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 */
- /*
- r0 = __bss_start
- r1 = __bss_end
- r2 = 0
- */
- clbss_l:cmp r0, r1 /* while not at end of BSS */
- #if defined(CONFIG_CPU_V7M)
- itt lo
- #endif
- //HS - Higher or Same, LO - Lower
- //__bss_start比__bss_end小,則把r2的值加載到以r0地址的存儲器中
- strlo r2, [r0] /* clear 32-bit BSS word */
- //r0 = r0 + 4
- addlo r0, r0, #4 /* move to next */
- blo clbss_l
- #endif
然後把新的gd指針存入r0,gd指針加上偏移量GD_RELOCADDR存入r1,兩者作爲形參傳遞給board_init_r,最後跳轉到board_init_r中執行:spl.c (common/spl)
- void board_init_r(gd_t *dummy1, ulong dummy2)
- {
- int i;
-
- debug(">>spl:board_init_r()\n");
- //CONFIG_SYS_SPL_MALLOC_START = 0x80a80000, CONFIG_SYS_SPL_MALLOC_SIZE = 0x100000
- #if defined(CONFIG_SYS_SPL_MALLOC_START)
- //初始化:設置0x80a80000 ~0x80B80000內容爲0
- 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();
- }
- #ifndef CONFIG_PPC
- /*
- * timer_init() does not exist on PPC systems. The timer is initialized
- * and enabled (decrementer) in interrupt_init() here.
- */
- timer_init();
- #endif
-
- #ifdef CONFIG_SPL_BOARD_INIT
- spl_board_init();
- #endif
spl_board_init: boot-common.c (arch/arm/cpu/armv7/omap-common)
主要工作:把片內ram的啓動參數加載到片外內存,初始化串口、gpmc、i2c、usb、看門狗、dpll
其中,裏面的save_omap_boot_params函數就是把片內ram的啓動參數加載到片外內存,獲取boot_device類型以及boot_mode,設置gd結構體。具體的啓動參數結構如下:
- void save_omap_boot_params(void)
- {
- //OMAP_SRAM_SCRATCH_BOOT_PARAMS = 0x4030B800 + 0x24 = 0x4030B824
- //boot_params參數存儲在片內的RAM區
- u32 boot_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS);
- struct omap_boot_parameters *omap_boot_params;
- int sys_boot_device = 0;
- u32 boot_device;
- u32 boot_mode;
-
- if ((boot_params < NON_SECURE_SRAM_START) ||
- (boot_params > NON_SECURE_SRAM_END))
- return;
-
- omap_boot_params = (struct omap_boot_parameters *)boot_params;
-
- boot_device = omap_boot_params->boot_device;
- boot_mode = MMCSD_MODE_UNDEFINED;
回到board_init_r函數:
- //獲取gd結構體中的boot_device
- board_boot_order(spl_boot_list);
- for (i = 0; i < ARRAY_SIZE(spl_boot_list) &&
- spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
- announce_boot_device(spl_boot_list[i]); //打印從哪裏啓動uboot
- if (!spl_load_image(spl_boot_list[i])) //找到並且加載u-boot.img到內存
- break;
- }
-
- if (i == ARRAY_SIZE(spl_boot_list) ||
- spl_boot_list[i] == BOOT_DEVICE_NONE) {
- puts("SPL: failed to boot from all boot devices\n");
- hang();
- }
-
- switch (spl_image.os) {
- case IH_OS_U_BOOT:
- debug("Jumping to U-Boot\n");
- break;
- #ifdef CONFIG_SPL_OS_BOOT
- case IH_OS_LINUX:
- debug("Jumping to Linux\n");
- spl_board_prepare_for_linux();
- jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
- #endif
- default:
- debug("Unsupported OS image.. Jumping nevertheless..\n");
- }
- #if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
- debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
- gd->malloc_ptr / 1024);
- #endif
-
- debug("loaded - jumping to U-Boot...");
- jump_to_image_no_args(&spl_image); //跳轉到u-boot.img中繼續運行
- }
將鏡像加載到內存的時候,此時SPL進行判斷,加載的鏡像是uboot還是kernel,然後便跳轉到鏡像的入口中進行執行。此時,SPL的使命便完成了。
==>jump_to_image_no_args(&spl_image);