http://blog.csdn.net/yangxingbo0311/article/details/7333525
一个可以执行的Image都会有一个入口点并且只有一个入口点,不管是WinCE还是Linux,都有唯一的一个入口,通常这个入口被放在Rom(flash)的0X0地址。例如在Uboot中:
输入:VIM /data/u-boot-1.1.6/cpu/s3c24xx/start.S
可见如下代码:
可见代码:
.globl_start
_start:
但是这个_start 入口又在何处呢?这个工作在linux中主要是由另一个关键的文件来实现。即链接器脚本文件。以后缀名.lds的文件。
gcc等编译器内置有缺省的链接器脚本。如果采用内置的缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的链接生成脚本文件。编写链接脚本,首先要对目标文件的格式有一定的了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,又源程序生成的目标代码中包含如下的段:
.text(正文段)包含程序的指令代码;
.data(数据段)包含固定的数据,如常量、字符串等;
.bss(未初始化数据段)包含未初始化的变量、数组等。
C++源程序的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。
先来看看uboot内的u-boot.lds文件:
输入指令:VIM vim /data/u-boot-1.1.6/board/samsung/smdk2416/u-boot.lds
可得:
即:
/*
2 * (C) Copyright 2002
3 * Gary Jennejohn, DENX Software Engineering,
4 *
5 * See file CREDITS for list of people who contributed to this
6 * project.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
22 */
23
24 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
25 OUTPUT_ARCH(arm)
26 ENTRY(_start)
27 SECTIONS
28 {
29 . = 0x00000000;
30 . = ALIGN(4);
31 .text :
32 {
33 cpu/s3c24xx/start.o (.text)
34 cpu/s3c24xx/s3c2416/cpu_init.o (.text)
35 cpu/s3c24xx/onenand_cp.o (.text)
36 cpu/s3c24xx/nand_cp.o (.text)
37 cpu/s3c24xx/movi.o (.text)
38 *(.text)
39 }
40 . = ALIGN(4);
41 .rodata : { *(.rodata) }
42 . = ALIGN(4);
43 .data : { *(.data) }
44 . = ALIGN(4);
45 .got : { *(.got) }
46
47 . = .;
48 __u_boot_cmd_start = .;
49 .u_boot_cmd : { *(.u_boot_cmd) }
50 __u_boot_cmd_end = .;
51
52 . = ALIGN(4);
53 .mmudata : { *(.mmudata) }
54
55 . = ALIGN(4);
56 __bss_start = .;
57 .bss : { *(.bss) }
58 _end = .;
59 }
对以上代码做好好的分析:
额,好像一开始就看这个比较复杂了,先来看看GUN官网上的形式完整的描述:
SECTIOS{
...
secname start BLOCK(align)(NOLOAD):AT(ldadr)
{contents}>region:phdr = fill
...
}
24 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")指定输出可执行文件是elf格式,32位ARM指令,小端
25 OUTPUT_ARCH(arm)指定输出可执行文件的平台为ARM
26 ENTRY(_start)
其中,ENTRY(_start)指明程序的入口点为_start标号。=0x00000000指明目标代码的起始地址为0x00000000。这一段地址可以是SDRAM的起始地址;
. = ALIGN(4);表示以四字节对齐
.text :
{
cpu/s3c24xx/start.o (.text) 代码的第一个代码部分,指明start.S是入口程序代码,被放到代码段的开头
cpu/s3c24xx/s3c2416/cpu_init.o (.text)
cpu/s3c24xx/onenand_cp.o (.text)
cpu/s3c24xx/nand_cp.o (.text)
cpu/s3c24xx/movi.o (.text)
*(.text)
}
40 . = ALIGN(4); 以四字节对齐
41 .rodata : { *(.rodata) } 指定只读数据段,RO段
42 . = ALIGN(4);
43 .data : { *(.data) }指定读/写数据段,RW段,表示从0x00000000开始放置所有目标文件的代码段
44 . = ALIGN(4);
45 .got : { *(.got) }指定got段,got段式是uboot自定义的一个段,非标准段。
46
47 . = .;
48 __u_boot_cmd_start = .; 把__u_boot_cmd_start赋值为当前位置,即起始位置
49 .u_boot_cmd : { *(.u_boot_cmd) }指定u_boot_cmd段,uboot把所有的uboot命令放在该段
50 __u_boot_cmd_end = .;把__u_boot_cmd_end赋值为当前位置,即结束位置
51
52 . = ALIGN(4);
53 .mmudata : { *(.mmudata) }
54
55 . = ALIGN(4);
56 __bss_start = .;把__bss_start赋值为当前位置,即bss段的开始位置
57 .bss : { *(.bss) }指定bss段
58 _end = .;把_end赋值为当前位置,即bss段的结束位置。
接下来就是start.S了。。本文源码来源于u-boot-1.1.6。
源码的分析参考网上的诸多博客的整理。如http://home.eeworld.com.cn/my/space.php?uid=135723&do=blog&id=25548。http://www.51hei.com/mcu/1132.html等。
都说bootloader分为两个阶段。。四极管也来看看这两个阶段都做了什么事情。
一、阶段1
阶段1通常包括以下步骤(以执行的先后顺序)
1、一些基本硬件初始化工作
2、为加载映像2准备RAM空间(RAM足够的情况下可以省略)
3、把映像2拷贝到RAM空间
4、跳转到映像2的入口点(一般是C入口点)
一般阶段1都会有如下具体工作:
1、定义ARM各个运行模式
2 、定义ARM各个运行模式堆栈的大小
3、根据处理器工作状态确定编译方式
4、初始化异常中断向量表
5、禁止看门狗
6、屏蔽中断
7、屏蔽子中断
8、设置时钟,初始化使能SDRAM
9、为各操作模式设置堆栈指针,将系统模式置为监管模式(SVC)并设置SP
10、将映像2拷贝到SDRAM的指定处。。。通过跳转进入阶段2
现在看源码分析:
Uboot start.S分析
四极管 2012-3-8
/*
* cpu/s3c24xx/start.S
*
* U-Boot - Startup Code for S3C24XX
*
* Copyright (c) 2006, Samsung Electronics
* All rights reserved.
*
* Based on cpu/arm926ejs/start.S
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* $Id: start.S,v 1.8 2008/05/23 00:26:34 eyryu Exp $
*/
/*
* mod by [email protected]
*
* Our U-Boot Memory Map
* (offset)
* -------------------------- 0x04000000
* | Stack (512KB) |
* -------------------------- 0x03f80000
* | Heap (1MB) |
* -------------------------- 0x03e80000
* | IRQ Stack (4KB) | <------------------------ if exists
* -------------------------- 0x03e70000
* | FIQ Stack (4KB) | <------------------------ if exists
* -------------------------- 0x03e60000
* | GBL (128B) |
* -------------------------- 0x03exxxxx
* | BSS and Reserved |
* -------------------------- 0x03e40000
* | U-Boot (256KB) |
* -------------------------- 0x03e00000
*/
#include
#include
#ifdef CONFIG_ENABLE_MMU
#include
#endif
#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE
#endif
#endif
#define no_compile 0
/********************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*******************************************************************/
.globl _start
_start: /*系统复位位置,各个异常向量对应的跳转代码,ARM中规定了异常向量的地址*/
b reset //复位 0x0
ldr pc, _undefined_instruction //未定义的指令异常 0x4
ldr pc, _software_interrupt //软件中断异常
ldr pc, _prefetch_abort//预取指令0xC
ldr pc, _data_abort//数据0x10
ldr pc, _not_used //未使用0x14
ldr pc, _irq//慢速中断异常0x18
ldr pc, _fiq//快速中断异常0x1C
/*.word伪操作作用于分配一段字内单元(分配的单元都是字对齐的),并用伪操作中的expr初始化。.long于.int作用与//相同*/
//以下.word 的含义如下:
.Word为GNU ARM汇编特有的伪操作,为分配一段字节内容单元(分配的单元为字对齐的),可以使用.word把标识符作为常量使用,如_irq:.word irq即把fiq存入内存变量_irq中,也即是把fiq放到地址_irq中。
_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 /*定义fiq变量到_fiq地址里面去,_fiq地址就是上面对应的,比如_fiq是0x0000 001C*/
.balignl 16,0xdeadbeef
/*对于这句话的理解内容就比较多了。。。。在Nunca Muer to 的空间里面有详细的对比分析。。为了以后难找,我还是把那段话敲出来。。。
先要弄明白.balignl的意思,这个其实应该算是一个伪操作符,伪操作符的意思就是机器码里,并没有一个汇编指令与其对应,是编译器来实现其功能的,.balignl是.balign的变体,.balign的意思是,在以当前地址开始,地址计数器必须是以第一个参数为整数倍的地址为尾,在前面记录一个字节长度的信息,信息内容为第二个参数。
.balignl 8,0xde
它的意思就是在以当前地址开始,在地址为8的倍数的位置的前面填入一个字节内容为0xde的内容。如果当前地址正好是8的倍数,则没有东西被写入到内存。
关于.balignl 16,0xdeadbeef这句,功能说明没有错,就是想在某个位置插入0xdeadbeef这个特殊的内存值。错就错在我对这个16的理解上面,16就是16个字节,这是没有错的,但是这个16的由来,并不是我所理解的至少16个字节,才能在任何情况下保证插入这个特殊的内存值。我在博客留言中回答,举了个例子pc=0x0000007地址,偏移量为8个字节时,这个时候就不够用4个字节的内容了,以此推导出的,至少有16个字节才能保证这个特殊的内存值的插入也是完全错误的。
举个反例,如果给那位网友的解释,那就算有16个字节的偏移量,那如果PC地址为0x000000F时,也只需要一个字符的空间,那这个0xdeadbeef的值是不够的,以此类推,就算这个值为任意一值,按我之前的解释的错误逻辑,也都有不满足的情况,呵呵,所以我之前的推论有误,我现在把16这个值的由来进行说明。
ARM920T处理器核心,支持32与16位两种指令长度,16位的指令叫做thumb指令集,由于我使用的是32位指令集,所有一切都是以32位指令集进行说明。
既然是32位指令集,所以一条指令就占32位,即4个字节,所以在调试器中,地址显示也是4字节一跳的,所以PC的值,也是4字节一跳的,并不存在可能PC的值为0x00000007的情况。
这个地方填16个偏移量,是因为
.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//占四个字节的内存
占了4x8 = 32 字节内存。
_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//占四个字节的内存
占了4X7=28个字节内存。
所以在这个.balignl 16,0xdeadbeef指令之前,一共占了4X15=60个字节的内存,所以本代码的作者当时就简单的在15这个数上,加了一个1,即16,把前面的指针移到地址为64的位置,然后在前面插上了0xdeadbeef这个特殊的值。
我不知道这个地方时作者的一个错误呢,歪打正着呢,还是怎么回事,其实这个偏移量的值有好多种情况,如果说最小值的话,那么也可以写成.balignl 8,0xdeadbeef,也同样可以达到相同的目的,因为60不是8的倍数,但是64是8的倍数(60到64之前都不是8的倍数,同样也不是16的倍数,所以写8和16都可行)如果写8,也正好插入到64前面,也即60这个内存起始地址,如果更大一点儿呢,那么填32也可以达到同样的效果,即.balignl 32,0xdeadbeef,道理同上。当然,不能为4,因为PC值在任何时候,都是4的倍数(64是4的倍数),只要不为0就为4的倍数,呵呵,这个值不行,如果用了这个值,0xdeadbeef永远也插不进去。
*/
/********************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!//做一些初始化,如果我们不是SDRAM启动的话
* setup Memory and board specific bits prior to relocation.
* relocate armboot to ram //重定位代码到SDRAM中
* setup stack//设置堆栈空间
* *******************************************************************/
/*保存变量的数据区*/
_TEXT_BASE:
.word TEXT_BASE //在TEXT_BASE定义在board\smdk2416、config.mk文件中这段话表示,用户告诉编译器编译地址的起始(或者说可以理解是为加载的地址,这样就能做到编译地址和运行地址的统一了)
/*
* Below variable is very important because we use MMU in U-Boot.
* Without it, we cannot run code correctly before MMU is ON.
* by scsuh.
*/
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_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 //svc为操作系统保护模式
*/
mrs r0,cpsr //取得当前程序状态寄存器cpsr到r0
bic r0,r0,#0x1f //这里是位清除指令,把中断全部清除,只置位模式控制位。为中断提供服务的通常是0S,设备驱动程序的责任,因此在Bootloader的执行过程中可以不必响应任何中断。
orr r0,r0,#0xd3 //计算为超级保护模式
msr cpsr,r0//设置cpsr为超级保护模式
/*以上设置的作用:
设置CPU运行在SVC32模式,ARM共有7种模式:
用户模式(usr):arm处理器正常的程序执行状态
快速中断模式(fiq):用于高速数据传输或通道处理
超级保护模式(svc):操作系统使用的保护模式
数据访问终止模式(abt):当数据或指令预终止时进入该模式,可用于虚拟存储及存储保护
系统模式(sys):运行具有特殊的操作系统任务
未定义指令中止模式(uud):当未定义的指令执行时进入该模式。可用于支持硬件协处理器的软件仿真。
通过设置ARM的CPSR寄存器,让CPU运行在操作系统保护模式,为后面进行其他操作做好准备工作。
*/
#if defined(CONFIG_S3C2443) ||defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)
/*
* Retention IO power will be turen off whel sleep mode,
* but, when wakeup process starts, User should write '1'
* produce power on retention IO. PM check
*/
ldr r0, =0x4c00006c
ldr r1, =0x4c000064
ldr r2, [r0]
tst r2, #0x8
ldreq r2, [r1]
orreq r2, r2, #0x10000 /* (1<<16) */
streq r2, [r1]
#endif
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
/*
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 p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
MCR指令用于将ARM处理器寄存器中的数据传输到协处理器寄存器中,格式为:
MCR 协处理器编码 ,协处理器操作码 1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2。
其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
*/
/*
* disable MMU stuff and caches禁止MMU和chache
*/
mrc p15, 0, r0, c1, c0, 0 //先把c1和c0寄存器的各位置0(r0=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
/*
* led GPH12 on
*/
#ifdef no_comiple
test1:
mov r1,#0x56000000
add r1,r1,#0x20 // set offset address
ldr r2,=(1<<6)
str r2,[r1,#0x0] // set GPC3 as output
ldr r2,=(0<<6)
str r2,[r1,#0x8] // disable pull-up/down
ldr r2,=(1<<3)
str r2,[r1,#0x4] // output low level
#endif
#ifdef CONFIG_ONENAND
/*
* With this check, we can change boot sequence as we want in run-time. * XXX: must modify to use "swp" not "ldr and str".
*/
check_onenand_boot:
mov r0, #0x1e400 /* check OneNAND via start buffer reg */
ldr r1, =0xfffffffe /* very tweaky and may occur bugs */
ldr r2, =0x00000f02
ldr r3, [r0]
str r1, [r0]
ldr r1, [r0]
str r3, [r0]
cmp r1, r2
bne 1024f
/* OneNAND is detected as boot device
* So, load <0x400 ~ 0xc00> to DataRam0
*/
fill_onenand_dr:
mov r2, #0
ldr r3, =0x0001e200
ldr r5, =0x0002 /* 0x400 ~ 0x800 */
ldr r6, =0x0001e400
ldr r7, =0x0802 /* fill 1KB in DR0 */
100: strh r2, [r3] /* block = 0, data buffer = 0 */
strh r2, [r3, #0x2] /* block = 0, data buffer = 0 */
strh r5, [r3, #0xe] /* set page, sector addr */
strh r7, [r6] /* set start buffer and count */
strh r2, [r6, #0x82] /* reset int status */
strh r2, [r6, #0x40] /* send LOAD command */
1: ldrh r8, [r6, #0x82] /* check int status */
tst r8, #(1<<15)
beq 1b
tst r5, #4;
ldr r5, =0x0004 /* 0x800 ~ 0xc00 */
add r7, r7, #0x0200 /* fill next 1KB 0x0a02 */
beq 100b
b 1024f
.ltorg /* without it, variables may go too far. */
1024:
#endif
/*
* Go setup Memory and board specific bits prior to relocation.
*/
bl lowlevel_init /* go setup pll,mux,memory */
/*进入lowlevel_init,这里主要是初始化存储寄存器,S3C2416的是Bank0-Bank6,比如位宽等,这个要根据自己的板子来进行响应的配置,比如网卡放在几个Bank,位宽多少,SDRAM放在那里,多大等。位于board/smdk2416/lowlevel_init.S:用于完成芯片存储器的初始化,执行完成后返回*/
#ifdef CONFIG_S3C2442
#ifdef CONFIG_PM
@ Check if this is a wake-up from sleep
ldr r1, PMST_ADDR
ldr r0, [r1]
tst r0, #0x8 @ PMST_SMR
bne WakeupStart
#endif
#endif
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
check_boot_device:
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip flash copy */
#ifdef CONFIG_BOOT_MOVINAND
ldr sp, _TEXT_PHY_BASE
bl movi_bl2_copy
b after_copy
#endif
/* check boot device is nand or nor */
ldr r0, =0x00000000
ldr r3, [r0]
ldr r1, =0xfffffffe
str r1, [r0]
ldr r2, [r0]
str r3, [r0]
cmp r1, r2
#if defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)
/* Now iROM on 2450 is not support eFuse */
#if 1
b nand_copy
#else
beq nand_copy
#endif
#else
beq nand_copy
#endif
#ifdef CONFIG_ONENAND
ldr r3, [r0, #0x400]
ldr r1, =0xfffffffe
str r1, [r0, #0x400]
ldr r2, [r0, #0x400]
str r3, [r0, #0x400]
cmp r1, r2
beq jump_to_onenand
#endif
/* nor copy */
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
//取得_start的地址到r0。如果是在flash中运行,则_start的值就是0,如果是在RAM中运行,则_start=_TEXT_BASE=TEXT_BASE=0X33F80000。
(_TEXT_BASE在board/smdk2416/config.mk中定义)
@ ldr r1, _TEXT_BASE//把_TEXT_BASE地址处的值_TEXT_BASE,也就是BOOT在RAM中运行地址移到r1。测试时在flash还是在ARM
ldr r1, _TEXT_PHY_BASE /* r1 <- destination */
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
b after_copy
nand_copy:
mov r0, #0x1000
bl copy_from_nand
#ifdef CONFIG_ONENAND
b after_copy
jump_to_onenand:
bl temp_copy_onenand
onenand_copy:
mov r0, #0x400
bl copy_from_nand
#endif
after_copy:
#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1 /* Set CR_M to enable MMU */
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif
/* Set up the stack*/
//初始化堆栈,为第二阶段的C语言做准备
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)
#else
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 */
#endif
//数据段_bss_start ßà_bss_end的初始化
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*/
//初始化直接使用汉字,一次就是64个位
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 //跳转到stage2
/*
Stage1 到此结束,然后开始stage2。也就是跳转到u-boot-1.1.6/board.c àstart_armboot中运行。把_start_armboot地址处的值也就是_start_armboot绝对地址移植到PC。
*/
_start_armboot:
.word start_armboot
#ifdef CONFIG_ENABLE_MMU
_mmu_table_base:
.word mmu_table
#endif
#ifdef CONFIG_ONENAND
temp_copy_onenand:
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_PHY_BASE /* test if we run from flash or RAM */
ldr r2, =0xbff
1: ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble 1b
adr r0, onenand_copy
ldr r1, _TEXT_PHY_BASE
add r0, r0, r1
mov pc, r0
.ltorg
#endif
/*
* copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)
* r0: size to be compared
*/
.globl copy_from_nand
copy_from_nand:
mov r10, lr /* save return address */
mov r9, r0
/* get ready to call C functions */
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
#ifdef CONFIG_ONENAND
cmp r9, #0x1000
bne 2f
bl copy_uboot_to_ram
b 3f
2: bl onenand_cp
#else
mov r9, #0x1000
bl copy_uboot_to_ram
#endif
3: tst r0, #0x0
bne copy_failed
#if defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)
/* Confirm Booting Status NAND Booting or iROM NAND*/
ldr r6, =0x40008000
ldr r7, =0x24564236
swp r8, r7, [r6]
swp r5, r8, [r6]
cmp r7, r5
/* If compare value is same between r7 and r5, Booting Device is iROM */
beq 444f
mov r0, #0 /* NAND Booting */
b 555f
444:
mov r0, #0x40000000 /* iROM booting */
#else
mov r0, #0
#endif
555:
ldr r1, _TEXT_PHY_BASE
1: ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne compare_failed /* not matched */
subs r9, r9, #4
bne 1b
4: mov lr, r10 /* all is OK */
mov pc, lr
copy_failed:
nop /* copy from nand failed */
b copy_failed
compare_failed:
nop /* compare failed */
b compare_failed
/*
* we assume that cache operation is done before. (eg. cleanup_before_linux())
* actually, we don't need to do anything about cache if not use d-cache in U-Boot
* So, in this function we clean only MMU. by scsuh
*
* void theLastJump(void *kernel, int arch_num, uint boot_params);
*/
#ifdef CONFIG_ENABLE_MMU
.globl theLastJump
theLastJump:
mov r9, r0
ldr r3, =0xfff00000
ldr r4, _TEXT_PHY_BASE
adr r5, phy_last_jump
bic r5, r5, r3
orr r5, r5, r4
mov pc, r5
phy_last_jump:
/*
* disable MMU stuff
*/
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
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
mov r0, #0
mov pc, r9
#endif
//以下是一些中断的定义和处理
/*******************************************************************
*
* 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
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
#ifdef CONFIG_PM
.align 4
PMCTL1_ADDR: .long 0x56000080
PMST_ADDR: .long 0x4C000068
PMSR0_ADDR: .long 0x560000B8
GPBCON: .long 0x56000010
GPBDAT: .long 0x56000014
GPFCON_reg: .long 0x56000050
GPFDAT_reg: .long 0x56000054
.align 5
sleep_setting:
@ prepare the SDRAM self-refresh mode
ldr r0, =0x48000024 @ REFRESH Register
ldr r1, [r0]
orr r1, r1,#(1<<22) @ self-refresh bit set
@ prepare MISCCR[19:17]=111b to make SDRAM signals(SCLK0,SCLK1,SCKE) protected
ldr r2,=0x56000080 @ MISCCR Register
ldr r3,[r2]
orr r3,r3,#((1<<17)|(1<<18)|(1<<19))
@ prepare the Power_Off mode bit in CLKCON Register
ldr r4,=0x4c00000c @ CLKCON Register
ldr r5,=(1<<3)
b set_sdram_refresh
.align 5
set_sdram_refresh:
str r1,[r0] @ SDRAM self-refresh enable
@ wait until SDRAM into self-refresh
mov r1, #64
1: subs r1, r1, #1
bne 1b
@ set the MISCCR & CLKCON register for power off
str r3,[r2]
str r5,[r4]
nop @ waiting for power off
nop
nop
b .
.align 5
WakeupStart:
@ Clear sleep reset bit
ldr r0, PMST_ADDR
mov r1, #(1<<1) @ PMST_SMR
str r1, [r0]
@ Release the SDRAM signal protections
ldr r0, PMCTL1_ADDR
ldr r1, [r0]
bic r1, r1, #((1<<17)|(1<<18)|(1<<19)) @ (SCLKE | SCLK1 | SCLK0)
str r1, [r0]
@ Max1718_Set(); @for case 135 i.e 300MHz operation
@ GPBCON = (GPBCON & ~((3 << 20) | (3 << 16) | (3 << 14))) | (1 << 20) | (1 << 16) | (1 << 14);
ldr r1, GPBCON
ldr r0, [r1]
bic r0, r0, #( (3 << 20) | (3 << 16) | (3 << 14) )
orr r0, r0, #( (1 << 20) | (1 << 16) | (1 << 14) )
str r0, [r1]
// GPB7, 8, 10 : Output
@ GPFCON = (GPFCON & ~(0xff << 8)) | (0x55 << 8); // GPF4~7: Output , shared with LED4~7
ldr r1, GPFCON_reg
ldr r0, [r1]
bic r0, r0, #( (0xff << 8) )
orr r0, r0, #( (0x55 << 8) )
str r0, [r1]
@ GPBDAT = (GPBDAT & ~(1 << 7)) | (0 << 7); //D4
ldr r1, GPBDAT
ldr r0, [r1]
bic r0, r0, #( (1 << 7) )
orr r0, r0, #( (0 << 7) )
str r0, [r1]
@ GPFDAT = (GPFDAT & ~(0xf << 4)) | (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4); //D3~0
ldr r1, GPFDAT_reg
ldr r0, [r1]
bic r0, r0, #( (0xf << 4) )
orr r0, r0, #( (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) ) @D3~0
str r0, [r1]
@ Go...
ldr r0, PMSR0_ADDR @ read a return address
ldr r1, [r0]
mov pc, r1
nop
nop
1: b 1b @ infinite loop
#endif
relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq stack_setup
最初看到这个代码的时候,发现功能还是很简单的,就是判断uboot是放在哪里的,flash? or SDRAM?如果放在SDRAM中,就不需要再把uboot代码从flash中搬移到SDRAM中,直接跳到stack_setup;如果是放在flash里的话,则要把代码从flash中搬移到指定的SDRAM地址(TEXT_BASE)中。细看之下,又发现了点问题:它是怎么知道uboot到底放在哪呢;又要把uboot放到SDRAM的什么地址去呢,也即TEXT_BASE是多少。
先说TEXT_BASE吧。TEXT_BASE在功能上是指示uboot将要SDRAM中存放的起始地址。(理解这个很重要)在uboot\cpu\s3c44b0\start.S中有如下声明和定义:_TEXT_BASE:。word TEXT_BASE 而在uboot\board\B2\config.mk文件中有如下赋值:TEXT_BASE = 0x0c100000。在基于dave\B2板子的uboot是把uboot放在SDRAM中的0x0c100000处的。(个人暂时认为这个TEXT_BASE应该是可以修改的,比如TEXT_BASE=0x0c100004)再说这个_start:当uboot在flash中运行的时候,_start是程序的开始,也即地址0。而当uboot在SDRAM的时候,这个_start应该是多少呢??经过反复想,反复想之后,才发现,这个_start就应该是TEXT_BASE。
由于_start是整个uboot的开头处,所以_start在uboot中的偏移地址_start_offset=0,这个无疑义。当uboot在flash中的时候,_start=0x00000000很好理解:flash映射起始地址为0x00000000。uboot放在flash当中的话,uboot起始地址就应该为0x00000000,而_start在uboot中的偏移地址为0,所以_start的绝对物理地址就应该是0x00000000。当uboot处于SDRAM中的时候,_start=??那么它为什么又会等于TEXT_BASE=0x0c1000000呢???原因就在于,我们要把(注意:是将要把,打算把)uboot搬到TEXT_BASE=0x0c100000(这个位置属于SDRAM的映射)处。那么uboot的绝对地址就应该是TEXT_BASE,而_start在uboot中的偏移地址是0,所以_start的绝对地址就是TEXT_BASE+0=TEXT_BASE。在ldr r1,_TEXT_BASE执行之后,r1=TEXT_BASE的;而adr r0,_start执行之后呢??adr指令是基于PC的相对寻址,执行之后r0=PC+_star_offset=PC。如果uboot放在SDRAM中的话,那么_start的绝对地址是TEXT_BASE,也即PC=TEXT_BASE。
至此,才明白了是如何判断是否要进行代码搬移的。
总结:开始一直以为,uboot在SDRAM中的话,其开始地址应该是SDRAM的映射开始地址,即0x0c000000,也即_start的绝对地址应该是0x0c000000。后来才发现,uboot放在SDRAM中的位置是由程序控制的,即放在TEXT_BASE处。这才明白上面那几行代码是怎么回事了~~~
话外音::突然间想到---TEXT_BASE是uboot在SDRAM的开始处,所以在SDRAM中的时候_start=TEXT_BASE。只有这样才能判断正确~~
在接下来就是第二阶段C语言实现部分。。。转帖分析。。
地址http://www.cevx.com/bbs/thread-28817-1-1.html
lib_arm/board.c:
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程: