上次梳理了一下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();
.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
. = ALIGN();
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
. = ALIGN();
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*_i2c_*)));
} >.sram
. = ALIGN();
__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, , r0, c1, c0, @ Read CP15 SCTLR Register
bic r0, #CR_V @ V =
mcr p15, , r0, c1, c0, @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, , r0, c12, c0, @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, # @ set up for MCR
mcr p15, , r0, c8, c7, @ invalidate TLBs
mcr p15, , r0, c7, c5, @ invalidate icache
mcr p15, , r0, c7, c5, @ invalidate BP array
mcr p15, , r0, c7, c10, @ DSB
mcr p15, , r0, c7, c5, @ ISB /*
* disable MMU stuff and caches
*/
mrc p15, , r0, c1, c0,
bic r0, r0, #0x00002000 @ clear bits (--V-)
bic r0, r0, #0x00000007 @ clear bits : (-CAM)
orr r0, r0, #0x00000002 @ set bit (--A-) Align
orr r0, r0, #0x00000800 @ set bit (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit (I) I-cache
#endif
mcr p15, , 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, # /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_DM
mov r9, #
#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, #
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, #
mov sp, r3
#else
bic sp, sp, # /* 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, , __bss_end - __bss_start); #ifndef CONFIG_DM
/* TODO: Remove settings of the global data pointer here */
gd = &gdata;
#endif board_init_r(NULL, );
}

  这里又调用了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()\n"); #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 = ;
#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\n", 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\n", 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..\n");
}
#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
gd->malloc_ptr / );
#endif jump_to_image_no_args(&spl_image);

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

u-boot的SPL源码流程分析的更多相关文章

  1. Flask源码流程分析(一)

    Flask源码流程分析: 1.项目启动: 1.实例化Flask对象 1. 重要的加载项: * url_rule_class = Rule * url_map_class = Map * session ...

  2. DRF视图的使用及源码流程分析

    django rest framework中对于APIView.GenericAPIView.ModelViewSet.mixins扩展类的分析. APIView 示例 根据实际程序来分析: urls ...

  3. @app.route源码流程分析

    @app.route(), 是调用了flask.app.py文件里面的Flask类的route方法,route方法所做的事情和add_url_rule类似,是用来为一个URL注册一个视图函数,但是我们 ...

  4. Spring事件监听ApplicationListener源码流程分析

    spring的事件机制是基于观察者设计模式的,ApplicationListener#onApplicationEvent(Event)方法,用于对事件的处理 .在容器初始化的时候执行注册到容器中的L ...

  5. MyBatis源码流程分析

    mybatis核心流程三大阶段 Mybatis的初始化  建造者模式 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.这种类型的设计模式属于创建型模式,它提 ...

  6. requireJS源码流程分析

  7. Flask启动原理,源码流程分析

    1.执行Flask的实例对象.run()方法 from flask import Flask,request,session app = Flask(__name__) app.secret_key ...

  8. Django-Rest-Framework部分源码流程分析

    class TestView(APIView): ''' 调用这个函数的时候,会自动触发authentication_classes的运行,所以会先执行上边的类 ''' authentication_ ...

  9. 不会DRF?源码都分析透了确定不来看?

    目录 不会DRF?源码都分析透了确定不来看? 快速使用DRF写出接口 序列化和反序列化 drf快速使用 views.py serializer.py urls.py 在settings的app中注册 ...

随机推荐

  1. CodeForces - 681A A Good Contest

    咳咳,从今天开始,每天做一个英语题,不论简单还是难,坚持到下学期的省赛,希望能有效果. 这题就是判断是否能成为red,如果他超越的人里面有在比赛前分数达到2400,并且在比赛后分数上升,那么他就能成为 ...

  2. OSQA的配置

    1.安装Python,我安装的是python 2.7.3 2.安装setuptools 下载setuptools,并安装 安装好以后,在pyton2.7/scripts的路径下将会有easy_inst ...

  3. Vue中method与computed的区别

    为了说明method与computed的区别,在此我想先来看看computed属性在vue官网中的说法:模板内的表达式是非常便利的,但是它们实际上只用于简单的运算.在模板中放入太多的逻辑会让模板过重且 ...

  4. php学习笔记upset函数。

    如果在函数中我们不希望再使用某个变量.可以使用unset()销毁函数, ②在php函数中,我们可以给某些参数赋一个默认初值. ③可以传一个默认的参数. ④php在传递变量的时候,默认是传递值传递,如果 ...

  5. JavaScript获取当前日期

    JavaScript获取当前日期 具体实现如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ...

  6. Java中的List转换成JSON报错(四)

    1.错误描述 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/colle ...

  7. span是没有value标签的,要向获得标签内部的值改怎么办。

    1,js实现 var div = document.getElementById('divId');var spans = div.getElementsByTagName('span');var s ...

  8. 护航者,腾讯云: 2017年度游戏行业DDoS态势报告—回溯与前瞻

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯游戏云 前言 自14年开始,全球DDoS攻击持续爆发,攻击峰值不断创记录.2017年,这种依靠超大流量不断冲击服务器和带宽造成业务 ...

  9. C# 高效字符串连接 StringBuilder介绍

    在介绍StringBuilder之前,必须要先了解string的特性. string在.NET中属于基本数据类型,也是基本数据类型中唯一的引用类型.字符串可以声明为常量,但它却放在了堆中. 一:不可改 ...

  10. 解决DOS批处理中一个困扰我几十个月的编码问题

    DOS批处理中的编码很有意思.&是命令连接符,先执行&左边的命令,再执行&右边的命令.|是管道操作,把左边的输出当作右边的输入.此外还有&&和||,当要表示这些 ...