S3C6410-uboot详细解读之start.S(修正版)

/*
 *  armboot - Startup Code for S3C6400/ARM1176 CPU-core
 *
 *  Copyright (c) 2007 Samsung Electronics
 *
 *
 * 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
 *
 * 2007-09-21 - Restructured codes by jsgood (jsgood.yang@samsung.com)
 * 2007-09-21 - Added moviNAND and OneNAND boot codes by jsgood (jsgood.yang@samsung.com)
 * Base codes by scsuh (sc.suh)
 */

#include <config.h>//config.h中定义了forlinx_boot_nand和forlinx_boot_ram256
#include <version.h>
#ifdef CONFIG_ENABLE_MMU
#include <asm/proc/domain.h>
#endif
#include <regs.h>//这里即S3C6410.H寄存器配置文件

#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE
#endif
#endif

/*
 *************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************
 */

//global 声明一个符号可被其他文档引用,相当于声明了一个全局变量,.globl 和.global 相同。
//该部分为处理器的异常处理向量表。地址范围为 0x0000 0000 ~ 0x0000 0020,刚好 8 条指令。最后一条末地址低位转化为二进制100000即32字节,每条指令4字节对齐
.globl _start//启动入口
// 以下是具有arm特色的异常向量表,为中断异常准备
//罗嗦几句,异常向量也可以出现在0xffff0000处,内核开启mmu后内存的开始空间通常为内核进程空间和页表空间,异常向量就不能安装在0地址处了
//这8个异常向量例外优先级,在下面注释中写了1>2
_start: b reset//1复位向量跳转到reset
 ldr pc, _undefined_instruction//6未定义,这句汇编意思为将 向量_undefined_instruction指向的地址赋予pc,pc为程序计数器
 ldr pc, _software_interrupt//6软中断
 ldr pc, _prefetch_abort//5预取指指令
 ldr pc, _data_abort//2数据访问中止
 ldr pc, _not_used//该异常向量未使用
 ldr pc, _irq//4中断处理 
 ldr pc, _fiq//3快速中断处理
// .word 伪操作 .word expressions: 定义一个字,并为之分配空间, 4bytes.

_undefined_instruction:
 .word undefined_instruction
_software_interrupt:
 .word software_interrupt
_prefetch_abort:
 .word prefetch_abort
_data_abort:
 .word data_abort
_not_used:
 .word not_used
_irq:
 .word irq
_fiq:
 .word fiq
_pad:
 .word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

 .balignl 16,0xdeadbeef
/* 当发生中断异常时,pc会跳转到.word的后面地址处 处理异常,

   undefined异常由arm核译码单元检测,并触发未定义指令异常请求,硬件设置pc的值为0x4,强制程序从内存0x4地址执行指令;

   0x8存放软件中断处理指令,arm中使用swi指令时触发软件中断,硬件设置PC的值为0x8,同时进入系统模式,多用在系统库的编写;

   prefetch异常,预取指中止异常,导致正在取的指令无法正常取出,这里需要注意流水线造成的pc值 ;

   data中止,无法获取数据,产生的原因有可能是内存未准备好、内存无读或写权限等一些原因产生的异常;

   0x14暂时未使用;

   0x18提供系统硬件中断跳转接口,一般我们的处理器都会引出很多的外部中断线,在这里能做的就是判断系统中断线产生的中断,注册中断,初始化中断,调用中断函数等等;

   0x1c地址为_fiq快速中断,一个系统在中断流水线上可能产生很多中断,但快中断只会有一个

*/
// .align 伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。.balign 的作用同.align。
// .align {alignment} {,fill} {,max}
// 其中:alignment 用于指定对齐方式,可能的取值为 2 的次幂,缺省为 4。fill 是填充内容,缺省用 0填充。max 是填充字节数最大值,假如填充字节数超过 max,
// 就不进行对齐,例如:
// .align 4 /* 指定对齐方式为字对齐 */

/*
 *************************************************************************
 *
 * 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标号所代表的是uboot代码的运行地址,对于s3c6410

   系统来说,如果nand flash启动方式,系统会把uboot镜像里面前4KB的内容映射到引导镜像区,即0x0地址,但是我们需要把uboot代码放到我们的SDRAM,原因是我们代码里面需要对变量做更改并且增加代码执行效率等

   下面代码的含义是定义uboot程序执行的运行地址,值为0xcfe0 0000,.word后面的值TEXT_BASE在编译的时候,通过向编译器传递参数获得,-DTEXT_BASE方式向编译器传递宏参,在编译的时候可以注意下编译的时候都会指定它的值,值的定义在config.mk中,Makefile会包含它。

*/

