Uboot启动(1)

文章详细阐述了Uboot在启动系统时如何加载Kernel到内存以及其工作流程,特别是入口点_start在arch/arm/lib/vectors.S中的定义和后续的reset函数。此外,还介绍了CPU初始化过程,包括模式切换、中断向量表重定位以及相关寄存器的设置等。
摘要由CSDN通过智能技术生成

Uboot的作用:启动系统时将 Kernel 带入到内存中, 之后 Bootloader 就没有用处了。

 Uboot的工作流程分析:

1.分析uboot.lds链接脚本

要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接脚本为 arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds
文件。

OUTPUT_FORMAT("elf32-littlearm", "elf32littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)//_start为当前的入口点,在文件arch/arm/lib/vectors.S中有定义
SECTIONS
 . = 0x00000000;
 . = ALIGN(4);
 .text :
 {
  *(.__image_copy_start)
  *(.vectors)
  arch/arm/cpu/armv7/start.o (.text*)//将 arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面。
  *(.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.*) }
}

打开文件在文件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后面就是中断向量表,在文件u-boot.map中可以找到__image_copy_start为:0x87800000,u-boot.map是uboot的映射文件,可以从中看到某个文件或者函数链接到了哪个地址。

段 .text 的地址设置为 0x87800000
                0x0000000000000000                . = 0x0
                0x0000000000000000                . = ALIGN (0x4)

.text           0x0000000087800000    0x3cd64
 *(.__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

其他变量,注意除了__image_copy_start以外,其他变量子每次编译都有可能发生变化。如下表所示:在后面的启动过程会使用到的变量。

 从lds文件可以知道,入口点是arch/arm/lib/vectors.S中的start:

_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中:

/*************************************************************************
 *
 * 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

reset 函数跳转到了 save_boot_params 函数,而 save_boot_params 函数同样定义在 start.S 里面。

ENTRY(save_boot_params)
	        b save_boot_params_ret	    @ back to my caller
ENDPROC(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  //读取寄存器cpsr的寄存器:程序状态寄存器
	        and	        r1, r0, #0x1f @ mask mode bits //提取cpsr的前五位判断处理器工作模式,并赋值r1
	        teq	        r1, #0x1a	  @ test for HYP mode //判断是否处于Hyp模式
	        bicne	        r0, r0, #0x1f	 @ clear all mode bits //清除模式
	        orrne	        r0, r0, #0x13	 @ set SVC mode //进入SVC模式
	        orr	        r0, r0, #0xc0	    @ disable FIQ and IRQ // CPSR与11000000或关闭FIQ和IRQ
	        msr	        cpsr,r0   //让cpu处于SVC模式,且关闭中断

/*
 * 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 @ Read CP15 SCTLR Register 读取 CP15 中 c1 寄存器的值到 r0 寄存器中
	        bic	        r0, #CR_V	       @ V = 0 清除bit13(向量表控制位)重定位向量表
	        mcr	        p15, 0, r0, c1, c0, 0	  @ Write CP15 SCTLR Register 重新写入

	        /* Set vector address in CP15 VBAR register */
	        ldr	        r0, =_start  //0x87800000, 也是向量表的起始地址
	        mcr	        p15, 0, r0, c12, c0, 0 @Set VBAR将 r0 寄存器的值(向量表值)写入到 CP15 的 c12 寄存器中,也就是 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

save_boot_params_ret:让cpu进入了SVC模式,并且支持中断向量表的重定位。CPSR寄存器前5位标识了处理器的工作模式:

SCTLR寄存器: bit13: V , 中断向量表基地址选择位,为 0 的话中断向量表基地址为 0X00000000,软件可以使用 VBAR 来重映射此基地址,也就是中断向量表重定位。为 1 的话中断向量表基地址为0XFFFF0000,此基地址不能被重映射。

 

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

#ifdef CONFIG_ARM_ERRATA_716044
	        mrc	        p15, 0, r0, c1, c0, 0	        @ read system control register
	        orr	        r0, r0, #1 << 11	        @ set bit #11
	        mcr	        p15, 0, r0, c1, c0, 0	        @ write system control register
#endif

#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
	        mrc	        p15, 0, r0, c15, c0, 1	        @ read diagnostic register
	        orr	        r0, r0, #1 << 4	        	        @ set bit #4
	        mcr	        p15, 0, r0, c15, c0, 1	        @ write diagnostic register
#endif

#ifdef CONFIG_ARM_ERRATA_743622
	        mrc	        p15, 0, r0, c15, c0, 1	        @ read diagnostic register
	        orr	        r0, r0, #1 << 6	        	        @ set bit #6
	        mcr	        p15, 0, r0, c15, c0, 1	        @ write diagnostic register
#endif

#ifdef CONFIG_ARM_ERRATA_751472
	        mrc	        p15, 0, r0, c15, c0, 1	        @ read diagnostic register
	        orr	        r0, r0, #1 << 11	        @ set bit #11
	        mcr	        p15, 0, r0, c15, c0, 1	        @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_761320
	        mrc	        p15, 0, r0, c15, c0, 1	        @ read diagnostic register
	        orr	        r0, r0, #1 << 21	        @ set bit #21
	        mcr	        p15, 0, r0, c15, c0, 1	        @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_845369
	        mrc	        p15, 0, r0, c15, c0, 1	        @ read diagnostic register
	        orr	        r0, r0, #1 << 22	        @ set bit #22
	        mcr	        p15, 0, r0, c15, c0, 1	        @ write diagnostic register
#endif

	        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

#ifdef CONFIG_ARM_ERRATA_798870
	        cmp	        r2, #0x30	        	        @ Applies to lower than R3p0
	        bge	        skip_errata_798870      @ skip if not affected rev
	        cmp	        r2, #0x20	        	        @ Applies to including and above R2p0
	        blt	        skip_errata_798870      @ skip if not affected rev

	        mrc	        p15, 1, r0, c15, c0, 0  @ read l2 aux ctrl reg
	        orr	        r0, r0, #1 << 7         @ Enable hazard-detect timeout
	        push	        {r1-r5}	        	        	        @ Save the cpu info registers
	        bl	        v7_arch_cp15_set_l2aux_ctrl
	        isb	        	        	        	        @ Recommended ISB after l2actlr update
	        pop	        {r1-r5}	        	        	        @ Restore the cpu info - fall through
skip_errata_798870:
#endif

#ifdef CONFIG_ARM_ERRATA_801819
	        cmp	        r2, #0x24	        	        @ Applies to lt including R2p4
	        bgt	        skip_errata_801819      @ skip if not affected rev
	        cmp	        r2, #0x20	        	        @ Applies to including and above R2p0
	        blt	        skip_errata_801819      @ skip if not affected rev
	        mrc	        p15, 0, r0, c0, c0, 6	        @ pick up REVIDR reg
	        and	        r0, r0, #1 << 3	        	        @ check REVIDR[3]
	        cmp	        r0, #1 << 3
	        beq	        skip_errata_801819	        @ skip erratum if REVIDR[3] is set

	        mrc	        p15, 0, r0, c1, c0, 1	        @ read auxilary control register
	        orr	        r0, r0, #3 << 27	        @ Disables streaming. All write-allocate
	        	        	        	        	        @ lines allocate in the L1 or L2 cache.
	        orr	        r0, r0, #3 << 25	        @ Disables streaming. All write-allocate
	        	        	        	        	        @ lines allocate in the L1 cache.
	        push	        {r1-r5}	        	        	        @ Save the cpu info registers
	        bl	        v7_arch_cp15_set_acr
	        pop	        {r1-r5}	        	        	        @ Restore the cpu info - fall through
skip_errata_801819:
#endif

#ifdef CONFIG_ARM_ERRATA_454179
	        cmp	        r2, #0x21	        	        @ Only on < r2p1
	        bge	        skip_errata_454179

	        mrc	        p15, 0, r0, c1, c0, 1	        @ Read ACR
	        orr	        r0, r0, #(0x3 << 6)	        @ Set DBSM(BIT7) and IBE(BIT6) bits
	        push	        {r1-r5}	        	        	        @ Save the cpu info registers
	        bl	        v7_arch_cp15_set_acr
	        pop	        {r1-r5}	        	        	        @ Restore the cpu info - fall through

skip_errata_454179:
#endif

#ifdef CONFIG_ARM_ERRATA_430973
	        cmp	        r2, #0x21	        	        @ Only on < r2p1
	        bge	        skip_errata_430973

	        mrc	        p15, 0, r0, c1, c0, 1	        @ Read ACR
	        orr	        r0, r0, #(0x1 << 6)	        @ Set IBE bit
	        push	        {r1-r5}	        	        	        @ Save the cpu info registers
	        bl	        v7_arch_cp15_set_acr
	        pop	        {r1-r5}	        	        	        @ Restore the cpu info - fall through

skip_errata_430973:
#endif

#ifdef CONFIG_ARM_ERRATA_621766
	        cmp	        r2, #0x21	        	        @ Only on < r2p1
	        bge	        skip_errata_621766

	        mrc	        p15, 0, r0, c1, c0, 1	        @ Read ACR
	        orr	        r0, r0, #(0x1 << 5)	        @ Set L1NEON bit
	        push	        {r1-r5}	        	        	        @ Save the cpu info registers
	        bl	        v7_arch_cp15_set_acr
	        pop	        {r1-r5}	        	        	        @ Restore the cpu info - fall through

skip_errata_621766:
#endif

	        mov	        pc, r5	        	        	        @ back to my caller
ENDPROC(cpu_init_cp15)

继续执行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函数,在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 //设置SP
	        bic	        sp, sp, #7 /* 8-byte alignment for ABI compliance *///sp 指针做 8 字节对齐处理
#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  //sp 指针减去 GD_SIZE, GD_SIZE 同样在 generic-asm-offsets.h 中定了,大小为248
	        bic	        sp, sp, #7 //sp 做 8 字节对齐,此时 sp 的地址为 0X0091FF00-248=0X0091FE08
	        mov	        r9, sp //将 sp 地址保存在 r9 寄存器中
#endif
#endif
	        /*
	         * Save the old lr(passed in ip) and the current lr to stack
	         */
	        push	        {ip, lr} //将 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} //将第 36 行入栈的 ip 和 lr 进行出栈,并将 lr 赋给 pc
ENDPROC(lowlevel_init)

其中,ldr  sp, =CONFIG_SYS_INIT_SP_ADDR 设置了SP指针为CONFIG_SYS_INIT_SP_ADDR

/include/configs/mx6ullevk.h
#define CONFIG_SYS_INIT_RAM_ADDR	                                                        IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE	        IRAM_SIZE

#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)

/arch/arm/include/asm/arch-mx6/imx-regs.h
#define IRAM_BASE_ADDR	        	        	        0x00900000

#if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \
	        defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))
#define IRAM_SIZE                    0x00040000
#else
#define IRAM_SIZE                    0x00020000
#endif

由以上可得:

CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000。
CONFIG_SYS_INIT_RAM_SIZE = 0x00020000 =128KB

则计算出:

include/generated/generic-asm-offsets.h
#define GENERATED_GBL_DATA_SIZE 256 /* (sizeof(struct global_data) + 15) & ~15	  @ */

CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 – 256 = 0x1FF00。
CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0X1FF00 = 0X0091FF00,

即如图所示:

执行完lowlevel_init()函数后:

 其中,lowlevel_init()调用了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; //如果 CPU 为 MX6SX、 MX6UL、 MX6ULL 或 MX6SLL
	        	        	        	        //中 的 任 意 一 种 , 那 么 就 会 直 接 返 回 , 相 当 于 s_init 函 数 什 么 都 没 做

	        /* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs
	         * to make sure PFD is working right, otherwise, PFDs may
	         * not output clock after reset, MX6DL and MX6SL have added 396M pfd
	         * workaround in ROM code, as bus clock need it
	         */

	        mask480 = ANATOP_PFD_CLKGATE_MASK(0) |
	        	        ANATOP_PFD_CLKGATE_MASK(1) |
	        	        ANATOP_PFD_CLKGATE_MASK(2) |
	        	        ANATOP_PFD_CLKGATE_MASK(3);
	        mask528 = ANATOP_PFD_CLKGATE_MASK(1) |
	        	        ANATOP_PFD_CLKGATE_MASK(3);

	        reg = readl(&ccm->cbcmr);
	        periph2 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK)
	        	        >> MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET);
	        periph1 = ((reg & MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)
	        	        >> MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET);

	        /* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */
	        if ((periph2 != 0x2) && (periph1 != 0x2))
	        	        mask528 |= ANATOP_PFD_CLKGATE_MASK(0);

	        if ((periph2 != 0x1) && (periph1 != 0x1) &&
	        	        (periph2 != 0x3) && (periph1 != 0x3))
	        	        mask528 |= ANATOP_PFD_CLKGATE_MASK(2);

	        writel(mask480, &anatop->pfd_480_set);
	        writel(mask528, &anatop->pfd_528_set);
	        writel(mask480, &anatop->pfd_480_clr);
	        writel(mask528, &anatop->pfd_528_clr);
}

总体流程图如下:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴伴学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值