作者:程老师,华清远见嵌入式学院讲师。
uboot的第一阶段设计的非常巧妙,几乎都是用汇编语言实现的,下面我们一起来看看它的精妙之处吧!
首先我们来看一下它的链接脚本,通过它我们可以知道它整个程序的各个段是怎么存放的。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)//所有的其他程序的代码段以四字节对齐放在它后面
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }//只读数据段
. = ALIGN(4);
.data : { *(.data) }//指定读/写数据段
. = ALIGN(4);
.got : { *(.got) }//指定got段,got段式是uboot自定义的一个段,非标准段
. = .;
__u_boot_cmd_start = .;//把__u_boot_cmd_start赋值为当前位置,即起始位置
.u_boot_cmd : { *(.u_boot_cmd) }//指定u_boot_cmd段,uboot把所有的uboot命令放在该段
__u_boot_cmd_end = .;//把 __u_boot_cmd_end赋值为当前位置,即结束位置
. = ALIGN(4);
__bss_start = .;//__bss_start赋值为当前位置,即bss段得开始位置
.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
_end = .;//把_end赋值为当前位置,即bss段得结束地址
}
从它我们可以看到,uboot运行的第一个程序是cpu/arm920t/start.S,其他的都有注释,就不多说了。
下面我们来看看在fs2410上通过nor flash启动,u-boot的启动流程:
电源一上电之后,pc指针从0x00000000 地址开始执行,也就是说这个时候程序跑在nor flash里面(别忘记,nor flash这个时候接在0x00000000地址,它是可以执行程序的只是速度没有在ram里面执行快)
这个时候有人可能会问那s3c2410片内的4kRam映射到哪呢?通过查看芯片手册这个时候它在0x40000000,如下图:
(使用NOR FLASH启动,设备地址映射图)
现在我们知道是那个程序在哪里存储,从哪个地址运行,下面我们来看看这段程序的汇编代码:
在这里我们知道运行的第一条指令是b start_code,呵呵!看到这段代码的时候许多人都认为_start的值是0x00000000,为什么是这个地址呢? 因为连接脚本上指定了。真的是这样吗?我们来看看我们编译好之后,在u-boot目录下有个System.map,这里面有各个变量的值,如下图:
哈哈,_start的值怎么会是0x33f80000?这是因为在顶层的Makefile里面我们指定了它的连接地址。
看到那个 -o u-boot了吗,就是通过它生成ELF格式的u-boot的,里面有个LDFLAGS变量,它是什么呢,我们继续往下面看:
(顶层的config.mk里面有这个变量的说明)
看到了没有,LDFLAGS里面除了指定链接脚本,如果TEXT_BASE不等于空,还加上了-Ttext$(TEXT_BASE),TEXT_BASE的值是多少呢?我们可以在board/samsung/smdk2410/config.mk里面有定义,它的值为0x33f80000。这样我就可以知道为什么System.map的地址都是0x33f80000。
好我们来看第一条汇编指令b start_code,从System.map文件我们可以知道,这条指令相当于b 0x33f80050。"0x33f80050"?你是否有疑问,这个时候内存还没有初始化呢,怎么能跳到内存中去了(sdram的起始地址0x30000000)。呵呵,其实不是这样的。还记得b指令是相对跳转指令吗,它和代码的位置是无关的,这种精妙的设计才使得后面的指令能继续的执行。
下面我们继续来看它的汇编指令:
呵呵,这段汇编代码很好理解,就是设置CPU为 管理模式。
这段汇编代码就不分析了,这是跟atmel相关的,我不管它,继续下面:
这段汇编代码也很好理解,就是关掉看门狗,然后关掉所有中断。
哦哦,设置时钟,还记得一上电FCLK是多少吗,只有12MHZ。
呵呵!跳转指令,我们来看看它做了什么,这样我们先从名字上来猜猜它是什么意思?cpu初始化,初始化什么呢?继续向下看:
呵呵,有英文注释,清cache,关闭MMU。都是用协处理指令实现的,如果看不懂,可以看看协处理器指令。多了解一点就多知道一点,不是吗?
从上面可以看到,还有一次跳转bl lowlevel_init。这里面有干了一些什么呢,我们可以到board/samsung/smdk2410/ lowlevel_init.S去看。呵呵,这里我就不截图了,其实我们只有知道,它在这里面干了初始化内存操作。
继续往下面看:
呵呵,终于到重点部分了,代码重定向。下面我们来看看它到底是怎么重定向的:
Adr r0,_start
这条汇编指令的意思就是把_start当前代码存储的地址赋给r0,假如这段代码存储在SDRAM里面,那么r0的值就是0x30000000。现在这段代码存储在NOR Flash里面,所以r0的值就是
0x0000000
Ldr r1,_TEXT_BASE
这条汇编指令的意思是把_TEXT_BASE的值作为地址,把这个地址的内容赋给r1,从下面可以知道:
_TEXT_BASE里面存储的内容是TEXT_BASE,也就是0x33f80000,所以r1的值就是0x33f80000
Cmp r0,r1
将r0和r1做比较,此时r0 = 0x0000000,r1 = 0x33f80000,显然不相等,那么执行的就是下面的汇编指令:
ldr r2, _armboot_start
由此可以知道r2的值是_start,也就是0x33f80000,也是整个代码的起始地址。
Ldr r3, _bss_start
由u-boot.lds的链接脚本可以知道,r3的值是整个代码得结尾.
Sub r2,r3,r2
这条指令的意思是r2 = r3 -r2,即r2 = 代码结束 - 代码开始,这样得到的是r2 = 代码的大小。
Add r2,r0,r2
这条指令的意思是r2 = r0 + r3,即 r2 = 代码开始 + 代码大小,这样得到的是r2 = nor falsh 里面代码的结尾
此时我们得到r0 = nor flash 代码的起始位置,r1 = 0x33f80000(sdram :0x300000000 ~ 0x34000000)
呵呵,这个时候代码就被从nor flash里面拷贝到sdram里面了。
这段代码的意思是设置一些堆栈
这段代码的意思是清bss段,
呵呵,终于看到pc指针的值被改变了。也就是这个时候,pc指针开始跳到sdram里面执行代码,这也就到了第二阶段(从语言阶段),后面的代码都是用C语言写得了。先分析到这里,下次我们看第二阶段。