_TEXT_BASE:
 .word TEXT_BASE
/*

在uboot里面会开启MMU,下面是在MMU开启前uboot在内存存放的是真实物理地址,值为0x5fe00000。强调一下,我们做的开发板的SDRAM在DMC1上,即访问物理内存的实际物理地址从0x50000000开始,SDRAM的大小为256M,正好是一个DMC1,所以内存的访问地址就是0x50000000-0x6FFFFFFF之间了。

*/

/*
 * 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
/*

   这个不解释也是可以的,但是还是要解释。很多人对_start的值有疑惑,认为是0x0,因为看到_start的标号在代码段最开始处,其实是错误的,汇编代码里面的标号是和编译时指定的运行地址有关系的。我们在编译程序的时候会通过-DTEXT_BASE=0xcfe00000参数告诉编译器我们程序将会运行在0xcfe00000地址,那么自然编译器会认为代码开始的时候就运行在这个地址,那么_start的值自然就是0xcfe00000了。总结之,标号的值与编译时指定的程序地址有关系,而与程序实际存放在内存出的位置无关。

*/

.globl _armboot_start// 声明 _armboot_start 并用 _start 来进行初始化,在 board/u-boot.lds 中定义。

_armboot_start:
 .word _start
/*

   下面的代码__bss_start的值是在u-boot.lds脚本里面定义的,虽然没给值,但是你要知道文件的大小和位置是由编译器指定的,那么还需要我们告诉它值吗?所以没值胜有值啦,由编译时编译器决定它们的值

 */

 

/*
 * These are defined in the board-specific linker script.
 */
// 声明_bss_start 并用__bss_start 来初始化,其中__bss_start 定义在和板相关的 u-boot.lds 中。
// _bss_start 保存的是__bss_start 这个标号所在的地址, 这里涉及到当前代码所在
// 的地址不是编译时的地址的情况, 这里直接取得该标号对应的地址, 不受编译时
// 地址的影响. _bss_end 也是同样的道理.

.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

/*
 * the actual真实 reset code
 */
/*
// MRS {} Rd,CPSR|SPSR 将 CPSR|SPSR 传送到 Rd
// 使用这两条指令将状态寄存器传送到一般寄存器,只修改必要的位,再将结果传送回状态寄存器,这
样能够最好地完成对 CRSP 或 SPSR 的修改
// MSR {} CPSR_|SPSR_,Rm 或是 MSR {} CPSR_f|SPSR_f,#
// MRS 和 MSR 配合使用,作为更新 PSR 的“读取--修改--写回”序列的一部分
// bic r0,r1,r2 ;r0:=r1 and not r2
// orr ro,r1,r2 ;r0:=r1 or r2
// 这几条指令执行完毕后,进入 SVC32 模式,该模式主要用来处理软件中断(SWI)


   uboot开始执行的第二条代码处即在这里了,下面的代码使得cpu的模式为管理模式,如果想使得为cpu为管理模式,需要保证cpsr寄存器的最低5位为10011,下面是把0xd3的值赋值给cpsr,0xd3即1101 0011,最高两位置1的意思为关闭中断和快中断,这是为了防止代码正在执行时,产生外部中断,导致程序跳转到异常向量表而无法正常按顺序执行。5位为0的意思是cpu的状态为arm状态,如果是1则cpu进入thumb态,thumb态处理16位指令代码和数据。

*/

reset:
 /*
  * set the cpu to SVC32 mode
  */
//设置 CPU 进入管理模式 即设置相应的 CPSR 程序状态字

 mrs r0,cpsr//将 CPSR 状态寄存器读取,保存到 R0 中

 bic r0,r0,#0x1f
 orr r0,r0,#0xd3
 msr cpsr,r0//将 R0 写入状态寄存器中


