add uboot and vanet

This commit is contained in:
geekard
2012-09-03 09:08:17 +08:00
parent 5ef7c20052
commit 05e8ae5877
60 changed files with 3909 additions and 70 deletions

View File

@@ -0,0 +1,7 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:23:01+08:00
====== U-boot源代码全分析系列 ======
Created Sunday 15 January 2012

View File

@@ -0,0 +1,164 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:23:20+08:00
====== 1 ======
Created Sunday 15 January 2012
http://blog.csdn.net/juana1/article/details/6699785
一、概述
U-Boot全称Universal Boot Loader是遵循GPL条款的开放源码项目是从FADSROM、8xxROM、PPCBOOT逐步发展演化而来的。其源码目录、编译形式与Linux内核很相似事实上不少U-Boot源码就是相应的Linux内核源程序的简化尤其是一些设备的驱动程序这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导当前它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义另外一层含义则是U-Boot除了支持PowerPC系列的处理器外还能支持MIPS、 x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标即支持尽可能多的嵌入式处理器和嵌入式操作系统。
就目前来看U-Boot对PowerPC系列处理器支持最为丰富对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11 月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前U-Boot项目正在他的领军之下众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入以支持更多的嵌入式操作系统的装载与引导。
选择U-Boot的理由如下
1、开放源码
2、支持多种嵌入式操作系统内核如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS
3、支持多个处理器系列如PowerPC、ARM、x86、MIPS、XScale
4、较高的可靠性和稳定性
5、较高的可靠性和稳定性
6、高度灵活的功能设置适合U-Boot调试、操作系统不同引导要求、产品发布等
7、丰富的设备驱动源码如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等
8、较为丰富的开发调试文档与强大的网络技术支持
本文将从整体架构出发尽自己的理解分析u-boot源码及其启动原理之后再介绍下移植u-boot时的一些技巧最后将以叙述移植的实例的方式来阐述其使用方法。
二、程序架构
要使用u-boot首先需要搞清楚它的程序架构要实现启动开发板需要修改哪些文件下面列举了uboot的主要目录结构
- board目标板相关文件主要包含SDRAM、Flash的驱动
- common独立于处理器体系结构的通用代码如内存大小探测与故障检测等它实现了u-boot的所有命令其中内置了一个shell脚本解释器(hush.c,a prototype bourne shell grammar parser)busybox中也使用了它
- cpu与处理器相关的文件如mpc8xx子目录下有串口、网口、LCD驱动及中断初始化等文件。其中cpu.c负责初始化CPU、设置指令cache和数据cache等interrupt.c负责设置系统的各种中断和异常如快速中断、开关中断、时钟中断、软件中断、预取中止和未定义指令等start.S负责u-boot启动时执行的第一个文件它主要设置系统堆栈和工作方式为跳转到C程序入口点做准备
- driver通用设备驱动如CFI Flash驱动目前对INTEL Flash支持较好
- docU-Boot的说明文档
- examples可在U-Boot下运行的示例程序如hello_world.c、timer.c
- includeU-Boot头文件注意configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件
- lib_xxx处理器体系相关的文件如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件lib_generic为通用的库函数实现
- net与网络功能相关的文件目录如bootp、nfs、sntp、tftp
- post上电自检文件目录目前仍有待于进一步完善
- rtcRTC驱动程序
- tools用于创建U-Boot S-RECORD和BIN镜像文件的工具
-fs文件系统程序包括ext2、Jffs2等
-disk硬盘接口程序。
在board目录下的每个子平台目录内都有一个连接脚本文件u-boot.lds从中可以找到u-boot的函数入口。另外该目录下还有一个config.mk文件用于设置TEXT_BASE的地址该地址就是希望运行的地址、链接地址。
u-boot 是一个层次式结构要让它跑起来应当至少提供串口驱动UART Driver、以太网驱动(Ethernet Driver)、Flash 驱动Flash 驱动以及USB 驱动USB Driver。目前通过USB 口下载程序显得不是十分必要所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用command 通过串口提供人机界面。
三、代码分析
本文的代码分析主要针对freescale的PowerPC芯片mpc83系列从u-boot启动的过程来分析其源代码目前大多数的bootloader都分为了Stage1和Stage2两个部分启动依赖于CPU体系结构的代码常放在Stage1且常用汇编语言实现在u-boot中功能代码集中在cpu/mpc83xx/start.S中它包括从系统上电后在基地址开始执行的部分它运行在flash中包括对cpu寄存器的初始化和将Stage2的代码拷贝到SDRAM中的代码。而Stage2则用于实现复杂的应用用C也有更好的可读性和移植性主要功能代码集中在lib_ppc/board.c中通过指定一系列的初始化函数表实现对系统的初始化工作。一般情况下u-boot编译后的程序不超过100k且Stage1的代码编译后的大小一般不超过10k
以下内容属转载虽是ARM但PowerPC与之类似
*************************************************************************
*/
ARM微处理器支持字节8位、半字16位、字32位3种数据类型
@向量跳转表每条占四个字节一个字地址范围为0x0000 0000@0x0000 0020
@ARM体系结构规定在上电复位后的起始位置必须有8条连续的跳
@转指令通过硬件实现。他们就是异常向量表。ARM在上电复位后@是从0x00000000开始启动的其实如果bootloader存在在执行
@下面第一条指令后就无条件跳转到start_code下面一部分并没@执行。设置异常向量表的作用是识别bootloader。以后系统每当有@异常出现则CPU会根据异常号从内存的0x00000000处开始查表@做相应的处理
/******************************************************
;当一个异常出现以后ARM会自动执行以下几个步骤:
;1.把下一条指令的地址放到连接寄存器LR(通常是R14).---保存位置
;2.将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中---保存CPSR
;3.根据异常类型强制设置CPSR的运行模式位
;4.强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中
*****************************************************************************/
首先来看下Stage1的过程系统主要实现了一下的功能
1、指定入口函数
一个可执行的镜像必须要有且只有一个全局入口通常情况下这个入口函数是放在ROM的起始位置而它是由处理器中断复位向量来决定的代码如下
[cpp] view plaincopy
. = EXC_OFF_SYS_RESET
.globl _start
_start: /* time t 0 */
li r21, BOOTFLAG_COLD /* Normal Power-On: Boot from FLASH*/
nop
b boot_cold
. = EXC_OFF_SYS_RESET + 0x10
.globl _start_warm
_start_warm:
li r21, BOOTFLAG_WARM /* Software reboot */
b boot_warm
需要注意的是,我们必须自己告诉编译器这个入口,而这个工作就是修改链接脚本文件(lds)。由上可见,函数执行开始后,一个立即读取指令后就是一个跳转语句。一般情况下(上电、复位等)程序都会执行boot_cold通过调用系统复位中断从System reset偏移向量0x100来获取指令每个中断向量有256个字节的空间。另外与start.s文件在一起的也有一个config.mk文件该文件用于定义编译选项。通过链接地址TEXT_BASE和运行地址.start的不同决定是否要复制代码。
2、设置异常向量(Exception Vector)
异常向量表也可称为中断向量表在mpc83xx中它是以0x100的偏移量连续分布的基地址的值取决于MSR[IP]当它为0是基地址为0x00000000为1时基地址为0xfff00000。该值是由启动方式决定的。源码如下
[cpp] view plaincopy
/*
* Vector Table
*/
.globl _start_of_vectors
_start_of_vectors:
/* Machine check */
STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
/* Data Storage exception. */
STD_EXCEPTION(0x300, DataStorage, UnknownException)
/* Instruction Storage exception. */
STD_EXCEPTION(0x400, InstStorage, UnknownException)
/* External Interrupt exception. */
#ifndef FIXME
STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt)
#endif
/* Alignment exception. */
. = 0x600
Alignment:
EXCEPTION_PROLOG(SRR0, SRR1)
mfspr r4,DAR
stw r4,_DAR(r21)
mfspr r5,DSISR
stw r5,_DSISR(r21)
addi r3,r1,STACK_FRAME_OVERHEAD
li r20,MSR_KERNEL
rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */
rlwimi r20,r23,0,25,25 /* copy IP bit from saved MSR */
lwz r6,GOT(transfer_to_handler)
mtlr r6
blrl
.L_Alignment:
.long AlignmentException - _start + EXC_OFF_SYS_RESET
.long int_return - _start + EXC_OFF_SYS_RESET
….
这里的代码太长了,就没再粘贴,有兴趣的可以下载源码看一下,上面的只包括了机器校验、数据存储异常、指令存储异常等异常处理函数,由上也可以看到其连续存储的特性。

