經過上一節 鏈接腳本 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 開始:
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