深入浅析Linux下uboot之(五)-----------------------:uboot 启动的第一阶段(详解)

经过上一节 链接脚本 u-boot.lds 分析:ENTRY(_start)用来指定整个程序的入口地址。所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。有点像C语言中的main。因此_start符号所在的文件就是整个程序的起始文件,_start所在处的代码就是整个程序的起始代码。用搜索代码工具搜索到一共7个_start,然后分析搜索出来的7处,发现有2个是api_example,2个是 onenand 相关的,都不是我们要找的。剩下3个都在 uboot/cpu/s5pc11x/start.S 文件中。因此 start.S 就是我们所要找的,也就是我们 uboot 启动第一阶段的代码的位置就在 start.S  

uboot 启动的第一阶段是 SD 卡或者 flash 加载到 SRAM 中完成的。uboot第一阶段主要就是初始化了板子内部的一些部件(譬如看门狗、时钟),然后初始化DDR并且完成重定位。

目录

目录

start.S 头文件分析:

构建异常向量表:

设置 cpu 为 SVC (超级用户)模式:

清cachae、关闭MMU:

识别并暂存启动介质选择:

第一次设置栈(SRAM中的栈):

lowlevel_init (cpu 相关硬件初始化): 检查复位、关看门狗、供电锁存、时钟、DDR、串口初始化、打印“OK”

再次设置供电锁存:

第二次设置栈:

重定位:

地址映射 MMU:内存管理单元

第三次设置栈:

清理 bss 段:

跳转到uboot第二阶段:



我们的分析就从 start.S 开始:

start.S 头文件分析:

#include 
#include 
#if defined(CONFIG_ENABLE_MMU)
#include 
#endif
#include 

#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE	CFG_UBOOT_BASE
#endif
#endif
  • #include :config.h是在include目录下的,这个文件不是源码中本身存在的文件,而是配置过程中自动生成的文件。是在跟 Makefile 同一级目录下的 mkconfig 脚本中:echo "#include " >>config.h (141行)生成的,$1 是在 Makefile 中传进去的,它的值是 x210_sd 。所以 config.h 中的内容是: #include 。所以 start.S 中包含的第一个头文件就是:include/configs/x210_sd.h,这个文件是整个 uboot 移植时的配置文件。这里面是好多宏。因此这个头文件包含将 include/configs/x210_sd.h 文件和 start.S 文件关联了起来。因此之后在分析start.S文件时,主要要考虑的就是 x210_sd.h 文件。
  • #include :包含了 include/version_autogenerated.h,这个头文件就是配置过程中自动生成的。里面就一行内容:#define U_BOOT_VERSION "U-Boot 2.3.4"。这里面定义的宏U_BOOT_VERSION的值是一个字符串,字符串中的版本号信息来自于 Makefile中 的配置值。这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。
  • #include :asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm,具体链接的实现是在跟 Makefile 同一级目录下的 mkconfig 脚本中:ln -s arch-$3 asm-$2/arch (80行),$2 是在 Makefile 中传进去的,它的值是 arm,所以实际文件是:include/asm-arm/proc-armv/domain.h 。
  • #include :一些寄存器的设置。也是用了链接的方式,具体链接的实现是在跟 Makefile 同一级目录下的 mkconfig 脚本中:ln -s $6.h regs.h (86行)。 $6 的值是 s5pc110 。所以它自己的路径是: include/s5pc110.h 。

uboot 开始的 16 个字节:

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
#endif
  • .word:就相当于c语言的 int 占4个字节,这边一共填充了 16 个字节。在SD卡启动/Nand启动等整个镜像开头需要16字节的校验头。这个占位的16字节只是保证正式的image的头部确实有16字节,但是这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  //快速中断异常

_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
  • .globl _start:globl 就是相当于C语言中的 extern,声明此变量,并且告诉链接器此变量是全局的,外部可以被访问到。u-boot.lds 里面定义了ENTRY(_start),即指定入口为 _start。 
  • b    reset :复位异常。CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。
  • 其他异常处理:异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是我们在uboot中并未非常细致的处理各种异常。
  • .balignl 16,0xdeadbeef:16 字节对齐,字节数不够的用 0xdeadbeef 填充。

基址地址:

_TEXT_BASE:
	.word	TEXT_BASE

