uboot研读笔记 | 14 - uboot启动流程分析(2016.03版本)-流程基本清晰-good

uboot研读笔记 | 14 - uboot启动流程分析(2016.03版本)_MCUlover666的技术博客_51CTO博客

【腾讯文档】uboot启动流程图(2016.03)​ ​https://docs.qq.com/flowchart/DR25oeU5KQmJydEhM​

文章目录
​ ​一、第一行代码​​
​ ​二、_start 函数​​
​ ​1. save_boot_params函数​​
​ ​2. save_boot_params_ret函数​​
​ ​三、lowlevel_init函数​​
​ ​1. lowlevel_init说明​​
​ ​2. lowlevel_init函数ARM v7实现​​
​ ​3. 栈地址CONFIG_SYS_INIT_SP_ADDR​​
​ ​4. s_init函数imx6实现​​
​ ​5. 一路返回​​
​ ​四、__main函数​​
​ ​1.设置C语言运行环境并调用board_init_f函数​​
​ ​2. 设置新的sp指针和gd指针,设置中间环境,调用重定位代码​​
​ ​3. 重定位向量表​​
​ ​4. 设置最终的环境​​
​ ​五、board_init_f 函数​​
​ ​六、relocate_code​​
​ ​七、relocate_vectors​​
​ ​八、board_init_r​​
​ ​九、一切就绪,uboot启动!​
 

一、第一行代码
要分析uboot启动流程,先得找到uboot启动的第一行代码,编译uboot,查看u-boot.map文件,找到Linker script and memory map这一节:

.text           0x0000000087800000    0x3e734
 *(.__image_copy_start)
 .__image_copy_start
                0x0000000087800000        0x0 arch/arm/lib/built-in.o
                0x0000000087800000                __image_copy_start
 *(.vectors)
 .vectors       0x0000000087800000      0x300 arch/arm/lib/built-in.o
                0x0000000087800000                _start
                0x0000000087800020                _undefined_instruction
                0x0000000087800024                _software_interrupt
                0x0000000087800028                _prefetch_abort
                0x000000008780002c                _data_abort
                0x0000000087800030                _not_used
                0x0000000087800034                _irq
                0x0000000087800038                _fiq
                0x0000000087800040                IRQ_STACK_START_IN
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
可以看到,uboot编译出的可执行程序中,最开始放置的还是中断向量表(vectors),第一行代码为 ​​_start​​​ 函数,地址在0x87800000处,并把此地址作为起始地址赋给 ​​__image_copy_start​​ 变量用于镜像拷贝。

二、_start 函数
map文件中给出了 _start 函数在 arch/arm/lib 下面,搜索一下在​​arch/arm/lib/Vectors.S​​文件中:

/*
 *************************************************************************
 *
 * Exception vectors as described in ARM reference manuals
 *
 * Uses indirect branch to allow reaching handlers anywhere in memory.
 *
 *************************************************************************
 */
 
_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
  .word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif

  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
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
可以看到,_start函数最先跳转到reset函数执行,不返回,接着依次执行后面的函数。

搜索一下,reset函数在​​arch/arm/cpu/armv7/start.S​​中:

/*************************************************************************
 *
 * Startup Code (reset vector)
 *
 * Do important init only if we don't start from memory!
 * Setup memory and board specific bits prior to relocation.
 * Relocate armboot to ram. Setup stack.
 *
 *************************************************************************/

  .globl  reset
  .globl  save_boot_params_ret

reset:
  /* Allow the board to save important registers */
  b save_boot_params
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
1. save_boot_params函数
复位时允许CPU先保存重要的寄存器,save_boot_params函数定义如下:

/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *  __attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
  b save_boot_params_ret    @ back to my caller
ENDPROC(save_boot_params)
  .weak save_boot_params
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
该函数跳转到 save_boot_params_ret 去执行,不返回。

2. save_boot_params_ret函数
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
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
(1) ​失能中断(FIQ和IRQ)并且设置CPU为SVC32模式​,除非已经处于HYP模式。

(2)​设置中断向量表地址为_start函数的地址​,在map文件中可以看到,为0x87800000。

(3)​进行CPU初始化​,调用函数初始化CP15和CRIT。

cpu_init_cp15函数的定义很长,截取片段如下:

/*************************************************************************
 *
 * cpu_init_cp15
 *
 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
 * CONFIG_SYS_ICACHE_OFF is defined.
 *
 *************************************************************************/
ENTRY(cpu_init_cp15)
  /*
   * Invalidate L1 I/D
   */
  mov r0, #0      @ set up for MCR
  mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
  mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
  mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array
  mcr     p15, 0, r0, c7, c10, 4  @ DSB
  mcr     p15, 0, r0, c7, c5, 4 @ ISB

  /*
   * disable MMU stuff and caches
   */
  mrc p15, 0, r0, c1, c0, 0
  bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
  bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
  orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
  orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
  bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
  orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
#endif
  mcr p15, 0, r0, c1, c0, 0

  mov r5, lr      @ Store my Caller
  mrc p15, 0, r1, c0, c0, 0 @ r1 has Read Main ID Register (MIDR)
  mov r3, r1, lsr #20   @ get variant field
  and r3, r3, #0xf    @ r3 has CPU variant
  and r4, r1, #0xf    @ r4 has CPU revision
  mov r2, r3, lsl #4    @ shift variant field for combined value
  orr r2, r4, r2    @ r2 has combined CPU variant + revision

  mov pc, r5      @ back to my caller
ENDPROC(cpu_init_cp15)
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
① 关闭 L1 I/D Cache。

② 禁用MMU和缓存。

③ 返回。

cpu_init_crit 函数的定义如下:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
/*************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************/
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)
#endif
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
可见,​uboot完成CPU初始化后,调用cpu_init_crit函数,跳转到板级初始化函数 lowlevel_init。至此,Mask ROM已经完成了基础内存的初始化,到这里由 lowlevel_init 来设置pll、mux、memory,来提高时钟速度和处理唤醒条件​。

三、lowlevel_init函数
1. lowlevel_init说明
目的:完成执行board_init_f() 函数之前必不可少的初始化。
不要使用全局变量或者BSS段
没有栈
不要设置SDRAM或者使用控制台
​仅仅完成最低限度的工作(设置栈即可)使 board_init_f 可以执行就好​。
2. lowlevel_init函数ARM v7实现
搜索一下,lowlevel_init函数的定义在 ​​arch/arm/cpu/armv7/lowlevel_init.S​​中。

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)
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
(1)设置临时堆栈,全局变量目前不可用;

(2)为仍然需要的板子设置全局变量,这段代码不久将会被移除;

(3)保存旧的lr寄存器和当前lr寄存器到栈;

(4)调用非常早期的初始化函数,它应该只做最基本的事情,不应该做:

设置DRAM
使用全局数据
清除BSS
尝试启动控制台
对于使用SPL启动的开发板,它应该为空,因为SPI可以完成所有的这些初始化在 SPL board_init_f() 函数中,该函数将在之后被立即调用。

3. 栈地址CONFIG_SYS_INIT_SP_ADDR
该宏定义表示要初始化的栈顶指针地址,在开发板BSP中的头文件​​include/configs/mx6ullatk.h​​定义:

#define CONFIG_SYS_INIT_SP_OFFSET \
  (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \
  (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
1.
2.
3.
4.
继续找定义,可以看到,栈目前是定义在​内存RAM​的:

#define CONFIG_SYS_INIT_RAM_ADDR  IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE  IRAM_SIZE
1.
2.
找到RAM大小的定义,在​​arch/arm/include/asm/arch-mx6/imx-regs.h​​文件中:

#if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \
  defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))
#define IRAM_SIZE                    0x00040000
#else
#define IRAM_SIZE                    0x00020000
#endif
1.
2.
3.
4.
5.
6.
我们使用的是imx6ull,内部RAM大小为0x00020000(128KB)。

接着找IRAM_BASE_ADDR的定义,同样在该文件中,为0x00900000。

#define IRAM_BASE_ADDR      0x00900000
1.


查阅参考手册的memory map,可以看到0x00900000这个地址是内部OCRAM的起始地址。

最后还有一个GENERATED_GBL_DATA_SIZE,搜索一下,在​​include/generated/generic-asm-offsets.h​​文件中,<font=“red”>​该文件是uboot编译时动态生成的​,表示

#define GENERATED_GBL_DATA_SIZE 256 /* (sizeof(struct global_data) + 15) & ~15  @ */
1.
这样,我们就可以计算出栈顶地址了:

CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 - 0x100 = 0x1FF00
CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0x1FF00 = 0x0091FF00
1.
2.
再回到lowlevel_init函数,对栈顶指针做了8字节对齐处理:

bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
1.
对0x0091FF00做8字节对齐,变为0x0091FE08,这就是最终设置的栈顶指针SP,栈空间大小为CONFIG_SYS_INIT_SP_OFFSET。

4. s_init函数imx6实现
经过搜索,s_init函数也是由不同的芯片厂商实现,imx6系列处理器的在​​arch/arm/cpu/armv7/mx6/soc.c​​文件中,s_init函数部分实现如下,​看上去像是要设置时钟​。

void s_init(void)
{
  struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
  struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
  u32 mask480;
  u32 mask528;
  u32 reg, periph1, periph2;

  if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||
      is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))
    return;
  ...
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
5. 一路返回
s_iinit函数执行完成后返回 lowlevel_init 函数,lowlevel_init 函数将之前存储的lr寄存器值恢复到pc,程序返回。

接着一路返回到 cpu_init_crit 函数被调用时候的返回地址(因为该函数调用时使用了bl指令),也就是start.S汇编文件中:

/* 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
1.
2.
3.
4.
5.
6.
7.
可以看到,cpu_init_crit执行完毕后,接下来就跳转到_main函数执行。

四、__main函数
​​__main​​​函数定义在​​arch/arm/lib/crt0.S​​文件中,这个文件处理U-Boot 启动过程中与目标无关的阶段,其中需要C运行时环境(设置好栈顶指针)。

​​__main​​函数的执行流程如下。

1.设置C语言运行环境并调用board_init_f函数
/*
 * 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
  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
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
(1)设置栈顶指针

C语言运行环境其实就是堆栈,栈用于函数调用和局部变量,设置栈的过程可以分为三步。

① 设置栈顶指针SP为CONFIG_SYS_INIT_SP_ADDR,并进行8字节对齐,值之前计算过。

② 将SP的值作为函数参数,调用board_init_f_alloc_reserve函数,该函数是一个通用函数,可以被各个架构调用,用于分配预留空间,定义在​​common/init/board_init.c​​文件中:

/**
 * ulong board_init_f_alloc_reserve - allocate reserved area
 *
 * This function is called by each architecture very early in the start-up
 * code to allow the C runtime to reserve space on the stack for writable
 * 'globals' such as GD and the malloc arena.
 *
 * @top:  top of the reserve area, growing down.
 * @return: bottom of reserved area
 */
ulong board_init_f_alloc_reserve(ulong top)
{
  /* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
  top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
  /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
  top = rounddown(top-sizeof(struct global_data), 16);

  return top;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
board_init_f_alloc_reserve函数的传入参数是栈顶地址,返回参数是预留空间的底部。

可以看到,预留空间分为两部分,如果开启了malloc,则为malloc预留一部分空间,大小为CONFIG_SYS_MALLOC_F_LEN;其次为GD变量(global_data结构体类型)预留空间,并且对齐到16个字节的倍数。

③ 将新的栈顶地址写入到SP中,设置完成。

(2)设置全局变量gd的地址。

在文件​​arch/arm/include/asm/global_data.h​​中,定义了使用r9寄存器作为指向全局变量gd的指针:

#ifdef CONFIG_ARM64
#define DECLARE_GLOBAL_DATA_PTR   register volatile gd_t *gd asm ("x18")
#else
#define DECLARE_GLOBAL_DATA_PTR   register volatile gd_t *gd asm ("r9")
#endif
1.
2.
3.
4.
5.
所以此处将r0寄存器的值写入r9寄存器,因为栈是向下增长的,所以此时的栈顶指针就是gd存储空间的基地址,也就是0x0091FA00。

gd_t类型在​​include/asm-generic/global_data.h​​中定义,用于管理全局变量,部分代码如下:

typedef struct global_data {
  bd_t *bd;
  unsigned long flags;
  unsigned int baudrate;
  unsigned long cpu_clk;  /* CPU clock in Hz!   */
  unsigned long bus_clk;
  //代码省略
  struct udevice *cur_serial_dev; /* current serial device */
  struct arch_global_data arch; /* architecture-specific data */
} gd_t;
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
设置完gd变量的地址后,调用函数​​board_init_f_init_reserve​​​,初始化第(1)步中预留的空间,在​​common/init/board_init.c​​文件中定义:

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(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 */
  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 = base;
  /* next alloc will be higher by one 'early malloc arena' size */
  base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
(3)清空r0寄存器,调用board_init_f函数,board_init_f 函数非常重要,完了下一节详细讲述。

2. 设置新的sp指针和gd指针,设置中间环境,调用重定位代码
#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
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
board_init_f函数中会初始化开发板的许多外设,包括DRAM,并将栈顶指针和sp放置到DRAM中,所以这里主要做了三件事情:

(1)设置新的栈顶指针为​​sp = gd->start_addr_sp​​

(2)设置新的gd指针为​​r9 = gd->bd​​

(3)设置r0寄存器的值为 ​​gd->relocaddr​​,​跳转到重定位代码relocate_code,并且返回到here标号处​。

3. 重定位向量表
重定位代码完成后,返回到here标号处,调用relocate_vectors函数,开始进行​向量表重定位​:

here:
/*
 * now relocate vectors
 */

  bl  relocate_vectors
1.
2.
3.
4.
5.
6.
4. 设置最终的环境
/* 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 defined(CONFIG_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
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
这里面主要调用函数 c_runtime_cpu_setup,然后清除BSS段,设置board_init_r函数的两个参数,​最终调用函数board_init_r​。

五、board_init_f 函数
在​​common/board_f.c​​文件中,找到 board_init_f 函数定义:

void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
  /*
   * For some archtectures, global data is initialized and used before
   * calling this function. The data should be preserved. For others,
   * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
   * here to host global data until relocation.
   */
  gd_t data;

  gd = &data;

  /*
   * Clear global data before it is accessed at debug print
   * in initcall_run_list. Otherwise the debug print probably
   * get the wrong vaule of gd->have_console.
   */
  zero_global_data();
#endif

  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)
  /* NOTREACHED - jump_to_copy() does not return */
  hang();