View File

@@ -0,0 +1,300 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:23:41+08:00
====== 2 ======
Created Sunday 15 January 2012
3、初始化CPU相关
下面为初始化CPU的代码实现的功能依次为屏蔽watchdog、初始化中断控制寄存器、清空Cache、关闭MMU等。
[cpp] view plaincopy
.globl init_e300_core
init_e300_core: /* time t 10 */
/* Initialize machine status; enable machine check interrupt */
li r3, MSR_KERNEL /*设置MSR允许数据\指令复制以及Machine check*/
rlwimi r3, r5, 0, 25, 25 /* preserve IP bit set by HRCW */
#ifdef DEBUG
rlwimi r3, r5, 0, 21, 22 /* debugger might set SE & BE bits */
#endif
SYNC /* Some chip revs need this... */
mtmsr r3
SYNC
mtspr SRR1, r3 /* Make SRR1 match MSR 中断相关*/
lis r3, CFG_IMMR@h
#if defined(CONFIG_WATCHDOG)
/* Initialise the Wathcdog values and reset it (if req) */
lis r4, CFG_WATCHDOG_VALUE
ori r4, r4, (SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR)
stw r4, SWCRR(r3)
/* and reset it */
li r4, 0x556C
sth r4, SWSRR@l(r3)
li r4, -0x55C7
sth r4, SWSRR@l(r3)
#else
/* 关闭Wathcdog */
lwz r4, SWCRR(r3)
/* Check to see if its enabled for disabling
once disabled by SW you can't re-enable */
andi. r4, r4, 0x4
beq 1f
xor r4, r4, r4
stw r4, SWCRR(r3)
1:
#endif /* CONFIG_WATCHDOG */
/* Initialize the Hardware Implementation-dependent Registers */
/* HID0 also contains cache control */
/*------------------------------------------------------*/
lis r3, CFG_HID0_INIT@h
ori r3, r3, CFG_HID0_INIT@l
SYNC
mtspr HID0, r3
lis r3, CFG_HID0_FINAL@h
ori r3, r3, CFG_HID0_FINAL@l
SYNC
mtspr HID0, r3
lis r3, CFG_HID2@h
ori r3, r3, CFG_HID2@l
SYNC
mtspr HID2, r3
/* 关闭MMU功能先清空所有BAT (块地址转换)*/
xor r0, r0, r0
mtspr DBAT0U, r0
mtspr DBAT0L, r0
mtspr DBAT1U, r0
mtspr DBAT1L, r0
mtspr DBAT2U, r0
mtspr DBAT2L, r0
mtspr DBAT3U, r0
mtspr DBAT3L, r0
mtspr IBAT0U, r0
mtspr IBAT0L, r0
mtspr IBAT1U, r0
mtspr IBAT1L, r0
mtspr IBAT2U, r0
mtspr IBAT2L, r0
mtspr IBAT3U, r0
mtspr IBAT3L, r0
SYNC
/* 禁用tlb(快表) */
li r3, 32
mtctr r3
li r3, 0
1: tlbie r3
addi r3, r3, 0x1000
bdnz 1b
SYNC
/* Done! */
Blr
这里需要注意的是当程序在Flash中运行时执行程序跳转时使用了跳转指令但是不是使用绝对地址的跳转(即直接对PC操作)。因为若使用绝对地址那么程序的取址就是相对于当前PC位置向前或向后的一段空间而不会跳进SDRAM中。
4、初始化内存控制器
PowerPC处理器初识化内存控制器就是通过操作BAT以及TLB来实现的将IBAT0~7以及DBAT0~7初始化并禁用TLB代码如下
[cpp] view plaincopy
/* setup_bats - set them up to some initial state */
.globl setup_bats
setup_bats:
addis r0, r0, 0x0000
/* IBAT 0 */
addis r4, r0, CFG_IBAT0L@h
ori r4, r4, CFG_IBAT0L@l
addis r3, r0, CFG_IBAT0U@h
ori r3, r3, CFG_IBAT0U@l
mtspr IBAT0L, r4
mtspr IBAT0U, r3
isync
/* DBAT 0 */
addis r4, r0, CFG_DBAT0L@h
ori r4, r4, CFG_DBAT0L@l
addis r3, r0, CFG_DBAT0U@h
ori r3, r3, CFG_DBAT0U@l
mtspr DBAT0L, r4
mtspr DBAT0U, r3
isync
#ifdef CONFIG_HIGH_BATS
/* IBAT 4 */
addis r4, r0, CFG_IBAT4L@h
ori r4, r4, CFG_IBAT4L@l
addis r3, r0, CFG_IBAT4U@h
ori r3, r3, CFG_IBAT4U@l
mtspr IBAT4L, r4
mtspr IBAT4U, r3
isync
/* DBAT 4 */
addis r4, r0, CFG_DBAT4L@h
ori r4, r4, CFG_DBAT4L@l
addis r3, r0, CFG_DBAT4U@h
ori r3, r3, CFG_DBAT4U@l
mtspr DBAT4L, r4
mtspr DBAT4U, r3
isync
#endif
/* Invalidate TLBs.
* -> for (val = 0; val < 0x20000; val+=0x1000)
* -> tlbie(val);
*/
lis r3, 0
lis r5, 2
1:
tlbie r3
addi r3, r3, 0x1000
cmp 0, 0, r3, r5
blt 1b
blr
上面只贴出了高低位各一个BAT的操作代码其他的类似。
5、复制程序到RAM
PowerPC中此段程序并不返回在将程序代码全部复制到ROM中后将会直接继续在RAM中运行
[cpp] view plaincopy
/*完成了代码复制不返回直接调用in_ram执行*/
addi r0, r10, in_ram - _start + EXC_OFF_SYS_RESET
mtlr r0
blr
.globl relocate_code
relocate_code:
mr r1, r3 /* 创建一个新的栈指针 */
mr r9, r4 /* 备份 */
mr r10, r5
mr r3, r5 /* r3:拷贝的终点 */
lis r4, CFG_MONITOR_BASE@h /* r4:拷贝的起点 */
ori r4, r4, CFG_MONITOR_BASE@l
lwz r5, GOT(__init_end)
sub r5, r5, r4 /* r5:拷贝的长度 */
li r6, CFG_CACHELINE_SIZE /* Cache Line Size */
/* New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address */
sub r15, r10, r4
/* First our own GOT */
add r14, r14, r15
/* then the one used by the C code */
add r30, r30, r15
/* Now relocate code */
cmplw cr1,r3,r4
addi r0,r5,3
srwi. r0,r0,2
beq cr1,4f /* In place copy is not necessary */
beq 7f /* Protect against 0 count */
mtctr r0
bge cr1,2f
la r8,-4(r4)
la r7,-4(r3)
/* copy */
1: lwzu r0,4(r8)
stwu r0,4(r7)
bdnz 1b
addi r0,r5,3
srwi. r0,r0,2
mtctr r0
la r8,-4(r4)
la r7,-4(r3)
/* and compare */
20: lwzu r20,4(r8)
lwzu r21,4(r7)
xor. r22, r20, r21
bne 30f
bdnz 20b
b 4f
/* compare failed */
30: li r3, 0
blr
2: slwi r0,r0,2 /* re copy in reverse order ... y do we needed it? */
add r8,r4,r0
add r7,r3,r0
3: lwzu r0,-4(r8)
stwu r0,-4(r7)
bdnz 3b
/*
* Now flush the cache: note that we must start from a cache aligned
* address. Otherwise we might miss one cache line.
*/
4: cmpwi r6,0
add r5,r3,r5
beq 7f /* Always flush prefetch queue in any case */
subi r0,r6,1
andc r3,r3,r0
mr r4,r3
5: dcbst 0,r4
add r4,r4,r6
cmplw r4,r5
blt 5b
sync /* Wait for all dcbst to complete on bus */
mr r4,r3
6: icbi 0,r4
add r4,r4,r6
cmplw r4,r5
blt 6b
7: sync /* Wait for all icbi to complete on bus */
isync
6、初始化堆栈
对于mpc83xx系列初始化堆栈代码如下
[cpp] view plaincopy
/* set up the stack pointer in our newly created cache-ram (r1) */
lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h
ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l
li r0, 0 /* Make room for stack frame header and */
stwu r0, -4(r1) /* clear final stack frame so that */
stwu r0, -4(r1) /* stack backtraces terminate cleanly */
7、跳转到Stage2入口处
这也是Stage1的最后一步程序在执行到这一步后基本的硬件初始化工作也就完成了下面是跳转的代码
[cpp] view plaincopy
GET_GOT /* initialize GOT access */
/* r3: IMMR */
lis r3, CFG_IMMR@h
/* run low-level CPU init code (in Flash)*/
bl cpu_init_f
/* r3: BOOTFLAG */
mr r3, r21
/* run 1st part of board init code (in Flash)*/
bl board_init_f
其中board_init_f就是Stage2的函数入口。
由上可以看出start.S的流程为异常向量——上电复位后进入复位异常向量——跳到启动代码处——设置处理器进入管理模式——关闭看门狗——关闭中断——设置时钟分频——关闭MMU和CACHE——进入lowlever_init.S——检查当前代码所处的位置如果在FLASH中就将代码搬移到RAM中。至此Stage1分析到此结束。
跳转到board_init_f后程序开始执行Stage2阶段代码多为C。

