一、程序的开始
在C语言中,main函数是整个程序的入口,这是规定。在uboot中因为有汇编参与,因此不能直接找main.c,整个程序的入口取决于中ENTRY声明的地方 ENTRY(_start),因此_start所在的文件就是整个程序的起始文件,所在的程序就是起始程序。
二、Start.S文件的分析
1、头文件包含
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>
(1)#include <config.h>。config.h是在include目录下的,这个文件不是源码中本身存在的文件,而是配置过程中自动生成的文件。(详见mkconfig脚本)。这个文件的内容其实是包含了一个头文件:#include <configs/x210_sd.h>".
(2)经过分析后,发现start.S中包含的第一个头文件就是:include/configs/x210_sd.h,这个文件是整个uboot移植时的配置文件。这里面是很多宏。因此这个头文件将include/configs/x210_sd.h文件和start.S文件关联了起来。之后在分析start.S文件时,主要要考虑的就是x210_sd.h文件。
(3)#include <version.h>。include/version.h中包含了include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。里面就一行内容:#define U_BOOT_VERSION "U-Boot1.3.4"。这里面定义的宏U_BOOT_VERSION的值是一个字符串,字符串中的版本号信息来自于Makefile中的配置值。这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。
(4)#include <asm/proc/domain.h>。asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm.经过分析后发现,实际文件是:include/asm-arm/proc-armv/domain.h
(5)从这里可以看出之前配置时创建的符号链接的作用,如果没有这些符号链接则编译时根本通不过,因为找不到头文件。(所以uboot不能在windows的共享文件夹下配置编译,因为windows中没有符号链接)
思考:为什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。这样的设计主要是为了可移植性。因为如果直接包含,则start
.S文件和CPU架构(和硬件)有关了,可移植性就差了。譬如我要把uboot移植到mips架构下,则start.S源代码中所有的头文件包含全部要修改。我们用了符号链接之后,则start.S中源代码不用改,只需要在具体的硬件移植时配置不同,创建的符号链接指向的不同,则可以具有可移植性。
2、启动代码的16字节头部
在SD卡启动/Nand启动等整个镜像开头需要16字节的校验头。(mkv210image.c中就是为了计算这个校验头)。做裸机程序时根本没考虑这16字节校验头,因为:1、如果我们是usb启动直接下载的方式启动的则不需要16字节校验头(irom application note);2、如果是SD卡启动mkv210image.c中会给原镜像前加16字节的校验头。
/*
*启动代码的16字节头部,SD卡启动\Nand启动等整个镜像开头需要16字节的校验头。
*mkv210image.c就是校验头。如果usb启动直接下载不需要校验头(irom application note),如果是SD卡启动
*mkv210image.c中会给原镜像前加16字节的校验头。
*uboot这里start.S中在开头位置放了16字节的填充占位,保证正式的image的头部确实有16字节,但目前这16
*字节内容不对,需要后面去计算校验和重新填充。sd_fusing文件夹中计算。
*/
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif
3、异常向量表的构建
(1)异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。
(2)异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是我们在uboot中并未非常细致的处理各种异常。
(3)复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。
.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
/*有点意思的deadbeef
(1).balignl 16,0xdeadbeef. 这一句指令是让当前地址对齐排布,如果当前地址不对齐则自动向后走地址直到对齐,并且向后走的那些内存要用0xdeadbeef来填充。
(2)0xdeadbeef这是一个十六进制的数字,这个数字很有意思,组成这个数字的十六进制数全是abcdef之中的字母,而且这8个字母刚好组成了英文的dead beef这两个单词,字面意思是坏牛肉。
(3)为什么要对齐访问?有时候是效率的要求,有时候是硬件的特殊要求。*/
4、TEXT_BASE
(1)TEXT_BASE就是Makefile中配置阶段的TEXT_BASE,是链接时指定的uboot的链接地址。(值是c3e00000)
(2)源代码中和配置Makefile中很多变量是可以互相运送的。简单来说有些符号的值可以从Makefile中传递到源代码中。
_TEXT_BASE:
.word TEXT_BASE
5、CFG_PHY_UBOOT_BASE
uboot在DDR中的物理地址 33e00000
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE
6、设置CPU为SVC模式
(1)msr cpsr_c, #0xd3 将CPU设置为禁止FIQ IRQ,ARM状态,SVC模式。
(2)其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件将其置为SVC模式。整个uboot工作时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
7、设置L2、L1cache和MMU
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有关的,不用去细看,大概知道即可。
cpu_init_crit:
#ifndef CONFIG_EVT1
#if 0
bl v7_flush_dcache_all
#else
bl disable_l2cache
mov r0, #0x0 @
mov r1, #0x0 @ i
mov r3, #0x0
mov r4, #0x0
lp1:
mov r2, #0x0 @ j
lp2:
mov r3, r1, LSL #29 @ r3 = r1(i) <<29
mov r4, r2, LSL #6 @ r4 = r2(j) <<6
orr r4, r4, #0x2 @ r3 = (i<<29)|(j<<6)|(1<<1)
orr r3, r3, r4
mov r0, r3 @ r0 = r3
bl CoInvalidateDCacheIndex
add r2, #0x1 @ r2(j)++
cmp r2, #1024 @ r2 < 1024
bne lp2 @ jump to lp2
add r1, #0x1 @ r1(i)++
cmp r1, #8 @ r1(i) < 8
bne lp1 @ jump to lp1
bl set_l2cache_auxctrl
bl enable_l2cache
#endif
#endif
bl disable_l2cache
bl set_l2cache_auxctrl_cycle
bl enable_l2cache
/*
* Invalidate L1 I/D
*/