深入淺析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中的棧):

再次設置供電鎖存:

第二次設置棧:

重定位:

地址映射 MMU:內存管理單元

第三次設置棧:

清理 bss 段:

跳轉到uboot第二階段:


我們的分析就從 start.S 開始:

start.S 頭文件分析:

#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>

#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE	CFG_UBOOT_BASE
#endif
#endif
  • #include <config.h>:config.h是在include目錄下的,這個文件不是源碼中本身存在的文件,而是配置過程中自動生成的文件。是在跟 Makefile 同一級目錄下的 mkconfig 腳本中:echo "#include <configs/$1.h>" >>config.h (141行)生成的,$1 是在 Makefile 中傳進去的,它的值是 x210_sd 。所以 config.h 中的內容是: #include <configs/x210_sd.h> 。所以 start.S 中包含的第一個頭文件就是:include/configs/x210_sd.h,這個文件是整個 uboot 移植時的配置文件。這裏面是好多宏。因此這個頭文件包含將 include/configs/x210_sd.h 文件和 start.S 文件關聯了起來。因此之後在分析start.S文件時,主要要考慮的就是 x210_sd.h 文件。
  • #include <version.h>:包含了 include/version_autogenerated.h,這個頭文件就是配置過程中自動生成的。裏面就一行內容:#define U_BOOT_VERSION "U-Boot 2.3.4"。這裏面定義的宏U_BOOT_VERSION的值是一個字符串,字符串中的版本號信息來自於 Makefile中 的配置值。這個宏在程序中會被調用,在uboot啓動過程中會串口打印出uboot的版本號,那個版本號信息就是從這來的。
  • #include <asm/proc/domain.h>: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 <regs.h>:一些寄存器的設置。也是用了鏈接的方式,具體鏈接的實現是在跟 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 <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>


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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章