首页 > 其他分享 >【IMX6ULL学习笔记】四、 U-BOOT启动流程

【IMX6ULL学习笔记】四、 U-BOOT启动流程

时间:2023-02-17 15:22:19浏览次数:34  
标签:函数 boot BOOT 笔记 BOOTM start init gd IMX6ULL

一、链接脚本 u-boot.lds 详解
要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接脚本为 arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds 文件。
打开 u-boot.lds,内容如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
(.__image_copy_start)
(.vectors)
arch/arm/cpu/armv7/start.o (.text
)
(.text)
}
. = ALIGN(4);
.rodata : { (SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata))) }
. = ALIGN(4);
.data : {
(.data)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(
(SORT(.u_boot_list*)));
}
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
(.rel)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
(.__end)
}
_image_binary_end = .;
. = ALIGN(4096);
.mmutable : {
(.mmutable)
}
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(
(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
(.bss)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(
(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { (.dynstr) }
.dynamic : { (.dynamic) }
.plt : { (.plt) }
.interp : { (.interp) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { (.gnu) }
.ARM.exidx : { (.ARM.exidx) }
.gnu.linkonce.armexidx : { (.gnu.linkonce.armexidx.) }
}

第 3 行 ENTRY(_start) : 代码入口为_start
第 10 行 *(.__image_copy_start) : uboot 拷贝起始地址
第 11 行 (.vectors) : 中断向量表
第 12 行 arch/arm/cpu/armv7/start.o (.text
) : start.S
第 13 行 (.text) : 代码段
第 28 行 .image_copy_end : U-BOOT拷贝结束地址
第 32 行 .rel_dyn_start : .rel.dyn 段起始地址
第 39 行 .rel_dyn_end : .rel.dyn 段结束地址
第 47 行 _image_binary_end = . : 镜像结束地址
第 52 行 .bss_start : .bss 段起始地址
第 61 行 .bss_end : .bss 段结束地址

_start 在文件 arch/arm/lib/vectors.S 中有定义:

include <config.h>

/*

  • Symbol _start is referenced elsewhere, so make it global
    /
    .globl _start
    /
  • Vectors have their own section so linker script can map them easily
    /
    .section ".vectors", "ax"
    /
  • 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

_start 后面就是中断向量表,从源码中的“.section ".vectors","ax”可以得到,此代码存放在.vectors 段里面。

打开 u-boot.map,找到如图所示位置:

u-boot.map 是 uboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,从图中可以看到__image_copy_start 为 0X87800000,而.text 的起始地址也是0X87800000。u-boot.lds 中 vectors 段保存中断向量表,从图中可以看出,vectors 段的起始地址也是 0X87800000,说明 uboot 的起始地址就是0X87800000。

第 10 行 *(.__image_copy_start) : 镜像起始地址 -- 0X87800000
第 11 行 (.vectors) : 中断向量表 -- 0X87800000
第 12 行 arch/arm/cpu/armv7/start.o (.text
)
:将 arch/arm/cpu/armv7/start.s
编译出来的代码放到中断向量表后面。
第 13 行 (.text) :其他的代码段就放到这里

二、U-Boot 启动流程详解
①reset 函数源码详解(汇编):
u-boot.lds 中我定义入口点是 arch/arm/lib/vectors.S 文件中的_start,首先会跳转到 reset 复位函数中去,然后设置向量表:
/*

  • 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

reset 函数在 arch/arm/cpu/armv7/start.S 里面,代码如下:

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

函数完成以下功能:
1.将处理器设置为 SVC 模式,并且关闭 FIQ 和 IRQ.
2.设置中断向量表可重定位,并设置中断向量偏移
3.初始化CP15
4.调用 cpu_init_crit -> lowlevel_init 和 _main

②lowlevel_init 函数源码详解(汇编):
函数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.设置 SP 指针、R9 寄存器
2.调用 s_init (空函数)

③_main 函数源码详解(汇编):
_main 函数定义在文件 arch/arm/lib/crt0.S 中,函数内容如下:
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
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 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
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

ENDPROC(_main)

在_main 函数里面调用board_init_f_alloc_reserve函数,此函数定义在文件 common/init/board_init.c 中,内容如下:
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; 

}

board_init_f_alloc_reserve 主要是留出早期的 malloc 和 gd 内存区域,其中CONFIG_SYS_MALLOC_F_LEN=0X400( 文件include/generated/autoconf.h 中定义 ) ,sizeof(struct global_data)=248(GD_SIZE 值),完成以后的内存分布如图 32.2.4.1 所示:

_main函数还调用board_init_f、relocate_code、relocate_vectors 和 board_init_r 这 4 个函数。

④board_init_f 函数源码详解(汇编、C):
board_init_f 函数主要有两个工作:
①、初始化一系列外设,比如DDR、串口、定时器,或者打印一些消息等。
②、初始化 gd 的各个成员变量,用于重定位 u-boot。u-boot 会将自己重定位到 DRAM 最后面的地址区域(u-boot 原先运行在 DDR 的 0x87800000 地址处),也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linux kernel 覆盖掉 u-boot,将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 u-boot 各部分分配好内存位置和大小,比如 gd 应该存放到哪个位置,malloc 内存池应该存放到哪个位置等等。这些信息都保存在 gd 的成员变量中,因此要对 gd 的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位 u-boot 的时候就会用到这个内存“分配图”。
函数定义在common/board_f.c 中:
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

/* Light up LED1 */
imx6_light_up_led1();

}

第 22 行:初始化 gd->flags=boot_flags=0。
第 23 行:设置 gd->have_console=0。
第 25 行:重点!调用initcall_run_list来运行初始化序列init_sequence_f 里的一些列函数,init_sequence_f 里包含了一系列初始化函数,定义在文件common/board_f.c 中,init_sequence_f 里面有大量的条件编译代码,去掉条件编译以后的 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/
board_early_init_f,
timer_init, /
initialize timer/
board_postclk_init,
get_clocks,
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)/
show_board_info,
INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET
init_func_i2c,
announce_dram_init,
/
TODO: unify all these dram functions? /
dram_init, /
configure available RAM banks /
post_init_f,
INIT_FUNC_WATCHDOG_RESET
testdram,
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,
reserve_mmu,
reserve_trace,
reserve_uboot,
reserve_malloc,
reserve_board,
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,
};

第 2 行:setup_mon_len 函数设置 gd 的 mon_len 成员变量,此处为__bss_end -_start,也就是整个代码的长度。
第 3 行:initf_malloc 函数初始化 gd 中跟 malloc 有关的成员变量,比如 malloc_limit,此函数会设置 gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN=0X400。malloc_limit 表示 malloc内存池大小。
第 5 行:arch_cpu_init 函数。
第 6 行:initf_dm 函数,驱动模型的一些初始化。
第 7 行:arch_cpu_init_dm 函数未实现。
第 8 行:mark_bootstage 函数应该是和啥标记有关的。
第 9 行:board_early_init_f 函数,板子相关的早期的一些初始化设置,I.MX6ULL 用来初始化串口的 IO 配置。
第 10 行:timer_init,初始化定时器,Cortex-A7 内核有一个定时器,这里初始化的就是 Cortex-A 内核的那个定时器。通过这个定时器来为 uboot 提供时间。
第 11 行:board_postclk_init,对于 I.MX6ULL 来说是设置 VDDSOC 电压。
第 12 行:get_clocks 函数用于获取一些时钟值,I.MX6ULL 获取的是 sdhc_clk 时钟,也就是 SD 卡外设的时钟。
第 13 行:env_init 函数是和环境变量有关的,设置 gd 的成员变量 env_addr,也就是环境变量的保存地址。
第 14 行:init_baud_rate 函数用于初始化波特率,根据环境变量 baudrate 来初始化 gd->baudrate。
第 15 行:serial_init,初始化串口。
第 16 行:console_init_f,设置 gd->have_console 为 1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。
第 17 行:display_options,通过串口输出一些信息,如图所示:

第 18 行:display_text_info,打印一些文本信息,如果开启 UBOOT 的 DEBUG 功能的话就会输出 text_base、bss_start、bss_end,形式如下:
debug("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",text_base, bss_start, bss_end);

第 19 行:print_cpuinfo 函数用于打印 CPU 信息

第 20 行:show_board_info 函数用于打印板子信息

第 21 行:INIT_FUNC_WATCHDOG_INIT,初始化看门狗,对于 I.MX6ULL 来说是空函数
第 22 行:INIT_FUNC_WATCHDOG_RESET,复位看门狗,对于 I.MX6ULL 来说是空函数
第 23 行:init_func_i2c 函数用于初始化 I2C,初始化完成以后会输出如图 32.2.5.5 所示信息:

第 24 行:announce_dram_init,此函数很简单,就是输出字符串“DRAM:”
第 26 行:dram_init,并非真正的初始化 DDR,只是设置 gd->ram_size 的值,对于正点原子 I.MX6ULL 开发板 EMMC 版本核心板来说就是 512MB。
第 27 行:post_init_f,用来完成一些测试,初始化 gd->post_init_f_time
第 29 行:testdram,测试 DRAM,空函数。
第 44 行:setup_dest_addr函数,设置目的地址,设置gd->ram_size,gd->ram_top,gd->relocaddr 这三个的值。通过串口打印出来如下所示:
gd->ram_size = 0X20000000 //ram 大小为 0X20000000=512MB
gd->ram_top = 0XA0000000 //ram 最高地址为0X80000000+0X20000000=0XA0000000
gd->relocaddr = 0XA0000000 //重定位后最高地址为 0XA0000000

第 45 行:reserve_round_4k 函数用于对 gd->relocaddr 做 4KB 对 齐 , 因 为gd->relocaddr=0XA0000000,已经是 4K 对齐了,所以调整后不变。
第 46 行:reserve_mmu,留出 MMU 的 TLB 表的位置,分配 MMU 的 TLB 表内存以后会对 gd->relocaddr 做 64K 字节对齐。完成以后 gd->arch.tlb_size、gd->arch.tlb_addr 和 gd->relocaddr 值为:
gd->arch.tlb_size= 0X4000 //MMU 的 TLB 表大小
gd->arch.tlb_addr=0X9FFF0000 //MMU 的 TLB 表起始地址,64KB 对齐以后
gd->relocaddr=0X9FFF0000 //relocaddr 地址

第 47 行:reserve_trace 函数,留出跟踪调试的内存,I.MX6ULL 没有用到。
第 48 行:reserve_uboot,留出重定位后的 uboot 所占用的内存区域,uboot 所占用大小由 gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 做 4K 字节对齐,并且重新设置 gd->start_addr_sp,结果如下:
gd->mon_len = 0XA8EF4
gd->start_addr_sp = 0X9FF47000
gd->relocaddr = 0X9FF47000

第 49 行:reserve_malloc,留出 malloc 区域,调整 gd->start_addr_sp 位置,malloc 区域由宏 TOTAL_MALLOC_LEN 定义,值如下:
TOTAL_MALLOC_LEN=0X1002000
gd->start_addr_sp=0X9EF45000 //0X9FF47000-16MB-8KB=0X9EF45000

第 50 行:reserve_board 函数,留出板子 bd 所占内存区,bd 是结构体bd_t,bd_t 大小为 80 字节,值如下:
gd->start_addr_sp=0X9EF44FB0
gd->bd=0X9EF44FB0

第 51 行:setup_machine,设置机器 ID,linux 启动的时候会和这个机器 ID 匹配,如果匹配的话 linux 就会启动正常。这是以前老版本的 uboot 和 linux 使用的,新版本使用设备树了,因此此函数无效。
第 52 行:reserve_global_data 函数,保留出 gd_t 的内存区域,gd_t 结构体大小为 248B,结果如下:
gd->start_addr_sp=0X9EF44EB8 //0X9EF44FB0-248=0X9EF44EB8
gd->new_gd=0X9EF44EB8

第 53 行:reserve_fdt,留出设备树相关的内存区域。
第 54 行:reserve_arch 是个空函数。
第 55 行:reserve_stacks,留出栈空间,先对 gd->start_addr_sp 减去16,然后做 16 字节对其。如果使能 IRQ 的话还要留出 IRQ 相应的内存,具体工作是由 arch/arm/lib/stack.c 文件中的函数 arch_reserve_stacks 完成。
第 56 行:setup_dram_config 函数设置 dram 信息,就是设置 gd->bd->bi_dram[0].start 和 gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小。
第 57 行:show_dram_config 函数,用于显示 DRAM 的配置。
第 58 行:display_new_sp函数,显示新的sp位置,即 gd->start_addr_sp,不
过要定义宏 DEBUG 。
第 60 行:reloc_fdt 函数用于重定位 fdt,没有用到。
第 61 行:setup_reloc,设置 gd 的其他一些成员变量,供后面重定位的时候使用,并且将以前的 gd 拷贝到 gd->new_gd 处。需要使能 DEBUG 才能看到相应的信息输出
board_init_f 函数执行完成,最终的内存分配如图 32.2.5.16 所示:

⑤relocate_code 函数源码详解:
relocate_code 为代码重定位函数,此函数负责将 uboot 拷贝到新的地方去,此函数定义在文件 arch/arm/lib/relocate.S 中.

⑥relocate_vectors 函数源码详解:
函数 relocate_vectors 对中断向量表做重定位,此函数定义在文件 arch/arm/lib/relocate.S 中.

⑦board_init_r 函数源码详解:
board_init_f 函数,在此函数里面会调用一系列的函数来初始化一些外设和 gd 的成员变量。但是 board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数 board_init_r 来完成的,board_init_r 函数定义在文件common/board_r.c中.
991 void board_init_r(gd_t new_gd, ulong dest_addr)
992 {
993 #ifdef CONFIG_NEEDS_MANUAL_RELOC
994 int i;
995 #endif
996
997 #ifdef CONFIG_AVR32
998 mmu_init_r(dest_addr);
999 #endif
1000
1001 #if !defined(CONFIG_X86) && !defined(CONFIG_ARM)
&& !defined(CONFIG_ARM64)
1002 gd = new_gd;
1003 #endif
1004
1005 #ifdef CONFIG_NEEDS_MANUAL_RELOC
1006 for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
1007 init_sequence_r[i] += gd->reloc_off;
1008 #endif
1009
1010 if (initcall_run_list(init_sequence_r))
1011 hang();
1012
1013 /
NOTREACHED - run_main_loop() does not return */
1014 hang();
1015 }

第 1010 行调用 initcall_run_list 函数来执行初始化序列 init_sequence_r,init_sequence_r 是一个函数集合,init_sequence_r 也定义在文件 common/board_r.c 中,去掉条件编译以后的 init_sequence_r 定义如下:
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,
INIT_FUNC_WATCHDOG_RESET
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
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
initr_net,
INIT_FUNC_WATCHDOG_RESET
run_main_loop,
};

第 2 行: initr_trace 函数,如果定义了宏 CONFIG_TRACE 的话就会调用函数
trace_init,初始化和调试跟踪有关的内容。
第 3 行:initr_reloc 函数用于设置 gd->flags,标记重定位完成。
第 4 行:initr_caches 函数用于初始化 cache,使能 cache。
第 5 行:initr_reloc_global_data 函数,初始化重定位后 gd 的一些成员变
量。
第 6 行:initr_barrier 函数,I.MX6ULL 未用到。
第 7 行:initr_malloc 函数,初始化 malloc。
第 8 行:initr_console_record 函数,初始化控制台相关的内容,I.MX6ULL 未
用到,空函数。
第 9 行:bootstage_relocate 函数,启动状态重定位。
第 10 行:initr_bootstage 函数,初始化 bootstage 什么的。
第 11 行:board_init 函数,板级初始化,包括 74XX 芯片,I2C、FEC、USB 和
QSPI 等。这里执行的是 mx6ull_alientek_emmc.c 文件中的
board_init 函数。
第 12 行:stdio_init_tables 函数,stdio 相关初始化。
第 13 行:initr_serial 函数,初始化串口。
第 14 行:initr_announce 函数,与调试有关,通知已经在 RAM 中运行。
第 18 行:power_init_board 函数,初始化电源芯片,正点原子的 I.MX6ULL 开
发板没有用到。
第 19 行:initr_flash 函数,对于 I.MX6ULL,没有定义宏
CONFIG_SYS_NO_FLASH 的话函数 initr_flash 才有效。但是
mx6_common.h 中定义了CONFIG_SYS_NO_FLASH,所以此函数无效。
第 21 行:initr_nand 函数,初始化 NAND,如果使用 NAND 版本核心板的话就会
初始化NAND。
第 22 行:initr_mmc 函数,初始化 EMMC,如果使用 EMMC 版本核心板的话就会
初始化EMMC。
第 23 行:initr_env 函数,初始化环境变量。
第 25 行:initr_secondary_cpu 函数,初始化其他 CPU 核,I.MX6ULL 只有一
个核,因此此函数没用。
第 27 行:stdio_add_devices 函数,各种输入输出设备的初始化,如 LCD
driver,I.MX6ULL 使用 drv_video_init 函数初始化 LCD。
第 28 行:initr_jumptable 函数,初始化跳转表。
第 29 行:console_init_r 函 数 , 控 制 台 初 始 化 , 初 始 化 完 成
以 后 此 函 数 会 调 用stdio_print_current_devices 函数来打
印出当前的控制台设备。
第 31 行:interrupt_init 函数,初始化中断。
第 32 行:initr_enable_interrupts 函数,使能中断。
第 33 行:initr_ethaddr 函数,初始化网络地址,也就是获取 MAC 地址。读取环
境变量“ethaddr”的值。
第 34 行:board_late_init 函数,板子后续初始化,定义在
mx6ull_alientek_emmc.c中,如果环境变量存储在 EMMC 或者 SD 卡
中的此函数会调用board_late_mmc_env_init 函数初始化 EMMC/SD。
会切换到正在时候用的 emmc 设备,代码如图所示:

图中第 46 行和第 47 行就是运行“mmc dev xx”命令,用于切换到正在使用的
EMMC 设备
第 38 行:initr_net 函 数 , 初 始 化 网 络 设 备 , 函 数 调 用 顺 序
为 :initr_net->eth_initialize->board_eth_init().
第 40 行:run_main_loop 行,主循环,处理命令。

⑧run_main_loop 函数源码详解:
uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 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;

}

“for(;

标签:函数,boot,BOOT,笔记,BOOTM,start,init,gd,IMX6ULL
From: https://www.cnblogs.com/KuDianWanJia/p/17130274.html

相关文章

  • 【IMX6ULL学习笔记】五、U-BOOT移植与解析
    一、移植自定义开发板流程1、添加mx6ull_kodo_emmc_defconfig配置文件(.config)在/configs目录下,复制mx6ull_14x14_evk_emmc_defconfig文件,重命名为mx6ull_kodo_emm......
  • SpringBoot 整合 RabbitMQ
    SpringBoot整合RabbitMQ生产者application.yml#配置RabbitMQ的基本信息spring:rabbitmq:#iphost:192.168.36.100#usernameuse......
  • 组合数学课程笔记(一):框架构建
    组合数学的严格定义是非常困难的,其设计的内容广泛,分类困难,体系性较弱。不过,我们可以把组合数学按照问题、工具、对象三种方法进行分类,例如图论,就是按照研究对象分出的内容......
  • JAVA 学习笔记(五)
      子类通过方法的重写机制可以隐藏继承父类的方法,把父类的状态和行为改变为子类自己的状态和行为.假如父类中有一个方法myMethod(),一旦子类重写了超类的方法myMethod......
  • SpringBoot
    SpringBoot2核心技术与响应式编程SpringBoot2核心技术SpringBoot2基础入门Spring能做什么?Spring的生态覆盖了:web开发数据访问安全控制分布式消息服务移动......
  • 多项式相关算法学习笔记(持续更新)
    第一次用博客园写学习笔记,写的不好请见谅~欢迎各位OIer在评论区说一下自己对这篇博客的建议!有关多项式的基本概念对于求和式\(\suma_nx^n\),如果是有限项相加,称为多项......
  • jeecgboot新建子模块问题
    jeecgboot新建子模块问题​ 新建子模块后Controller接口在ShiroConfig中配置了不需要权限访问,但是访问时仍然报错:token失效​ 可能原因:新建子模块的包名不是org.je......
  • dp学习笔记
    目录斜率优化dpH.仓库建设思路代码J.土地购买思路:代码斜率优化dpH.仓库建设思路很容易想暴力,因为只能往后送物资,从后往前计算dp[i]为在i这里建造仓库且i~n都有地可去......
  • SpringBoot配置与打包基础
    本篇主要记录SpringBoot使用的基础配置SpringBootMaven配置SpringBootmaven依赖关系我们创建springboot项目后,会发现项目的pom文件都会继承自spring-boot-starter-p......
  • Feign远程调用结合fallback(Springboot包扫描)
    Feign远程调用结合fallback(Springboot包扫描)微服务项目中,各微服务模块间互相调用,通常使用HTTP协议调用,为了优雅和快速调用服务,通常使用HTTP客户端,如Feign为各服务编写......