#endif
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
这里面的核心代码只有两行:

if (initcall_run_list(init_sequence_f))
    hang();
1.
2.
这两行代码完成了​一系列板级外设的初始化​。

init_sequence_f 如下,初始化函数表省略其中部分代码。

static init_fnc_t init_sequence_f[] = {
  setup_mon_len,
  initf_malloc,
  initf_console_record,
  arch_cpu_init,    /* basic arch cpu dependent setup */
  initf_dm,
  arch_cpu_init_dm,
  mark_bootstage,   /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
  board_early_init_f,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
    defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
    defined(CONFIG_SPARC)
  timer_init,   /* initialize timer */
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
  board_postclk_init,
#endif
#if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
  get_clocks,
#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 */
  print_cpuinfo,    /* display cpu info (and speed) */
#if defined(CONFIG_DISPLAY_BOARDINFO)
  show_board_info,
#endif
  INIT_FUNC_WATCHDOG_INIT
  INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
  init_func_i2c,
#endif
  announce_dram_init,
  /* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
    defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
  dram_init,    /* configure available RAM banks */
#endif
  INIT_FUNC_WATCHDOG_RESET
  INIT_FUNC_WATCHDOG_RESET
  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,
  reserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
    defined(CONFIG_ARM)
  reserve_mmu,
#endif
  reserve_trace,
#if !defined(CONFIG_BLACKFIN)
  reserve_uboot,
#endif
#ifndef CONFIG_SPL_BUILD
  reserve_malloc,
  reserve_board,
#endif
  setup_machine,
  reserve_global_data,
  reserve_fdt,
  reserve_arch,
  reserve_stacks,
  setup_dram_config,
  show_dram_config,
  display_new_sp,
  INIT_FUNC_WATCHDOG_RESET
  reloc_fdt,
  setup_reloc,
  NULL,
};
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
这其中比较重要的一些初始化函数如下。

(1)setup_mon_len:设置gd的mon_len成员变量,也就是整个代码的长度;

(2)initf_malloc:设置gd中和malloc有关的成员变量;

(3) board_early_init_f:板子早期的一些初始化设置,imx6ull中用来初始化串口的IO配置(在​​board/freescale/mx6ullatk/mx6ullatk.c​​中实现);

int board_early_init_f(void)
{
  setup_iomux_uart();

  return 0;
}
1.
2.
3.
4.
5.
6.
(4)timer_init:初始化内核定时器,为uboot提供时钟节拍,在​​arch/arm/imx-common/timer.c​​中实现;

(5)board_postclk_init:imx6ull中用来设置VDDSOC电压,在​​arch/arm/cpu/armv7/mx6/soc.c​​中实现;

int board_postclk_init(void)
{
  /* NO LDO SOC on i.MX6SLL */
  if (is_cpu_type(MXC_CPU_MX6SLL))
    return 0;

  set_ldo_voltage(LDO_SOC, 1175); /* Set VDDSOC to 1.175V */

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
(6)get_clocks:获取一些时钟值,imx6ull获取了SD卡外设的时钟(sdhc_clk),在​​arch/arm/imx-common/speed.c​​中实现:

gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK);
1.
(7)env_init:初始化环境变量。

在​​common​​文件夹下,每个存储介质都提供了一个env_init()函数,有这么多文件:

这个时候就要来看看这个目录下的Makefile了,果然是使用配置项来控制:

在文件​​include/configs/mx6ullatk.h​​中找到配置定义:

/* FLASH and environment organization */
#define CONFIG_FSL_QSPI
#define CONFIG_ENV_IS_IN_MMC
1.
2.
3.
(8)init_baud_rate:初始化波特率,在​​common/board_f.c​​文件中定义:

static int init_baud_rate(void)
{
  gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
  return 0;
}
1.
2.
3.
4.
5.
可以看到,这里是从环境变量中寻找baudrate的值,如果没有,则默认是​​CONFIG_BAUDRATE=115200​​。

(9)serial_init:初始化串口通信设置,在文件​​drivers/serial/serial.c​​中定义:

/**
 * serial_init() - Initialize currently selected serial port
 *
 * This function initializes the currently selected serial port. This
 * usually involves setting up the registers of that particular port,
 * enabling clock and such. This function uses the get_current() call
 * to determine which port is selected.
 *
 * Returns 0 on success, negative on error.
 */
int serial_init(void)
{
  gd->flags |= GD_FLG_SERIAL_READY;
  return get_current()->start();
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
(10)console_init_f:初始化控制台,在文件​​common/console.c​​中定义:

/* Called before relocation - use serial functions */
int console_init_f(void)
{
  gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE
  if (getenv("silent") != NULL)
    gd->flags |= GD_FLG_SILENT;
#endif

  print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
(11)display_options:打印uboot版本信息和编译信息,在​​lib/display_options.c​​文件中定义。

int display_options (void)
{
#if defined(BUILD_TAG)
  printf ("\n\n%s, Build: %s\n\n", version_string, BUILD_TAG);
#else
  printf ("\n\n%s\n\n", version_string);
#endif
  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
version_string在​​cmd/version.c​​文件中定义:

const char __weak version_string[] = U_BOOT_VERSION_STRING;
1.
U_BOOT_VERSION_STRING在​​include/generated/version_autogenerated.h​​文件中定义,这个文件是构建时自动生成的,包含uboot版本信息,编译器版本信息,链接器版本信息:

#define PLAIN_VERSION "2016.03-gc37bca7-dirty"
#define U_BOOT_VERSION "U-Boot " PLAIN_VERSION
1.
2.
这行是uboot日志打印的第一行:

U-Boot 2016.03-g4e04879 (Jan 15 2020 - 17:28:42 +0800)
1.
(12)display_text_info:用来打印调试信息,需要相应的宏配置开启。

(13)print_cpuinfo:用来显示CPU信息和主频,在​​arch/arm/imx-common/cpu.c​​文件中定义,函数过多不放代码了,下面三行日志都是该函数打印:

CPU:   Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
CPU:   Industrial temperature grade (-40C to 105C) at 31C
Reset cause: POR
1.
2.
3.
(14)show_board_info:打印开发板信息,在​​common/board_info.c​​文件中定义。

/*
 * If the root node of the DTB has a "model" property, show it.
 * Then call checkboard().
 */
int show_board_info(void)
{
#if defined(CONFIG_OF_CONTROL) && !defined(CONFIG_CUSTOM_BOARDINFO)
  DECLARE_GLOBAL_DATA_PTR;
  const char *model;

  model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);

  if (model)
    printf("Model: %s\n", model);
#endif

  return checkboard();
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
​​checkboard​​​函数由不同开发板的bsp实现,imx6ull-atk的实现如下,在文件​​board/freescale/mx6ullatk/mx6ullatk.c​​:

int checkboard(void)
{
  puts("Board: i.MX6ULL ATK ALPHA\n");

  return 0;
}
1.
2.
3.
4.
5.
6.
(15)INIT_FUNC_WATCHDOG_RESET:初始化看门狗,imx6ull中该宏为空。

(16)init_func_i2c:在​​common/board_f.c​​文件中定义:

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
static int init_func_i2c(void)
{
  puts("I2C:   ");
#ifdef CONFIG_SYS_I2C
  i2c_init_all();
#else
  i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
  puts("ready\n");
  return 0;
}
#endif
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
i2c_init_all函数在​​drivers/i2c/i2c_core.c​​中定义:

/*
 * i2c_init_all():
 *
 * not longer needed, will deleted. Actual init the SPD_BUS
 * for compatibility.
 * i2c_adap[] must be initialized beforehead with function pointers and
 * data, including speed and slaveaddr.
 */
void i2c_init_all(void)
{
  i2c_init_board();
  i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM);
  return;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
打印出的日志为:

I2C:   ready
1.
(17)announce_dram_init:在​​common/board_f.c​​文件中定义:

static int announce_dram_init(void)
{
  puts("DRAM:  ");
  return 0;
}
1.
2.
3.
4.
5.
(18)dram_init:设置​​gd->ram_size​​的值,​并非真正初始化DRAM​,在​​board/freescale/mx6ullatk/mx6ullatk.c​​文件中定义:

int dram_init(void)
{
  gd->ram_size = imx_ddr_size();

  return 0;
}
1.
2.
3.
4.
5.
6.
(19)reserve_开头的函数,是进行代码重定位,将uboot搬运到DRAM中执行,并预留各种需要的空间。

(20)setup_machine:设置机器ID,老的linux内核中启动时会匹配machid,但新的linux内核中使用设备树,此值不使用。

static int setup_machine(void)
{
#ifdef CONFIG_MACH_TYPE
  gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif
  return 0;
}
1.
2.
3.
4.
5.
6.
7.
(21)setup_dram_config:设置DRAM配置,在文件​​common/board_f.c​​中:

static int setup_dram_config(void)
{
  /* Ram is board specific, so move it to board code ... */
  dram_init_banksize();

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
imx6ull未重新实现dram_init_banksize函数。

(22)show_dram_config:打印DRAM配置信息,在文件​​common/board_f.c​​中:

static int show_dram_config(void)
{
  unsigned long long size;

#ifdef CONFIG_NR_DRAM_BANKS
  int i;

  debug("\nRAM Configuration:\n");
  for (i = size = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
    size += gd->bd->bi_dram[i].size;
    debug("Bank #%d: %llx ", i,
          (unsigned long long)(gd->bd->bi_dram[i].start));
#ifdef DEBUG
    print_size(gd->bd->bi_dram[i].size, "\n");
#endif
  }
  debug("\nDRAM:  ");
#else
  size = gd->ram_size;
#endif

  print_size(size, "");
  board_add_ram_info(0);
  putc('\n');

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
uboot打印的日志为:

DRAM:  512 MiB
1.
(23)display_new_sp:打印新的sp指针,在文件​​common/board_f.c​​中,需要开启调试模式:

static int display_new_sp(void)
{
  debug("New Stack Pointer is: %08lx\n", gd->start_addr_sp);

  return 0;
}
1.
2.
3.
4.
5.
6.
(24)reloc_fdt:在文件​​common/board_f.c​​中

static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBED
  if (gd->flags & GD_FLG_SKIP_RELOC)
    return 0;
  if (gd->new_fdt) {
    memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
    gd->fdt_blob = gd->new_fdt;
  }
#endif

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
(25)setup_reloc:在文件​​common/board_f.c​​中。

六、relocate_code
relocate_code用于重定位代码,定义在文件​​arch/arm/lib/relocate.S​​中,代码如下:

/*
 * void relocate_code(addr_moni)
 *
 * This function relocates the monitor code.
 *
 * NOTE:
 * To prevent the code below from containing references with an R_ARM_ABS32
 * relocation record type, we never refer to linker-defined symbols directly.
 * Instead, we declare literals which contain their relative location with
 * respect to relocate_code, and at run time, add relocate_code back to them.
 */

ENTRY(relocate_code)
  ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
  subs  r4, r0, r1    /* r4 <- relocation offset */
  beq relocate_done   /* skip relocation */
  ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */

copy_loop:
  ldmia r1!, {r10-r11}    /* copy from source address [r1]    */
  stmia r0!, {r10-r11}    /* copy to   target address [r0]    */
  cmp r1, r2      /* until source end address [r2]    */
  blo copy_loop

  /*
   * fix .rel.dyn relocations
   */
  ldr r2, =__rel_dyn_start  /* r2 <- SRC &__rel_dyn_start */
  ldr r3, =__rel_dyn_end  /* r3 <- SRC &__rel_dyn_end */
fixloop:
  ldmia r2!, {r0-r1}    /* (r0,r1) <- (SRC location,fixup) */
  and r1, r1, #0xff
  cmp r1, #23     /* relative fixup? */
  bne fixnext

  /* relative fix: increase location by offset */
  add r0, r0, r4
  ldr r1, [r0]
  add r1, r1, r4
  str r1, [r0]
fixnext:
  cmp r2, r3
  blo fixloop

relocate_done:

#ifdef __XSCALE__
  /*
   * On xscale, icache must be invalidated and write buffers drained,
   * even with cache disabled - 4.2.7 of xscale core developer's manual
   */
  mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
  mcr p15, 0, r0, c7, c10, 4  /* drain write buffer */
#endif

  /* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
  mov pc, lr
#else
  bx  lr
#endif

ENDPROC(relocate_code)
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
此函数主要完成镜像拷贝和重定位,镜像地址从__image_copy_start开始,到__image_copy_end结束,拷贝的目标地址由参数传进来,也就是r0寄存器的值。重定位的原理此处不展开,值得用另外一篇文章专门讲述。

七、relocate_vectors
relocate_vectors函数用于重定位向量表,定义在文件​​arch/arm/lib/relocate.S​​中,代码如下:

/*
 * Default/weak exception vectors relocation routine
 *
 * This routine covers the standard ARM cases: normal (0x00000000),
 * high (0xffff0000) and VBAR. SoCs which do not comply with any of
 * the standard cases must provide their own, strong, version.
 */

  .section  .text.relocate_vectors,"ax",%progbits
  .weak   relocate_vectors

ENTRY(relocate_vectors)

#ifdef CONFIG_CPU_V7M
  /*
   * On ARMv7-M we only have to write the new vector address
   * to VTOR register.
   */
  ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
  ldr r1, =V7M_SCB_BASE
  str r0, [r1, V7M_SCB_VTOR]
#else
#ifdef CONFIG_HAS_VBAR
  /*
   * If the ARM processor has the security extensions,
   * use VBAR to relocate the exception vectors.
   */
  ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
  mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */
#else
  /*
   * Copy the relocated exception vectors to the
   * correct address
   * CP15 c1 V bit gives us the location of the vectors:
   * 0x00000000 or 0xFFFF0000.
   */
  ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
  mrc p15, 0, r2, c1, c0, 0 /* V bit (bit[13]) in CP15 c1 */
  ands  r2, r2, #(1 << 13)
  ldreq r1, =0x00000000   /* If V=0 */
  ldrne r1, =0xFFFF0000   /* If V=1 */
  ldmia r0!, {r2-r8,r10}
  stmia r1!, {r2-r8,r10}
  ldmia r0!, {r2-r8,r10}
  stmia r1!, {r2-r8,r10}
#endif
#endif
  bx  lr

ENDPROC(relocate_vectors)
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
该函数最终完成将uboot重定位后的首地址​​gd->relocaddr​​的,也就是新的向量表地址,写入到寄存器VBAR中,设置向量表偏移。

八、board_init_r
board_init_f函数中,会初始化一些外设和gd的成员变量,但并没有初始化所有的外设,还需要一些后续工作,这些工作就是由b

oard_init_r函数完成的,该函数定义在文件​​common/board_r.c​​中,代码如下:

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
  int i;
#endif

#ifdef CONFIG_AVR32
  mmu_init_r(dest_addr);
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
  gd = new_gd;
#endif

#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();
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
该函数中主要执行初始化序列 init_sequence_r,init_sequence_r是一个函数表,也定义在该文件中,部分代码如下:

/*
 * 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?
 */
init_fnc_t init_sequence_r[] = {
  initr_trace,
  initr_reloc,
  initr_caches,
  initr_reloc_global_data,
  initr_barrier,
  initr_malloc,
  initr_console_record,
  bootstage_relocate,
  initr_bootstage,
  board_init, /* Setup chipselects */
  stdio_init_tables,
  initr_serial,
  initr_announce,
  INIT_FUNC_WATCHDOG_RESET
  INIT_FUNC_WATCHDOG_RESET
  INIT_FUNC_WATCHDOG_RESET
  power_init_board,
  initr_flash,
  initr_nand,
  initr_mmc,
  initr_env,
  INIT_FUNC_WATCHDOG_RESET
  initr_secondary_cpu,
  INIT_FUNC_WATCHDOG_RESET
  stdio_add_devices,
  initr_jumptable,
  console_init_r,   /* fully init console as a device */
  INIT_FUNC_WATCHDOG_RESET
  interrupt_init,
  initr_enable_interrupts,
  initr_ethaddr,
  board_late_init,
  INIT_FUNC_WATCHDOG_RESET
  initr_net,
  run_main_loop,
};
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
其中比较重要的有。

(1)initr_caches:初始化cache,使能cache。

(2)board_init:板级初始化,包括74XX芯片、I2C、FEC、USB、QSPI等,在​​board/freescale/mx6ullatk/mx6ullatk.c​​中定义:

int board_init(void)
{
  /* Address of boot parameters */
  gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;

#ifdef CONFIG_SYS_I2C_MXC
  setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1);
#endif

#ifdef  CONFIG_FEC_MXC
  setup_fec(CONFIG_FEC_ENET_DEV);
#endif

#ifdef CONFIG_USB_EHCI_MX6
  setup_usb();
#endif

#ifdef CONFIG_FSL_QSPI
  board_qspi_init();
#endif

#ifdef CONFIG_NAND_MXS
  setup_gpmi_nand();
#endif

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
(3)initr_mmc:初始化emmc,在​​common/board_r.c​​中定义:

#ifdef CONFIG_GENERIC_MMC
static int initr_mmc(void)
{
  puts("MMC:   ");
  mmc_initialize(gd->bd);
  return 0;
}
#endif
1.
2.
3.
4.
5.
6.
7.
8.
对应在uboot启动日志中打印这行:

(4)initr_env:初始化环境变量。

(5)stdio_add_devices:各种输入输出的设备初始还,比如LCD drvier,在文件​​common/stdio.c​​中定义,其中会调用到drv_video_init,在该函数中初始化LCD,打印日志:

(6)console_init_r,控制台初始化,在文件​​common/console.c​​中定义,该函数的最后会调用 print_pre_console_buffer函数来打印当前的控制台设备,对应的日志如下:

(7)interrupt_init和initr_enable_interrupts,初始化中断并使能中断。

(8)initr_ethaddr:初始化网络地址,获取MAC地址,读取环境变量​​ethaddr​​的值。

(9)board_late_init:板子后续初始化,此函数定义在文件 ​​board/freescale/mx6ullatk/mx6ullatk.c​​中,代码如下:

int board_late_init(void)
{
#ifdef CONFIG_CMD_BMODE
  add_board_boot_modes(board_boot_modes);
#endif

#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
  setenv("board_name", "EVK");

  if (is_mx6ull_9x9_evk())
    setenv("board_rev", "9X9");
  else
    setenv("board_rev", "14X14");
#endif

#ifdef CONFIG_ENV_IS_IN_MMC
  board_late_mmc_env_init();
#endif

  set_wdog_reset((struct wdog_regs *)WDOG1_BASE_ADDR);

  return 0;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
其中,如果环境变量定义在MMC中,board_late_mmc_env_init会初始化MMC,切换到正在使用的emmc设备,如下,在​​board/freescale/common/mmc.c​​文件中定义:

void board_late_mmc_env_init(void)
{
  char cmd[32];
  char mmcblk[32];
  u32 dev_no = mmc_get_env_dev();

  if (!check_mmc_autodetect())
    return;

  setenv_ulong("mmcdev", dev_no);

  /* Set mmcblk env */
  sprintf(mmcblk, "/dev/mmcblk%dp2 rootwait rw",
    mmc_map_to_kernel_blk(dev_no));
  setenv("mmcroot", mmcblk);

  sprintf(cmd, "mmc dev %d", dev_no);
  run_command(cmd, 0);
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
对应uboot的启动日志为:

(10)initr_net:初始化网络设备,该函数定义在​​common/board_r.c​​文件中,代码如下:

#ifdef CONFIG_CMD_NET
static int initr_net(void)
{
  puts("Net:   ");
  eth_initialize();
#if defined(CONFIG_RESET_PHY_R)
  debug("Reset Ethernet PHY\n");
  reset_phy();
#endif
  return 0;
}
#endif
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
其中 eth_initialize函数会调用到 board_eth_init,定义在文件​​board/freescale/mx6ullatk/mx6ullatk.c​​中,代码如下:

int board_eth_init(bd_t *bis)
{
  setup_iomux_fec(CONFIG_FEC_ENET_DEV);
  board_eth_hard_reset(CONFIG_FEC_ENET_DEV);

  return fecmxc_initialize_multi(bis, CONFIG_FEC_ENET_DEV,
               CONFIG_FEC_MXC_PHYADDR, IMX_FEC_BASE);
}
1.
2.
3.
4.
5.
6.
7.
8.
对应uboot的启动日志如下:

最后,一切就绪,调用run_main_loop,uboot启动!

九、一切就绪,uboot启动!
uboot启动后会自动倒计时,如果按下回车键则进入命令行模式,如果未按下则自动启动Linux内核,该功能是由run_main_loop完成的。

run_main_loop函数定义在文件​​common/board_r.c​​中,代码如下:

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;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
至于main_loop如何处理各种命令,我们下篇文章接着讲述。
-----------------------------------
©著作权归作者所有:来自51CTO博客作者MCUlover666的原创作品,请联系作者获取转载授权,否则将追究法律责任
uboot研读笔记 | 14 - uboot启动流程分析(2016.03版本)
https://blog.51cto.com/u_13640625/5148687

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值