上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html

讲到uboot-spl的工作流程,接下来简述一下uboot.bin的工作流程,这对应BL2的流程。

BL2的主要文件和任务流程如下:

arch/arm/cpu/armv7/start.S
           1. 设置CPU为SVC模式
           2. 关闭MMU
           3. 关闭Cache
           4. 跳转到lowlevel_init.S low_level_init
board/samsung/origen/lowlevel_init.S
           5. 初始化时钟
           6. 初始化内存
           7. 初始化串口
           8. 关闭看门狗
           9. 跳转到crt0.S _main
arch/arm/lib/crt0.S
           10. 设置栈
           11. 初始化C运行环境
           12. 调用board_init_f()
arch/arm/lib/board.c
           13. board_init_f对全局信息GD结构体进行填充
arch/arm/lib/crt0.S
           14. 代码重定位------------BL2的最后的工作, 执行完就进入DRAM执行BL2

1.首先从board_init_f函数开始,它是定义在/u-boot/arch/arm/lib/board.c文件中。

它的作用是初始化开发板。需要注意的是,此时程序是在flash中运行的。

 void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp; #if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN9IW1P1)
memset((void*)0x00000000, , *);
#endif
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); memset((void *)gd, , sizeof(gd_t));
gd->mon_len = _bss_end_ofs + sizeof(struct spare_boot_head_t);
gd->debug_mode = ;

gd是一个保存在ARM的r8寄存器中的gd_t结构体的指针,该结构体包括了u-boot中所有重要的全局变量,它是在arch/arm/include/asm目录下的global_data.h文件内被定义的。上述代码的作用是为gd分配地址,并清零,最后得到整个u-boot的长度。gd_t结构体的定义如下:

typedef    struct    global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
/* "static data" needed by at91's clock.c */
unsigned long cpu_clk_rate_hz;
unsigned long main_clk_rate_hz;
unsigned long mck_rate_hz;
unsigned long plla_rate_hz;
unsigned long pllb_rate_hz;
unsigned long at91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
/* "static data" needed by most of timer.c on ARM platforms */
unsigned long timer_rate_hz;
unsigned long tbl;
unsigned long tbu;
unsigned long long timer_reset_value;
unsigned long lastinc;
#endif
#ifdef CONFIG_IXP425
unsigned long timestamp;
#endif
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long ram_size_mb; /* RAM size MB*/
unsigned long mon_len; /* monitor len */
unsigned long irq_sp; /* irq stack pointer */
unsigned long start_addr_sp; /* start_addr_stackpointer */
unsigned long reloc_off;
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
unsigned long tlb_addr;
#endif
#if defined(CONFIG_ALLWINNER)
int uart_console;
int boot_card_num;
unsigned int layer_para;
unsigned int layer_hd;
int key_pressd_value;
int axp_power_soft_id;
int power_step_level;
int pmu_suspend_chgcur;
int pmu_runtime_chgcur;
int limit_vol;
int limit_cur;
int limit_pcvol;
int limit_pccur;
int power_main_id;
int power_slave_id;
char *script_mod_buf;
int script_main_key_count;
int force_shell;
uint malloc_noncache_start;
int lockflag;
uint chargemode;
uint force_download_uboot;
int securemode;
uint vbus_status; //0: 未知;1:存在;2:不存在
uint debug_mode;
#endif
void **jt; /* jump table */
char env_buf[]; /* buffer for getenv() before reloc. */
} gd_t;
 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != ) {
hang ();
}
}

上述代码的作用是循环调用init_sequence函数指针数组中的成员,该数组成员函数主要完成一些初始化的工作。

其中init_sequence的定义如下:

init_fnc_t *init_sequence[] = {
//#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
//#endif
sunxi_probe_securemode,
#if defined(CONFIG_USE_NEON_SIMD)
arm_neon_init,
#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 */
display_inner, /* show the inner version */
print_commit_log,
script_init,
#if defined(SUNXI_OTA_TEST)
display_ota_test,
#endif
get_debugmode_flag,
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
smc_init,
init_func_pmubus,
power_source_init,
check_update_key,
check_uart_input,
dram_init, /* configure available RAM banks */
sunxi_set_secure_mode,
NULL,
};

board_early_init_f函数(在board/samsung/smdk2410目录下的smdk2410.c文件内)完成ARM的时钟频率和IO的设置;

timer_init函数(在arch/arm/cpu/arm920t/s3c24x0目录下的timer.c文件内)完成定时器4的设置;

env_init函数(在common目录下的env_flash.c文件内,因为include/configs/smdk2410.h中定义了CONFIG_ENV_IS_IN_FLASH)完成环境变量的设置;

init_baudrate函数(在arch/arm/lib目录下的board.c文件内)完成波特率的设置;

serial_init函数(在drivers/serial目录下的serial_s3c24x0.c文件内,因为include/configs/smdk2410.h中定义了CONFIG_S3C24X0_SERIAL)完成串口通讯的设置;

