Chinaunix首页 | 论坛 | 博客
  • 博客访问: 399649
  • 博文数量: 83
  • 博客积分: 2011
  • 博客等级: 大尉
  • 技术积分: 741
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-04 22:51
文章分类

全部博文(83)

文章存档

2009年(83)

我的朋友

分类: LINUX

2009-06-01 20:41:48

u-boot-1.1.6学习笔记 Vicegod 2008-5-28
1、 u-boot-1.1.6 目录结构
 .
 |-- board -->平台依赖,存放电路板相关源文件的目录,例如:dave、smdk2410等
 |-- cpu -->平台依赖,存放与具体CPU型号相关源文件的目录,例如:arm720t、arm920t、i386等
 |-- lib_arm ----------------------------------------------------------------------------------------------------->
 |-- lib_avr32 -->
 |-- lib_blackfin -->
 |-- lib_i386 -->平台依赖,用于存放对相应体系结构通用的文件,主要用于实现相关平台通用的函数
 |-- lib_m68k -->比如lib_arm :
 |-- lib_microblaze -->存放对ARM体系结构通用的文件,主要用于实现ARM平台通用的函数
 |-- lib_mips -->
 |-- lib_nios -->
 |-- lib_nios2 -->
 |-- lib_ppc ------------------------------------------------------------------------------------------------------->
 |-- lib_generic -->通用,对所有体系结构通用的库函数的实现,比如vsprintf、string等函数的实现
 |-- include -->通用,头文件和开发板配置文件,所有开发板的配置文件都在 include/configs目录下
 |-- common -->通用,通用的多功能函数实现,比如U-BOOT的命令setenv、bootm、cp等函数的实现
 |-- drivers -->通用,通用设备的驱动程序
 |-- disk -->通用,硬盘接口驱动程序
 |-- dtt -->通用,传感器的驱动程序
 |-- fs -->通用,存放文件系统相关的程序

 |-- nand_spl -->通用,Nand Flash boot的程序
 |-- net -->通用,存放网络相关的程序
 |-- post -->通用,存放上电自检的程序
 |-- rtc -->通用,实时时钟(RTC)的驱动程序
 |-- examples -->应用例程,一些独立运行的应用程序的例子,例如helloworld
 |-- tools -->工具,存放制作S-Record或者U-boot格式的映像等工具,例如mkimage
 `-- doc -->文档,开发使用文档
2、u-boot 启动流程:
 |--> lowlevel_init: |
 |--> cup_init_crit: | |--> cpu_init_crit: |
_start: --> reset: | |--> reset: --> relocate: --> _start_armboot: --> Start_armboot() --> main_loop() ---|
 ↑ |
 |__________|
 u-boot-1.1.6/cpu/xxx/Start.S _start:
 u-boot-1.1.6/cpu/xxx/Start.S reset:
 u-boot-1.1.6/cpu/xxx/Start.S cup_init_crit:
 u-boot-1.1.6/board/yyy/lowlevel_init.S lowlevel_init:
 u-boot-1.1.6/cpu/xxx/Start.S relocate:
 u-boot-1.1.6/cpu/xxx/Start.S _start_armboot:
 u-boot-1.1.6/lib_arm/board.c start_armboot()
 u-boot-1.1.6/common/main.c main_loop()
 
说明:xxx(板子上具体的cpu型号,如arm920t)
 yyy(开发板的型号,如smdk2410)

 
 
 
3、u-boot-1.1.6/cpu/xxx/start.S分析
#include
#include
/* 这段代码的主要作用是初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备
好正确的环境 */
#if defined(CONFIG_OMAP1610)
#include <./configs/omap1510.h>
#elif defined(CONFIG_OMAP730)
#include <./configs/omap730.h>
#endif
/*************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************/
.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
 
 .balignl 16,0xdeadbeef
/************************************************分析****************************************************
 * 从中我们可以看出,ARM支持7种异常。问题是发生了异常后ARM是如何响应的呢?
 * 第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从
* 复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么什么时候会执行
* 到后面几个异常处理函数呢?步骤是这样的:
 *

 * 当一个异常出现以后,ARM会自动执行以下几个步骤:
 * (1) 把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。
 * (2) 将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。
 * (3) 根据异常类型,强制设置CPSR的运行模式位。
 * (4) PC(程序计数器)被强制成相关异常向量处理函数地址,从而跳转到相应的异常处理程序中。
 *
 * 当异常处理完毕后,ARM会执行以下几步操作从异常返回:
 * (1) 将连接寄存器LR的值减去相应的偏移量后送到PC中
 * (2) 将SPSR复制回CPSR中
 * (3) 若在进入异常处理时设置了中断禁止位,要在此清除
 *
 * ARM规定了异常向量的地址:
 * b reset ; 复位 0x0
 * ldr pc, _undefined_instruction ; 未定义的指令异常 0x4
 * ldr pc, _software_interrupt ; 软件中断异常 0x8
 * ldr pc, _prefetch_abort ; 预取指令 0xc
 * ldr pc, _data_abort ; 数据 0x10
 * ldr pc, _not_used ; 未使用 0x14
 * ldr pc, _irq ; 慢速中断异常 0x18
 * ldr pc, _fiq ; 快速中断异常 0x1c
 * 这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到
* 相应的处理程序,然后再返回到主程序继续执行。
 *
 * .balignl 16,0xdeadbeef, 将地址对齐到16的倍数,如果地址寄存器的值(PC)跳过4个字节才是16的倍数,
* 则使用0xdeadbeef填充这4个字节,如果它跳过1、2、3个字节,则填充值不确定。如果地址寄存器的值(PC)
* 是16的倍数,则无需移动。

 ********************************************************************************************************/
/*************************************************************************
 *
 * 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
 
.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
 
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
 .word 0x0badc0de
 
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
 .word 0x0badc0de
#endif
 
/*************************************************分析**********************************************************
 * 上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它的使用。
* 还有一些变量的值是通过连接脚本得到的,比如TEXT_BASE位于/u-boot-1.1.6/board/xxx(开发板目录名称)/config.mk
* 文件里。__bss_start、_end位于/u-boot-1.1.6/board/xxx(开发板目录名称)/u-boot.lds文件里,具体值是由编译器算出来的。
 ***************************************************************************************************************/
 
/* the actual reset code*/
/* 系统的复位代码。系统一上电,就跳到这里运行 */
reset:
 /*
 * set the cpu to SVC32 mode
 */

 mrs r0,cpsr /* 取得当前程序状态寄存器cpsr到r0 */
 bic r0,r0,#0x1f /* 这里使用位清除指令,把中断全部清除,只置位模式控制位 为中断提供服务通常是 OS
*设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。
*/
 orr r0,r0,#0xd3 /* 计算为超级保护模式 */
 msr cpsr,r0 /* 设置cpsr为超级保护模式 */
 
/***********************************************分析*************************************************************
 * 设置cpu运行在SVC32模式。ARM共有7种模式:
 * 用户模式(usr): arm处理器正常的程序执行状态
 * 快速中断模式(fiq): 用于高速数据传输或通道处理
 * 外部中断模式(irq): 用于通用的中断处理
 * 超级保护模式(svc): 操作系统使用的保护模式
 * 数据访问终止模式(abt): 当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护
 * 系统模式(sys): 运行具有特权的操作系统任务
 * 未定义指令中止模式(und): 当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
 *
* 通过设置ARM的CPSR寄存器,让CPU运行在操作系统保护模式,为后面进行其它操作作好准备了。
 ****************************************************************************************************************/
 
 /*
 * we do sys-critical inits only at reboot,
 * not when booting from ram!
 */
 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT

 bl cpu_init_crit
/******************************************************************************
* BL为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址
* ARM 指令集中的4条跳转指令可以完成从当前指令向前或向后的32MB 的地址空间的跳转,
 * 用的是相对寻址,它们是:B、BL、BLX、BX
 *******************************************************************************/
#endif
 
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* 重定位Boot代码到RAM内存,将Boot代码从FLASH移到RAM中 */
relocate: /* relocate U-Boot to RAM */
 adr r0, _start /* r0 <- current position of code */
/**************************************************************************
* 把_start的相对地址移到r0, 相对寻址以程序计数器PC 的当前值为基地址,
* 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
* 它是与位置无关的,主要看Boot在哪里运行,也就是PC指针在哪里 (假设_start偏移量为0),
* 例如这段代码在 0x02000000 (FLASH起始地址)运行,即此时PC=0x02000000,那么 adr r0, _start 得到 r0 = 0x02000000;
* 如果在地址 0x81008000(Boot在RAM中加载地址)运行,即此时PC=0x81008000,那么r0就是 0x81008000 了。
*
* 此处要注意ldr与adr的区别,看下面的代码片段:
* ldr r0, _start
* adr r0, _start
* ldr r0, =_start
* nop
* mov pc, lr
* _start:

* nop
* 下面是反汇编的结果:
* 0c008000 <_start-0x14>:
* c008000: e59f000c ldr r0, [pc, #12] ; c008014 <_start>
* c008004: e28f0008 add r0, pc, #8 ; 0x8
* c008008: e59f0008 ldr r0, [pc, #8] ; c008018 <_start+0x4>
* c00800c: e1a00000 nop (mov r0,r0)
* c008010: e1a0f00e mov pc, lr
*
* 0c008014 <_start>:
* c008014: e1a00000 nop (mov r0,r0)
*
* 分析:
* ldr r0, _start
* 从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000
*
* adr r0, _start
* 取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的是相对的位置。例如这段代码在 0x0c008000 运行,
* 那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。即当前PC值加上_start的偏移量。
*
* ldr r0, =_start
* 这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,
* 一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,所以不能直接用 mov 指令来给 r0 赋一个 32bit 的常量,
* 所以需要多出一个空间存放 _start 的真正数据,这个数据是在 link 的时候确定的,在这里就是 0x0c008014)。
* 因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014
**************************************************************************/

 ldr r1, _TEXT_BASE/* test if we run from flash or RAM */ /* 把_TEXT_BASE地址处的值TEXT_BASE,也就是BOOT在RAM中运行地址移到r1 */
 cmp r0, r1 /* don't reloc during debug */ /* 比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行 */
 beq stack_setup
 
/* 如果是在FLASH中运行, 则把FLASH中的Boot代码移到RAM中,然后再运行 */
 ldr r2, _armboot_start /* 把_armboot_start地址处的值也就是_start绝对地址(也即在内存中的地址,这个绝对
* 地址是在 link 的时候确定的,如0x81008000)移到r2 */
 ldr r3, _bss_start /* 把_bss_start地址处的值也就是__bss_start绝对地址(也即在内存中的地址,这个绝对
* 地址是在 link 的时候确定的)移到r3 */
 sub r2, r3, r2 /* r2 <- size of armboot */ /* 计算引导代码大小并存到r2 */
 add r2, r0, r2 /* r2 <- source end address */ /* 计算引导代码最后相对地址并存入r2 */
 
copy_loop:
 ldmia r0!, {r3-r10} /* copy from source address [r0] */ /* 从源地址[r0]读取32个字节到寄存器,并更新r0 */
 stmia r1!, {r3-r10} /* copy to target address [r1] */ /* 拷贝寄存器r3-r10的32个字节值保存到 [r1]指明的地址,并更新r1的值 */
 cmp r0, r2 /* until source end addreee [r2] */ /* 循环拷贝,直到把所有引导代码都移到内存 */
 ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
 
 /* Set up the stack */
/* 在内存中建立起堆栈 */
stack_setup:
 ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
 sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ

 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 sub sp, r0, #12 /* leave 3 words for abort-stack */
 
/* 初始化内存中bss段中数据为0 */
clear_bss:
 ldr r0, _bss_start /* find start of bss segment*/ /* 把_bss_start地址处存储的绝对地址移到r0 */
 ldr r1, _bss_end /* stop here */ /* 把_bss_end地址处存储的绝对地址移到r1 */
 mov r2, #0x00000000 /* clear */
 
clbss_l:
 str r2, [r0] /* clear loop... STR 指令用于从源寄存器中r2将一个32 位的字数据传送到存储器中[r0]*/
 add r0, r0, #4
 cmp r0, r1
 ble clbss_l /* 小于或等于跳转 */
 
 ldr pc, _start_armboot /***********************************************************
* 已经准备好了堆栈,就可跳到C写的代码里了,也就是
 * 跳到内存中的/u-boot-1.1.6/board.c --> start_armboot中运行了
 * 把_start_armboot地址处的值也就是start_armboot绝对地址值移到pc
 * 神啊!终于跳到C代码了。
 ***********************************************************/
_start_armboot:
 .word start_armboot
 
/*************************************************************************

 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************/
/**************************************************************************
* 1、关闭 MMU和CPU 内部指令/数据 (I/D)cache。
* 2、设置 CPU 的速度和时钟频率。
* 3 、RAM 初始化。
****************************************************************************/
cpu_init_crit:
 /* flush v4 I/D caches*/
 mov r0, #0
 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
/******************************************************************************************************
* MCR 指令用于将ARM 处理器寄存器中的数据传送到协处理器寄存器中,格式为:
* MCR 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
* 其中协处理器操作码1 和协处理器操作码2 为协处理器将要执行的操作,
* 源寄存器为ARM 处理器的寄存器,目的寄存器1 和目的寄存器2 均为协处理器的寄存器。
******************************************************************************************************/
 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
 
 / * disable MMU stuff and caches*/
 mrc p15, 0, r0, c1, c0, 0

 bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */
 bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */
 orr r0, r0, #0x00000002 /* set bit 2 (A) Align */
 orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */
 mcr p15, 0, r0, c1, c0, 0
 
 / * Go setup Memory and board specific bits prior to relocation.*/
 mov ip, lr /* perserve link reg across call */
 bl lowlevel_init /* go setup pll,mux,memory */ /* 位于u-boot-1.1.6/board/xxx(开发板目录名称)/lowlevel_init.S */
 mov lr, ip /* restore link */
 mov pc, lr /* back to my caller */ /* 从cpu_init_crit子函数返回 */
/*************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************/
@ IRQ stack frame.
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40

#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */
 .macro bad_save_user_regs
 @ carve out a frame on current user stack
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12
 
 ldr r2, _armboot_start
 sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
 @ get values for "aborted" pc and cpsr (into parm regs)
 ldmia r2, {r2 - r3}

 add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack
 add r5, sp, #S_SP
 mov r1, lr
 stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
 mov r0, sp @ save current stack into r0 (param register)
 .endm
 
 .macro irq_save_user_regs
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12} @ Calling r0-r12
 @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
 add r8, sp, #S_PC
 stmdb r8, {sp, lr}^ @ Calling SP, LR
 str lr, [r8, #0] @ Save calling PC
 mrs r6, spsr
 str r6, [r8, #4] @ Save CPSR
 str r0, [r8, #8] @ Save OLD_R0
 mov r0, sp
 .endm
 
 .macro irq_restore_user_regs
 ldmia sp, {r0 - lr}^ @ Calling r0 - lr
 mov r0, r0
 ldr lr, [sp, #S_PC] @ Get PC
 add sp, sp, #S_FRAME_SIZE
 subs pc, lr, #4 @ return & move spsr_svc into cpsr

 .endm
 
 .macro get_bad_stack
 ldr r13, _armboot_start @ setup our mode stack
 sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
 
 str lr, [r13] @ save caller lr in position 0 of saved stack
 mrs lr, spsr @ get the spsr
 str lr, [r13, #4] @ save spsr in position 1 of saved stack
 mov r13, #MODE_SVC @ prepare SVC-Mode
 @ msr spsr_c, r13
 msr spsr, r13 @ switch modes, make sure moves will execute
 mov lr, pc @ capture return pc
 movs pc, lr @ jump to next instruction & switch modes.
 .endm
 
 .macro get_irq_stack @ setup IRQ stack
 ldr sp, IRQ_STACK_START
 .endm
 
 .macro get_fiq_stack @ setup FIQ stack
 ldr sp, FIQ_STACK_START
 .endm
 
/*

 * exception handlers
 */
 .align 5 /*‘.align 5’向后移动位置计数器直至32(2^5)的倍数(计数器的最低的5位为0)。如果地址已经是32倍数,则无需移动。*/
undefined_instruction:
 get_bad_stack
 bad_save_user_regs
 bl do_undefined_instruction
 
 .align 5
software_interrupt:
 get_bad_stack
 bad_save_user_regs
 bl do_software_interrupt
 
 .align 5
prefetch_abort:
 get_bad_stack
 bad_save_user_regs
 bl do_prefetch_abort
 
 .align 5
data_abort:
 get_bad_stack
 bad_save_user_regs
 bl do_data_abort
 

 .align 5
not_used:
 get_bad_stack
 bad_save_user_regs
 bl do_not_used
 
#ifdef CONFIG_USE_IRQ
 
 .align 5
irq:
 get_irq_stack
 irq_save_user_regs
 bl do_irq
 irq_restore_user_regs
 
 .align 5
fiq:
 get_fiq_stack
 /* someone ought to write a more effiction fiq_save_user_regs */
 irq_save_user_regs
 bl do_fiq
 irq_restore_user_regs
#else
 
 .align 5
irq:

 get_bad_stack
 bad_save_user_regs
 bl do_irq
 
 .align 5
fiq:
 get_bad_stack
 bad_save_user_regs
 bl do_fiq
 
#endif
4、U-Boot启动内核分析
加载内核映像和根文件系统映像,规划它们的内存占用布局:
 
这里包括两个方面:(1)内核映像所占用的内存范围;(2)根文件系统所占用的内存范围。在规划内存占用的布局时,主要考虑基地址和映像的大小两个
方面。
 
对于内核映像,一般将其拷贝到从(MEM_START+0x8000) 这个基地址开始的大约1MB大小的内存范围内(嵌入式 Linux 的内核一般都不操过 1MB)。
为什么要把从 MEM_START 到 MEM_START+0x8000 这段 32KB 大小的内存空出来呢?这是因为 Linux 内核要在这段内存中放置一些全局数据结
构,如:启动参数和内核页表等信息。而对于根文件系统映像,则一般将其拷贝到 MEM_START+0x0010,0000 开始的地方。如果用 Ramdisk 作为根文
件系统映像,则其解压后的大小一般是1MB。
 
应该说,在将内核映像和根文件系统映像拷贝到 RAM 空间中后,就可以准备启动 Linux 内核了。但是在调用内核之前,应该作一步准备工作,即:设
置 Linux 内核的启动参数。

 
调用内核:
Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 地址处。在跳转时,下列条件要满足:
1. CPU 寄存器的设置:
R0=0;
R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址;
 
2. CPU 模式:
必须禁止中断(IRQs和FIQs);
CPU 必须 SVC 模式;
 
3. Cache 和 MMU 的设置:
MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;
 
 
/*
 * (C) Copyright 2002-2006
 * Wolfgang Denk, DENX Software Engineering, .
 *
 * (C) Copyright 2002
 * Sysgo Real-Time Solutions, GmbH <>
 * Marius Groeger <>
 *

 * 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
 */
 
/*
 * To match the U-Boot user interface on ARM platforms to the U-Boot
 * standard (as on PPC platforms), some messages with debug character
 * are removed from the default U-Boot build.
 *
 * Define DEBUG here if you want additional info as shown below
 * printed upon startup:

 *
 * U-Boot code: 00F00000 -> 00F3C774 BSS: -> 00FC3274
 * IRQ Stack: 00ebff7c
 * FIQ Stack: 00ebef7c
 */
 
#include
#include
#include
#include
#include
#include
 
#ifdef CONFIG_DRIVER_SMC91111
#include "../drivers/smc91111.h"
#endif
#ifdef CONFIG_DRIVER_LAN91C96
#include "../drivers/lan91c96.h"
#endif
 
DECLARE_GLOBAL_DATA_PTR;
 
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
void nand_init (void);
#endif
 

ulong monitor_flash_len;
 
#ifdef CONFIG_HAS_DATAFLASH
extern int AT91F_DataflashInit(void);
extern void dataflash_print_info(void);
#endif
 
#ifndef CONFIG_IDENT_STRING
#define CONFIG_IDENT_STRING ""
#endif
 
const char version_string[] =
 U_BOOT_VERSION" (" __DATE__ " - " __TIME__ ")"CONFIG_IDENT_STRING;
 
#ifdef CONFIG_DRIVER_CS8900
extern void cs8900_get_enetaddr (uchar * addr);
#endif
 
#ifdef CONFIG_DRIVER_RTL8019
extern void rtl8019_get_enetaddr (uchar * addr);
#endif
 
/*
 * Begin and End of memory area for malloc(), and current "brk"
 */
static ulong mem_malloc_start = 0;

static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;
 
static
void mem_malloc_init (ulong dest_addr)
{
 mem_malloc_start = dest_addr;
 mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
 mem_malloc_brk = mem_malloc_start;
 
 memset ((void *) mem_malloc_start, 0,
 mem_malloc_end - mem_malloc_start);
}
 
void *sbrk (ptrdiff_t increment)
{
 ulong old = mem_malloc_brk;
 ulong new = old + increment;
 
 if ((new < mem_malloc_start) || (new > mem_malloc_end)) {
 return (NULL);
 }
 mem_malloc_brk = new;
 
 return ((void *) old);
}

 
/************************************************************************
 * Init Utilities *
 ************************************************************************
 * Some of this code should be moved into the core functions,
 * or dropped completely,
 * but let's get it working (again) first...
 */
 
static int init_baudrate (void)
{
 char tmp[64]; /* long enough for environment variables */
 int i = getenv_r ("baudrate", tmp, sizeof (tmp));
 gd->bd->bi_baudrate = gd->baudrate = (i > 0)
 ? (int) simple_strtoul (tmp, NULL, 10)
 : CONFIG_BAUDRATE;
 
 return (0);
}
 
static int display_banner (void)
{
 printf ("\n\n%s\n\n", version_string);
 debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
 _armboot_start, _bss_start, _bss_end);
#ifdef CONFIG_MODEM_SUPPORT

 debug ("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
 debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
 debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
 
 return (0);
}
 
/*
 * WARNING: this code looks "cleaner" than the PowerPC version, but
 * has the disadvantage that you either get nothing, or everything.
 * On PowerPC, you might see "DRAM: " before the system hangs - which
 * gives a simple yet clear indication which part of the
 * initialization if failing.
 */
static int display_dram_config (void)
{
 int i;
 
#ifdef DEBUG
 puts ("RAM Configuration:\n");
 
 for(i=0; i
 printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);

 print_size (gd->bd->bi_dram[i].size, "\n");
 }
#else
 ulong size = 0;
 
 for (i=0; i
 size += gd->bd->bi_dram[i].size;
 }
 puts("DRAM: ");
 print_size(size, "\n");
#endif
 
 return (0);
}
 
#ifndef CFG_NO_FLASH
static void display_flash_config (ulong size)
{
 puts ("Flash: ");
 print_size (size, "\n");
}
#endif /* CFG_NO_FLASH */
 
 
/*
 * Breathe some life into the board...

 *
 * Initialize a serial port as console, and carry out some hardware
 * tests.
 *
 * The first part of initialization is running from Flash memory;
 * its main purpose is to initialize the RAM so that we
 * can relocate the monitor code to RAM.
 */
 
/*
 * All attempts to come up with a "common" initialization sequence
 * that works for all boards and architectures failed: some of the
 * requirements are just _too_ different. To get rid of the resulting
 * mess of board dependent #ifdef'ed code we now make the whole
 * initialization sequence configurable to the user.
 *
 * The requirements for any new initalization function is simple: it
 * receives a pointer to the "global data" structure as it's only
 * argument, and returns an integer return code, where 0 means
 * "continue" and != 0 means "fatal error, hang the system".
 */
typedef int (init_fnc_t) (void);
 
int print_cpuinfo (void); /* test-only */
 
init_fnc_t *init_sequence[] = {

 cpu_init, /* basic cpu dependent setup */
 board_init, /* basic board dependent setup */
 interrupt_init, /* set up exceptions */
 env_init, /* initialize environment */
 init_baudrate, /* initialze baudrate settings */
 serial_init, /* serial communications setup */
 console_init_f, /* stage 1 init of console */
 display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
 print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
 checkboard, /* display board info */
#endif
 dram_init, /* configure available RAM banks */
 display_dram_config,
 NULL,
};
 
void start_armboot (void)
{
 init_fnc_t **init_fnc_ptr;
 char *s;
#ifndef CFG_NO_FLASH
 ulong size;

#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
 unsigned long addr;
#endif
 
 /* Pointer is writable since we allocated a register for it */
 gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
 /* compiler optimization barrier needed for GCC >= 3.4 */
 __asm__ __volatile__("": : :"memory");
 
 memset ((void*)gd, 0, sizeof (gd_t));
 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
 memset (gd->bd, 0, sizeof (bd_t));
 
 monitor_flash_len = _bss_start - _armboot_start;
 
 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
 if ((*init_fnc_ptr)() != 0) {
 hang ();
 }
 }
 
#ifndef CFG_NO_FLASH
 /* configure available FLASH banks */
 size = flash_init ();
 display_flash_config (size);

#endif /* CFG_NO_FLASH */
 
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
 /*
 * reserve memory for VFD display (always full pages)
 */
 /* bss_end is defined in the board-specific linker script */
 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 size = vfd_setmem (addr);
 gd->fb_base = addr;
#endif /* CONFIG_VFD */
 
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
 /*
 * reserve memory for LCD display (always full pages)
 */
 /* bss_end is defined in the board-specific linker script */
 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 size = lcd_setmem (addr);
 gd->fb_base = addr;

#endif /* CONFIG_LCD */
 
 /* armboot_start is defined in the board-specific linker script */
 mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
 
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
 puts ("NAND: ");
 nand_init(); /* go init the NAND */
#endif
 
#ifdef CONFIG_HAS_DATAFLASH
 AT91F_DataflashInit();
 dataflash_print_info();
#endif
 
 /* initialize environment */
 env_relocate ();
 
#ifdef CONFIG_VFD
 /* must do this after the framebuffer is allocated */
 drv_vfd_init();
#endif /* CONFIG_VFD */
 
 /* IP Address */
 gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
 

 /* MAC Address */
 {
 int i;
 ulong reg;
 char *s, *e;
 char tmp[64];
 
 i = getenv_r ("ethaddr", tmp, sizeof (tmp));
 s = (i > 0) ? tmp : NULL;
 
 for (reg = 0; reg < 6; ++reg) {
 gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
 if (s)
 s = (*e) ? e + 1 : e;
 }
 
#ifdef CONFIG_HAS_ETH1
 i = getenv_r ("eth1addr", tmp, sizeof (tmp));
 s = (i > 0) ? tmp : NULL;
 
 for (reg = 0; reg < 6; ++reg) {
 gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
 if (s)
 s = (*e) ? e + 1 : e;
 }
#endif

 }
 
 devices_init (); /* get the devices list going. */
 
#ifdef CONFIG_CMC_PU2
 load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
 
 jumptable_init ();
 
 console_init_r (); /* fully init console as a device */
 
#if defined(CONFIG_MISC_INIT_R)
 /* miscellaneous platform dependent initialisations */
 misc_init_r ();
#endif
 
 /* enable exceptions */
 enable_interrupts ();
 
 /* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
 extern void emac_set_mac_addr (const char *addr);
 if (getenv ("ethaddr")) {
 emac_set_mac_addr(gd->bd->bi_enetaddr);
 }

#endif
 
#ifdef CONFIG_DRIVER_CS8900
 cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
 
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
 if (getenv ("ethaddr")) {
 smc_set_mac_addr(gd->bd->bi_enetaddr);
 }
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
 
 /* Initialize from environment */
 if ((s = getenv ("loadaddr")) != NULL) {
 load_addr = simple_strtoul (s, NULL, 16);
 }
#if (CONFIG_COMMANDS & CFG_CMD_NET)
 if ((s = getenv ("bootfile")) != NULL) {
 copy_filename (BootFile, s, sizeof (BootFile));
 }
#endif /* CFG_CMD_NET */
 
#ifdef BOARD_LATE_INIT
 board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)

#if defined(CONFIG_NET_MULTI)
 puts ("Net: ");
#endif
 eth_initialize(gd->bd);
#endif
 /* main_loop() can return to retry autoboot, if so just run it again. */
 for (;;) {
 main_loop ();
 }
 
 /* NOTREACHED - no way out of command loop except booting */
}
 
void hang (void)
{
 puts ("### ERROR ### Please RESET the board ###\n");
 for (;;);
}
 
#ifdef CONFIG_MODEM_SUPPORT
static inline void mdm_readline(char *buf, int bufsiz);
 
/* called from main loop (common/main.c) */
extern void dbg(const char *fmt, ...);
int mdm_init (void)
{

 char env_str[16];
 char *init_str;
 int i;
 extern char console_buffer[];
 extern void enable_putc(void);
 extern int hwflow_onoff(int);
 
 enable_putc(); /* enable serial_putc() */
 
#ifdef CONFIG_HWFLOW
 init_str = getenv("mdm_flow_control");
 if (init_str && (strcmp(init_str, "rts/cts") == 0))
 hwflow_onoff (1);
 else
 hwflow_onoff(-1);
#endif
 
 for (i = 1;;i++) {
 sprintf(env_str, "mdm_init%d", i);
 if ((init_str = getenv(env_str)) != NULL) {
 serial_puts(init_str);
 serial_puts("\n");
 for(;;) {
 mdm_readline(console_buffer, CFG_CBSIZE);
 dbg("ini%d: [%s]", i, console_buffer);
 

 if ((strcmp(console_buffer, "OK") == 0) ||
 (strcmp(console_buffer, "ERROR") == 0)) {
 dbg("ini%d: cmd done", i);
 break;
 } else /* in case we are originating call ... */
 if (strncmp(console_buffer, "CONNECT", 7) == 0) {
 dbg("ini%d: connect", i);
 return 0;
 }
 }
 } else
 break; /* no init string - stop modem init */
 
 udelay(100000);
 }
 
 udelay(100000);
 
 /* final stage - wait for connect */
 for(;i > 1;) { /* if 'i' > 1 - wait for connection
 message from modem */
 mdm_readline(console_buffer, CFG_CBSIZE);
 dbg("ini_f: [%s]", console_buffer);
 if (strncmp(console_buffer, "CONNECT", 7) == 0) {
 dbg("ini_f: connected");
 return 0;

 }
 }
 
 return 0;
}
 
/* 'inline' - We have to do it fast */
static inline void mdm_readline(char *buf, int bufsiz)
{
 char c;
 char *p;
 int n;
 
 n = 0;
 p = buf;
 for(;;) {
 c = serial_getc();
 
 /* dbg("(%c)", c); */
 
 switch(c) {
 case '\r':
 break;
 case '\n':
 *p = '\0';
 return;

 
 default:
 if(n++ > bufsiz) {
 *p = '\0';
 return; /* sanity check */
 }
 *p = c;
 p++;
 break;
 }
 }
}
#endif /* CONFIG_MODEM_SUPPORT */

阅读(2680) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~