欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

u-boot启动流程是怎样的

发布时间:2016-07-08 15:48  回复:0  查看:4039   最后回复:2016-07-08 15:48  
本文将结合u-boot“board—>machine—>arch—>cpu”框架, 教你 学习u-boot   中平台相关部分的启动流程。并通过对启动流程的简单分析,掌握u-boot移植的基本方法。
平台相关部分的启动流程分析
本文先不涉及 u-boot 和平台相关的 Kconfig/Makefile 部分,以 ARM64 为例,假定 u-boot 首先从arch/arm/cpu/armv8/start.S _start 接口开始执行。因此我们从 _start 开始分析。
3 :后续 u-boot 的移植指南中,会介绍该假定的依据。
4 :启动流程分析的过程中,我们会重点解释、归纳出代码中以 CONFIG_ 为前缀的配置项,后续 u-boot 的移植工作,大部分就是这些配置项的确定过程。
1 _start
_start u-boot 启动后的第一个执行地址,对 armv8 来说,它只是简单的跳转到 reset 处执行,如下:
.globl        _start
_start:
        b        reset
2 reset
reset的代码如下:
reset:
#ifdef CONFIG_SYS_RESET_SCTRL
        bl reset_sctrl
#endif
        /*
         * Could be EL3/EL2/EL1, Initial State:
         * Little Endian, MMU Disabled, i/dCache Disabled
         */
        adr        x0, vectors
        switch_el x1, 3f, 2f, 1f
3:        msr        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:        msr        vbar_el2, x0
        mov        x0, #0x33ff
        msr        cptr_el2, x0                /* Enable FP/SIMD */
        b        0f
1:        msr        vbar_el1, x0
        mov        x0, #3 << 20
        msr        cpacr_el1, x0                /* Enable FP/SIMD */
0:
        /* 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
#ifdef 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 */
master_cpu:
        /* On the master CPU */
#endif /* CONFIG_ARMV8_MULTIENTRY */
        bl        _main
主要做如下事情:
1 reset SCTRL 寄存器
具体可参考 reset_sctrl 函数,由 CONFIG_SYS_RESET_SCTRL 控制,一般不需要打开。该配置项的解释如下:
Reset the SCTRL register at the very beginning of execution to avoid interference from stale mappings set up by early firmware/loaders/etc.
2 )根据当前的 EL 级别,配置中断向量、 MMU Endian i/d Cache 等。
3 )配置 ARM 的勘误表
具体可参考 apply_core_errata 函数,由 CONFIG_ARM_ERRATA_XXX 控制,在项目的初期,可以不打开,后续根据实际情况打开)。
4 )调用 lowlevel_init
的功能解释如下(具体可参考 u-bootreadme文档):
        - purpose: essential init to permit execution to reach board_init_f()
        - no global_data or BSS
        - there is no stack (ARMv7 may have one but it will soon be removed)
        - must not set up SDRAM or use console
        - must only do the bare minimum to allow execution to continue to
                board_init_f()
        - this is almost never needed
        - return normally from this function
一般情况下,不需要实现。 start.S中也有一个 WEAK 类型的定义,由 CONFIG_GICV2 | CONFIG_GICV3 控制,一般情况下,没有打开的必要。
5 )如果是多 CPU 的场景,处理其它的 CPU boot
CPU 功能由 CONFIG_ARMV8_MULTIENTRY 控制,不需要打开。
6 )跳转到 arm 公共的 _main 中执行
ARM64 平台的 _main 位于 crt0_64.S文件中,具体请参考下面的描述。
3 _main
crt0 C-runtime Startup Code 的简称,意思就是运行 C 代码之前的准备工作。关于 _main 函数, crt0_64.S中有非常详细的注释(这一点要给 u-boot 100 个赞!),大家可以参考。该函数的定义如下:
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
        ldr        x0, =(CONFIG_SPL_STACK)
#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_BD]                /* x18 <- gd-="">bd */
        sub        x18, x18, #GD_SIZE                /* new GD is below bd */
        adr        lr, relocation_return
        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 */