console_init_f函数(在common目录下的console.c文件内)完成第一阶段的控制台初始化;

display_banner函数(在arch/arm/lib目录下的board.c文件内)用来打印输出一些信息;

dram_init函数(在board/samsung/smdk2410目录下的smdk2410.c文件内)用来配置SDRAM的大小。

 #if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
if(gd->ram_size)
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
else
addr = CONFIG_SYS_SDRAM_BASE + (1U<<);

得到SDRAM的末位物理地址,即SDRAM的空间分布。

 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
/* reserve TLB table */
addr -= ( * ); /* round down to next 64 kB limit */
addr &= ~(0x10000 - ); gd->tlb_addr = addr;
debug("TLB table at: %08lx\n", addr);
#endif /* round down to next 4 kB limit */
addr &= ~( - );
debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

分配SDRAM的高64kB区域作为TLB,并且该区域也被用于U-Boot。16KB保存TLB表,

     /*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~( - ); debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> , addr);

分配SDRAM的下一个单元为U-Boot代码段、数据段及BSS段。

 #ifndef CONFIG_SPL_BUILD
/*
* reserve memory for malloc() arena
*/
100 addr_sp = addr - TOTAL_MALLOC_LEN;
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> , addr_sp);
#ifdef CONFIG_NONCACHE_MEMORY
104 addr_sp &= (~(0x00100000 -));
addr_sp -= CONFIG_NONCACHE_MEMORY_SIZE;
#endif
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp); #ifdef CONFIG_MACH_TYPE
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp); /* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
134 /* leave 3 words for abort-stack */
addr_sp -= ; /* 8-byte alignment for ABI compliance */
138 addr_sp &= ~0x07;
#else
addr_sp += ; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif

第100行的意思为在SDRAM中又开辟了一块malloc空间,该区域是紧挨着上面定义的U-Boot区域的下面。然后在SDRAM中又分别依次定义了bd结构体空间、gd结构体空间和3个字大小的异常中断堆空间。其中bd结构体的数据原型为bd_t数据结构,它表示的是“板级信息”结构体,这些信息包括开发板的波特率、IP地址、ID、以及DRAM等信息,它是在arch/arm/include/asm目录下的u-boot.h文件中定义的。下图详细描述了SDRAM的空间分配情况(地址从上到下递减):

64KB的TLB
4KB的RAM空间
4KB的U-Boot代码段、数据段及BSS段
malloc空间
bd空间
gd空间
3字异常中断堆空间
栈空间
     gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */ gd->relocaddr = addr + sizeof(struct spare_boot_head_t) + sizeof(uboot_hash_value);
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;

上述代码主要的作用是为gd结构体赋值,其中display_dram_config函数的作用是计算SDRAM的大小,并把它通过串口显示在控制台上。

     memcpy(id, (void *)gd, sizeof(gd_t));

     relocate_code(addr_sp, id, addr + sizeof(struct spare_boot_head_t)+sizeof(uboot_hash_value));

     /* NOTREACHED - relocate_code() does not return */
}

在board_init_f函数的最后是跳转到relocate_code函数体内,这个函数是在arch/arm/cpu/arm920t目录下的start.s文件内,也就是说从最开始的start.s跳到board.c,又从board.c跳回到了start.s中,这是因为此时程序需要重定向,即把代码从flash中搬运到ram中,这个过程是需要汇编这个低级语言来完成的。传递给relocate_code函数的三个参数分别栈顶地址、数据ID(即全局结构gd)在SDRAM中的起始地址和在SDRAM中存储U-Boot的起始地址。需要注意的是relocate_code函数执行完后,并不会返回到relocate_code (addr_sp, id, addr);的下一条语句继续执行。而是继续运行start.S的程序。

下面继续看start.S的程序:

/*
* void relocate_code (addr_sp, gd, addr_moni)
*
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
*/
.globl relocate_code relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */

取得三个参数,分别放入寄存器r4、r5和r6。

    /* Set up the stack                            */
stack_setup:
mov sp, r4

设置堆栈地址。

    /* Set up irq stack */
add r4, r4, #
add r4, r4, #0x2000
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x12
msr cpsr_c, r0
mov sp, r4

设置IRQ 栈。

/* Set up svc stack */
sub r4, r4, #0x2000
sub r4, r4, #
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x13
msr cpsr_c, r0

设置SVN 栈。

    adr    r0, _start
cmp r0, r6
moveq r9, # /* no relocation. relocation offset(r9) = 0 */
beq clear_bss /* skip relocation */ @ mov r9, #
@ b clear_bss mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _image_copy_end_ofs
add r2, r0, r3 /* r2 <- source end address */ copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop

判断U-Boot是在什么位置上,如果在SDRAM中,则直接跳到BSS段清零函数处即可;如果在FLASH中,则要把U-Boot复制到SDRAM中指定的位置处。