/*操作系统先注册一个总的中断,然后去查是由哪个中断源产生的中断,再去查用户注册的中断表,查出来后就去执行用户定义的用户中断处理函数。
ldr
pc, _undefined_instruction 表示把_undefined_instruction 存放的数值存放到 pc 指针上,
_undefined_instruction:
.word undefined_instruction 表示未定义的这个异常是由.word 来定
义的,它表示定义一个字,一个 32 位的数,.word 后面的数表示把该标识的编译地址写入
当前地址,标识是不占用任何指令的。把标识存放的数值 copy 到指针 pc 上面,那么标识上
存放的值是什么?是由.word undefined_instruction 来指定的,pc 就代表你运行代码的地址,
她就实现了 CPU 要做一次跳转时的工作。
什么是编译地址?什么是运行地址?
32 位的处理器,它的每一条指令是 4 个字节,以 4 个字节存储顺序,进行顺序执行,CPU
是顺序执行的,只要没发生什么跳转,它会顺序进行执行,编译器会对每一条指令分配一个
编译地址,这是编译器分配的,在编译过程中分配的地址,我们称之为编译地址。
运行地址是指,程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪里,
哪里就是运行的地址。比如有一个指令的编译地址是 0x5,实际运行的地址是 0x200,如果
用户将指令烧到 0x200 上,那么这条指令的运行地址就是 0x200,当编译地址和运行地址不
同的时候会出现什么结果?结果是不能跳转,编译后会产生跳转地址,如果实际地址和编译
后产生的地址不相等,那么就不能跳转。C 语言编译地址都希望把编译地址和实际运行地址
放在一起的,但是汇编代码因为不需要做 C 语言到汇编的转换,可以人为的去写地址,所以
直接写的就是他的运行地址,这就是为什么任何 bootloader 刚开始会有一段汇编代码,
因为起始代码编译地址和实际地址不相等,这段代码和汇编无关,跳转用的运行地址。编译地址
和运行地址如何来算呢?假如有两个编译地址 a=0x10,b=0x7,b 的运行地址是 0x300,那
么 a 的运行地址就是 b 的运行地址加上两者编译地址的差值,a-b=0x10-0x7=0x3,a 的运行
地址就是 0x300+0x3=0x303。
假设 uboot 上两条指令的编译地址为 a=0x33000007 和 b=0x33000001,这两条指令都落在
bank6 上,现在要计算出他们对应的运行地址,要找出运行地址的始地址,这个是由用户烧
录进去的,假设运行地址的首地址是 0x0,则 a 的运行地址为 0x7,b 为 0x1,就是这样算出来的。
为什么要分配编译地址?这样做有什么好处,有什么作用?
比如在函数 a 中定义了函数 b,当执行到函数 b 时要进行指令跳转,要跳转到 b 函数所对应
的起始地址上去,编译时,编译器给每条指令都分配了编译地址,如果编译器已经给分配了
地址就可以直接进行跳转,查找 b 函数跳转指令所对应的表,进行直接跳转,因为有个编译
地址和指令对应的一个表,如果没有分配,编译器就查找不到这个跳转地址,要进行计算,
非常麻烦。
什么是相对地址?
以 NOR Flash 为例,NOR Falsh 是映射到 bank0 上面,SDRAM 是映射到 bank6 上面,uboot
和内核最终是在 SDRAM 上面运行,
最开始我们是从 Nor Flash 的零地址开始往后烧录,
uboot
中至少有一段代码编译地址和运行地址是不一样的,
编译 uboot 或内核时,都会将编译地址
放入到 SDRAM 中,他们最终都会在 SDRAM 中执行,刚开始 uboot 在 Nor Flash 中运行,运
行地址是一个低端地址,是 bank0 中的一个地址,但编译地址是 bank6 中的地址,这样就会
导致绝对跳转指令执行的失败,
所以就引出了相对地址的概念。
那么什么是相对地址呢?至少在 bank0 中 uboot 这段代码要知道不能用 b+编译地址这样的方法去跳转指令,因为这段代码的编译地址和运行地址不一样,
那如何去做呢?要去计算这个指令运行的真实地址,计算出来后再做跳转,应该是 b+运行地址,不能出现 b+编译地址,而是 b+运行地址,而运行地址是算出来的。
*/
/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */
         /*
         * we do sys-critical inits only at reboot,
         * not when booting from ram!
         */
/* 以下标号所在处的代码比较多,将做逐步分析,这段代码主要的工作也就是改了一些硬件寄存器和内存初始化工作 */
cpu_init_crit:
 /*
  指令的含义为刷新指令和数据缓存。mcr的意思是把arm寄存器的值赋值给coprocesser寄存器,拿第一条指令来说,p15代表协处理器,0为一定的值,指令中0b0000四位来表示,现在无具体作用,如果不是0则结果未知,后面的r0是即将写入c7目标寄存器中的值,后面还有个c7所代表的意思为额外操作码,如果不是c0,则表示的是同一个寄存器的不同物理寄存器,因为同一个寄存器的名字并不代码通一个物理内存,我们在学rpsr的时候应该知道这点,最后的0提供附加信息,用于区分同一寄存器的不同物理寄存器,如无附加信息,请保持为0值,否则结果不可预测。下面三行代码不难看出,c7、c8的值被清为0,为什么要清为零呢,你需要去看arm1176jzf-s芯片手册了,其中是有说明的
  */
 /*
  * 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 */
 /*
 
总之还是去查arm11核芯片手册,因为以下改的内容是协处理器

  c1,那么你就该去查c1是用来干什么的。查看得知,是控制寄存器,查看手册是online books12.2.2 Primary register allocation一节,其中13,9,8位为 V、R、S:V位是对高端异常向量表的支持,如果选择0异常向量表为0x00000000-0x0000001c,如果选择1异常向量表就是FFFF0000-FFFF001c;R位用于ROM保护的,具体的还要与c5里面的配合,这都是MMU惹的祸,很烦,但是现在我们还没有讲到MMU,所以为什么这样做,也必须到讲到MMU的时候才见分晓了,S在这里面的意思也是用于系统保护的,和MMU又是有很大的关系。

  bic r0, r0, #0x00000087 @ clear bits 7, 2:0(B--- -CAM) 的B位为0表示支持小little-endian,1表示支持big-endian格式的系统内存

  CAM为第三位,M为0代表禁止MMU,反之打开,A代表地址对齐检查,0代表禁止,C代表指令数据cache控制,0为禁止

  orr r0, r0, #0x00000002 @ set bit 2 (A) Align 这段指令又比较犯贱了,打开地址对齐检查了,这是应该的O(∩_∩)O~,后面又 设置12位为1,含义是如果数据cache和指令cache是分开的话,这里面置1的含义将会打开指令缓存

*/
 /*
  * disable MMU stuff and caches
  */
/*为什么要关闭 catch 和 MMU 呢?catch 和 MMU 是做什么用的?
Catch 是 cpu 内部的一个 2 级缓存,她的作用是将常用的数据和指令放在 cpu 内部,MMU
是用来做虚实地址转换用的,我们的目的是设置控制的寄存器,寄存器都是实地址,如果既
要开启 MMU 又要做虚实地址转换的话,中间还多一步,先要把实地址转换成虚地址,然后再做设置,
但对 uboot 而言就是起到一个简单的初始化的作用和引导操作系统,如果开启 MMU 的话,很麻烦,也没必要,所以关闭 MMU.
说到 catch 就必须提到一个关键字 Volatile,以后在设置寄存器时会经常遇到,他的本质
是告诉编译器不要对我的代码进行优化,优化的过程是将常用的代码取出来放到 catch 中,
它没有从实际的物理地址去取,它直接从 cpu 的缓存中去取,
但常用的代码就是为了感知一些常用变量的变化,如果正在取数据的时候发生跳变,那么就感觉不到变量的变化了,所以在这种情况下要用 Volatile 关键字告诉编译器不要做优化,每次从实际的物理地址中去取指令,这就是为什么关闭 catch 关闭 MMU。但在 C 语言中是不会关闭 catch 和 MMU 的,会打开,如果编写者要感知外界变化,或变化太快,从 catch 中取数据会有误差,就加一个关键字 Volatile。
*/
 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
/*

   以下代码的作用是为了给256M的内存在MMU开启的时候把0x70000000作为重映射的基地址

   c15协处理器寄存器在s3c6410上有特殊作用,它是外部内存端口映射寄存器,32位,在开关MMU的时候发生作用,且优先级最高

   这里的0x7000 0000为外部端口的基地址,如你没开MMU,PHY和Peri port映射的地址将相同。通过下面的内容后,我们知道我们原来uboot代码是放置到0x5fe00000的,现在便只能通过0x57e00000+0x70000000虚拟地址来访问uboot起始地址了,也即text_base CFE0 0000。

   使用C15的方法是:

   1.Opcode_1 set to 0

   2.CRn set to c15

   3.CRm set to c2

   4.Opcode_2 set to 4

   还有问题请参考arm1176jzfs芯片手册

 */
 /* Peri port setup */
 ldr r0, =0x70000000
 orr r0, r0, #0x13
     mcr p15,0,r0,c15,c2,4       @ 256M(0x70000000-0x7fffffff)

#ifdef CONFIG_BOOT_ONENAND
 ldr r0, =0x70000000  @ onenand controller setup
 orr r0, r0, #0x100000
 ldr r1, =0x4000
 orr r1, r1, #0xe0
 str r1, [r0]


/*
关闭看门狗,关闭中断,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际中会专门启动一个线程或进程会专门喂狗,当上层软件出现故障时就会停止喂狗,停止喂狗之后,cpu 会自动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在 cpu 内部使用看门狗,cpu 内部的看门狗是复位的 cpu,当开发板很复杂时,有好几个 cpu 时,就不能完全让板子复位,但我们通常都让整个板子复位。看门狗每隔短时间就会喂狗,问题是在两次喂狗之间的时间间隔内,运行的代码的时间是否够用,两次喂狗之间的代码是否在两次喂狗的时间延迟之内,如果在延迟之外的话,代码还没改完就又进行喂狗,代码永远也改不完。
*/
#if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)
 orr r0, r0, #300  @ disable watchdog//关闭关门狗
 mov r1, #1//寄存器中的值全为1
 str r1, [r0]//将管理中断的寄存器地址赋给ro

 mov r1, #0x23000000  @ start buffer register
 orr r1, r1, #0x30000
 orr r1, r1, #0xc800
#else
 mov r1, =0x20000000  @ start buffer register
 orr r1, r1, #0xc30000
 orr r1, r1, #0xc800