View File

@@ -0,0 +1,174 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:24:09+08:00
====== 3 ======
Created Sunday 15 January 2012
这里首先更正下上一篇中的一个错误,最后一步中的跳转代码当时一时仓促贴错了,先改正如下:
7、跳转到Stage2入口处
这也是Stage1的最后一步程序在执行到这一步后基本的硬件初始化工作也就完成了下面是跳转的代码
[cpp] view plaincopy
clear_bss:
/* 执行清空bss操作 */
lwz r3,GOT(__bss_start)
#if defined(CONFIG_HYMOD)
/*
* For HYMOD - the environment is the very last item in flash.
* The real .bss stops just before environment starts, so only
* clear up to that point.
* taken from mods for FADS board
* 检查当前代码位置
*/
lwz r4,GOT(environment)
#else
lwz r4,GOT(_end)
#endif
/* 计算跳转 */
cmplw 0, r3, r4
beq 6f
li r0, 0
5:
stw r0, 0(r3)
addi r3, r3, 4
cmplw 0, r3, r4
bne 5b
6:
mr r3, r9 /* Global Data pointer */
mr r4, r10 /* Destination Address */
bl board_init_r
其中board_init_r就是Stage2的函数入口。
由上可以看出start.S的流程为异常向量——上电复位后进入复位异常向量——跳到启动代码处——设置处理器进入管理模式——关闭看门狗——关闭中断——设置时钟分频——关闭MMU和CACHE——进入low level初始化代码——检查当前代码所处的位置如果在FLASH中就将代码搬移到RAM中。至此Stage1分析到此结束。
跳转到board_init_r后程序开始执行Stage2阶段代码多为C。
望谅解。
在上篇分析完Stage1的汇编代码后又细看了两个C函数也是属于很重要的初始化函数所以在分析Stage2的代码前先还要看下Stage1的两个C代码程序分别为cpu_init_f和board_init_f二者在汇编程序in_flash中被调用虽是C代码但实现的仍是基本的初始化功能且运行在ROM中属于Stage1的范畴。
先看cpu_init_f函数它是用来初始化low-level CPU的主要功能包括建立内存映射map、初始化一些寄存器和UPM(User-Programmable Machine)。
首先初始化一个结构体:
[cpp] view plaincopy
gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET);
/* Clear initial global data */
memset ((void *) gd, 0, sizeof (gd_t));
此处的结构体gd_t是一个放在启动后很早就可用的内存中的就像mpc8xx、mpc82xx的DPRAM或者是数据cache的locked parts主要用于存放一小部分系统初始化时要用的全局变量直到初始化内存控制器可用RAM之前这是我们唯一能用的全局变量。这个区间是很小的在本区间中只有256个字节。之后就开始配置各种设备的时钟模式下面是对PCI和DMA的配置
[cpp] view plaincopy
#ifdef CFG_SCCR_PCICM
/* PCI & DMA clock mode */
im->clk.sccr = (im->clk.sccr & ~SCCR_PCICM) |
(CFG_SCCR_PCICM << SCCR_PCICM_SHIFT);
#endif
这个配置的选项是要根据datasheet里的SCCR寄存器来定的下面是mpc83xx的一个系统时钟控制寄存器的各位
根据说明来对应uboot中include/Mpc83xx.h中的相关配置不过大多数的CPU型号都已经配置好了一般无需改动。
接下来初始化一些寄存器如复位控制寄存器DDR控制驱动寄存器等
[cpp] view plaincopy
/* RSR - Reset Status Register - clear all status */
gd->reset_status = im->reset.rsr;
im->reset.rsr = ~(RSR_RES);
/* RMR - Reset Mode Register contains checkstop reset enable*/
im->reset.rmr = (RMR_CSRE & (1<<RMR_CSRE_SHIFT));
/* LCRR - Clock Ratio Register */
im->lbus.lcrr = CFG_LCRR;
/* Enable Time Base & Decrimenter ( so we will have udelay() )*/
im->sysconf.spcr |= SPCR_TBEN;
/* System General Purpose Register */
im->sysconf.sicrh = CFG_SICRH;
这些都需要对照着Datasheet里的第四章Reset,Clockig,and Initialization一一比对着看这样才能加深印象(尽管大多数实际应用中都不用修改)。
最后是初始化内存映射下面代码作用为将bank0映射到Flash的bank0的初始地址后面还有部分代码将根据需要映射bank1~7的
[cpp] view plaincopy
im->lbus.bank[0].br = CFG_BR0_PRELIM;
im->lbus.bank[0].or = CFG_OR0_PRELIM;
im->sysconf.lblaw[0].bar = CFG_LBLAWBAR0_PRELIM;
im->sysconf.lblaw[0].ar = CFG_LBLAWAR0_PRELIM;
再来看下board_init_f函数它主要用于在启动时尽快的提供一个控制台接口(串口)用于输出错误信息并且初始化内存以便于复制代码。这段代码的编写需要注意全局变量是只读的BSS还未初始化堆栈空间也很小(尽量不要有复杂操作)。最开始先进行一系列的初始化操作和ARM系列类似将初始化函数列表放在结构体init_sequence中
[cpp] view plaincopy
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr) () != 0) {
hang ();
}
}
主要包括板件前期初始化、获取CPU及总线时钟、初始化SDRAM时钟、初始化串口等操作。此时内存已经映射好了就可以在DRAM中运行程序了接下来我们需要在RAM的末端保存一些数据包括uboot和Linux不能操作的区域、内核日志缓存、PRAM(被保护的RAM)、LCD帧缓存、监听代码、板件信息等。
[cpp] view plaincopy
/*通过修改gd->ram_size可以使uboot无法访问指定区间*/
gd->ram_size -= CFG_MEM_TOP_HIDE;
addr = CFG_SDRAM_BASE + get_effective_memsize();
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/*保存内核日志*/
addr -= (LOGBUFF_RESERVE);
debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
#endif
#endif
#ifdef CONFIG_PRAM
/* reserve protected RAM */
i = getenv_r ("pram", (char *)tmp, sizeof (tmp));
reg = (i > 0) ? simple_strtoul ((const char *)tmp, NULL, 10) : CONFIG_PRAM;
addr -= (reg << 10); /* size is in kB */
debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */
#ifdef CONFIG_LCD
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */
#if defined(CONFIG_VIDEO) && defined(CONFIG_8xx)
/* reserve memory for video display (always full pages) */
addr = video_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VIDEO */
#ifdef CONFIG_AMIGAONEG3SE
gd->relocaddr = addr;
#endif
/* reserve memory for malloc() arena */
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, addr_sp);
/* (permanently) allocate a Board Info struct and a permanent copy of the "global" dat*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug ("Reserving %zu Bytes for Board Info at: %08lx\n",sizeof (bd_t), addr_sp);
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp);
接下来的代码比较容易理解就是将板件信息存储到结构体board_info里包括串口信息、PHY芯片信息、启动参数、RAM参数、flash信息等。
分析完这两个C函数后Stage1的分析就全部结束了。接下来跳入board_init_r函数中开始Stage2。

View File

@@ -0,0 +1,302 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:29:55+08:00
====== MPC8314 e300核 uboot 调试 一 ======
Created Sunday 15 January 2012
http://blog.csdn.net/gorilla0123/article/details/5899452
历经2个多月完成了MPC8314最小系统uboot及Linux内核和根文件系统的调试。这是我第一次从头开始做小系统和内核的移植工作虽然调试的比较辛苦但是收获还是很多的。下面就介绍一下调试的过程和一些原理性的东西。
1 MPC8314 上电流程
系统上电后经过若干个时钟后MPC8314会检测复位配置输入信号CFG_RESET_SOURCE[03]来确定硬件配置字源选择在目标板上可以设置跳线来改变CFG_RESET_SOURCE[03]选择硬件配置字源。本系统中的硬件配置字存放在CPLD模拟的Flash空间中。
{{./1.gif}}
从相应的地方读取硬件配置字RCWLRCWH会设置相应的寄存器。其中RCWH中的BMS位值为1定义了e300核心的MSR[IP]位初始值如上图所示MSR[IP]为1决定中断向量的前缀为0xFFF启动存储空间的位置为0xFF80_0000~0xFFFF_FFFF;SWEN位为0禁止软件看门狗ROMLOC位为0b110RLEXT位为0b00确定了选择local bus GPCM-16bit ROM为启动ROM。复位向量和本地地址映射的默认启动ROM访问将直接指向ROMLOC指定的接口。选中的启动ROM的本地访问窗口LBLAW0将被使能并初始化基地址LBLAWBAR0为0xFF80_0000窗口大小为8M。这时Local Bus上的片选CS0的寄存器值为BR00000_0000,OR00000_0000。整个4G空间全是ROM每16M重复一次。
中断向量的前缀为0xFFF复位向量为100决定了系统复位后的第一条指令从0xFFF0_0100处获得。
/cpu/mpc83xx/u-boot.lds文件时连接器脚本文件其中
.text :
{
cpu/mpc83xx/start.o (.text)
。 。 。 。 。
}
。 。 。 。 。 。
ENTRY(_start)
规定了代码段从/cpu/mpc83xx/start.s开始。而ENTRY(_start)这一句告诉编译器uboot.bin的镜像入口点为start.s中的_start标号。
所以我们要将UBOOT的代码烧写到16M Flash中偏移15M的地方保证了从0xFFF0_0100的空间取到的指令为uboot代码。并且还需要保证0xFFF0_0100为start标号。
2 uboot启动流程
2.1 uboot启动概述
Uboot的启动是从/cpu/mpc83xx/start.s中的_start标号开始的经历了 /cpu/mpc83xx/start.s/cpu/mpc83xx/cpu_init.c /lib_ppc/Board.c等几个文件中的多个汇编和C函数最后会在/lib_ppc/Board.c中的board_init_r函数中进入命令死循环等待执行键入的命令。其具体的流程如下图所示
{{./2.gif}}
2.2 init_e300_core 函数
初始化e300核心禁止中断响应只允许machine check中断和system reset中断禁止指令和数据地址转换即关闭MMU进行实地址转换设置为supervisor级别禁止看门狗无效指令和数据cache为系统创建一个干净可靠的初始环境。
2.3 窗口重映射
从前面MPC8314上电流程可以看出上电之后第一条代码是从0xFFF0_0100的地方开始执行的但是flash并不一定会分配在0xFF00_0000到0xFFFF_FFFF的地方以我们16M的为例。在本系统中Flash的地址就被分配到了0xFE00_0000到0xFEFF_FFFF的地方。所以这其中需要做一个跳转这正是这段代码中map_flash_by_law1remap_flash_by_law0等函数要做的具体的流程可以由下面的五张图来说明
1 开始时BR0OR0为全零4G全是重复的FlashCPU通过LBLAW0访问Flash
空间 FF80_0000到 FFFF_FFFF。
{{./3.gif}}
2 map_flash_by_law1函数使用LBLAW1映射FE00_0000到FEFF_FFFF这段空间。
{{./4.gif}}
3 跳转到FE00_0000这段空间执行代码由于4G空间重复只要偏移地址计算正确就
会顺序执行。
{{./5.gif}}
4 remap_flash_by_law0函数设置BR0为FE00_0000OR0大小为16M。
{{./6.gif}}
5 remap_flash_by_law0函数设置LBLAW0窗口映射FE00_0000到FE7F_FFFF区域
{{./7.gif}}
清除LBLAW1。
2.4 Dcache 中分配空间做堆栈
程序跑到这里就要进入第一个C函数了C函数的运行需要堆栈空间但这时RAM还没有初始化只能在Dcache中锁定一定的空间用于C的堆栈空间。
lock_ram_in_cache函数在Dcache中锁定4k的空间。
下面的几行代码将堆栈指针指向刚刚分配好的Dcache空间。
lis r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@h
ori r1, r1, (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET)@l
li r0, 0
stwu r0, -4(r1)
2.5 cpu_init_f 函数
该函数是系统执行的第一个C语言的函数主要是做一些CPU 寄存器的初始化其中最重要的部分是初始化Local Access Windows的值和Local Bus上的片选BROR的值。这些值需要在/include/configs/MPC8315ERDB.h中配置好。
2.6 board_init_f 函数
该函数为板级初始化的第一个函数会对板子上很多硬件外设做初始化其中最重要的为init_sequence数组该数组里面包含了很多板级的硬件初始化函数在board_init_f函数中会依次的调用该数组中的函数去初始化各个硬件该数组如下
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
#if !defined(CONFIG_8xx_CPUCLK_DEFAULT)
get_clocks, /* get CPU and bus clocks (etc.) */
#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) /
&& !defined(CONFIG_TQM885D)
adjust_sdram_tbs_8xx,
#endif
init_timebase,
#endif
#ifdef CFG_ALLOC_DPRAM
#if !defined(CONFIG_CPM2)
dpram_init,
#endif
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
board_postclk_init,
#endif
env_init,
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)
get_clocks_866, /* get CPU and bus clocks according to the environment variable */
sdram_adjust_866, /* adjust sdram refresh rate according to the new clock */
init_timebase,
#endif
init_baudrate,
serial_init,
console_init_f,
display_options,
#if defined(CONFIG_8260)
prt_8260_rsr,
prt_8260_clks,
#endif /* CONFIG_8260 */
#if defined(CONFIG_MPC83XX)
prt_83xx_rsr,
#endif
checkcpu,
#if defined(CONFIG_MPC5xxx)
prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */
#if defined(CONFIG_MPC8220)
prt_mpc8220_clks,
#endif
checkboard,
INIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)
misc_init_f,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
#if defined(CONFIG_DTT) /* Digital Thermometers and Thermostats */
dtt_init,
#endif
#ifdef CONFIG_POST
post_init_f,
#endif
INIT_FUNC_WATCHDOG_RESET
init_func_ram,
#if defined(CFG_DRAM_TEST)
testdram,
#endif /* CFG_DRAM_TEST */
INIT_FUNC_WATCHDOG_RESET
NULL, /* Terminate this list */
};
可以看到时钟,内存,串口,控制台等初始化函数的调用,其中串口的初始化要先于内存初始化。
2.7 relocate_code 函数
到目前为止boot代码都是在Flash中运行但是代码最终是要到RAM中运行的在上面的board_init_f函数中已经将RAM初始化好了具备了在RAM中运行程序的能力现在relocate_code函数需要做两个事情
1 从Flash中拷贝uboot的代码到RAM
2 记下现在执行代码的偏移跳转到RAM中相应的位置执行。
2.8 board_init_r 函数
该函数为板级初始化的第二阶段主要是初始化PCIPCIE网口Flash等设备关闭看门狗把前面借dcache做堆栈的空间解锁还给cache。在一切设备都初始化好后便会进去main_loop的死循环中。

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,261 @@
Content-Type: text/x-zim-wiki
Wiki-Format: zim 0.4
Creation-Date: 2012-01-15T19:25:07+08:00
====== U-Boot源码分析之Makefile ======
Created Sunday 15 January 2012
之前用过两个版本u-boot分析过它的Start.S文件(PowerPC、ARM)源代码,也移植过内部的各部分硬件驱动及组件(串口、I2C、SPI、Flash文件系统、USB、DMA等)源码自我感觉比较熟悉了。但最近逛CSDN论坛发现有不少人在问U-Boot中的Makefile的一些参数含义及配置方法这才觉得忽视了U-Boot源码中最重要的组织者。正好又在ChinaUnix上看见了一篇http://blog.chinaunix.net/u3/90973/showart_1815948.html解析的是1.1.6版本2410平台的Makefile。参考了一下这里分析2011.6版FreeScale的mpc83xx系列处理器平台。
u-boot的源代码包含了对几十种处理器、数百种开发板的支持可是对于特定的开发板配置编译过程只需要其中部分程序。这里就需要用到Makefile了。
编译
以mpc8313erdb板为例编译的过程分两部
# make mpc8313erdb_config
# make
顶层Makefile分析
要了解一个LINUX工程的结构必须看懂Makefile尤其是顶层的没办法UNIX世界就是这么无奈什么东西都用文档去管理、配置。还是以mpc8313为例顺序分析Makefile大致的流程及结构如下
1) Makefile中定义了源码及生成的目标文件存放的目录,目标文件存放目录BUILD_DIR可以通过make O=dir或者export BUILD_DIR=dir两种方式指定。如果没有指定则设定为源码的根目录一般编译的时候都建议指定输出目录这样可以不影响其他的源码结构便于管理至于它的控制流程每一步指令都有详细注释感兴趣的可以看一下再看下其它目录变量的定义
OBJTREE和LNDIR为存放生成文件的目录TOPDIR与SRCTREE为源码所在目录
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
2定义变量MKCONFIG这个变量指向一个脚本即顶层目录的mkconfig。
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
在编译U-BOOT之前先要执行
#make mpc8313erdb_33_config(u-boot中有两种主频的8313处理器所以也要添加配置)
mpc8313erdb_33_config是Makefile的一个目标定义如下
mpc8313erdb_33_config : unconfig
@$(MKCONFIG) -a MPC8313ERDB ppc mpc8313 mpc8313erdb freescale
unconfig::
@mkdir -p $(obj)include
@if [ "$(findstring _33_,$@)" ] ; then \
$(XECHO) -n "...33M ..." ; \
echo "#define CFG_33MHZ" >>$(obj)include/config.h ; \
fi ; \
if [ "$(findstring _66_,$@)" ] ; then \
$(XECHO) -n "...66M..." ; \
echo "#define CFG_66MHZ" >>$(obj)include/config.h ; \
fi ;
显然,在执行# make mpc8313erdb_33__config时先执行unconfig目标注意不指定输出目标时objsrc变量均为空unconfig下面的命令清理上一次执行make *_config时生成的头文件和makefile的包含文件。主要是include/config.h和include/config.tmp文件。
然后才执行命令@$(MKCONFIG) -a MPC8313ERDB ppc mpc8313 mpc8313erdb freescale
MKCONFIG 是顶层目录下的mkcofig脚本文件后面五个是传入的参数。
对于mpc8313erdb_33_config而言mkconfig主要做三件事
在include文件夹下建立相应的文件软连接如果是PowerPC体系将执行以下操作
#ln -s asm-ppc asm
#ln -s arch-mpc8313erdb asm-ppc
生成Makefile包含文件include/config.mk内容很简单定义了四个变量
ARCH = ppc
CPU = mpc83xx
BOARD = mpc8313erdb
VENDOR = freescale
生成include/config.h头文件只有一行
/* Automatically generated - do not edit */
#include "config/ mpc8313erdb.h"
mkconfig脚本文件的执行至此结束继续分析Makefile剩下部分。
3包含include/config.mk其实也就相当于在Makefile里定义了上面四个变量而已。
4) 指定交叉编译器前缀:
ifeq ($(ARCH),ppc)#这里根据ARCH变量指定编译器前缀。
CROSS_COMPILE = ppc-8xx-
endif
5)包含config.mk:
#包含顶层目录下的config.mk这个文件里面主要定义了交叉编译器及选项和编译规则
# load other configuration
include $(TOPDIR)/config.mk
下面分析config.mk的内容
包含体系开发板CPU特定的规则文件
ifdef ARCH #指定预编译体系结构选项
sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules
endif
ifdef CPU #定义编译时对齐,浮点等选项
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include CPU specific rules
endif
ifdef SOC #没有这个文件
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include SoC specific rules
endif
ifdef BOARD #指定特定板子的镜像连接时的内存基地址,重要!
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
@定义交叉编译链工具
# Include the make variables (CC, etc...)
#
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB = $(CROSS_COMPILE)RANLIB
定义AR选项ARFLAGS调试选项DBGFLAGS优化选项OPTFLAGS
 预处理选项CPPFLAGSC编译器选项CFLAGS连接选项LDFLAGS
 LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) 
