共计 8479 个字符,预计需要花费 22 分钟才能阅读完成。
(3)U-Boot 启动流程详解
- lowlevel_init 函数详解
函数 lowlevel_init 在文件 arch/arm/cpu/armv7/lowlevel_init.S 中定义
#include <asm-offsets.h>
#include <config.h>
#include <linux/linkage.h>
ENTRY(lowlevel_init)
/*
* Setup a temporary stack. Global data is not available yet.
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR /* 宏定义在 include/configs/mx6ullevk.h sp 指向 0X91FF00,这属于 IMX6UL/IMX6ULL 的外部 ram*/
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 /* GD_SIZE = 248,定义在 generic-asm-offsets.h*/
bic sp, sp, #7 /* 八字节对齐 */
mov r9, sp /*SP = 0X0091FF00-248=0X0091FE08*/
#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 /* 跳转到 s_init,实现初始化 */
pop {ip, pc} /* 弹出堆栈,并将 lr 的值返回给 PC*/
ENDPROC(lowlevel_init)
- s_init 函数详解
上一节的初始化跳转到 s_init,该函数在定义在 arch/arm/cpu/armv7/mx6/soc.c 的 808 行。
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; // 判断到 CPU 类型是 MXC_CPU_MX6ULL,返回
/* 省略。。。*/
}
函数返回到 cpu_init_crit,而后又返回到 save_boot_params_ret,继续执行 _main 函数。
- _main 函数详解
_main 函数在文件 arch/arm/lib/crt0.S 中
/*
* entry point of crt0 sequence
*/
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 设置 sp = 0X0091FF00
#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 /* 此函数有一个参数,参数为 r0 中的值, 留出晚期的 malloc 内存区域和 gd 内存区域,该函数返回 top=0X0091FA00。该函数定义在 common/init/board_init.c*/
mov sp, r0 /* 将 board_init_f_alloc_reserve 返回值 0X0091FA00 放入堆栈指针 */
/* set up gd here, outside any C code */
mov r9, r0 /* r9 寄存器寄存着全局变量 gd 的地址 */
bl board_init_f_init_reserve /* 此函数用于初始化 gd,同时设置 gd->malloc_base=0X0091FB00,这个也就是 early malloc 的起始地址。该函数定义在 common/init/board_init.c -68*/
mov r0, #0
bl board_init_f /* 初始化 DDR,定时器,实现代码拷贝等等, 定义在 common/board_f.c -1037*/
#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.
*/
/*
从新设置环境 (sp 和 gd)、获取 gd->start_addr_sp 的值赋给 sp,在函数 board_init_f
中会初始化 gd 的所有成员变量,其中 gd->start_addr_sp=0X9EF44E90,所以这里相当于设置 sp=gd->start_addr_sp=0X9EF44E90。0X9EF44E90 是 DDR 中的地址,阐明新的 sp 和 gd 将会寄存到 DDR 中,而不是外部的 RAM 了。GD_START_ADDR_SP=64,*/
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 GD_BD=0,计算出新的 gd 的地位,并赋给 r9*/
sub r9, r9, #GD_SIZE /* new GD is below bd 新的 gd 在 bd 上面,所以 r9 减去 gd 的大小就是新的 gd 的地位,获取到新的 gd 的地位当前赋值给 r9。*/
adr lr, here /* 设置 lr 寄存器为 here,这样前面执行其余函数返回的时候就返回到了第 122 行的 here 地位处 */
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off GD_RELOC_OFF=68*/
/*lr 寄存器的值加上 r0 寄存器的值,从新赋值给 lr 寄存器。因为接下来要重定位代码,也就是把代码拷贝到新的中央去 ( 当初的 uboot 寄存的起始地址为 0X87800000,上面要
将 uboot 拷贝到 DDR 最初面的地址空间出,将 0X87800000 开始的内存空进去 ),其中就包含
here,因而 lr 中的 here 要应用重定位后的地位。*/
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 读取 gd->relocaddr 的值赋给 r0 寄存器,此时 r0 寄存器就保留着 uboot 要拷贝的目标地址,为 0X9FF47000。GD_RELOCADDR=48*/
b relocate_code /* 此函数负责将 uboot 拷贝到新的中央去,此函数定义在文件 arch/arm/lib/relocate.S -67*/
here:
/*
* now relocate vectors
*/
bl relocate_vectors /* 对中断向量表做重定位, arch/arm/lib/relocate.S
-16*/
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here arch/arm/cpu/armv7/start.S -77*/
#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 /*147-159, 革除 BSS 段 */
#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 设置 board_init_r 第一个参数 */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr 设置 board_init_r 第二个参数 */
/* 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! common/board_r.c -997*/
#endif
/* we should not return here. */
#endif
ENDPROC(_main)
- board_init_f 函数详解
board_init_f 函数次要有两个工作:
①、初始化一系列外设,比方串口、定时器,或者打印一些音讯等。
②、初始化 gd 的各个成员变量,uboot 会将本人重定位到 DRAM 最初面的地址区域,也就是将本人拷贝到 DRAM 最初面的内存区域中。这么做的目标是给 Linux 腾出空间,避免 Linux kernel 笼罩掉 uboot,将 DRAM 后面的区域残缺的空进去。在拷贝之前必定要给 uboot 各局部调配好内存地位和大小,比方 gd 应该寄存到哪个地位,malloc 内存池应该寄存到哪个地位等等。这些信息都保留在 gd 的成员变量中,因而要对 gd 的这些成员变量做初始化。最终造成一个残缺的内存“调配图”,在前面重定位 uboot 的时候就会用到这个内存“调配图”。
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;
/*initcall_run_list() 函数负责顺次调用 init_sequence_f 中的初始化函数,这个函数我只能说是稍微了解,要让我写,我是写不出的 */
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();}
用于初始化调用函数的原型如下,
int initcall_run_list(const init_fnc_t init_sequence[])
{
const init_fnc_t *init_fnc_ptr;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
unsigned long reloc_ofs = 0;
int ret;
if (gd->flags & GD_FLG_RELOC)
reloc_ofs = gd->reloc_off;
#ifdef CONFIG_EFI_APP
reloc_ofs = (unsigned long)image_base;
#endif
debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs);
if (gd->flags & GD_FLG_RELOC)
debug("(relocated to %p)\n", (char *)*init_fnc_ptr);
else
debug("\n");
ret = (*init_fnc_ptr)();
if (ret) {printf("initcall sequence %p failed at call %p (err=%d)\n",
init_sequence,
(char *)*init_fnc_ptr - reloc_ofs, ret);
return -1;
}
}
return 0;
}
被调用的函数列表中有太多行,很多宏定义决定了某些函数是否调用,把这个去除后的函数如下:
static init_fnc_t init_sequence_f[] = {
setup_mon_len, /* 设置 gd 的 mon_len 成员变量 */
initf_malloc, /* 初始化 malloc 内存池大小和 malloc 指针 */
initf_console_record, /* 未定义,返回 0 */
arch_cpu_init, /* basic arch cpu dependent setup */
initf_dm, /* 驱动模型的一些初始化, 未定义 */
arch_cpu_init_dm, /* 未定义 */
mark_bootstage, /* need timer, go after init dm 做标记 board_init_f*/
board_early_init_f, /*I.MX6ULL 用来初始化串口的 IO 配置 */
timer_init, /* initialize timer */
board_postclk_init, /* I.MX6ULL to Set VDDSOC to 1.175V*/
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 输入版本信息 UBoot 日期和工夫 */
display_text_info, /* show debugging info if required 若开启 debug,会输入 text_base、bss_start、bss_end*/
print_cpuinfo, /* display cpu info (and speed) */
show_board_info,
INIT_FUNC_WATCHDOG_INIT /* 空函数 */
INIT_FUNC_WATCHDOG_RESET /* 空函数 */
init_func_i2c,
announce_dram_init,
dram_init, /* configure available RAM banks 设置 gd->ram_size */
post_init_f, /* 上电复位自检 */
testdram, /* 空函数 */
setup_dest_addr, /* 设置目标地址,设置 gd->ram_size,gd->ram_top,gd->relocaddr 这三个的值 */
reserve_round_4k, /*reserve_round_4k 函 数 用 于 对 gd->relocaddr 做 4KB 对 齐,因 为 gd->relocaddr=0XA0000000,曾经是 4K 对齐了,所以调整后不变。*/
reserve_mmu, /* 留出 MMU 的 TLB 表的地位,调配 MMU 的 TLB 表内存当前会对 gd->relocaddr 做 64K 字节对齐 */
reserve_trace, /* 留出跟踪调试的内存,I.MX6ULL 没有用到 */
reserve_uboot, /* 留出重定位后的 uboot 所占用的内存区域,uboot 所占用大小由 gd->mon_len 所指定,留出 uboot 的空间当前还要对 gd->relocaddr 做 4K 字节对齐,并且从新设置 gd->start_addr_sp*/
reserve_malloc,
reserve_board,
setup_machine, /* 老版本应用,新设施没有用 */
reserve_global_data, /* 保留出 gd_t 的内存区域 */
reserve_fdt, /* 留出设施树相干的内存区域 */
reserve_arch, /* 空函数 */
reserve_stacks, /* 留出栈空间,16 字节对齐 */
setup_dram_config, /* 设置 DRAM,通知 linux DRAM 的起始地址和大小 */
show_dram_config, /* 显示 DRAM 信息 */
display_new_sp, /* 开启 debug 后,显示 gd->start_addr_sp*/
reloc_fdt, /* 重定位 fdt,没有用到 */
setup_reloc, /* 设置 gd 的其余一些成员变量,供前面重定位的时候应用,并且将以前的 gd 拷贝到 gd->new_gd 处。*/
}
正文完