zoukankan      html  css  js  c++  java
  • u-boot的SPL源码流程分析

      上次梳理了一下SPL的基本概念和代码总体思路,这次就针对代码跑的流程做个梳理。SPL中,入口在u-boot-spl.lds中

    ENTRY(_start)
    SECTIONS
    {
    .text :
    {
    __start = .;
    *(.vectors)                          //进入中断向量表,对应的跳转到U-boot/arch/arm/lib/vectors.S文件处理
    arch/arm/cpu/armv7/start.o (.text*) //跳转到对应的启动加载项,后续针对这个做处理。
    *(.text*)
    } >.sram
    . = ALIGN(4);
    .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
    . = ALIGN(4);
    .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
    . = ALIGN(4); 
    .u_boot_list : {
    KEEP(*(SORT(.u_boot_list*_i2c_*)));
    } >.sram
    . = ALIGN(4); 
    __image_copy_end = .;

      在这里,启动加载会跳转到文件arch/arm/cpu/armv7/start.S中,这个是怎么处理的呢?在这里,文件的主要工作有下面几种:

    A 重启保存启动参数:

    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

    B 设置向量表并跳转:

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

    C 针对CP15协处理器做优化,并关闭Icache MMU和TLBS,具体代码如下:

    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

    为什么要关闭这些特性呢?
    /*******************************************************   
    *1、为什么要关闭mmu?  
     *因为MMU是把虚拟地址转化为物理地址得作用  
     *而我们现在是要设置控制寄存器,而控制寄存器本来就是实地址(物理地址),  
     *再使能MMU,不就是多此一举了吗?   
    *2、为什么要关闭cache?      
     *catch和MMU是通过CP15管理的,刚上电的时候,CPU还不能管理他们。
    *所以上电的时候MMU必须关闭,指令cache可关闭,可不关闭,但数据cache一定要关闭
    *否则可能导致刚开始的代码里面,去取数据的时候,从catch里面取,
    *而这时候RAM中数据还没有cache过来,导致数据预取异常      
    *******************************************************************/

     D 系统的重要寄存器和内存时钟初始化。

    /*************************************************************************
     *
     * 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
    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
            bic     sp, sp, #7 /* 8-byte alignment for ABI compliance */
    #ifdef CONFIG_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)

      其实,这里面做的事情是比较简单的,就是完成一些简单的初始化动作。主要的工作还是在board_init_f()函数里面完成。

    文字里面解释很清楚了,这里就不做赘述了。

      在最后的跳转__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 */
        .........................................................................................
    
    #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 */
            ldr     pc, =board_init_r       /* this is auto-relocated! */
    
            /* we should not return here. */
    #endif
    
    ENDPROC(_main)

      这里做的工作还挺多,主要就是初始化C的运行环境和调用单板board_init_f(0)初始化操作。

    下面就要看board_init_r的工作了。在文件arch/arm/lib/spl.c中可以看到:

    /*
     * In the context of SPL, board_init_f must ensure that any clocks/etc for
     * DDR are enabled, ensure that the stack pointer is valid, clear the BSS
     * and call board_init_f.  We provide this version by default but mark it
     * as __weak to allow for platforms to do this in their own way if needed.
     */
    void __weak board_init_f(ulong dummy)
    {       
            /* Clear the BSS. */
            memset(__bss_start, 0, __bss_end - __bss_start);
    
    #ifndef CONFIG_DM
            /* TODO: Remove settings of the global data pointer here */
            gd = &gdata;
    #endif
            
            board_init_r(NULL, 0);
    }

      这里又调用了common/spl/spl.c文件中的board_init_r(),让我们看看这个都做了什么改动呢?

    void board_init_r(gd_t *dummy1, ulong dummy2)
    {
            u32 boot_device;
            int ret;
    
            debug(">>spl:board_init_r()
    ");
    
    #if defined(CONFIG_SYS_SPL_MALLOC_START)
            mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
                            CONFIG_SYS_SPL_MALLOC_SIZE);
            gd->flags |= GD_FLG_FULL_MALLOC_INIT;
    #elif defined(CONFIG_SYS_MALLOC_F_LEN)
            gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
            gd->malloc_ptr = 0;
    #endif
            if (IS_ENABLED(CONFIG_OF_CONTROL) &&
                            !IS_ENABLED(CONFIG_SPL_DISABLE_OF_CONTROL)) {
                    ret = fdtdec_setup();
                    if (ret) {
                            debug("fdtdec_setup() returned error %d
    ", ret);
                            hang();
                                          }
            }
    
    #ifndef CONFIG_PPC
            /*
             * timer_init() does not exist on PPC systems. The timer is initialized
             * and enabled (decrementer) in interrupt_init() here.
             */
            timer_init();
    #endif
    
    #ifdef CONFIG_SPL_BOARD_INIT
            spl_board_init();
    #endif
    
            boot_device = spl_boot_device();
            debug("boot device - %d
    ", boot_device);
            switch (boot_device) {
    #ifdef CONFIG_SPL_RAM_DEVICE
            case BOOT_DEVICE_RAM:
                    spl_ram_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_MMC_SUPPORT
            case BOOT_DEVICE_MMC1:
            case BOOT_DEVICE_MMC2:
            case BOOT_DEVICE_MMC2_2:
                    spl_mmc_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_NAND_SUPPORT
            case BOOT_DEVICE_NAND:
                    spl_nand_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_ONENAND_SUPPORT
            case BOOT_DEVICE_ONENAND:
                    spl_onenand_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_NOR_SUPPORT
            case BOOT_DEVICE_NOR:
                    spl_nor_load_image();
                    break;
                                                   
    #endif
    #ifdef CONFIG_SPL_YMODEM_SUPPORT
            case BOOT_DEVICE_UART:
                    spl_ymodem_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_SPI_SUPPORT
            case BOOT_DEVICE_SPI:
                    spl_spi_load_image();
                    break;
    #endif
    #ifdef CONFIG_SPL_ETH_SUPPORT
            case BOOT_DEVICE_CPGMAC:
    #ifdef CONFIG_SPL_ETH_DEVICE
                    spl_net_load_image(CONFIG_SPL_ETH_DEVICE);
    #else
                    spl_net_load_image(NULL);
    #endif
                    break;
    #endif
    #ifdef CONFIG_SPL_USBETH_SUPPORT
            case BOOT_DEVICE_USBETH:
                    spl_net_load_image("usb_ether");
                    break;
    #endif
    #ifdef CONFIG_SPL_USB_SUPPORT
    #endif
            default:
                    debug("Unsupported OS image.. Jumping nevertheless..
    ");
            }
    #if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
            debug("SPL malloc() used %#lx bytes (%ld KB)
    ", gd->malloc_ptr,
                  gd->malloc_ptr / 1024);
    #endif
    
            jump_to_image_no_args(&spl_image);
    
                                                                                                                                                                                       

      其实,这里面做了那么多,整体的思路就是按照不同的启动模式,调用不同的image来下载,到此,SPL的使命基本结束了。

  • 相关阅读:
    评审管理小结
    安全性测试入门 (五):Insecure CAPTCHA 验证码绕过
    安全性测试入门 (四):Session Hijacking 用户会话劫持的攻击和防御
    安全性测试入门 (三):CSRF 跨站请求伪造攻击和防御
    测试管理:问题驱动的测试过程改进
    逻辑思维驱动 (测试) 工作管理
    测试管理:用量化的思想管理工作
    软件质量报告模板-产品质量度量
    一篇短文再谈“敏捷”
    Windows XP系列全下载(均为MSDN原版)
  • 原文地址:https://www.cnblogs.com/dylancao/p/8626155.html
Copyright © 2011-2022 走看看