指定了起始地址TEXT_BASE
@指定编译规则:
$(obj)%.s: %.S
$(CPP) $(AFLAGS) -o $@ $
回到顶层makefile文件
6U-boot需要的目标文件。
OBJS = cpu/$(CPU)/start.o # 顺序很重要start.o必须放第一位
OBJS := $(addprefix $(obj),$(OBJS))
7需要的库文件
LIBS = lib_generic/libgeneric.a
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).a"; fi) 上面的意思是根据厂商选择编译通用文件这里为freescale
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += drivers/bios_emulator/libatibiosemu.a
LIBS += drivers/block/libblock.a
LIBS += drivers/dma/libdma.a
LIBS += drivers/hwmon/libhwmon.a
LIBS += drivers/i2c/libi2c.a
LIBS += drivers/input/libinput.a
LIBS += drivers/misc/libmisc.a
LIBS += drivers/mmc/libmmc.a
LIBS += drivers/mtd/libmtd.a
LIBS += drivers/mtd/nand/libnand.a
LIBS += drivers/mtd/nand_legacy/libnand_legacy.a
LIBS += drivers/mtd/onenand/libonenand.a
LIBS += drivers/mtd/spi/libspi_flash.a
LIBS += drivers/net/libnet.a
LIBS += drivers/net/sk98lin/libsk98lin.a
LIBS += drivers/pci/libpci.a
LIBS += drivers/pcmcia/libpcmcia.a
LIBS += drivers/spi/libspi.a
ifeq ($(CPU),mpc83xx)
LIBS += drivers/qe/qe.a
endif
LIBS += drivers/rtc/librtc.a
LIBS += drivers/serial/libserial.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/video/libvideo.a
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) $(VERSION_FILE)
LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
根据上面的include/config.mk文件定义的ARCH、CPU、BOARD、SOC这些变量。硬件平台依赖的目录文件可以根据这些定义来确定。
8最终生成的各种镜像文件
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
$(obj) u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
分析一下最关键的u-boot ELF文件镜像的生成
依赖目标depend :生成各个子目录的.depend文件.depend列出每个目标文件的依赖文件。生成方法调用每个子目录的make _depend。
depend dep: $(VERSION_FILE)
for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
@依赖目标version生成版本信息到版本文件VERSION_FILE中。
$(VERSION_FILE):
@( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
'$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))' \
) > $@.tmp
@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
@伪目标SUBDIRS: 执行tools ,examples ,post,post\cpu 子目录下面的make文件。
SUBDIRS = tools \
examples \
post \
post/cpu
.PHONY : $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@ all
@依赖目标$(OBJS)即cpu/start.o
$(OBJS):
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
@依赖目标$(LIBS),这个目标太多,都是每个子目录的库文件*.a 通过执行相应子目录下的make来完成
$(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@))
@依赖目标$(LDSCRIPT)
$(LDSCRIPT): depend $(obj)include/autoconf.mk
$(MAKE) -C $(dir $@) $(notdir $@)
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
对于mpc8313,LDSCRIPT即连接脚本文件是board/freescale/mpc8313erdb/u-boot.lds定义了连接时各个目标文件是如何组织的。
@执行连接命令:
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
其实就是把start.o和各个子目录makefile生成的库文件按照LDFLAGS连接在一起生成ELF文件u-boot 和连接时内存分配图文件u-boot.map。
9)对于各子目录的makefile文件主要是生成*.o文件然后执行AR生成对应的库文件。如lib_generic文件夹Makefile
LIB = $(obj)libgeneric.a
COBJS = bzlib.o bzlib_crctable.o bzlib_decompress.o \
bzlib_randtable.o bzlib_huffman.o \
crc32.o ctype.o display_options.o ldiv.o \
string.o vsprintf.o zlib.o
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
$(LIB): $(obj).depend $(OBJS) #项层Makefile执行make libgeneric.a
$(AR) $(ARFLAGS) $@ $(OBJS)
整个makefile剩下的内容全部是各种不同的开发板的*_config:目标的定义了。
概括起来工程的编译流程也就是通过执行执行一个make *_config传入ARCHCPUBOARDVENDOR参数mkconfig根据参数将include头文件夹相应的头文件夹连接好生成config.h。然后执行make分别调用各子目录的makefile 生成所有的obj文件和obj库文件*.a最后连接所有目标文件生成镜像。不同格式的镜像都是调用相应工具由elf镜像直接或者间接生成的。
剩下的工作就是分析U-Boot源代码了有兴趣的可以看下我对Start.S分析的文章。