/*
 * 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
  • _TEXT_BASE 相当于指针的名字   .wrod相当于int类型,TEXT_BASE  相当于他的值
  • TEXT_BASE :有些符号的值可以从 Makefile 中传递到源代码中。在Makefile 分析时,同一级目录下的config.mk 里面的 TEXT_BASE,其实就是我们链接时指定的uboot的链接地址。它的值是 0xc3e00000。是在 Makefile 中给出来的:@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
  • CFG_PHY_UBOOT_BASE:uboot在DDR中的物理地址。
  • .word __bss_start:bss段的起始地址
  • .word _end:bss段的结束地址

设置 cpu 为 SVC (超级用户)模式

reset:
	/*
	 * set the cpu to SVC32 mode and IRQ & FIQ disable
	 */
	@;mrs	r0,cpsr  //读取CPSR程序状态寄存器,保存到R0中
	@;bic	r0,r0,#0x1f //把 r0 中的低 5 位清 0
	@;orr	r0,r0,#0xd3  //将r0与0xd3算数或运算,然后将结果给r0,即把r0的bit[7:6]和bit[4]和bit[2:0]置为1。
	@;msr	cpsr,r0  //将r0中的值赋给状态寄存器cpsr
	msr	cpsr_c, #0xd3		@ I & F disable, Mode: 0x13 - SVC  
  • msr    cpsr_c, #0xd3   :整段代码运行的就只有这一句,将CPU设置为禁止FIQ IRQ,将 cpu 设置成 SVC 模式。其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件将其置为SVC模式。整个uboot工作时CPU一直处于SVC模式。cpsr 的c位写入0xd3 ,cpsr是程序状态寄存器,它的c位是模式位。0xd3:11010011, 1101就是0x13,svc模式,第5位t位o,表示在arm状态
  • 其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件将其置为SVC模式。整个uboot工作时CPU一直处于SVC模式。整个uboot工作时cpu一直处于svc模式。(保险措施)

清cachae、关闭MMU

  mrc	p15, 0, r0, c1, c0, 0
  bic	r0, r0, #0x00002000     @ clear bits 13 (--V-)
  bic	r0, r0, #0x00000007     @ clear bits 2:0 (-CAM)
  orr	r0, r0, #0x00000002     @ set bit 1 (--A-) Align
  orr	r0, r0, #0x00000800     @ set bit 12 (Z---) BTB
  mcr 	p15, 0, r0, c1, c0, 0
  • 关闭MMU:一开始还没有做虚拟地址映射,现在还不能操作相关的地址,在启动第一阶段的后面会在打开 MMU。

