uboot-spl的程序流程主要包含下面的几个函数:

_start->reset->save_boot_params->cpu_init_crit->lowlevel_init->_main->board_init_f

在armv7架构的uboot-spl,主要需要做如下事情

    • 关闭中断,svc模式
    • 禁用MMU、TLB
    • 芯片级、板级的一些初始化操作 
      • IO初始化
      • 时钟
      • 内存
      • 选项,串口初始化
      • 选项,nand flash初始化
      • 其他额外的操作
    • 加载BL2,跳转到BL2

下面详细分析一下它的过程。(注意,本人是以uboot2011的源码版本来分析的。)

一、首先,通过uboot-spl编译脚本/u-boot/arch/arm/cpu/armv7/u-boot-spl.lds,程序如下:

...//前面省略
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.text :
{
__start = .;
arch/arm/cpu/armv7/start.o (.text)
*(.text*)
} >.sram . = ALIGN();
.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram . = ALIGN();
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
. = ALIGN();
__image_copy_end = .;
_end = .; .bss :
{
. = ALIGN();
__bss_start = .;
*(.bss*)
. = ALIGN();
__bss_end__ = .;
} >.sdram
}

对于任何程序,入口函数是在链接时决定的,uboot的入口是由链接脚本决定的。uboot下armv7链接脚本默认目录为arch/arm/cpu/u-boot.lds。这个可以在配置文件中与CONFIG_SYS_LDSCRIPT来指定。

入口地址也是由连接器决定的,在配置文件中可以由CONFIG_SYS_TEXT_BASE指定。这个会在编译时加在ld连接器的选项-Ttext中。

链接脚本中这些宏的定义在linkage.h中,看字面意思也明白,程序的入口是在_start.,后面是text段,data段等。

所以uboot-spl的代码入口函数是_start。

二、_start的定义在/u-boot/arch/arm/cpu/armv7/start.S中:

.globl _start
_start: 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 /* irq中断向量 */
ldr pc, _fiq /* fiq中断向量 *
#ifdef CONFIG_SPL_BUILD /* 中断向量表入口地址 */
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#else
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#endif /* CONFIG_SPL_BUILD */ .global _end_vect
_end_vect: .balignl ,0xdeadbeef

.global声明_start为全局符号,_start就会被连接器链接到,也就是链接脚本中的入口地址了。
以上代码是设置arm的异常向量表,arm异常向量表如下:

 

地址

异常

进入模式

描述

0x00000000

复位

管理模式

复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行

0x00000004

未定义指令

未定义模式

遇到不能处理的指令时,产生未定义指令异常

0x00000008

软件中断

管理模式

执行SWI指令产生,用于用户模式下的程序调用特权操作指令

0x0000000c

预存指令

中止模式

处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常

0x00000010

数据操作

中止模式

处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常

0x00000014

未使用

未使用

未使用

0x00000018

IRQ

IRQ

外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常

0x0000001c

FIQ

FIQ

快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常

8种异常分别占用4个字节,因此每种异常入口处都填写一条跳转指令,直接跳转到相应的异常处理函数中,reset异常是直接跳转到reset函数,其他7种异常是用ldr将处理函数入口地址加载到pc中。
后面汇编是定义了7种异常的入口函数,这里没有定义CONFIG_SPL_BUILD,所以走后面一个。
接下来定义的_end_vect中用.balignl来指定接下来的代码要16字节对齐,空缺的用0xdeadbeef,方便更加高效的访问内存。

后面,开始声明中断起始地址:

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de /* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif /* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de

这里声明中断处理函数栈起始地址,给出的值是0x0badc0de,是一个非法值,注释也说明了,这个值会在运行时重新计算,代码是在interrupt_init中。

/*
* the actual reset code
*/ reset:
bl save_boot_params
/*
* set the cpu to SVC32 mode
*/
@ mov r0, #
@ cmp r0, #
@ beq reset
here:
mrs r0, cpsr
bic r0, r0, #0x1f  /*工作模式位清零 */ 
orr r0, r0, #0xd3 /*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1 */
msr cpsr,r0
/*以上代码将CPU的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。*/ #if defined(CONFIG_ARM_A7)
@set SMP bit
mrc p15, , r0, c1, c0,
orr r0, r0, #(<<)
mcr p15, , r0, c1, c0,
#endif
@#if defined(CONFIG_ARCH_SUN9IW1P1)
@ ldr r0, =0x008000e0
@ ldr r1, =0x16aa0001
@ str r1, [r0]
@#endif

在上电或者重启后,处理器取得第一条指令就是b reset,所以会直接跳转到reset函数处,而reset首先是跳转到save_boot_params中。

三、save_boot_params的定义如下:

void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)__attribute__((weak, alias("save_boot_params_default")));

save_boot_params函数在/u-boot/arch/arm/cpu/armv7/cpu.c中定义,该函数什么都没做。
这句话的意思:__attribute__((weak))将本模块的save_boot_params转成弱符号类型,如果该函数在其他地方没有定义,则为空函数。
由于它没有定义,所以这是个空函数。

接着,后面运行这段程序:

#if defined(CONFIG_OMAP34XX)
/* Copy vectors to mask ROM indirect addr */
adr r0, _start @ r0 <- current position of code
add r0, r0, # @ skip reset vector
mov r2, # @ r2 <- size to copy
add r2, r0, r2 @ r2 <- source end address
mov r1, #SRAM_OFFSET0 @ build vect addr
mov r3, #SRAM_OFFSET1
add r1, r1, r3
mov r3, #SRAM_OFFSET2
add r1, r1, r3
next:
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end address [r2]
bne next @ loop until equal */
#if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT)
/* No need to copy/exec the clock code - DPLL adjust already done
* in NAND/oneNAND Boot.
*/
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif /* NAND Boot */
#endif /* CONFIG_OMAP34XX */
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif

这里需要注意,ARM默认的异常向量表入口在0x0地址,uboot的运行介质(norflash nandflash sram等)映射地址可能不在0x0起始的地址,所以需要修改异常向量表入口。
与网上的不同的是,这里没有对协处理器cp15的操作。
在cpy_clk_code之前的汇编的作用如下:

  1. 初始化异常向量表
  2. 设置cpu svc模式,关中断
  3. 设置异常向量入口

如果没有定义CONFIG_SYS_NAND_BOOT和CONFIG_SYS_ONENAND_BOOT,则进入cpy_clk_code。
四、下面直接看cpu_init_crit。

cpu_init_crit:
/*
* Invalidate L1 I/D
*/
...//省略详细的内容 /*
* disable MMU stuff and caches
*/
...//省略详细的内容 /*
* 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.
*/
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
mov lr, ip @ restore link
mov pc, lr @ back to my caller
#endif

在禁止了L1 I/D、MMU之后,调用了lowlevel_init。他在/u-boot/arch/arm/cpu/armv7/lowlevel_init.S中有定义
五、lowlevel_init函数

它是与特定开发板相关的初始化函数,在这个函数里会做一些pll初始化,如果不是从mem启动,则会做memory初始化,方便后续拷贝到mem中运行。
lowlevel_init函数则是需要移植来实现,做clk初始化以及ddr初始化
从cpu_init_crit返回后,_start的工作就完成了,接下来就要调用_main,总结一下_start工作:

  1. 前面总结过的部分,初始化异常向量表,设置svc模式,关中断
  2. 初始化mmu cache tlb
  3. 板级初始化,pll memory初始化

六、初始化栈指针sp为调用board_init_f做准备,这个过程一般是在_main中实现的。

call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, # /* 8-byte alignment for ABI compliance */
ldr r0,=0x00000000 @bl boot_standby_relocat bl board_init_f

首先进行堆栈的设置,然后就跳转到board_init_f函数,其中传递给该函数的参数为0。

然后调用/u-boot/arch/arm/lib/board.c
此时已经进入C语言的代码,spl就结束了。

参考:

http://blog.csdn.net/xiaohai1232/article/details/60775435#t0

http://blog.csdn.net/skyflying2012/article/details/25804209

http://blog.csdn.net/ooonebook/article/details/52957395

https://wenku.baidu.com/view/254d6bc3d15abe23482f4dec.html

https://wenku.baidu.com/view/eb73e1edb8f67c1cfad6b89b.html

上一篇:http://www.cnblogs.com/yeqluofwupheng/p/7341989.html

下一篇:http://www.cnblogs.com/yeqluofwupheng/p/7355248.html

uboot学习之uboot-spl的程序流程分析的更多相关文章

  1. u-boot 学习系列 1 - SPL

    u-boot这个东西从自我N年前使用到现在,变化好多,今天开始重新研究下,本系列的研究都是基于BeagleBoneBlack(bbb)开发板和 u-boot v201801版本的. SPL介绍 在源代 ...

  2. uboot学习之uboot启动流程简述

    一.uboot启动分为了三个阶段BL0.BL1.BL2:BL0表示上电后运行ROM中固化的一段程序,其中ROM中的程序是厂家写进去的,所以具体功能可能根据厂家芯片而有所不同.功能如下: 初始化系统时钟 ...

  3. 01 . Go框架之Beego简介部署及程序流程分析

    Beego简介 beego是一个使用Go语言来开发WEB引用的GoWeb框架,该框架起始于2012年,由一位中国的程序员编写并进行公开,其目的就是为大家提供一个高效率的web应用开发框架.该框架采用模 ...

  4. u-boot的SPL源码流程分析

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

  5. uboot学习之uboot.bin的运行流程

    上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html 讲到uboot-spl的工作流程,接下来简述一下uboot.bin的工作流程,这对应 ...

  6. uboot学习之五-----uboot如何启动Linux内核

    uboot和内核到底是什么?uboot实质就是一个复杂的裸机程序:uboot可以被配置也可以做移植: 操作系统内核本身就是一个裸机程序,和我们学的uboot和其他裸机程序没有本质的区别:区别就是我们操 ...

  7. ucore操作系统学习笔记(一) ucore lab1系统启动流程分析

    一.ucore操作系统介绍 操作系统作为一个基础系统软件,对下控制硬件(cpu.内存.磁盘网卡等外设),屏蔽了底层复杂多样的硬件差异:对上则提供封装良好的应用程序接口,简化应用程序开发者的使用难度.站 ...

  8. stm32 usb数据接收与数据发送程序流程分析

    http://blog.csdn.net/u011318735/article/details/17424349 既然学习了USB,那就必须的搞懂USB设备与USB主机数据是怎么通讯的.这里主要讲设备 ...

  9. WordPress程序流程分析

    index.php 统一入口文件 包含wp-blog-heaer.php 包含wp-load.php 包含wp-config.php 数据库.语言包配置等 包含wp-setting.php 对各种运行 ...

随机推荐

  1. Web API Media Type Formatter

    public static void Register(HttpConfiguration config) { // Web API configuration and services // Web ...

  2. HandlerMethodArgumentResolver(二):Map参数类型和固定参数类型【享学Spring MVC】

    每篇一句 黄金的导电性最好,为什么电脑主板还是要用铜? 飞机最快,为什么还有人做火车? 清华大学最好,为什么还有人去普通学校? 因为资源都是有限的,我们现实生活中必须兼顾成本与产出的平衡 前言 上文 ...

  3. 集群、限流、缓存 BAT 大厂无非也就是这么做

    前言 前阵子有网友询问,如何优化网站?这个问题真的很大,跟他简单的聊了一下,随便说了几点,觉得有必要整理一篇文章出来,正好前阵子在做爬虫博客,于是把大体思路分享出来,与大家互通有无,共同进步. 优化 ...

  4. pt-online-schema-change使用详解

    一.pt-online介绍 pt-online-schema-change是percona公司开发的一个工具,在percona-toolkit包里面可以找到这个功能,它可以在线修改表结构 原理: 首先 ...

  5. python 04 列表

    1.列表——list [ ] 有序.可变.支持索引查看. 存储数据,支持大多数数据类型:字符串,数字,布尔值.列表.集合.元组.字典等. 1.1 定义: lst(勿用list)  lst = [&qu ...

  6. Java之戳中痛点 - (8)synchronized深度解析

    概览: 简介:作用.地位.不控制并发的影响 用法:对象锁和类锁 多线程访问同步方法的7种情况 性质:可重入.不可中断 原理:加解锁原理.可重入原理.可见性原理 缺陷:效率低.不够灵活.无法预判是否成功 ...

  7. 【原创】Linux PSCI框架

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  8. Scratch 3下载,最新版Scratch下载,macOS、Windows版

    下载地址:https://scratch.mit.edu/download 废话不多说,先上下载地址! 之前小弟学习Scratch,用的2.0发现诸多BUG,到度娘想下最新版却没有发现一篇比较正经的文 ...

  9. ResourceManager学习之Application,状态机管理

    Application管理 YARN中,Application是指应用程序,他可能启动多个运行实例,每个运行实例由一个ApplicationMaster与一组该ApplicationMaster启动的 ...

  10. HTML(三)链接,<head>,css样式

    HTML链接 HTML 超链接 一个未访问过的链接显示为蓝色字体并带有下划线 访问过的链接显示为紫色并带有下划线 点击链接时,链接显示为红色并带有下划线 注意:如果为这些超链接设置了 CSS 样式,展 ...