一、前言

在开始分析u-boot重定向之前,先总结一下前两小节u-boot在运行的过程中都有哪些操作:

1.设置CPU为SVC模式
2.设置异常向量表
3.关闭Cache和MMU
   SOC芯片的设置
4.处理唤醒的条件
5.判断是否在内存中运行
6.如果不在内存中运行,则初始化系统时钟和内存控制器(第一阶段)
   如果在内存中运行,则不需要初始化系统时钟和内存控制器(第二阶段)
7.初始化uart 
8.设置了sp
9.调用board_init_f
-----------------------------------------------------------------
第一阶段代码:
board_init_f  功能将u_boot从存储器搬移到内存,跳到内存中运行
第二阶段代码:
board_init_f 
(1)执行了初始化序列表 (板子相关的初始化操作)
(2)为u_boot重定向预留内存

u-boot_2013.01启动流程分析(三)(for exynos4412)

    u-boot_2013.01启动流程分析(三)(for exynos4412)

u-boot_2013.01启动流程分析(三)(for exynos4412)

u-boot_2013.01启动流程分析(三)(for exynos4412)

最上面两幅图的 board_init_f 分别定义的函数 分别是u_boot 第一与第二阶段的board_init_f代码,分别实现自搬移到内存和初始化序列表等不同的功能。具体实现是 : 自搬移函数所在的mmc_boot.c文件所在目录下有个makefile文件,它的条件是 如果定义了CONFIG_SPL_BUILD 就生成 .o 文件 所以spl目录下的Makefile生成的第一阶段的u_boot中包含这个mmc_boot.o文件,因为spl目录下的Makefile中定义了 CONFIG_SPL_BUILD := y ,而生成第二阶段的U_boot的Makefile中没有定义这一句话,所以 它就会生成右面这幅图的 . o 文件,互不干扰。

在刚开始学习的时候,有点想不通为什么spl目录下的Makefile中定义了 CONFIG_SPL_BUILD := y 了,但第一阶段与第二阶段的代码还是都生成了,后来仔细想了想才明白他是有两个Makefile生成了不同阶段的u-boot(有两个,一个顶层目录下的u-boot还有一个spl目录下的u-boot),所以互不干扰。


二、u-boot的重定向之前内存的分配

源码如下:

addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;

/*内存的基地址(4000 0000 + 内存的大小(1 G = 4000 000 = 内存的顶端(addr = 8000 0000 )*/

/* reserve TLB table */
gd->tlb_size = 4096 * 4;
addr -= gd->tlb_size;
/*Addr现在指向内存的顶端,然后向下偏移TLB大小 4K x 4*/

/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);
/* 这种操作是又从 TLB 向下减了 64kb,也是字节对齐操作*/
001 00000000000000000000000000000000000
                                  -1
--------------------------------------------
000 11111111111111111111111111111111111

111 0 000 0000 0000 0000 0000 0000 0000 0000 


gd->tlb_addr = addr;//自己标记为 addr
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);//addr指针又向下移动了4kb
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;//给uboot在高内存预留空间
addr &= ~(4096 - 1);

 /*Addr又向下移动了 u_boot大小的地址

 addr指针又向下移动了4kb

 此时 addr 保存的是这的地址*/

/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
/*TOTAL_MALLOC_LEN =1M + 16kb

 Addr又向下移动了这么多大小的地址 addr_sp 来保 */

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

/*分配 bd_t 结构体大小的空间,同时addr_sp向下偏移这么多 bd 这个指针强行指向这里bd指针的值保存在gd结构体 bd成员中*/


       //记录板子的ID
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /*board id for Linux */

addr_sp -= sizeof (gd_t);//再分配 gd_t 结构体类型大小的地址

id = (gd_t *) addr_sp;  //首地址用 id 来保存


/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;//用gd 结构体指向的成员irq_sp 来保存此时的地址

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;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
 /*reloc_off 中保存的的 是整个u_boot 相对于代码段起始地址的偏移量*/

memcpy(id, (void *)gd, sizeof(gd_t));//gd 所指向的内容拷贝到 id 所指向的地址中


return 0;
}

接下来代码重定向:

              u_boot会将自己从内存的底部搬移到内存顶端,这样做的目的是为了给Linux内核运行预留空间,同时也可以保护自己           的数据不被破坏

三、u-boot的重定向内存分布分析

       看了上面一段代码,发现它的作用是先将指针指向内存的顶端,然后再一步步的从顶端开始预留空间,然后预留空间的起始地址分别保存在gd结构体的各个变量中,我们来画一下内存分配图看看gd结构体的变量到底保存的是哪些值
u-boot_2013.01启动流程分析(三)(for exynos4412)

u-boot_2013.01启动流程分析(三)(for exynos4412)
 可以对照着代码结合这两个图片可以很清晰的分析出u-boot在重定向之前内存分配的问题。


(本人技术经验浅薄,文中难免有疏漏错误之处,请在评论中予以提醒,共同进步,非常感谢!)

相关文章: