uboot源码分析1-启动第一阶段

笔者发现以前做的时候,不是很详细,现在回头看很多东西需要重新查~从这篇开始尽量写的详细,以前的慢慢补好。
慢慢更新~ 如有不足之处,希望大家多多指点。

start.S引入

1.u-boot.lds中找到start.S入口
/*
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, 
 *
 * 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
 */

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

	. = ALIGN(4);
	.text      :
	{
	  cpu/s5pc11x/start.o	(.text)
	  cpu/s5pc11x/s5pc110/cpu_init.o	(.text)
	  board/samsung/x210/lowlevel_init.o	(.text)
          cpu/s5pc11x/onenand_cp.o      (.text)                 
          cpu/s5pc11x/nand_cp.o (.text)                     
          cpu/s5pc11x/movi.o (.text) 
          common/secure_boot.o (.text) 
	  common/ace_sha1.o (.text)
	  cpu/s5pc11x/pmic.o (.text)
	  *(.text)
	}

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	.got : { *(.got) }

	__u_boot_cmd_start = .;
	.u_boot_cmd : { *(.u_boot_cmd) }
	__u_boot_cmd_end = .;

	. = ALIGN(4);
	.mmudata : { *(.mmudata) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) }
	_end = .;
}

由ENTRY(_start)
(1)在C语言中整个项目的入口就是main函数(这是C语言规定的),所以譬如说一个有10000个.c文件的项目,第一个要分析的文件就是包含了main函数的那个文件。
(2)在uboot中因为有汇编阶段参与,因此不能直接找main.c。整个程序的入口取决于链接脚本中27行ENTRY声明的地方。ENTRY(_start)因此_start符号所在的文件就是整个程序的起始文件,_start所在处的代码就是整个程序的起始代码。


2.SourceInsight中如何创建文件
打开SI,选择Project -> Newprojice
uboot源码分析1-启动第一阶段_第1张图片
uboot源码分析1-启动第一阶段_第2张图片
注释:如果有些你想要的文件类型没有添加,在Options -> Document Options中添加
uboot源码分析1-启动第一阶段_第3张图片


3.SourceInsight中如何找到文件
(1)当前状况:我们知道在uboot中的1000多个文件中有一个符号叫_start,但是我们不知道这个符号在哪个文件中。这种情况下要查找一个符号在所有项目中文件中的引用,要使用SourceInsight的搜索功能。
uboot源码分析1-启动第一阶段_第4张图片
(2)利用SI工具搜索到一共7个_start,

然后分析搜索出来的7处,发现有2个是api_example,2个是onenand相关的,都不是我们要找的。剩下3个都在uboot/cpu/s5pc11x/start.S文件中。(uboot是根目录)

(3)然后进入start.S文件中,发现57行中就是_start标号的定义处,于是乎我们就找到了整个uboot的入口代码,就是第57行。


4.SI中找文件技巧
(1)以上,找到了start.S文件,下面我们就从start.S文件开始分析uboot第一阶段。
(2)在SI中,如果我们知道我们要找的文件的名字,但是我们又不知道他在哪个目录下,我们要怎样找到并打开这个文件?方法是在SI中先打开右边的工程项目管理栏目,然后点击最左边那个(这个是以文件为单位来浏览的),然后在上面输入栏中输入要找的文件的名字。我们在输入的时候,SI在不断帮我们进行匹配,即使你不记得文件的全名只是大概记得名字,也能帮助你找到你要找的文件。
uboot源码分析1-启动第一阶段_第5张图片

5.补:笔者刚用sourceinsight时候,字体太小了不适应,补充一下修改在哪修改字体。
uboot源码分析1-启动第一阶段_第6张图片





start.S解析1

1.不简单的头文件包含
#include 
#include 
#if defined(CONFIG_ENABLE_MMU)
#include 
#endif
(1)#include 。config.h是在include目录下的,这个文件不是源码中本身存在的文件,而是配置过程中自动生成的文件。(详见mkconfig脚本)。这个文件的内容其实是包含了一个头文件:#include ".

定位到:mkconfig 140-141行。
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include " >>config.h

注释:

$(@:_config=)  arm  s5pc11x  x210  samsung  s5pc110

x210_sd_config里的_config部分用空替换,得到:x210_sd,这就是第一个参数,所以:

$1: x210_sd

$2: arm

$3: s5pc11x

$4: x210

$5: samsumg

$6: s5pc110

所以,$# = 6

>如图:uboot源码分析1-启动第一阶段_第7张图片

(2)经过分析后,发现start.S中包含的第一个头文件就是:include/configs/x210_sd.h,这个文件是整个uboot移植时的配置文件。这里面是好多宏。因此这个头文件包含将include/configs/x210_sd.h文件和start.S文件关联了起来。因此之后在分析start.S文件时,主要要考虑的就是x210_sd.h文件。
如图:

并不是因为x210是inand就选择x210_sd.h,与这没有半毛钱关系,是配置过程决定的!!!

(3)#include 。include/version.h中包含了include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。里面就一行内容:#define U_BOOT_VERSION "U-Boot 1.3.4"。这里面定义的宏U_BOOT_VERSION的值是一个字符串,字符串中的版本号信息来自于Makefile中的配置值。这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。
笔者做一个实验:更改makefile中的配置值:
1.修改Makefile:
uboot源码分析1-启动第一阶段_第8张图片
2.配置、编译一下Makefile(不必全部编译完,中断即可)


(4)#include 。asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm,proc也是个符号链接,指的是proc-armv(详解上一章节分析mkconfig脚本时,这里直接分析给你看)。

是不是三星大佬有点傻呢,故意搞个符号链接!!!当然不是请往下看

(5)经过分析后发现,实际文件是:include/asm-arm/proc-armv/domain.h

(6)#include
uboot源码分析1-启动第一阶段_第9张图片
所以reg.s就是s5pc110.h的一个链接符号,指向s5pc110.h
如图:


(6)从这里可以看出之前配置时创建的符号链接的作用,如果没有这些符号链接则编译时根本通不过,因为找不到头文件。(所以uboot不能在windows的共享文件夹下配置编译,因为windows中没有符号链接

思考:为什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。这样的设计主要是为了可移植性。因为如果直接包含,则start
.S文件和CPU架构(和硬件)有关了,可移植性就差了。uboot源码分析1-启动第一阶段_第10张图片
譬如我要把uboot移植到mips架构下,则start.S源代码中所有的头文件包含全部要修改。我们用了符号链接之后,则start.S中源代码不用改,只需要在具体的硬件移植时配置不同,创建的符号链接指向的不同,则可以具有可移植性。



start.S解析2

1、启动代码的16字节头部
(1) 裸机中讲过,在SD卡启动/Nand启动等整个镜像开头需要 16字节 的校验头。(mkv210image.c中就是为了计算这个校验头)。我们以前做裸机程序时根本没考虑这16字节校验头;
因为:1、如果我们是usb启动直接下载的方式启动的则不需要16字节校验头(irom application note);2、如果是SD卡启动mkv210image.c中会给原镜像前加16字节的校验头。
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
#endif

注释:.world 相当于c语言中的int或者unsigned int 四字节

(2)uboot这里start.S中在开头位置放了16字节的填充占位,这个占位的16字节只是 保证正式的image的头部确实有16字节,但是这16字节的内容是不对的,还是需要后面去计算校验和然后重新填充的。( uboot/su_fusing/C110-FVT1-mkbl1.c中) uboot源码分析1-启动第一阶段_第11张图片


2.异常向量表的构建
.globl _start
_start: b	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

_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
_pad:
	.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

	.balignl 16,0xdeadbeef
	
/*
 *************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * setup Memory and board specific bits prior to relocation.
 * relocate armboot to ram
 * setup stack
 *
 *************************************************************************
 */

_TEXT_BASE:
	.word	TEXT_BASE

(1)异常向量表是硬件决定的,软件只是 参照硬件的设计来实现它。
---------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------
在此补充复习一个ARM裸机知识:

关于ARM的异常:

1.什么是异常:

正常工作之外的流程都叫异常(例如 中断)

2.怎样处理:

1、通过异常向量表
(1) 关于异常向量表:
(2) 所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的
(3) 当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作)
(4) 异常向量表是硬件向软件提供的处理异常的支持

2、处理机制:

uboot源码分析1-启动第一阶段_第12张图片

3、当异常产生时,ARM core:
- 拷贝 CPSR到 SPSR_
- 设置适当的CPSR位:
、改变处理器状态进入ARM
、改变处理器模式进入相应异常模式
、设置中断禁止位禁止相应中断(如果需要)
- 保存返回地址到 LR_
- 设置PC为相应的异常变量

4、 返回时,异常处理需要:
- 从SPSR_恢复CPSR
- 从LR_恢复PC
- Note:这些操作只能在ARM态执行
---------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------

(2)异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。 但是我们在uboot中并 未非常细致的处理各种异常。
(3) 复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。

3.有点意思的deadbeef
(1).balignl 16,0xdeadbeef. 这一句指令是让当前地址对齐排布, 如果当前地址不对齐则自动向后走地址直对齐,并且向后走的那些内存要用0xdeadbeef来填充。
(2)0xdeadbeef这是一个十六进制的数字,这个数字很有意思,组成这个数字的十六进制数全是abcdef之中的字母,而且这8个字母刚好组成了英文的dead beef这两个单词,字面意思是坏牛肉。 ( 没卵用!!!你写0x00000000也没问题。外国银用来提升逼格的~)
(3)为什么要对齐访问?有时候是效率的要求,有时候是硬件的特殊要求。

4.TEXT_BASE等
(1)sourceinsight第100行这个TEXT_BASE(上面45、46行)就是上个课程中分析Makefile时讲到的那个 配置阶段的TEXT_BASE,其实就是我们链接时指定的uboot的链接地址。(值就是c3e00000)


(2)源代码中和配置Makefile中很多变量是可以互相运送的。简单来说 有些符号的值可以从Makefile中传递到源代码中。


start.S解析3

1.

_TEXT_PHY_BASE:
	.word	CFG_PHY_UBOOT_BASE

.globl _armboot_start
_armboot_start:
	.word _start



所以CFG_PHY_UBOOT_BASE 为0x33e00000,uboot在DDR中的物理地址;

而我们前面指定的链接地址为0xc3e00000;这是虚拟地址; 

其实这两个就是一个地址,即虚拟地址映射。后面会讲~ 


2.设置CPU为SVC模式

reset:
	/*
	 * set the cpu to SVC32 mode and IRQ & FIQ disable
	 */
	@;mrs	r0,cpsr
	@;bic	r0,r0,#0x1f
	@;orr	r0,r0,#0xd3
	@;msr	cpsr,r0
	msr	cpsr_c, #0xd3		@ I & F disable, Mode: 0x13 - SVC
(1)ARM 汇编中@是GNU汇编的规范,就是注释!
--------------------------------------------------------------------------------------------------------------------
补充:

gnu汇编中的一些符号:

1.@用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
2. #做注释,一般放在行首,表示这一行都是注释而不是代码
3. :以冒号结尾的是标号
4. .点号在gnu汇编中表示当前指令的地址
5. #立即数前面要加#或$,表示这是个立即数

--------------------------------------------------------------------------------------------------------------------
(2)msr cpsr_c, #0xd3 将CPU设置为禁止FIQ IRQ,ARM状态,SVC模式。(0xd3=0b11010011)

--------------------------------------------------------------------------------------------------------------------

补充:

mrs & msr
cpsr 和 spsr的区别和联系:
cpsr是程序状态寄存器,整个SoC中只有一个;
spsr是程序状态保存寄存器,有5个分别在5种异常模模式下,作用是当从普通模式进入异常模式时,
用来保存之前普通模式下的spsr,以在返回普通模式时恢复原来的cpsr
注:
1.mrs 用来读psr, msr用来写psr(cpsr、spsr)
2.cpsr寄存器比较特殊,需要专门的指令访问,这就是mrs和msr

--------------------------------------------------------------------------------------------------------------------

(3)其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件方法将其置为SVC模式-(不依赖硬件的预先设置)。整个uboot工作时CPU一直处于SVC模式。


