u-boot分析(二)

由于这两天家里有点事,所以耽误了点时间,没有按时更新,今天我首先要跟大家说说我对于u-boot分析的整体的思路,然后呢我以后的博客会按照这个内容更新,希望大家关注。

言归正传,我首先说一说我以后的思路,对于u-boot呢,我会结合2440、6410、210这三款主流的学习芯片进行分析,首先会结合u-boot的源码以及我以前的arm启动流程一文http://www.cnblogs.com/wrjvszq/p/4204703.html 总结出u-boot的工作流程,然后以后的博文会结合u-boot源码、芯片手册等内容去分析,u-boot为什么去这么做。

Ok我们下面进入我们今天的内容,今天我会以210为例来分析其u-boot的工作流程,因为2440,6410的启动流程大家跟着u-boot源码中的注释就能解决。

我们根据上篇博文(http://www.cnblogs.com/wrjvszq/p/4206975.html)中提到的方法,可以得到其的入口为arch\arm\cpu\armv7\start.S中的_start我们可以轻松的找到其对应代码,下面我们对其工作流程进行分析:

1.     设置中断向量表。

 .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

       ldr  pc, _fiq

2.     设置处理器到svc的模式(因为我们的上电将会触发reset)

 reset:
bl save_boot_params//空函数
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0

继续跟随代码往下走我们会找到

 #ifndef CONFIG_SKIP_LOWLEVEL_INIT

       bl   cpu_init_crit//跳转至cpu_init_crit

 #endif

 cpu_init_crit:

3.     让L1的I/D caches失效

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

4.     关闭MMU和caches

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

下面代码我们将跳转至lowlevel_init(我们在此以三星的smart210为例)我们可以在\board\samsung\smart210\lowlevel_init.S中找到

     /*
* 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 #ifndef CONFIG_SPL_BUILD

5.     检查reset状态(reset分为两种1、掉电的reset   2、从睡眠唤醒的reset)所以要对其进行区分,以便跳过部分代码。

     push    {lr}//保存lr的值以便待会返回

     /* check reset status  */

     ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfff6ffff
cmp r1, #0x10000
beq wakeup_reset_pre
cmp r1, #0x80000
beq wakeup_reset_from_didle

6.     恢复IO引脚为默认值

     /* IO Retention release */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
ldr r1, [r0]
ldr r2, =IO_RET_REL
orr r1, r1, r2
str r1, [r0]

7.     关闭看门狗

     /* Disable Watchdog */
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #
str r1, [r0]

8.     SRAM、SROM初始化

 /* SRAM(2MB) init for SMDKC110 */
/* GPJ1 SROM_ADDR_16to21 */
ldr r0, =ELFIN_GPIO_BASE ldr r1, [r0, #GPJ1CON_OFFSET]
bic r1, r1, #0xFFFFFF
ldr r2, =0x444444
orr r1, r1, r2
str r1, [r0, #GPJ1CON_OFFSET] ldr r1, [r0, #GPJ1PUD_OFFSET]
ldr r2, =0x3ff
bic r1, r1, r2
str r1, [r0, #GPJ1PUD_OFFSET] /* GPJ4 SROM_ADDR_16to21 */
ldr r1, [r0, #GPJ4CON_OFFSET]
bic r1, r1, #(0xf<<)
ldr r2, =(0x4<<)
orr r1, r1, r2
str r1, [r0, #GPJ4CON_OFFSET] ldr r1, [r0, #GPJ4PUD_OFFSET]
ldr r2, =(0x3<<)
bic r1, r1, r2
str r1, [r0, #GPJ4PUD_OFFSET] /* CS0 - 16bit sram, enable nBE, Byte base address */
ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
mov r1, #0x1
str r1, [r0] /* PS_HOLD pin(GPH0_0) set to high */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
ldr r1, [r0]
orr r1, r1, #0x300
orr r1, r1, #0x1
str r1, [r0]

以下代码是在判断是不是在内存中运行,如果是在内存中运行则跳过部分代码,我们这是第一次运行BL,并且从nand启动的所以代码不在内存中不用关心。

 /* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0x00ffffff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq 1f /* r0 == r1 then skip sdram init */

9.     初始化时钟

 /* init system clock */
bl system_clock_init

10.  初始化内存

 /* Memory initialize */
bl mem_ctrl_asm_init

11.  串口简单初始化

 :
/* for UART */
bl uart_asm_init

12.  取消存储保护区

     bl tzpc_init
//我们没有用onenand所以跳过 #if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif

13.  简单初始化nand

 #if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif

14.  关闭ABB

     /* ABB disable */
ldr r0, =0xE010C300
orr r1, r1, #(0x1<<)
str r1, [r0] /* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET] pop {pc}//函数返回到调用者

代码将会返回到这里

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

代码继续返回到

     /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif

15.  设置堆栈

 /* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, # /* 8-byte alignment for ABI compliance */
ldr r0,=0x00000000

下面代码在判断我们现在是不是在内存中运行,如果在内存将跳转至board_init_in_ram

 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210)
adr r4, _start
ldr r5,_TEXT_BASE
cmp r5,r4
beq board_init_in_ram

下面代码在判断从什么地方启动的我们是从nand启动的所以跳转至nand_boot_210

 ldr    r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1 /* NAND BOOT */
cmp r2, #0x0 @ 512B -cycle
moveq r3, #BOOT_NAND cmp r2, #0x2 @ 2KB -cycle
moveq r3, #BOOT_NAND cmp r2, #0x4 @ 4KB -cycle -bit ECC
moveq r3, #BOOT_NAND cmp r2, #0x6 @ 4KB -cycle -bit ECC
moveq r3, #BOOT_NAND cmp r2, #0x8 @ OneNAND Mux
moveq r3, #BOOT_ONENAND /* SD/MMC BOOT */
cmp r2, #0xc
moveq r3, #BOOT_MMCSD /* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR /* Uart BOOTONG failed */
cmp r2, #(0x1<<)
moveq r3, #BOOT_SEC_DEV ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET] ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
beq nand_boot_210
cmp r1, #BOOT_MMCSD
beq mmcsd_boot_210

下面代码将会跳转至board_init_f_nand

 nand_boot_210:
bl board_init_f_nand mmcsd_boot_210:
bl board_init_f
board_init_in_ram:
#endif
bl board_init_f

我们继续可以看看board_init_f_nand

 void board_init_f_nand(unsigned long bootflag)
{
__attribute__((noreturn)) void (*uboot)(void);
copy_uboot_to_ram_nand();//复制nand中的bl2到内存 /* Jump to U-Boot image */
uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳转至内存执行uboot
(*uboot)();
/* Never returns Here */
}

16.  复制nand中的bl2到内存

l  我们可以到copy_uboot_to_ram_nand函数中看到其将我们的BL2复制到CONFIG_SYS_TEXT_BASE地址处,用SI(Source Insight)我们可以看到这个地址为0x23E00000,我在arm启动流程一文中说到过210的地址布局可以知道其内存是从0x20000000开始的,所以上面这个地址在内存中

 int copy_uboot_to_ram_nand (void)
{
int large_block = ;
int i;
vu_char id; NAND_CONTROL_ENABLE();
NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00; /* wait for a while */
for (i=; i<; i++);
id = NFDATA8_REG;
id = NFDATA8_REG; if (id > 0x80)
large_block = ; /* read NAND Block.
* 128KB ->240KB because of U-Boot size increase. by scsuh
* So, read 0x3c000 bytes not 0x20000(128KB).
*/
return nandll_read_blocks(CONFIG_SYS_TEXT_BASE, COPY_BL2_SIZE, large_block);
}

17.  跳转至内存执行uboot

l  通运行代码段的方式完成向BL2的跳转

         /* Jump to U-Boot image */
uboot = (void *)CONFIG_SYS_TEXT_BASE;//跳转至内存执行uboot
(*uboot)();

到此我们第一阶段的代码分析完成,接下来我们进行第二阶段的分析,由于我们的210第二阶段的代码入口和第一阶段相同,所以第一阶段执行的代码又会被执行一次,直到运行到下面代码的时候才会发生变化

 #if defined(CONFIG_TINY210) || defined(CONFIG_SMART210)
adr r4, _start
ldr r5,_TEXT_BASE
cmp r5,r4
beq board_init_in_ram

我们代码将会跳到board_init_in_ram运行

 board_init_in_ram:
#endif
bl board_init_f

我们继续跳进board_init_f这个函数在arch\arm\lib\board.c中在这个函数中比较重要的一段代码,下面的代码是在遍历一个函数指针数组,并且一一执行

     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != ) {
hang ();
}
}

通过查找我们可以发现这个数组中的内容,是一大堆东西的初始化函数,也就是说上面的代码进行了一系列的初始化

 init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
NULL,
};

经过上面的代码后其会返回到start.s中继续运行,中间又经过一系列东西,好多和第一阶段重复,所以这里就不一一分析了,最终通过下面代码进入board_init_r函数

     ldr    r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr _board_init_r_ofs:
.word board_init_r - _start

在这个函数中又进行了硬件和软件的初始化最后会落脚在

     /* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}

通过上面的代码对用户命令进行解析。至此210的u-boot工作流程分析完毕,2440和6410比较简单,基本按照注释就可以总结出来。

下面我将自己总结的流程跟大家分享一下

l  2440

第一阶段

1.        设置中断向量表

2.        设置处理器工作模式为svc

3.        刷新I/Dcaches

4.        关闭MMU和caches

5.        关闭看门狗

6.        关所有中断

7.        设置系统时钟

8.        初始化串口

9.        初始化nand

10.    初始化内存

11.    复制BL到内存

12.    设置堆栈

13.    清楚BSS段

第二阶段

1.        初始化串口

2.        LCD初始化

3.        网卡初始化

4.        Led初始化

5.        执行用户命令

l  6410

第一阶段

1.        设置中断向量表

2.        设置处理器工作模式为svc

3.        刷新I/Dcaches

4.        关闭MMU和caches

5.        外设基地址初始化

6.        点亮led

7.        关看门狗

8.        初始化时钟

9.        初始化串口

10.    初始化nand

11.    初始化内存

12.    复制BL到内存

13.    设置堆栈

14.    清楚BSS段

第二阶段

1.        初始化串口

2.        LCD初始化

3.        网卡初始化

4.        Led初始化

5.        执行用户命令

l  210

第一阶段

1.        设置中断向量表

2.        设置处理器工作模式为svc

3.        让L1的I/D caches失效

4.        关闭MMU和caches

5.        检查reset状态

6.        回复io引脚为默认值

7.        关看门狗

8.        Sram、srom初始化

9.        初始化时钟

10.    初始化内存

11.    初始化串口

12.    取消存储保护区

13.    初始化nand

14.    关闭ABB

15.    设置堆栈

16.    复制BL2到内存

17.    跳转到内存执行

第二阶段

1.        初始化串口

2.        LCD初始化

3.        网卡初始化

4.        Led初始化

5.        执行用户命令

u-boot分析(二)----工作流程分析的更多相关文章

  1. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  2. 第2章 rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释. 以下是本文的姊妹篇: 1.rsync(一):基本命令和用法 2.rsync(二):ino ...

  3. [国嵌笔记][030][U-Boot工作流程分析]

    uboot工作流程分析 程序入口 1.打开顶层目录的Makefile,找到目标smdk2440_config的命令中的第三项(smdk2440) 2.进入目录board/samsung/smdk244 ...

  4. rsync算法原理和工作流程分析

    本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释.本文不会介绍如何使用rsync命令(见rsync基本用法),而是详细解释它如何实现高效的增 ...

  5. rsync(三)算法原理和工作流程分析

    在开始分析算法原理之前,简单说明下rsync的增量传输功能. 假设待传输文件为A,如果目标路径下没有文件A,则rsync会直接传输文件A,如果目标路径下已存在文件A,则发送端视情况决定是否要传输文件A ...

  6. Kafka之工作流程分析

    Kafka之工作流程分析 kafka核心组成 一.Kafka生产过程分析 1.1 写入方式 producer采用推(push)模式将消息发布到broker,每条消息都被追加(append)到分区(pa ...

  7. 【转】Hostapd工作流程分析

    [转]Hostapd工作流程分析 转自:http://blog.chinaunix.net/uid-30081165-id-5290531.html Hostapd是一个运行在用户态的守护进程,可以通 ...

  8. J2EE进阶(十八)基于留言板分析SSH工作流程

    J2EE进阶(十八)基于留言板分析SSH工作流程   留言板采用SSH(Struts1.2 + Spring3.0 + Hibernate3.0)架构.   工作流程(以用户登录为例):   首先是用 ...

  9. openVswitch(OVS)源代码分析之工作流程(数据包处理)

    上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到 ...

随机推荐

  1. BaseNavigationController自定义导航栏

    #import <UIKit/UIKit.h> @interface RCDNavigationViewController : UINavigationController<UIG ...

  2. freemarker常用标签解释遍历

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...

  3. POJ1049 Microprocessor Simulation

    题目来源:http://poj.org/problem?id=1049 题目大意: 一种小型的微处理器有以下特性: 1. 每个字长4bit. 2. 地址用2个字进行编码.先高位字后低位字,即高位字大的 ...

  4. 80C51单片机介绍

    80C51单片机属于MCS-51系列单片机,由Intel公司开发,其结构是8048的延伸,改进了8048的缺点. 增加了如乘(MUL).除(DIV).减(SUBB).比较(CMP).16位数据指针.布 ...

  5. shell入门基础必备

    作者:KornLee 2005-02-03 15:32:57 来自:Linux先生    1.建立和运行shell程序   什么是shell程序呢? 简单的说shell程序就是一个包含若干行 shel ...

  6. Storm(1)-centos7下安装单机版Strom

    1.所需软件: jdk8.zookeeper.storm 2.安装zookeeper单机版 下载:http://zookeeper.apache.org/releases.html#download ...

  7. 1.1 Go安装与项目结构初始化

    软件安装安装包下载地址为:https://golang.org/dl/ 如果打不开可以: https://golang.google.cn/dl/ https://dl.google.com/go/g ...

  8. java——数组操作

    排序.二分查找.复制数组.填充 package follow_pack; import java.util.Arrays; import java.text.DecimalFormat; public ...

  9. vue中组件传值方式汇总

    在应用复杂时,推荐使用vue官网推荐的vuex,以下讨论简单SPA中的组件间传值. 一.路由传值 路由对象如下图所示: 在跳转页面的时候,在js代码中的操作如下,在标签中使用<router-li ...

  10. c++ 迭代器失效学习 effective-STL 9条

    https://www.cnblogs.com/newbeeyu/p/6883122.html  结合 effective  STL 条款9 https://www.cnblogs.com/fnlin ...