/* TODO: For SPL, call spl_relocate_stack_gd() to alloc stack relocation */
/*
* Clear BSS section
*/
        ldr        x0, =__bss_start        /* this is auto-relocated! */
        ldr        x1, =__bss_end        /* this is auto-relocated! */
        mov        x2, #0
clear_loop:
        str        x2, [x0]
        add        x0, 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 /* !CONFIG_SPL_BUILD */
ENDPROC(_main)
功能可总结为(大部分翻译自 crt0_64.S中的注释):
1 )设置 C 代码的运行环境,为调用 board_init_f 接口做准备。包括:
a )设置堆栈( C 代码的函数调用,堆栈是必须的)。如果当前的编译是 SPL (由 CONFIG_SPL_BUILD 定义),可单独定义堆栈基址( CONFIG_SPL_STACK ),否则,通过 CONFIG_SYS_INIT_SP_ADDR 定义堆栈基址。
b )调用 board_init_f_alloc_reserve接口,从堆栈开始的地方,为 u-boot 中大名鼎鼎的 GD ('global data')  数据结构,分配空间。
c )调用 board_init_f_init_reserve接口,对 GD 进行初始化。
2 )调用 board_init_f 函数,完成一些前期的初始化工作,例如:
a )点亮一个 Debug 用的 LED 灯,表示 u-boot 已经活了。
b )初始化 DRAM DDR system 范围的 RAM 等。
c )计算后续代码需要使用的一些参数,包括 relocation destination the future stack the future GD location 等。
5 :关于 u-boot relocation 操作,后续会有专门的文章介绍。
3 )如果当前是 SPL (由 CONFIG_SPL_BUILD 控制),则 _main 函数结束,直接返回。如果是正常的 u-boot ,则继续执行后续的动作。
4 )根据 board_init_f 指定的参数,执行 u-boot relocation 操作。
5 )清除 BBS 段。
6 )调用 board_init_r函数,执行后续的初始化操作(已经不再本文的讨论范围了,具体请参考后续的分析文章)。
总结1 SPL功能
SPL Secondary Program Loader 的简称,之所以称作 secondary ,是相对于 ROM code 来说的。 SPL u-boot 中独立的一个代码分支,由 CONFIG_SPL_BUILD 配置项控制,是为了在正常的 u-boot image 之外,提供一个独立的、小 size SPL image ,通常用于那些 SRAM 比较小(或者其它限制)、无法直接装载并运行整个 u-boot 的平台。
如果使用了 SPL 功能, u-boot 的启动流程通常是:
ROM code 加载 SPL 并运行;
SPL 进行必要的初始化之后,加载 u-boot 并运行;
u-boot 进行后续的操作。
因此,如果使用 SPL 功能,需要尽可能的减少 SPL 的代码量,以减小它的 size
配置项总结
经过第 3 章的流程分析,我们可以总结出和 平台相关部分的启动流程 有关的配置项,记录如下:
CONFIG_SYS_RESET_SCTRL ,控制是否在启动的时候 reset SCTRL 寄存器,一般不需要打开;
CONFIG_ARM_ERRATA_XXX ,控制 ARM core 的勘误信息,一般不需要打开;
CONFIG_GICV2 CONFIG_GICV3 ,控制 GIC 的版本,用到的时候再说明;
CONFIG_ARMV8_MULTIENTRY ,控制是否在 u-boot 中使用多 CPU ,一般不需要;
CONFIG_SPL_BUILD ,是否是能 SPL 的编译,需要的话可以打开;
CONFIG_SPL_STACK ,如果配置了 CONFIG_SPL_BUILD ,是否为 SPL image 配置单独的 stack SP 基址),如果需要,通过该配置项配置,如果不需要,则使用 CONFIG_SYS_INIT_SP_ADDR
CONFIG_SYS_INIT_SP_ADDR ,配置 u-boot stack SP 基址),对于 u-boot 功能来说,必须提供。
原文来自:蜗窝科技
您还未登录,请先登录

热门帖子

最新帖子