3.设置L2、L1cache和MMU

bl	disable_l2cache

	bl	set_l2cache_auxctrl_cycle

	bl	enable_l2cache
	
       /*
        * Invalidate L1 I/D
        */
        mov	r0, #0                  @ set up for MCR
        mcr	p15, 0, r0, c8, c7, 0   @ invalidate TLBs
        mcr	p15, 0, r0, c7, c5, 0   @ invalidate icache

       /*
        * disable MMU stuff and caches
        */
        mrc	p15, 0, r0, c1, c0, 0
        bic	r0, r0, #0x00002000     @ clear bits 13 (--V-)
        bic	r0, r0, #0x00000007     @ clear bits 2:0 (-CAM)
        orr	r0, r0, #0x00000002     @ set bit 1 (--A-) Align
        orr	r0, r0, #0x00000800     @ set bit 12 (Z---) BTB
        mcr 	p15, 0, r0, c1, c0, 0
(1)bl disable_l2cache // 禁止L2 cache
(2)bl set_l2cache_auxctrl_cycle // l2 cache相关初始化
(3)bl enable_l2cache // 使能l2 cache
(4)刷新L1 cache的icache和dcache。
(5)关闭MMU
总结: 上面这5步都是和CPU的cache和mmu有关的,不用去细看,大概知道即可
注释: MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。

4.识别并暂存启动介质选择
/* Read booting information */
ldr	r0, =PRO_ID_BASE
ldr	r1, [r0,#OMR_OFFSET]
bic	r2, r1, #0xffffffc1

.................................
	/* NAND BOOT */
	cmp	r2, #0x0		@ 512B 4-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x2		@ 2KB 5-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x4		@ 4KB 5-cycle	8-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x6		@ 4KB 5-cycle	16-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x8		@ OneNAND Mux
	moveq	r3, #BOOT_ONENAND

	/* SD/MMC BOOT */
	cmp     r2, #0xc
	moveq   r3, #BOOT_MMCSD	

	/* NOR BOOT */
	cmp     r2, #0x14
	moveq   r3, #BOOT_NOR	
查询得:
PRO_ID_BASE  0xE0000000(在include/s5pc110.h中定义)
OMR_OFFSET   0x04
BIC:BIC{条件}{S}  目的寄存器,操作数1,操作数2;BIC指令用亍清除操作数1的某些位,幵把结果放置到目的寄存器中。
所以r2的值为0x3E;
(1)从哪里启动是由SoC的OM5:OM0这6个引脚的高低电平决定的。
(2)实际上在210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。
(3)我们代码中可以通过读取这个寄存器的值然后判断其值来确定当前选中的 启动介质是Nand还是SD还是其他的。
(4)start.S的225-227行(上面1-4行)执行完后,在r2寄存器中存储了一个数字,这个数字等于某个特定值时就表示SD启动,等于另一个特定值时表示从Nand启动····
(5)260行(上面25行)中给r3中赋值#BOOT_MMCSD(0x03),这个在SD启动时实际会被执行,因此执行完这一段代码后r3中存储了0x03,以后备用。


5.设置栈(SRAM中的栈)并调用lowlevel_init
/*
	 * Go setup Memory and board specific bits prior to relocation.
	 */

	ldr	sp, =0xd0036000 /* end of sram dedicated to u-boot */
	sub	sp, sp, #12	/* set stack */
	mov	fp, #0
	
	bl	lowlevel_init	/* go setup pll,mux,memory */
(1)284-286行(上面5-7行)第一次设置栈。这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的, 指定的原则就是这块空间只给栈用,不会被别人占用。
注:见iROM_Application
uboot源码分析1-启动第一阶段_第13张图片

(2)在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。
注:影子寄存器:对r14这个名字来说,在ARM中共有6个(每种模式都有:User、FIQ、IRQ、SVC、Undef、Abort)名叫r14(又叫lr)的寄存器, 但是在每种特定处理器模式下,只有一个r14是当前可见的,其他的r14必须切换到他的对应模式下才能看到。这种设计叫影子寄存器(banked register)。

你可能感兴趣的:(cortex-A8裸机编程)