识别并暂存启动介质选择

      /* Read booting information */
        ldr	r0, =PRO_ID_BASE
        ldr	r1, [r0,#OMR_OFFSET]
        bic	r2, r1, #0xffffffc1

	/* NAND BOOT */
	cmp	r2, #0x0		@ 512B 4-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x2		@ 2KB 5-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x4		@ 4KB 5-cycle	8-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x6		@ 4KB 5-cycle	16-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x8		@ OneNAND Mux
	moveq	r3, #BOOT_ONENAND

	/* SD/MMC BOOT */
	cmp     r2, #0xc
	moveq   r3, #BOOT_MMCSD	

	/* NOR BOOT */
	cmp     r2, #0x14
	moveq   r3, #BOOT_NOR	
/* NAND BOOT */
	cmp	r2, #0x0		@ 512B 4-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x2		@ 2KB 5-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x4		@ 4KB 5-cycle	8-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x6		@ 4KB 5-cycle	16-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x8		@ OneNAND Mux
	moveq	r3, #BOOT_ONENAND

	/* SD/MMC BOOT */
	cmp     r2, #0xc
	moveq   r3, #BOOT_MMCSD	

	/* NOR BOOT */
	cmp     r2, #0x14
	moveq   r3, #BOOT_NOR	

#if 0	/* Android C110 BSP uses OneNAND booting! */
	/* For second device booting */
	/* OneNAND BOOTONG failed */
	cmp     r2, #0x8
	moveq   r3, #BOOT_SEC_DEV
#endif

	/* Uart BOOTONG failed */
	cmp     r2, #(0x1<<4)
	moveq   r3, #BOOT_SEC_DEV
	
	ldr	r0, =INF_REG_BASE
	str	r3, [r0, #INF_REG3_OFFSET]   
  • 从哪里启动是由SoC的OM5:OM0这6个引脚的高低电平决定的。实际上在210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。我们代码中可以通过读取这个寄存器的值然后判断其值来确定当前选中的启动介质是Nand还是SD还是其他的。
  •  bic    r2, r1, #0xffffffc1:执行完后,在r2寄存器中存储了一个数字,这个数字等于某个特定值时就表示SD启动,等于另一个特定值时表示从Nand启动

第一次设置栈(SRAM中的栈)

	ldr	sp, =0xd0036000 /* end of sram dedicated to u-boot */
	sub	sp, sp, #12	/* set stack */
	mov	fp, #0
  • 第一次设置栈。这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。
  • 在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。所以这里设置栈是为了调用接下的一个函数 lowlevel_init 。

lowlevel_init (cpu 相关硬件初始化): lowlevel_init.S 

bl	lowlevel_init	/* go setup pll,mux,memory */
  • lowlevel_init 里面实现了cpu 相关硬件初始化:检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印'O'、tzpc初始化、打印'K'。其中值得关注的:关看门狗、开发板供电锁存、时钟初始化、DDR初始化、打印"OK"

再次设置供电锁存:

	ldr	r0, =0xE010E81C  /* PS_HOLD_CONTROL register */
	ldr	r1, =0x00005301	 /* PS_HOLD output high	*/
	str	r1, [r0]
  • 在 lowlevel_init  函数里面已经设置过供电锁存了,这里做2次是不会错的,虽然没有意义,但是为了谨慎保险起见这边还是再次的设置了一遍。

第二次设置栈:

	/* 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 */
  • 这是第二次设置栈,原因是DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在 SRAM 中了;原来 SRAM 中内存大小空间是有限的,栈放在那里要注意不能使用过多的栈否则栈会溢出,我们及时将栈迁移到 DDR 中也是为了尽可能避免栈使用时溢出。

重定位:

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

#if defined(CONFIG_EVT1)
	/* If BL1 was copied from SD/MMC CH2 */
	ldr	r0, =0xD0037488
	ldr	r1, [r0]
	ldr	r2, =0xEB200000
	cmp	r1, r2
	beq     mmcsd_boot
#endif

	ldr	r0, =INF_REG_BASE
	ldr	r1, [r0, #INF_REG3_OFFSET]
	cmp	r1, #BOOT_NAND		/* 0x0 => boot device is nand */
	beq	nand_boot
	cmp	r1, #BOOT_ONENAND	/* 0x1 => boot device is onenand */
	beq	onenand_boot
	cmp     r1, #BOOT_MMCSD
	beq     mmcsd_boot
	cmp     r1, #BOOT_NOR
	beq     nor_boot
	cmp     r1, #BOOT_SEC_DEV
	beq     mmcsd_boot

nand_boot:
	mov	r0, #0x1000
	bl	copy_from_nand
	b	after_copy

onenand_boot:
	bl	onenand_bl2_copy
	b	after_copy

mmcsd_boot:
#if DELETE
	ldr     sp, _TEXT_PHY_BASE      
	sub     sp, sp, #12
	mov     fp, #0
#endif
	bl      movi_bl2_copy
	b       after_copy

nor_boot:
	bl      read_hword
	b       after_copy
  • 这里判断运行地址是在SRAM中还是DDR中,不过本次判断的目的不同(上次判断是为了决定是否要执行初始化时钟和DDR的代码)这次判断是为了决定是否进行 uboot 的 重定位。
  • 重定位:冷启动时当前情况是uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到 SRAM 中正在运行,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(33e00000),这个加载过程就叫重定位。
  • 这边是判断 r1 、 r2 是否相等,如果相等跳过重定位,不相等就选择 nand_boot、onenand_boot、mmcsd_boot、nor_boot中相应的启动介质去重定位。如 mmcsd_boot ,执行的是 movi_bl2_copy 这个函数,它是在 uboot/cpu/s5pc11x/movi.c 中。是一个C语言的函数。

地址映射 MMU:内存管理单元

  • 什么是MMU:MMU就是memory management unit,内存管理单元。MMU实际上是SOC中一个硬件单元,它的主要功能就是实现虚拟地址到物理地址的映射。MMU单片在CP15协处理器中进行控制,也就是说要操控MMU进行虚拟地址映射,方法就是对cp15协处理器的寄存器进行编程。
  • 什么是物理地址:物理地址就是物理设备设计生产时赋予的地址。像裸机中使用的寄存器的地址就是CPU设计时指定的,这个就是物理地址。物理地址是硬件编码的,是设计生产时确定好的,一旦确定了就不能改了。寄存器的物理地址是无法通过编程修改的,是多少就是多少,只能通过查询数据手册获得并操作。坏处就是不够灵活。一个解决方案就是使用虚拟地址。
  • 什么是虚拟地址:虚拟地址意思就是在我们软件操作和硬件被操作之间增加一个层次,叫做虚拟地址映射层。有了虚拟地址映射后,软件操作只需要给虚拟地址,硬件操作还是用原来的物理地址,映射层建立一个虚拟地址到物理地址的映射表。当我们软件运行的时候,软件中使用的虚拟地址在映射表中查询得到对应的物理地址再发给硬件去执行(虚拟地址到物理地址的映射是不可能通过软件来实现的)。
  • 地址映射还可以实现访问控制:在管理上对内存进行分块,然后每块进行独立的虚拟地址映射,然后在每一块的映射关系中同时还实现了访问控制(对该块可读、可写、只读、只写、不可访问等控制),回想在C语言中编程中经常会出现一个错误:Segmentation fault。实际上这个段错误就和MMU实现的访问控制有关。当前程序只能操作自己有权操作的地址范围(若干个内存块),如果当前程序指针出错访问了不该访问的内存块则就会触发段错误。
  • 地址映射还可以实现cache:cache的工作和虚拟地址映射有关系。cache是快速缓存,意思就是比CPU慢但是比DDR块。CPU嫌DDR太慢了,于是乎把一些DDR中常用的内容事先读取缓存在cache中,然后CPU每次需要找东西时先在cache中找。如果cache中有就直接用cache中的;如果cache中没有才会去DDR中寻找。

使能域访问(cp15的c3寄存器):

#if defined(CONFIG_ENABLE_MMU)
enable_mmu:
	/* enable domain access */
	ldr	r5, =0x0000ffff
	mcr	p15, 0, r5, c3, c0, 0		@load domain access register
  • cp15协处理器内部有c0到c15共16个寄存器,这些寄存器每一个都有自己的作用。我们通过mrc和mcr指令来访问这些寄存器。所谓的操作cp协处理器其实就是操作cp15的这些寄存器。c3寄存器在mmu中的作用是控制域访问。域访问是和MMU的访问控制有关的。

设置TTB(cp15的c2寄存器:

	/* 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
  • TTB就是translation table base,转换表基地址。首先要明白什么是TT(translation table转换表),TTB其实就是转换表的基地址。
  • 转换表是建立一套虚拟地址映射的关键。转换表分2部分,表索引和表项。表索引对应虚拟地址,表项对应物理地址。一对表索引和表项构成一个转换表单元,能够对一个内存块进行虚拟地址转换。(映射中基本规定中规定了内存映射和管理是以块为单位的,至于块有多大,要看你的MMU的支持和你自己的选择。在ARM中支持3种块大小,细表1KB、粗表4KB、段1MB)。真正的转换表就是由若干个转换表单元构成的,每个单元负责1个内存块,总体的转换表负责整个内存空间(0-4G)的映射。
  • 整个建立虚拟地址映射的主要工作就是建立这张转换表。转换表放置在内存中的,放置时要求起始地址在内存中要xx位对齐。转换表不需要软件去干涉使用,而是将基地址TTB设置到cp15的c2寄存器中,然后MMU工作时会自动去查转换表。
     

使能MMU单元(cp15的c1寄存器):

	/* Enable the MMU */
mmu_on:
	mrc	p15, 0, r0, c1, c0, 0
	orr	r0, r0, #1
	mcr	p15, 0, r0, c1, c0, 0
	nop
	nop
	nop
	nop
  • cp15的c1寄存器的bit0控制MMU的开关。只要将这一个bit置1即可开启MMU。开启MMU之后上层软件层的地址就必须经过TT的转换才能发给下层物理层去执行。

第三次设置栈:

skip_hw_init:
	/* Set up the stack						    */
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
	ldr	sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#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                        */
#if defined(CONFIG_USE_IRQ)
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
	sub	sp, r0, #12		/* leave 3 words for abort-stack    */
  • 第三次设置栈。这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。
  • 我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:2MB-uboot大小-0x1000=1.8MB左右。这个空间既没有太浪费内存,又足够安全。

清理 bss 段:

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

跳转到uboot第二阶段:

	ldr	pc, _start_armboot

_start_armboot:
	.word start_armboot
  • start_armboot是uboot/lib_arm/board.c中,这是一个C语言实现的函数。这个函数就是uboot的第二阶段。这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。
  • 远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。这里这个远跳转就是uboot第一阶段和第二阶段的分界线。

至此汇编到此结束。

第一阶段完整代码:

#include 
#include 
#if defined(CONFIG_ENABLE_MMU)
#include 
#endif
#include 


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

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
#endif

.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
_pad:
	.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

	.balignl 16,0xdeadbeef
/*
 *************************************************************************
 *
 * 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

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


/*
 * the actual reset code
 */

reset:
	/*
	 * set the cpu to SVC32 mode and IRQ & FIQ disable
	 */
	msr	cpsr_c, #0xd3		@ I & F disable, Mode: 0x13 - SVC


/*
 *************************************************************************
 *
 * 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:
	
	bl	disable_l2cache

	bl	set_l2cache_auxctrl_cycle

	bl	enable_l2cache
	
       /*
        * Invalidate L1 I/D
        */
        mov	r0, #0                  @ set up for MCR
        mcr	p15, 0, r0, c8, c7, 0   @ invalidate TLBs
        mcr	p15, 0, r0, c7, c5, 0   @ invalidate icache

       /*
        * disable MMU stuff and caches
        */
        mrc	p15, 0, r0, c1, c0, 0
        bic	r0, r0, #0x00002000     @ clear bits 13 (--V-)
        bic	r0, r0, #0x00000007     @ clear bits 2:0 (-CAM)
        orr	r0, r0, #0x00000002     @ set bit 1 (--A-) Align
        orr	r0, r0, #0x00000800     @ set bit 12 (Z---) BTB
        mcr 	p15, 0, r0, c1, c0, 0


        /* Read booting information */
        ldr	r0, =PRO_ID_BASE
        ldr	r1, [r0,#OMR_OFFSET]
        bic	r2, r1, #0xffffffc1


	/* NAND BOOT */
	cmp	r2, #0x0		@ 512B 4-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x2		@ 2KB 5-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x4		@ 4KB 5-cycle	8-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x6		@ 4KB 5-cycle	16-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x8		@ OneNAND Mux
	moveq	r3, #BOOT_ONENAND

	/* SD/MMC BOOT */
	cmp     r2, #0xc
	moveq   r3, #BOOT_MMCSD	

	/* NOR BOOT */
	cmp     r2, #0x14
	moveq   r3, #BOOT_NOR	

	/* Uart BOOTONG failed */
	cmp     r2, #(0x1<<4)
	moveq   r3, #BOOT_SEC_DEV
	
	ldr	r0, =INF_REG_BASE
	str	r3, [r0, #INF_REG3_OFFSET]     

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

	ldr	sp, =0xd0036000 /* end of sram dedicated to u-boot */
	sub	sp, sp, #12	/* set stack */
	mov	fp, #0
	
	bl	lowlevel_init	/* go setup pll,mux,memory */
	/* To hold max8698 output before releasing power on switch,
	 * set PS_HOLD signal to high
	 */
	ldr	r0, =0xE010E81C  /* PS_HOLD_CONTROL register */
	ldr	r1, =0x00005301	 /* PS_HOLD output high	*/
	str	r1, [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 */

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

#if defined(CONFIG_EVT1)
	/* If BL1 was copied from SD/MMC CH2 */
	ldr	r0, =0xD0037488
	ldr	r1, [r0]
	ldr	r2, =0xEB200000
	cmp	r1, r2
	beq     mmcsd_boot
#endif

	ldr	r0, =INF_REG_BASE
	ldr	r1, [r0, #INF_REG3_OFFSET]
	cmp	r1, #BOOT_NAND		/* 0x0 => boot device is nand */
	beq	nand_boot
	cmp	r1, #BOOT_ONENAND	/* 0x1 => boot device is onenand */
	beq	onenand_boot
	cmp     r1, #BOOT_MMCSD
	beq     mmcsd_boot
	cmp     r1, #BOOT_NOR
	beq     nor_boot
	cmp     r1, #BOOT_SEC_DEV
	beq     mmcsd_boot

nand_boot:
	mov	r0, #0x1000
	bl	copy_from_nand
	b	after_copy

onenand_boot:
	bl	onenand_bl2_copy
	b	after_copy

mmcsd_boot:
#if DELETE
	ldr     sp, _TEXT_PHY_BASE      
	sub     sp, sp, #12
	mov     fp, #0
#endif
	bl      movi_bl2_copy
	b       after_copy

nor_boot:
	bl      read_hword
	b       after_copy


after_copy:

#if defined(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
	mcr	p15, 0, r0, c1, c0, 0
	nop
	nop
	nop
	nop
#endif

skip_hw_init:
	/* Set up the stack						    */
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
	ldr	sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#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                        */
#if defined(CONFIG_USE_IRQ)
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
	sub	sp, r0, #12		/* leave 3 words for abort-stack    */

#endif

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
	
	ldr	pc, _start_armboot

_start_armboot:
	.word start_armboot

 

你可能感兴趣的:(uboot)