add uboot and vanet
7
Zim/Research/u-boot/U-boot源代码全分析系列.txt
Normal 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
|
||||
|
||||
164
Zim/Research/u-boot/U-boot源代码全分析系列/1.txt
Normal 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支持较好)
|
||||
|
||||
- doc:U-Boot的说明文档;
|
||||
|
||||
- examples:可在U-Boot下运行的示例程序,如hello_world.c、timer.c;
|
||||
|
||||
- include:U-Boot头文件,注意:configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
|
||||
|
||||
- lib_xxx:处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件,lib_generic为通用的库函数实现;
|
||||
|
||||
- net:与网络功能相关的文件目录,如bootp、nfs、sntp、tftp;
|
||||
|
||||
- post:上电自检文件目录,目前仍有待于进一步完善;
|
||||
|
||||
- rtc:RTC驱动程序;
|
||||
|
||||
- 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
|
||||
….
|
||||
|
||||
这里的代码太长了,就没再粘贴,有兴趣的可以下载源码看一下,上面的只包括了机器校验、数据存储异常、指令存储异常等异常处理函数,由上也可以看到其连续存储的特性。
|
||||
300
Zim/Research/u-boot/U-boot源代码全分析系列/2.txt
Normal 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。
|
||||
174
Zim/Research/u-boot/U-boot源代码全分析系列/3.txt
Normal 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。
|
||||
@@ -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[0:3]来确定硬件配置字源选择,在目标板上可以设置跳线来改变CFG_RESET_SOURCE[0:3],选择硬件配置字源。本系统中的硬件配置字存放在CPLD模拟的Flash空间中。
|
||||
{{./1.gif}}
|
||||
从相应的地方读取硬件配置字(RCWL,RCWH)后,会设置相应的寄存器。其中RCWH中的BMS位值为1,定义了e300核心的MSR[IP]位初始值,如上图所示,MSR[IP]为1决定中断向量的前缀为0xFFF,启动存储空间的位置为0xFF80_0000~0xFFFF_FFFF;SWEN位为0,禁止软件看门狗;ROMLOC位为0b110,RLEXT位为0b00确定了选择local bus GPCM-16bit ROM为启动ROM。复位向量和本地地址映射的默认启动ROM访问将直接指向ROMLOC指定的接口。选中的启动ROM的本地访问窗口(LBLAW0)将被使能,并初始化基地址LBLAWBAR0为0xFF80_0000,窗口大小为8M。这时,Local Bus上的片选CS0的寄存器值为BR0:0000_0000,OR0:0000_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_law1,remap_flash_by_law0等函数要做的,具体的流程可以由下面的五张图来说明:
|
||||
|
||||
|
||||
|
||||
1 开始时,BR0,OR0为全零,4G全是重复的Flash,CPU通过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_0000,OR0大小为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上的片选BR,OR的值。这些值需要在/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 函数
|
||||
|
||||
|
||||
|
||||
该函数为板级初始化的第二阶段,主要是初始化PCI,PCIE,网口,Flash等设备,关闭看门狗,把前面借dcache做堆栈的空间解锁,还给cache。在一切设备都初始化好后,便会进去main_loop的死循环中。
|
||||
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
261
Zim/Research/u-boot/U-boot源代码全分析系列/U-Boot源码分析之Makefile.txt
Normal 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目标,注意不指定输出目标时,obj,src变量均为空,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
|
||||
预处理选项CPPFLAGS,C编译器选项CFLAGS,连接选项LDFLAGS
|
||||
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
|
||||
|
||||
指定了起始地址TEXT_BASE
|
||||
@指定编译规则:
|
||||
$(obj)%.s: %.S
|
||||
$(CPP) $(AFLAGS) -o $@ $
|
||||
回到顶层makefile文件:
|
||||
6)U-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传入ARCH,CPU,BOARD,VENDOR参数,mkconfig根据参数将include头文件夹相应的头文件夹连接好,生成config.h。然后执行make分别调用各子目录的makefile 生成所有的obj文件和obj库文件*.a,最后连接所有目标文件,生成镜像。不同格式的镜像都是调用相应工具由elf镜像直接或者间接生成的。
|
||||
剩下的工作就是分析U-Boot源代码了,有兴趣的可以看下我对Start.S分析的文章。
|
||||