Uboot启动流程分析(一)

1、前言

Linux系统的启动需要一个bootloader程序,该bootloader程序会先初始化DDR等外设,然后将Linux内核从flash中拷贝到DDR中,最后启动Linux内核,uboot的全称为Universal Boot Loader,Linux系统中常用的bootloader就是uboot,接下来,将会进行简单的uboot启动流程分析,uboot的源码为uboot-imx-rel_imx_4.15_2.1.0。

2、uboot入口

 在分析之前,需要对整个uboot工程进行编译,生成一些分析时需要用到的文件,例如链接文件uboot.lds和uboot映射文件uboot.map,通过链接文件,可以找到uboot的入口,找到uboot启动后运行的第一行代码。

在uboot源码根目录下找到链接文件uboot.lds,如下:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)   //当前入口_start
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.*) }
}

在上面的链接文件中,可以确定uboot的入口为_start,该定义在arch/arm/lib/vectors.S文件中:

_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
    .word    CONFIG_SYS_DV_NOR_BOOT_CFG
#endif

    b    reset    //中断向量表,跳转到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后,运行b reset代码,跳转到reset中运行,b reset后面跟照中断向量表,reset的定义,对于不通的架构不一样,对于NXP的i.mx6ul芯片,该定义在arch/arm/cpu/armv7/start.S文件中:

    .globl    reset
    .globl    save_boot_params_ret

reset:
    /* Allow the board to save important registers */
    b    save_boot_params    //跳到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寄存器的值到r0寄存器中(cps:bit0~bit4保存处理器工作模式)
    and    r1, r0, #0x1f        @ mask mode bits    //r0的值与0x1f相与,结果保存到r1寄存器
    teq    r1, #0x1a        @ test for HYP mode    //判断当前处理器模式是否是HYP模式
    bicne    r0, r0, #0x1f        @ clear all mode bits    //如果CPU不处于HYP模式,则清除bit0~bit4
    orrne    r0, r0, #0x13        @ set SVC mode    //设置为SVC模式
    orr    r0, r0, #0xc0        @ disable FIQ and IRQ    //禁止FIQ和IRQ
    msr    cpsr,r0        //将当前r0寄存器的值回写到cpsr寄存器

/*
 * 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    //读取SCTLR寄存器
    bic    r0, #CR_V        @ V = 0    //设置V = 0
    mcr    p15, 0, r0, c1, c0, 0    @ Write CP15 SCTLR Register

    /* Set vector address in CP15 VBAR register */
    ldr    r0, =_start    //设置vector地址到CP15 VBAR寄存器
    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    //跳转到cpu_init_cp15
    bl    cpu_init_crit    //跳转到cpu_init_crit
#endif

    bl    _main    //跳转到_main

在上面的代码中主要是对arm处理器的运行模式进行设置,对一些寄存器进行赋值操作,对于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 //跳到lowlevel_init,设置pll、mux和memory
ENDPROC(cpu_init_crit)
#endif

在cpu_init_crit函数中,跳到了lowlevel_init函数中运行,接下来,详细分析一下lowlevel_init和_main函数。

扫描二维码关注公众号,回复: 8101952 查看本文章

3、lowlevel_init函数

4、_main函数

猜你喜欢

转载自www.cnblogs.com/Cqlismy/p/12000889.html