摘要
下面将详细解释uboot中的start.S中的每一行代码。详细到,每个指令的语法和含义,都进行详细讲解,使得此文读者可以真正搞懂具体的含义,即what。
以及对于一些相关的问题,深入探究为何要这么做,即why。
对于uboot的start.S,主要做的事情就是系统的各个方面的初始化。
从大的方面分,可以分成这几个部分:
下面来对start.S进行详细分析,看看每一个部分,是如何实现的。
/* * armboot - Startup Code for ARM920 CPU-core * * Copyright (c) 2001 Marius Gr鰃er <[email protected]> * Copyright (c) 2002 Alex Z黳ke <[email protected]> * Copyright (c) 2002 Gary Jennejohn <[email protected]> * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include <config.h> #include <version.h> /* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ .globl _start
globl是个关键字,对应含义为: http://re-eject.gbadev.org/files/GasARMRef.pdf表 1.1. global的语法
所以,意思很简单,就是相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问 所以,你可以看到
中,有用到此变量: ENTRY(_start) 即指定入口为_start,而由下面的_start的含义可以得知,_start就是整个start.S的最开始,即整个uboot的代码的开始。 |
_start: b reset
_start后面加上一个冒号’:’,表示其是一个标号Label,类似于C语言goto后面的标号。 而同时,_start的值,也就是这个代码的位置了,此处即为代码的最开始,相对的0的位置。 而此处最开始的相对的0位置,在程序开始运行的时候,如果是从NorFlash启动,那么其地址是0, _stat=0 如果是重新relocate代码之后,就是我们定义的值了,即,在
中的: TEXT_BASE = 0x33D00000 表示是代码段的基地址,即 _start=TEXT_BASE=0x33D00000 关于标号的语法解释:
而_start标号后面的: b reset 就是跳转到对应的标号为reset的位置。 |
ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
ldr命令的语法为:
上面那些ldr的作用,以第一个_undefined_instruction为例,就是将地址为_undefined_instruction中的一个word的值,赋值给pc。 |
_undefined_instruction: .word undefined_instruction _software_interrupt: .word software_interrupt _prefetch_abort: .word prefetch_abort _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq
http://re-eject.gbadev.org/files/GasARMRef.pdf
所以上面的含义,以_undefined_instruction为例,就是,此处分配了一个word=32bit=4字节的地址空间,里面存放的值是undefined_instruction。 而此处_undefined_instruction也就是该地址空间的地址了。用C语言来表达就是: _undefined_instruction = &undefined_instruction 或 *_undefined_instruction = undefined_instruction 在后面的代码,我们可以看到,undefined_instruction也是一个标号,即一个地址值,对应着就是在发生“未定义指令”的时候,系统所要去执行的代码。 (其他几个对应的“软件中断”,“预取指错误”,“数据错误”,“未定义”,“(普通)中断”,“快速中断”,也是同样的做法,跳转到对应的位置执行对应的代码。) 所以: ldr pc, 标号1 ...... 标号1:.word 标号2 ...... 标号2: ......(具体要执行的代码) 的意思就是,将地址为标号1中内容载入到pc,而地址为标号1中的内容,正好装的是标号2。 用C语言表达其实很简单: PC = *(标号1) = 标号2 对PC赋值,即是实现代码跳转,所以整个这段汇编代码的意思就是: 跳转到标号2的位置,执行对应的代码。 |
.balignl 16,0xdeadbeef
balignl这个标号的语法及含义:
所以意思就是,接下来的代码,都要16字节对齐,不足之处,用0xdeadbeef填充。 其中关于所要填充的内容0xdeadbeef,刚开始没看懂是啥意思,后来终于搞懂了。 经过( 此处0xdeadbeef本身没有真正的意义,但是很明显,字面上的意思是,(坏)死的牛肉。 虽然其本身没有实际意义,但是其是在十六进制下,能表示出来的,为数不多的,可读的单词之一了。 另外一个相对常见的是:0xbadc0de,意思是bad code,坏的代码,注意其中的o是0,因为十六进制中是没有o的。 这些“单词”,相对的作用是,使得读代码的人,以及在查看程序运行结果时,容易看懂,便于引起注意。 而关于自己之前,随意杜撰出来的,希望起到搞笑作用,表示good beef(好的牛肉)的0xgoodbeef,实际上,在十六进制下,会出错的,因为十六进制下没有o和 g这两个字母。 |
/* ************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * relocate armboot to ram * setup stack * jump to second stage * ************************************************************************* */ _TEXT_BASE: .word TEXT_BASE .globl _armboot_start _armboot_start: .word _start
/* * These are defined in the board-specific linker script. */ .globl _bss_start _bss_start: .word __bss_start .globl _bss_end _bss_end: .word _end
关于_bss_start和_bss_end都只是两个标号,对应着此处的地址。 而两个地址里面分别存放的值是__bss_start和_end,这两个的值,根据注释所说,是定义在开发板相关的链接脚本里面的,我们此处的开发板相关的链接脚本是:
其中可以找到__bss_start和_end的定义: __bss_start = .; .bss : { *(.bss) } _end = .; 而关于_bss_start和_bss_end定义为.glogl即全局变量,是因为uboot的其他源码中要用到这两个变量,详情请自己去搜索源码。 |
.globl FREE_RAM_END FREE_RAM_END: .word 0x0badc0de .globl FREE_RAM_SIZE FREE_RAM_SIZE: .word 0x0badc0de
关于FREE_RAM_END和FREE_RAM_SIZE,这里只是两个标号,之所以也是声明为全局变量,是因为uboot的源码中会用到这两个变量。 但是这里有点特别的是,这两个变量,将在本源码start.S中的后面要用到,而在后面用到这两个变量之前,uboot的C源码中,会先去修改这两个值,具体的逻辑是: 本文件start.S中,后面有这两句: ldr pc, _start_armboot _start_armboot: .word start_armboot 意思很明显,就是去调用start_armboot函数。 而start_armboot函数是在:
中: init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup */ ...... NULL, }; void start_armboot (void) { init_fnc_t **init_fnc_ptr; ...... for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } ...... } 即在start_armboot去调用了cpu_init。 cpu_init函数是在:
中: int cpu_init (void) { /* * setup up stacks if necessary */ #ifdef CONFIG_USE_IRQ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; FREE_RAM_END = FIQ_STACK_START - CONFIG_STACKSIZE_FIQ - CONFIG_STACKSIZE; FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; #else FREE_RAM_END = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4 - CONFIG_STACKSIZE; FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1; #endif return 0; } 在cpu_init中,根据我们的一些定义,比如堆栈大小等等,去修改了IRQ_STACK_START ,FIQ_STACK_START ,FREE_RAM_END和FREE_RAM_SIZE的值。 至于为何这么修改,后面遇到的时候会具体再解释。 |
/* * the actual reset code */ reset: /* * set the cpu to SVC32 mode */ mrs r0,cpsr
CPSR 是当前的程序状态寄存器(Current Program Status Register), 而 SPSR 是保存的程序状态寄存器(Saved Program Status Register)。 具体细节,可参考: |
|
MRS - Move From Status Register MRS指令的语法为:
所以,上述汇编代码含义为,将CPSR的值赋给R0寄存器。 |
msr cpsr,r0
MSR - Move to Status Register msr的指令格式是:
此行汇编代码含义为,将r0的值赋给CPSR。 |
所以,上面四行汇编代码的含义就很清楚了。
先是把CPSR的值放到r0寄存器中,然后清除bit[4:0],然后再或上
0xd3=11 0 10111b
表 1.5. CPSR=0xD3的位域及含义
CPSR位域 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
位域含义 | I | F | M4 | M3 | M2 | M1 | M0 | |
0xD3 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |
对应含义 | 关闭中断IRQ | 关闭快速中断FIQ | 设置CPU为SVC模式,这和上面代码注释中的“set the cpu to SVC32 mode”,也是一致的。 |
关于为何设置CPU为SVC模式,而不是设置为其他模式,请参见本文档后面的章节:第 3.2 节 “uboot初始化中,为何要设置CPU为SVC模式而不是设置为其他模式”