u-boot的SPL源码流程分析

摘要:
上次,我整理了SPL的基本概念和一般代码思想,这次我整理了代码运行过程sram.=对齐;。rodata:{*}˃.sram.=对齐;。数据:{*}˃.sram.=ALIGN;。u_boot_list:{KEEP;}˃。斯拉姆=对齐;__image_ copy_ end=。;这里,启动加载将跳转到arch/arm/cpu/armv7/start文件。这在S中是如何处理的?这里,该文件的主要工作如下:A重新启动并保存启动参数:reset:/*允许boardtosaveimpantregistries*/bsave_boot_paramssave_boot_params_ret:/**禁用中断,同时设置输入SVC32mode,*exceptifinHYPmodealready*/mrsr0,cpsrandr1,r0,#0x1f@maskmodebitsteqr1 # 0x1a@testforHYPmodebicner0,r0,#0x1f@clearallmodebitsorrner0,r0,#0x13@setSVCmodeorrr0,r0,#0xc0@disableFIQandIRQmsrcpsr,r0B设置向量表并跳转到:/**Setupvector:**/#if/*SetV=0inCP15SCTLR寄存器中VBARtopointtovector*/mrcp15,0,r0,c1,c0,0@ReadCP15SCTLRRegisterbicr0 # CR_V@V=0mcrp15,0,r0,c1,c0,0@WriteCP15SCTLRRegister/*设置向量dressinCP15VBAR寄存器*/ldrr0,=_ startmcrp15,0,r0,c12,c0,0@SetVBAR#endif/*任务ROM代码应具有PLLandothers表*/#ifndefCONFIG_ SKIP_ LOWLEVEL_ INITblcpu_ init_ cp15blcpu_init_ crit#endifbl_MainC优化CP15协处理器并关闭IcacheMMU和TLBS。具体代码如下:ENTRY/**InvalidateL1I/D*/movr0#0@setupforMCRmcrp15,0,r0,c8,c7,0@invalidateTLBsmcrp15,0,r0,c7,c5,0@invalidateicachemcrp15,0,r0,c7,c5,6@invalidateBParraymcrp15,0,r0,c7,c10,4@DSBmcrp15,0,r0,c7,c5,4@ISB/**禁用MMUstuffandcaches*/mrcp15,0,r0,c1,c0,0bicr0,r1,#0x00002000@clearbits13bicr0,r0,#0x00000007@clearbits2:0或0,r0,#0x00000002@setbit1Alignorrr0,r0,#0x00000800@setbit11BTB#ifdefCONFIG_ SYS_ ICACHE_ OFFbicr0,r0,#0x00001000@clearbit12I-cache#其他错误0、r0、#0x00001000@setbit12I-cache#Endifmcrp15,0,r0,c1,c0,0为什么应该关闭这些功能*Catch和MMU通过CP15进行管理。当它们通电时,CPU无法管理它们。

上次梳理了一下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 forHYP mode
        bicne   r0, r0, #0x1f@ clear all mode bits
        orrne   r0, r0, #0x13           @ setSVC 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 forMCR
        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(ulongdummy)
{       
        /*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, ulongdummy2)
{
        u32 boot_device;
        intret;
        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
        caseBOOT_DEVICE_RAM:
                spl_ram_load_image();
                break;
#endif
#ifdef CONFIG_SPL_MMC_SUPPORT
        caseBOOT_DEVICE_MMC1:
        caseBOOT_DEVICE_MMC2:
        caseBOOT_DEVICE_MMC2_2:
                spl_mmc_load_image();
                break;
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
        caseBOOT_DEVICE_NAND:
                spl_nand_load_image();
                break;
#endif
#ifdef CONFIG_SPL_ONENAND_SUPPORT
        caseBOOT_DEVICE_ONENAND:
                spl_onenand_load_image();
                break;
#endif
#ifdef CONFIG_SPL_NOR_SUPPORT
        caseBOOT_DEVICE_NOR:
                spl_nor_load_image();
                break;
#endif
#ifdef CONFIG_SPL_YMODEM_SUPPORT
        caseBOOT_DEVICE_UART:
                spl_ymodem_load_image();
                break;
#endif
#ifdef CONFIG_SPL_SPI_SUPPORT
        caseBOOT_DEVICE_SPI:
                spl_spi_load_image();
                break;
#endif
#ifdef CONFIG_SPL_ETH_SUPPORT
        caseBOOT_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
        caseBOOT_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的使命基本结束了。

免责声明:文章转载自《u-boot的SPL源码流程分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇redis实现对账(集合比较)功能域渗透之ldap协议下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

bootloader总体操作设计

bootloarder设计蓝图(不想做设计师的程序员不是好程序员):bootloarder的作用:将linux内核启动起来设计方法:模仿u-bootu-boot:支持多种嵌入式cpu的bootloarder程序,自主模式,自动引导内核启动。u-boot还有一种开发模式建立U-Boot工程:----------------------------------...

2.1.1优化程序性能

性能优化有三个层次: 系统层次 算法层次 代码层次 系统层次关注系统的控制流程和数据流程,优化主要考虑如何减少消息传递的个数;如何使系统的负载更加均衡;如何充分利用硬件的性能和设施;如何减少系统额外开销(比如上下文切换等)。 算法层次关注算法的选择(用更高效的算法替换现有算法,而不改变其接口);现有算法的优化(时间和空间的优化);并发和锁的优化(增加任务的...

数据库SQL优化大总结之 百万级数据库优化方案(转载)

网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。 这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的话推荐给更多的人,让更多的人看到、纠正以及补充。   一、百万级数据库优化方案 1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 o...

面向云原生的混沌工程工具-ChaosBlade

作者 | 肖长军(穹谷)阿里云智能事业群技术专家 导读:随着云原生系统的演进,如何保障系统的稳定性受到很大的挑战,混沌工程通过反脆弱思想,对系统注入故障,提前发现系统问题,提升系统的容错能力。ChaosBlade 工具可以通过声明式配置执行混沌实验,简单高效。本文将会重点介绍 ChaosBlade 以及云原生相关的实验场景实践。 ChaosBlade 介...

[译]GPUView

【本文翻译自GPUView的开发者Matt的blog.  https://graphics.stanford.edu/~mdfisher/GPUView.html  】 【 GPUview可以在 https://docs.microsoft.com/en-us/windows-hardware/get-started/adk-install 这里下载到】...

使用 VisualVM 进行性能分析及调优

VisualVM 是一款免费的集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优。这些功能包括生成和分析海量数据、跟踪内存泄漏、监控垃圾回收器、执行内存和 CPU 分析,同时它还支持在 MBeans 上进行浏览和操作。本文主要介绍如何使用 VisualVM 进行性能分析及调优。   概述 开发大...