1.u-boot启动第一阶段
start.S (arch\arm\cpu\arm920t)
_start: b start_code1.1 set the cpu to SVC32 mode
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
1.2 关闭看门狗
# define pWTCON 0x53000000
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
1.3 屏蔽中断 mask all IRQs by setting all bits in the INTMR - default
# define INTMSK 0x4A000008 /* Interrupt-Controller base addresses *///2410 部分 2440 没有
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
1.4 设置时钟 ===========时钟设置有问题=================================================================
/* FCLK:HCLK:PCLK = 1:2:4 */1.5cpu 底层初始化
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //在初始化内存之前没有进行时钟的设置
#endif
cpu_init_crit:
a.flush v4 I/D caches
b.disable MMU stuff and caches
c.bl lowlevel_init //底层的初始化
1.6 跳转到 bl _main
bl _main==> bic sp, sp, #清低8位。
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) //0x30000f80 看确定sp的值分析一节
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, #GD_SIZE /* allocate one GD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
确定GD_SIZE的大小
反汇编文件: 770: e24dd080 sub sp, sp, #128 ; 0x80
==>GD_SIZE = 128 = 0X80
===>sp = 0x30000f80 - 0x80 = 30000f00
==> sp 的值为0x30000f00。这里的mov r8,sp 可以通过r8寄存器,来访问目前sp(0x30000f00) 这个地址空间
mov r8, sp /* GD is above SP */
mov r0, #0
mov r0,#0 这里的r0 是给 bl board_init_f函数传递参数
解释r8:
DECLARE_GLOBAL_DATA_PTR; Board.c \arch\arm\lib)
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8") Global_data.h(\arch\arm\include\asm)
r8 是一个register 变量。在编译的时候,会通过 添加 编译选项 -ffix= r8 。表示r8 被占用。
总结:在调用C语言之前,设置了栈指针sp 的地址。
系统在初始化内存sdram之前,没有配置时钟。存在不合理的地方,移植2440的时候,需要进行改进
====================================================================================
2.u-boot
2.1 void board_init_f(ulong bootflag) 函数分析
2.1.1
gd->mon_len = _bss_end_ofs;__bss_end__:在u-boot.lds 中进行的定义
_bss_end_ofs:的定义
.globl _bss_end_ofs
_bss_end_ofs:
.word __bss_end__ - _start
从而可以得到 _bss_end_ofs 表示的是整个代码的大小
2.1.2
#elif defined CONFIG_OF_SEPARATE
/* FDT is at end of image */
gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
此处:CONFIG_OF_SEPARATE 并没有定义。
这一行代码是做什么用的?
const void *fdt_blob; /* Our device tree, NULL if none */
这里是选择u-boot 是否支持设备树的选项
这里也是关于设备树的
/* Allow the early environment to override the fdt address */
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
(uintptr_t)gd->fdt_blob);
2.1.3 函数数组的执行
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
2.1.3.1 init_sequence
arch_cpu_init, /* basic arch cpu dependent setup */
mark_bootstage,
board_early_init_f,
timer_init, /* initialize timer */
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 */
print_cpuinfo, /* display cpu info (and speed) */
dram_init, /* configure available RAM banks */
NULL, //表示结束
1): arch_cpu_init, /* basic arch cpu dependent setup
int arch_cpu_init(void)
__attribute__((weak, alias("__arch_cpu_init")));
int __arch_cpu_init(void)
{
return 0;
}
2): board_early_init_f(void) //设置时钟
3):timer_init, /* initialize timer */ 设置定时器
4):dram_init, /* configure available RAM banks */ 设置内存 内存的大小
2.1.4
memset((void *)gd, 0, sizeof(gd_t));//初始化0x30000f00 到0x30000f80之前的内存单元。
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
gd->ram_size = PHYS_SDRAM_1_SIZE;
//引发一个问题:此时的gd为什么能够对结构体成员进行赋值呢?
//通过前后的分析。可以知道在第一阶段的末尾,设置的sp指针的地址。
// 0x30000f00 从这个地址增大128个字节的地方是输入GD 的。
//通过语句mov r8,sp ,能够推导出什么呢?ram_size的数据,是存储在
//0x30000f00 到0x30000f80之间的
//board_init_f 分析到最后,有 memcpy(id, (void *)gd, sizeof(gd_t));
//memcpy 是内存拷贝函数。表达的意思是将之前的gd的位置0x30000f00拷贝到高端 addr_sp addr_sp - sizeof (bd_t) - sizeof (gd_t)
//位置处。(此处的理解,需要结合后边的addr_sp的设定)
#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
D==> addr = 0x34000000 即SDRAM地址最高端
/* reserve TLB table */
gd->tlb_size = 4096 * 4;
addr -= gd->tlb_size;
==>为页表保留
D==>addr = 0x34000000 - 0x4000 = 0x33ffc000
addr = 0x34000000 - TLB
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);
==> address = addr & (0x10000-1)
addr & 0xffff
0x33ffc000 & 0xffff
==>0x33ffc000
D==> addr = 0x33ffc000
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);
==> addr = addr & 0xfff
=0x33ffc000 & 0xfff
D==>addr = 0x33ffc000
/*==>addr = addr - _bss_end_ofs;
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~(4096 - 1);
此时并不清楚_bss_end_ofs 多大 ====> 通过反汇编查找 _bss_end_ofs 可以得到 000bbfd8 == >751K
假设_bss_end_ofs 为0x3000,为u-boot的code data bss 段保留这片空间。
则
D==>addr = 0x33ff0000
addr = 0x34000000 - TLB - _bss_end_ofs = 33ff0000 - 0x000bbfd8
==》 33F34048
addr &= ~(4096 - 1);
==> addr = 0x33f34000
/*
* reserve memory for malloc() arena 保留malloc 空间==>TOTAL_MALLOC_LEN = 0x410000 (详见TOTAL_MALLOC_LE 长度分析一节)
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)
D==>addr_sp = 0X33BE0000
/*
* (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;
D==>addr_sp = addr_sp - sizeof (bd_t)
addr_sp -= sizeof (bd_t); //为board Info 保留一块空间
bd = (bd_t *) addr_sp; //堆addr_sp进行强制类型的转换。并复制给bd (bd 里边包含了bi_arch_number 见bd gd 结构体分析) gd->bd = bd; //将bd 赋值给gd的成员bd。这样就可以通过gd这个全局 的寄存器变量来找到boadr info 信
addr_sp -= sizeof (gd_t); //为gd_t 保留一块空间 为后续的将低端的GD数据拷贝到高端D==>addr_sp addr_sp - sizeof (bd_t) - sizeof (gd_t) 地址做准备
id = (gd_t *) addr_sp;
D==>addr_sp = addr_sp - sizeof (bd_t) - sizeof (gd_t)
//此段数据并没有起作用,但是还是有必要分析一些。
//这是一些关于设备树的一些操作。
//主要是设备树这块数据的重定向。此处保留重定向的地址空间
#if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)/* setup stackpointer for exeptions */
/*
* If the device tree is sitting immediate above our image then we
* must relocate it. If it is embedded in the data section, then it
* will be relocated with other data.
*/
if (gd->fdt_blob) {
fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
addr_sp -= fdt_size;
new_fdt = (void *)addr_sp;
debug("Reserving %zu Bytes for FDT at: %08lx\n",
fdt_size, addr_sp);
}
#endif
gd->irq_sp = addr_sp; //将IRQ 的堆栈指针设置为addr_sp ( D==>addr_sp = addr_sp - sizeof (bd_t) - sizeof (gd_t) )
/* leave 3 words for abort-stack */
addr_sp -= 12; //保留12字节的 abort-stack
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07; //清低8位
D==>addr_sp = addr_sp - sizeof (bd_t) - sizeof (gd_t) - 12
==>addr_sp 为addr - 堆空间 - addr_sp - sizeof (bd_t) - sizeof (gd_t) - 12
============内存空间分配结束=============================
//设置波特率
gd->bd->bi_baudrate = gd->baudrate; init_baudrate 进行的波特率设置
//设置内存的起始地址和大小
dram_init_banksize();
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
void __dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; //0x30000000
gd->bd->bi_dram[0].size = gd->ram_size; // 在board_init_f 进行的设置 大小为0x04000000 64M
}
void dram_init_banksize(void)
__attribute__((weak, alias("__dram_init_banksize")));
gd->relocaddr = addr; /* Start address of U-Boot in RAM */ //U-boot 在RAM的起始地址
gd->start_addr_sp = addr_sp; /* start_addr_stackpointer */ //栈指针的起始地址
gd->reloc_off = addr - _TEXT_BASE; //重定位的偏移量 即搬移后u-boot的首地址
_TEXT_BASE 的值为0
addr的地址
假设_bss_end_ofs 为0x3000,为u-boot的code data bss 段保留这片空间。
D==>addr = 0x33ff0000
memcpy(id, (void *)gd, sizeof(gd_t)); //将低端的gd所在的数据,拷贝到高端id处
--------------------------------- 最顶端 0x34000000
| TLB table |
----------------------------------| 0x34000000 - 0x4000 = 0x33ffc000
| U-boot 的 code data bss |
----------------------------------| addr = 0x34000000 - TLB - _bss_end_ofs = 0x33f34000
| malloc 空间 (0x410000) |
----------------------------------| addr_sp = addr - TOTAL_MALLOC_LEN;
| Board Info struct bd_t |
---------------------------------- addr_sp = addr_sp - sizeof (bd_t)
| 全局变量 gd_t |
----------------------------------| (irq_sp)addr_sp = addr_sp - sizeof (bd_t) -sizeof(gd_t)
|abort-stack (12字节) |
----------------------------------| addr_sp = addr_sp - sizeof (bd_t) -sizeof(gd_t) - 12
2.2 准备 relocate_code 参考文献 http://blog.csdn.net/skyflying2012/article/details/25804209
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_sp, gd, addr_moni). Trick here is that
* we'll return 'here' but relocated.
*/
首先看一下定义的宏 Generic-asm-offsets.h (\include\generated) 这些宏其实是一些偏移量的大小 http://www.cnblogs.com/woainilsr/p/3472409.html
#define GENERATED_GBL_DATA_SIZE (128) /* (sizeof(struct global_data) + 15) & ~15 */ldr sp, [r8, #GD_START_ADDR_SP] /* r8 = gd->start_addr_sp */
#define GENERATED_BD_INFO_SIZE (32) /* (sizeof(struct bd_info) + 15) & ~15 */
#define GD_SIZE (128) /* sizeof(struct global_data) */
#define GD_BD (0) /* offsetof(struct global_data, bd) */
#define GD_RELOCADDR (52) /* offsetof(struct global_data, relocaddr) */
#define GD_RELOC_OFF (72) /* offsetof(struct global_data, reloc_off) */
#define GD_START_ADDR_SP (68) /* offsetof(struct global_data, start_addr_sp) */
//设置sp指针的位置。设置为多少呢?取出r8寄存器的地址,同时加上 GD_START_ADDR_SP (68) 地址单元的值,就是sp指针
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */跳转到重定位relocate_code 代码去
ldr r8, [r8, #GD_BD] /* r8 = gd->bd */
sub r8, r8, #GD_SIZE /* new GD is below bd */ 此时r8指向的空间为bd的首地址
adr lr, here //在重定位完成后,返回地址
ldr r0, [r8, #GD_RELOC_OFF] /* lr = gd->start_addr_sp */ [r8, #GD_RELOC_OFF] 得到重定位的偏移量
add lr, lr, r0 //在here的基础上加上重定位的偏移量
ldr r0, [r8, #GD_START_ADDR_SP] /* r0 = gd->start_addr_sp */ //void relocate_code (addr_sp, gd, addr_moni) 重定位代码的第一个参数
mov r1, r8 /* r1 = gd */ //第二个参数
ldr r2, [r8, #GD_RELOCADDR] /* r2 = gd->relocaddr */ //第三个参数,即重定位的位置
2.3 重定位代码 relocate_code
第一个参数:addr_sp 空间中去掉abort-stack后的地址位置。也就是栈指针的首地址。栈是往下进行增长的
第二个参数 gd_t 全局变量参数的首地址,保存了数据信息
第三个参数:addr_moni 重定位的地址
mov r4, r0 /* save addr_sp */adr r0, _start //伪指令代码adl 的分析 见https://www.douban.com/note/331036776/
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
//把 _start地址读出来,而且这个地址是相对当前pc的
cmp r0, r6 比较 _start 的地址和定位的目标地址是否相等,
如果相等,则跳出。即代码已经在目标地址。不需要进行数据的搬移
moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */
beq relocate_done /* skip relocation */
如果不相等。即需要进行代码的搬移操作。搬移到目标地址,高端的物理内存空间
mov r1, r6 /* r1 <- scratch for copy_loop */ r1 表示代码搬移的目的地址
ldr r3, _bss_start_ofs r3 表示 bss 段的开始。也就是说仅仅包含了代码段和数据段 连接脚本中有相应的定义 . = 0x00000000; 预示着 _bss_start_ofs 表示代码段和数据段的长度。不包括bss段。
add r2, r0, r3 /* r2 <- source end address */ r2 = r0 +r3 = 代码的起始地址+ _bss_start_ofs = 搬移地址的末地址。
=================从NOR FLASH 拷贝到SDRAM 完成 ===========================
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */ 从r0单元中读出8个字节的数据存在r9 r10 中,同时r0 自动加8
stmia r1!, {r9-r10} /* copy to target address [r1] */ 将刚才读出的数据放入r1单元的8个
cmp r0, r2 /* until source end address [r2] */ 比较数据源地址和 目的地址的尾地址是否相同
blo copy_loop //小于则跳转 也就是数据搬移还没有结束
程序的链接地址是0 。访问全局变量,静态变量和调用函数时。是基于0 地址编译而得到的地址
现在把程序赋值到SDRAM中,此时处于修改代码
把原来的是基于0 地址编译而得到的地址 。改为新地址
本来访问nor flash 中的变量。可以直接通过 0x100 访问。现在变量在sdram中,那么就需要使用新的地址来访问
========================================================================
重定位-->修改代码的地址
有些变量
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */ 读内存指令
//r0 = 0 //代码段的基地址
sub r9, r6, r0 /* r9 <- relocation offset */
//r9 = r6 -r0 = 代码搬移的目标地址 - 代码段的基地址 = 重定位的偏移量
//r9 = 0x33f34000 - 0 = 0x33f34000
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
//r10 = 符号表的首地址,这个地址是连接脚本中相对于链接脚本中的地址量。
//r10 = 0x0007cb24 (反汇编得到)
add r10, r10, r0 /* r10 <- sym table in FLASH */
//r10 = r10 +r0 = 代码段的基地址 + 符号表的地址 = 符号表在flash 中的地址
//r10 = 0x0007cb24 + 0 = 0007cb24
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
//r2 = rel_dyn的首地址。 这个地址是连接脚本中相对于链接脚本中的地址量。
//r2 = 0x00073b9c
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
//r2 = r2 +r0 = 在flash中rel_dyn的地址
//r2 = 0x00073b9c
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
//在flash中 rel_dyn_end d地址
// r3 = 0007cb24
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
//将r2单元的数据读到r0 。即rel_dyn在flash的地址
//第一次循环:r0 = [r2] = [0x00073b9c] = 0x00000020
add r0, r0, r9 /* r0 <- location to fix up in RAM */
// r0 = r0+r9 = 代码段的基地址 + 重定位的偏移量 = 在RAM中的地址
//第一次循环: r0= r0 + r9 = 0x00000020 + 0x33f34000 = 0x33f34020
ldr r1, [r2, #4]
//r1 = *(r2 +4) = 取出rel_dyn + 4 单元的数据到r1中
//第一次循环: r1+= [r1 +4 ] = [0x00073b9c +4 ] = [0x00073ba0] = 00000017
and r7, r1, #0xff
//第一次循环: r7 = r1 & 0xff = 00000017
cmp r7, #23 /* relative fixup? */
//第一次循环: 比较r7是否等于23 ==>等于
beq fixrel
//第一次循环:跳转到fixrel
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
//第一此循环:r1 = [r0] = [0x000020 ] = 000001a0 //此处的r0 为什么是0x000020 而不是 0x33f34020
//此处实际的[r0] 应当是0x33f34020.实际中 0x000020 和0x33f34020单元里边的内容值是一样的。都是取出来。然后进行修改里边的代码。
add r1, r1, r9
// 第一此循环: r1 = r1 + r9 = 0x000001a0 + 0x33f34000 = 0x33F341A0
fixnext:
str r1, [r0]
//第一此循环: [0x33f34020] = 0x33F341A0
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
//第一此循环: r2 = r2 +8 = 0x00073b9c + 8 = 0x00073ba4
cmp r2, r3
// 第一此循环: 0x0x00073ba4 != 0x0007cb24
blo fixloop
// 第一此循环: 小于继续跳转 到fixloop
//此处还有待于完善
====== 相对动态段的反汇编代码
Disassembly of section .rel.dyn:
00073b9c <__image_copy_end>:
73b9c: 00000020 andeq r0, r0, r0, lsr #32 //第一次循环
73ba0: 00000017 andeq r0, r0, r7, lsl r0 // 17 表示的是一个标号。
73ba4: 00000024 andeq r0, r0, r4, lsr #32 //第二次循环
73ba8: 00000017 andeq r0, r0, r7, lsl r0
73bac: 00000028 andeq r0, r0, r8, lsr #32
73bb0: 00000017 andeq r0, r0, r7, lsl r0
73bb4: 0000002c andeq r0, r0, ip, lsr #32
===============
mov pc, lr
将lr的值给pc
程序跳转到here
here:
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end__ /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
//清楚bss段
为调用 boadr_init_r 做准备
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r8 /* gd_t */
ldr r1, [r8, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r /* this is auto-relocated! */
汇编调用C语言,函数由两个参数 gd_t 和dest_addr
void board_init_r(gd_t *id, ulong dest_addr)
对应的反汇编代码:
7d8: e1a00008 mov r0, r8
7dc: e5981034 ldr r1, [r8, #52] ; 0x34
7e0: e59ff008 ldr pc, [pc, #8] ; 7f0 <clbss_l+0x30>
pc = pc +8 +8 = 0x7f0
7f0: 000008ac andeq r0, r0, ip, lsr #17
//跳转到0x000008ac处执行 board_init_r
====board_init_r 调用第二阶段=======
000008ac <board_init_r>:
8ac: e5982004 ldr r2, [r8, #4]
8b0: e92d4008 push {r3, lr}
8b4: e59f30b4 ldr r3, [pc, #180] ; 970 <board_init_r+0xc4>
8b8: e3822001 orr r2, r2, #1
8bc: e5882004 str r2, [r8, #4]
8c0: e5932000 ldr r2, [r3]
8c4: e59f30a8 ldr r3, [pc, #168] ; 974 <board_init_r+0xc8>
8c8: e1a04001 mov r4, r1
8cc: e5832000 str r2, [r3]
8d0: eb0001ad bl f8c <__enable_caches>
8d4: eb01729f bl 5d358 <board_init>