#ifndef CONFIG_SPL_BUILD
/*
* fix .rel.dyn relocations
*/
@ldr r0, _TEXT_BASE /* r0 <- Text base */
adr r0, _start
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #]
and r7, r1, #0xff
cmp r7, # /* relative fixup? */
beq fixrel
cmp r7, # /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR # /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, # /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
b clear_bss
_rel_dyn_start_ofs:
.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
.word __rel_dyn_end - _start
_dynsym_start_ofs:
.word __dynsym_start - _start #endif /* #ifndef CONFIG_SPL_BUILD */

上述代码的含义是对rel.dyn进行重定向。

...
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

该段代码的作用是跳转到board_init_r函数,并且给该函数传递了两个参数:全局结构gd在SDRAM中的起始地址和在SDRAM中存储U-Boot的起始地址。board_init_r函数是在arch/arm/lib目录下的board.c文件中,也就是又回到了上面执行过的board_init_f函数所在的board.c文件中。以后,程序就开始在SDRAM中运行了。

以上是uboot.bin的流程。

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

uboot学习之uboot.bin的运行流程的更多相关文章

  1. IOS学习笔记1—Iphone程序运行流程

    Iphone程序运行流程 main.m文件,iOS应用程序的主入口 main函数的两个参数为命令行参数,在ios开发中不会用到这些元素,包括这两个参数是为了与标准ANSI C保持一致 UIApplic ...

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

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

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

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

  4. Junit4学习(三)Junit运行流程

    一,验证Junit测试方法的流程 1,在test/com.duo.util右键,新建测试类 2,生成后的代码: package com.duo.util; import static org.juni ...

  5. 跟着百度学习php之ThinkPHP的运行流程-2

    Thinkphp为了提高编译的效率,第一次运行的时候thinkphp会把文件全部编译到temp目录下的~runtime.php文件,在第二次运行的时候会直接读取这个文件.所以我们在线下自己写代码测试的 ...

  6. 跟着百度学习php之ThinkPHP的运行流程-1

    我在index\Lib\Action\目录下新建了一个ShowAction.class.php文件.ps:该目录是控制器的目录. 然后这个文件中继承了action这个类.代码如下: <?php ...

  7. u-boot学习(两):u-boot简要分析

    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 看到不错的文章.不要加入收藏夹, ...

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

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

  9. 第二天-uboot学习

    源码阅读方法1.源码目录结构2.配置(支持当前使用的硬件)3.编译(Makefile)4.启动流程 工具使用1.在同一文件查找 shitf+8 N n进行上下查找 2.在工程目录中 ctags ubo ...

随机推荐

  1. 【JS档案揭秘】第四集 关于this的讨论到此为止

    网上关于this的指向问题的博客文章很多,但大多数都是复制粘贴,也不能用简洁的语言讲清楚,而是不停地写一些示例,看得人云里雾里. 这一集,我只给出结论,以及判定的通用方法,至于是否确实如我所讲,大家可 ...

  2. Spring Boot 与 Mybatis、Mysql整合使用的例子

    第一步: 创建一个SpringBoot的工程,在其中的Maven依赖配置中添加对JDBC.MyBatis.Mysql Driver的依赖具体如下: <!-- JDBC --> <de ...

  3. 通过 Channel 实现 Goroutine Pool

    最近用到了 Go 从 Excel 导数据到服务器内部 用的是 http 请求 但是发现一个问题 从文件读取之后 新开 Goroutine 会无限制新增 导致全部卡在初始化请求 于是乎就卡死了 问题模拟 ...

  4. javaio字节流复制文件夹

    public class Copy1 { public static void main(String[] args) throws IOException { File src=new File(& ...

  5. unity_实用小技巧(避免游戏对象被销毁时声音消失)

    在游戏中我们使用碰撞检测,当两个物体发生碰撞时产生声音音效,代码如下: 如果使用上述代码,我们会发现,在脚本中使用AudioSource声明该声音,当该物体被销毁时声音也会立刻停止. 但是我们希望声音 ...

  6. 初尝RabbitMQ消息队列

    RabbitMQ 是什么? 消息中间件 作用?     用于分布式项目中的模块解耦 用法? 创建队列 创建消息工厂并设置 (生产者额外步骤 : 创建消息) 创建连接,通道 声明队列 生产者 : 发送消 ...

  7. (五十八)c#Winform自定义控件-管道阀门(工业)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  8. Installing the JMeter CA certificate for HTTPS recording

    参考: http://jmeter.apache.org/usermanual/component_reference.html#HTTP(S)_Test_Script_Recorder User m ...

  9. zoj - 4059 Kawa Exam scc + dsu

    Kawa Exam 题解:先scc把图变成树, 然后对于这若干棵树在进行dsu的操作. dsu就是先找到最大的子树放在一边,然后先处理小的子树,最后处理大的子树.无限递归. 重要的一点就是 是否重新添 ...

  10. codeforces 735C. Tennis Championship(贪心)

    题目链接 http://codeforces.com/contest/735/problem/C 题意:给你一个数n表示有几个人比赛问最多能赢几局,要求两个比赛的人得分不能相差超过1即得分为2的只能和 ...