uboot-2015-07的start.S的文件启动过程(2)
2016-06-12 18:45
281 查看
1.在文件的最开始有这样的注释
/* ************************************************************************* * * 启动代码 (被 ARM 的 reset 调用,不包括中断) * * 只有不从 memory 启动的时候才做重要的初始化 * 重定位代码到 ram * 设置栈 * 跳转到启动第二阶段 * ************************************************************************* */
上面已经大致交代了这个start.S文件要做的事情
在u-boot.lds文件里面指明了入口是_start标号,所以开始会先加载_start的内容,而此函数入口在vector.s里面(与以往内核不同的地方)。里面会设置中断向量表等等,当然,这些在编译的时候已经加载过了,芯片复位的时候会跳到reset处继续执行代码
# 2.首先设置cpu处于管理模式
reset: /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0
# 3.关看门狗,屏蔽中断
# define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ # endif /* 关看门狗 */ ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] /* 屏蔽所有中断 */ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif
# 4.初始化时钟分频系数
/* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0]
# 5.CPU初始化
/* 擦除DCache与ICache */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* 禁止MMU与Cache */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0 /* 重定位之前, 通过lowlevel_init函数设置RAM时间参数 */ mov ip, lr bl lowlevel_init mov lr, ip mov pc, lr
# 6.调用\_main
这里开始有点疑惑,怎么没有内存设置以及重定位代码就直接开始\_main了,SourceInsight全局搜索**_main**发现在crt0.S里面有**_main**函数体
**我们先看下_main函数的介绍**
/* * 1. 为调用 board_init_f() C函数设置初始化环境 * 环境仅仅提供栈以及 GD ('global data') 数据结构的存放空间, 只有已经初始化的静态变量可以在该阶段使用 * * 2. 调用 board_init_f()。为系统从RAM启动准备硬件环境, board_init_f() 必须使用 GD 来 * 存放在最后阶段需要用到的数据。数据包括重定位的目标地址, 未来要使用的栈, 以及未来要使用的 GD 的新地址 * * 3. 为 stack and GD 设置中转环境 * * 4. 调用 relocate_code(). 函数重定位u-boot到 board_init_f() 指定的位置 * * 5. 为调用 board_init_r() 设置最新的环境。初始化BSS,非静态的全局变量和系统RAM中的栈 * GD保留被 board_init_f() 设置的值。 有些CPU还有一些关于内存的操作没有进行, 因此要调用 call c_runtime_cpu_setup * * 6. 跳转到 board_init_r(). */
7.设置C运行环境并且调用 board_init_f(0)
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /* CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - GENERATED_GBL_DATA_SIZE = 0x30000f50 */ bic sp, sp, #7 /* 8字节对齐 */ mov r2, sp sub sp, sp, #GD_SIZE /* 在栈上面分配GD的空间,GD_SIZE = 168 */ bic sp, sp, #7 /* 8字节对齐 */ mov r9, sp /* GD is above SP */ mov r1, sp mov r0, #0 /* 清空GD空间,以待重新赋值 */ clr_gd: cmp r1, r2 strlo r0, [r1] /* clear 32-bit GD word */ addlo r1, r1, #4 /* move to next */ blo clr_gd bl board_init_f /* 调用board_init_f对GD等等进行初始化,SP = 30000E80 */
值得说的是上面的GENERATED_GBL_DATA_SIZE宏定义,它是由DEFINE(GENERATED_GBL_DATA_SIZE,(sizeof(struct global_data) + 15) & ~15);获得的,也就是相当于#define GENERATED_GBL_DATA_SIZE ((sizeof(struct global_data) + 15) & ~15)。
#define DEFINE(sym, val) \ asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))
这个语句在编译的时候会被转换为.h文件以供别的文件包含,这个宏定义的作用大概应该是让形如#define GENERATED_GBL_DATA_SIZE ((sizeof(struct global_data) + 15) & ~15)可以随用随定义(此处存在疑问,为什么不直接宏定义呢),补充:这个宏可以使全局变量gd_t可以在.S文件里面使用汇编代码直接访问到,类似 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ 这样的语句
另外由于 mov r9, sp 这句话的作用,以后要想在u-boot其它文件里面访问此时的SP(也就是gd的位置),就需要在文件头部加上DECLARE_GLOBAL_DATA_PTR,原型是
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9") //在2015版的里面使用下面的函数得到,原理是一样的 static inline gd_t *get_gd(void) { gd_t *gd_ptr; __asm__ volatile("mov %0, r9\n" : "=r" (gd_ptr)); return gd_ptr; }
8.单板初始化board_init_f
(需要注意的是,本处选择的board_init_f为board.c里面的,但是从2014以后就默认编译的是board_f里面的函数了,这里为了学习方便,暂且选择board.c里面的函数进行分析,以后再去分析另一个分支代码,切换代码到board.c里面的方法在以后的真正移植过程中会说到)初始化全局变量gd,调用函数队列
memset((void *)gd, 0, sizeof(gd_t)); /* 初始化gd填充为0 */ gd->mon_len = (ulong)&__bss_end - (ulong)_start; /* 整个u-boot代码与数据段的总大小,可以由u-boot.lds文件得出 */ /* 调用一个初始化函数队列 */ for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
下面是init_sequence函数对列里面的函数
board_early_init_f /* 时钟初始化,引脚初始化,不同的单板要进行不同的设置 */ timer_init /* 定时器初始化 */ env_init, /* initialize environment */ init_baudrate, /* 波特率初始化,默认CONFIG_BAUDRATE为115200 */ gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE); serial_init, /* 串口初始化 */ console_init_f, /* console初始化 */ display_banner, /* 表明代码运行到这里了 */ print_cpuinfo, /* 打印cpu信息 */ dram_init, /* 配置可用的RAM大小,默认PHYS_SDRAM_1_SIZE为64M */ //dram_init 函数内部有 gd->ram_size = PHYS_SDRAM_1_SIZE;
初始化程序最终存放地址addr
addr = CONFIG_SYS_SDRAM_BASE + get_effective_memsize(); /* addr指向SDRAM的结尾,0x34000000处,get_effective_memsize() = 64M */ /* 保留 TLB table,PGTABLE_SIZE默认为4K */ gd->arch.tlb_size = PGTABLE_SIZE; addr -= gd->arch.tlb_size; /* addr减去TLB大小 */ /* 下移到下一个64K开始处,相当于64KB对齐 */ addr &= ~(0x10000 - 1); gd->arch.tlb_addr = addr; /* TLB的指向当前的addr */ /* 4KB对齐 */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lx\n", addr); /* * 为 U-Boot 代码, 数据与bss保留空间,gd->mon_len;在函数刚开始的时候就赋值了,大小就是u-boot整个编译出来的文件大小 * 4KB对齐 */ addr -= gd->mon_len; addr &= ~(4096 - 1); /* * 为 malloc() 函数保留堆区 */ addr_sp = addr - TOTAL_MALLOC_LEN; /* addr的栈等于addr减去堆的大小 */ /* * 保留 Board Info 结构体的空间与一个gd_t的副本 */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; /* 设置栈指针 */ addr_sp -= sizeof (gd_t); /* 栈减去gd_t副本空间 */ id = (gd_t *) addr_sp; /* id赋值为当前addr_sp的值 */ gd->irq_sp = addr_sp; /* 为中断栈留3个字节 */ addr_sp -= 12; /* 8字节对齐 */ addr_sp &= ~0x07; gd->relocaddr = addr; /* 重定位的目标地址为代码区的起始地址 */ gd->start_addr_sp = addr_sp; /* 栈地址为上面的addr_sp */ gd->reloc_off = addr - (ulong)&_start; /* 重定位的偏移量是代码区存放地址减去0地址 */ memcpy(id, (void *)gd, sizeof(gd_t)); /* 拷贝gd_t到重定位之后的预留gd_t副本的地址 */
重定位代码之前的准备
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp,sp为重定位代码栈区起始地址 */ bic sp, sp, #7 /* 8字节对齐 */ ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* 新的GD在bd下方,参见内存分布图 */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off,r0为重定位偏移量 */ add lr, lr, r0 /* 重定位之后代码的返回地址 */ ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr,r0为重定位的目标地址 */ b relocate_code
开始重定位代码
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */ subs r4, r0, r1 /* r4 <- relocation offset */ beq relocate_done /* skip relocation */ ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */ /* 到现在为止,比较重要的几个寄存器的值为 * r0 = gd->reloc_off,r0为重定位偏移量,本处也就是目标地址 * r1 = __image_copy_start,r1为需要重定位代码当前的起始地址,也就是代码段的开始0 * r4 = r0 - r1,r4为重定位的偏移值,偏移值减去0还是0 * r2 =__image_copy_end,r2为需要重定位代码的结束地址,r2 - r1就是需要重定位代码长度了 */ copy_loop: ldmia r1!, {r10-r11} /* 从源地址 [r1] 开始拷贝,pop到r10与r11里面,一次8个字节 */ stmia r0!, {r10-r11} /* 拷贝到目标地址 [r0] */ cmp r1, r2 /* 一直到 [r1] 等于 [r2], 说明代码拷贝结束 */ blo copy_loop /* * 重定位修正 .rel.dyn */ ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ fixloop: ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */ and r1, r1, #0xff cmp r1, #23 /* relative fixup? */ bne fixnext /* relative fix: increase location by offset */ add r0, r0, r4 ldr r1, [r0] add r1, r1, r4 str r1, [r0] fixnext: cmp r2, r3 blo fixloop
9.跳转运行第二第阶段代码
到这里已经完成了整个uboot的重定位以及一些基本设备的初始化,接下来就是调用board_init_r,这个是uboot启动的第二阶段,包括nand flash的初始化,nor flash 等等设备初始化,以及各种命令的初始化。最终内存的规划如下(虽然不同版本的uboot内存分布有所不同,但是大体上都是相同的):
相关文章推荐
- DNS配置及理论详解
- Qt-configure配置选项
- tomcat简介及原理解说
- Nginx的proxy_cache缓存功能
- 【上海交大OJ1061】小M的服务器
- 变量的初始化顺序
- 用 pystun 获取局域网的NAT类型和公网IP
- packet for query is too large <1781> 1024 >. you can change this value on the server by setting the
- Python学习笔记 —— 模块
- SQL 删除索引错误
- 隐马尔可夫模型介绍
- 异常处理__code
- Linux下Rsync服务部署
- 博客移动
- Multithreading annd Grand Central Dispatch on ios for Beginners Tutorial-多线程和GCD的入门教程
- U盘安装CentOS7的最终解决方案
- mvc模式
- svn客户端与svn_eclipse插件不同步问题
- windows程序设计(1)---第一个windows程序--MessageBox
- PHP 工厂模式