#endif

 sub r0, r1, #0x0400  @ start address1 register

 ldr r2, [r1, #0x84]  @ ecc bypass
 orr r2, r2, #0x100
 str r2, [r1, #0x84]

 mov r3, #0x0  @ DFS, FBA
 str r3, [r0, #0x00]
 str r3, [r0, #0x04]  @ select dataram for DDP as 0

 mov r4, #0x104  @ interrupt register
 mov r5, #0x0002  @ FPA, FSA
 mov r6, #0x0800  @ BSA

onenand_bl1_load:
 str r5, [r0, #0x1c]  @ save FPA, FSA
 orr r6, r6, #0x02  @ BSC
 str r6, [r1, #0x00]  @ save BSA, BSC
 str r3, [r1, r4]  @ clear interrupt
 str r3, [r1, #0x80]  @ write load command

 mov r7, #0x100  @ need small delay

onenand_wait_loop1:
 subs r7, r7, #0x1
 bne onenand_wait_loop1

 add r5, r5, #0x2  @ next FPA, FSA
 sub r6, r6, #0x2
 add r6, r6, #0x200  @ next BSA
 cmp r5, #0x8
 bne onenand_bl1_load
#endif

 /*
  * Go setup Memory and board specific bits prior to relocation.
  */
/*

   下面是一条跳转指令,代码这里不贴,但是其中的代码很重要,在lowlevel.S中实现比如说点亮LED灯、关闭watchdog、关闭中断、系统时钟初始、nand flash初始化、内存控制器初始化。不过说实在的,去仔细分析这些初始化的过程,对于你对如何控制硬件有很大的帮助, 对于这个函数,所要说的东西太多,会在后面的文章中单独分析它,现在先知道功能就好,没有它代码无法启动。bl lowlevel_init 下来初始化各个 bank,把各个 bank 设置必须搞清楚,对以后移植复杂的 uboot 有很大帮助*/
 bl lowlevel_init /* go setup pll,mux,memory */
/*如果换一块开发板有可能改哪些东西?
上述小结,首先,cpu 的运行模式,如果需要对 cpu 进行设置那就设置,管看门狗,关中断不用改,
时钟有可能要改,如果能正常使用则不用改,关闭 catch 和 MMU 不用改,设置 bank 有可能
要改。最后一步拷贝时看地址会不会变,如果变化也要改,执行内存中代码,地址有可能要改*/


 /* 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.
  */
/* 跳转出来以后,继续执行下面的代码,下面的代码是判断程序是否已经在ram中了,在的话就不拷贝,直接跳转到after_copy了,否则继续执行下面的代码 */ 
  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   */
/*

   下面代码通过函数copy_from_nand函数把代码拷贝到ram中。steppingstone只能拷贝4KB,我们需要把所有的代码搬运到内存中
我们知道s3c6410可以通过SD、onenand、nand启动,但是我们这里做了简化,先只从nand启动,以后会再增加SD卡启动,Nor Flash 和 Nand Flash 本质区别就在于是否进行代码拷贝,
也就是下面代码所表述:无论是 Nor Flash 还是 Nand Flash,核心思想就是将 uboot 代码搬运到内存中去运行,但是没有拷贝 bss 后面这段代码,只拷贝 bss 前面的代码,bss 代码是放置全局变量的。Bss 段代码是为了清零,拷贝过去再清零重复操作

*/
#ifdef CONFIG_BOOT_NOR   /* relocate U-Boot to RAM */
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_PHY_BASE /* r1 <- destination                */
 ldr r2, _armboot_start
 ldr r3, _bss_start
 sub r2, r3, r2  /* r2 <- size of armboot            */
 add r2, r0, r2  /* r2 <- source end address         */

nor_copy_loop:
 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 nor_copy_loop
 b after_copy
#endif

#ifdef CONFIG_BOOT_NAND//我们需要的
 mov r0, #0x1000
 bl copy_from_nand
#endif

#ifdef CONFIG_BOOT_MOVINAND//also
 ldr sp, _TEXT_PHY_BASE
 bl movi_bl2_copy
 b after_copy
#endif

#ifdef CONFIG_BOOT_ONENAND
 ldr sp, =0x50000000  @ temporary stack

#ifdef CONFIG_S3C6400
 mov r1, =0x20000000  @ start buffer register
 orr r1, r1, #0xc30000
 orr r1, r1, #0xc800
#else
 mov r1, #0x23000000  @ start buffer register
 orr r1, r1, #0x30000
 orr r1, r1, #0xc800
#endif

 ldr r2, [r1, #0x84]  @ ecc bypass
 orr r2, r2, #0x100
 str r2, [r1, #0x84]

 sub r0, r1, #0x0400  @ start address1 register

 str r3, [r0, #0x00]
 str r3, [r0, #0x04]  @ select dataram for DDP as 0

 mov r4, #0x104  @ interrupt register

 mov r6, #0x0c00  @ fixed dataram1 sector number
 str r6, [r1, #0x00]

 mov r3, #0x0  @ DFS, FBA
 mov r5, #0x0000  @ FPA, FSA
 ldr r9, =CFG_PHY_UBOOT_BASE @ destination

onenand_bl2_load:
 str r3, [r0, #0x00]  @ save DFS, FBA
 str r5, [r0, #0x1c]  @ save FPA, FSA

 mov r7, #0x0  @ clear interrupt
 str r7, [r1, r4]
 str r7, [r1, #0x80]  @ write load command

 mov r8, #0x1000
onenand_wait_loop2:
 subs r8, r8, #0x1
 bne onenand_wait_loop2

onenand_wait_int:   @ wait INT and RI
 ldr r7, [r1, r4]
 mov r8, #0x8000
 orr r8, r8, #0x80
 tst r7, r8
 beq onenand_wait_int

 mov r7, #0x0  @ clear interrupt
 str r7, [r1, r4]

 mov r8, #0xc00  @ source address (dataram1)
 mov r10, #0x40  @ copy loop count (64 = 2048 / 32)

 stmia sp, {r0-r7}  @ backup

onenand_copy_to_ram:
 ldmia r8!, {r0-r7}
 stmia r9!, {r0-r7}
 subs r10, r10, #0x1
 bne onenand_copy_to_ram

 ldmia sp, {r0-r7}  @ restore

 add r5, r5, #0x4  @ next FPA
 cmp r5, #0x100  @ last FPA?
 bne onenand_bl2_load

 /* next block */
 mov r5, #0x0  @ reset FPA
 add r3, r3, #0x1  @ next FBA
 cmp r3, #0x2  @ last FBA?
 bne onenand_bl2_load
 b after_copy
#endif

#ifdef CONFIG_BOOT_ONENAND_IROM
 ldr sp, _TEXT_PHY_BASE
 bl onenand_bl2_copy
 b after_copy
#endif

after_copy:
/*

   打开MMU功能

   协处理器c3的作用是存储的保护和控制,用在MMU中为内存的域访问控制,c3为32位寄存器,每两位为一个访问控制特权,0x00代表没有访问权限,这时候访问将失效;0x01为客户类型,将根据地址变换条目中的访问控制位决定是否允许特定内存访问;0x10是保留的,暂时没有使用,0x11为管理者权限,不考虑地址变换条目中的权限控制位,将不会访问内存失效。

   ldr r5, =0x0000ffff
   mcr p15, 0, r5, c3, c0, 0,代码的含义为设置高8个域无访问权限,低8个域为管理者权限。

   接着下面通过mcr p15, 0, r1, c2, c0, 0指令给c2赋值,c2用于保存页表基地址。所谓页表基地址即是虚实转换的内存页表的首地址。

   这里r1的值赋值给了c2,r1的值为0x5fexxxxx,c2高14位是储存页表的基地址

   最后代码很简单,打开MMU。

*/
#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
/*

   堆栈初始化代码,我们在这里定义了CONFIG_MEMORY_UPPER_CODE

   sp的值为0xCFE0 0000+20 0000-c即d000 00000
*/
skip_hw_init:
 /* Set up the stack          */
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段内容为0 */

clear_bss:
 ldr r0, _bss_start  /* find start of bss segment        */
 ldr r1, _bss_end  /* stop here                        */
 mov  r2, #0x00000000  /* clear                            */

clbss_l:
 str r2, [r0]  /* clear loop...                    */
 add r0, r0, #4
 cmp r0, r1
 ble clbss_l
/* 跳转到uboot代码的第二个阶段,第二阶段基本上都是用C实现的,幸好前面sp的值已经设置好了 */

 ldr pc, _start_armboot

_start_armboot:
 .word start_armboot

#ifdef CONFIG_ENABLE_MMU
_mmu_table_base:
 .word mmu_table
#endif

/*
 * copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)
 * r0: size to be compared
 * Load 1'st 2blocks to RAM because U-boot's size is larger than 1block(128k) size
 */
 .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 */
 mov r9, #0x1000
 bl copy_uboot_to_ram

3: tst  r0, #0x0
 bne copy_failed

 ldr r0, =0x0c000000
 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
 sub sp, sp, #S_FRAME_SIZE  @ carve out a frame on current user stack
 stmia sp, {r0 - r12}   @ Save user registers (now in svc mode) r0-r12

 ldr r2, _armboot_start
 sub r2, r2, #(CFG_MALLOC_LEN)
 sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
 ldmia r2, {r2 - r3}   @ get values for "aborted" pc and cpsr (into parm regs)
 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
 add r8, sp, #S_PC   @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
 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 (enter in banked mode)
 sub r13, r13, #(CFG_MALLOC_LEN) @ move past malloc pool
 sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ move to reserved a couple spots for 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_bad_stack_swi
 sub r13, r13, #4   @ space on current stack for scratch reg.
 str r0, [r13]   @ save R0's value.
 ldr r0, _armboot_start  @ get data regions start
 sub r0, r0, #(CFG_MALLOC_LEN) @ move past malloc pool
 sub r0, r0, #(CFG_GBL_DATA_SIZE+8) @ move past gbl and a couple spots for abort stack
 str lr, [r0]   @ save caller lr in position 0 of saved stack
 mrs r0, spsr   @ get the spsr
 str lr, [r0, #4]   @ save spsr in position 1 of saved stack
 ldr r0, [r13]   @ restore r0
 add r13, r13, #4   @ pop stack entry
 .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_swi
 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
 .align 5
.global arm1136_cache_flush
arm1136_cache_flush:
  mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache
  mov pc, lr   @ back to caller

#if defined(CONFIG_INTEGRATOR) && defined(CONFIG_ARCH_CINTEGRATOR)
/* Use the IntegratorCP function from board/integratorcp/platform.S */
#elif defined(CONFIG_S3C64XX)
/* For future usage of S3C64XX*/
#else
 .align 5
.globl reset_cpu
reset_cpu:
 ldr r1, rstctl /* get addr for global reset reg */
 mov r3, #0x2 /* full reset pll+mpu */
 str r3, [r1] /* force reset */
 mov r0, r0
_loop_forever:
 b _loop_forever
rstctl:
 .word PM_RSTCTRL_WKUP

#endif

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值