uboot移植之啟動過程詳解1
/*******************************************************************************
uboot啟動過程第一階段的分析(start.s檔案)
核心:start.s的分析,
其他:虛擬地址的簡單介紹,lowlever_init函式的分析
時間:2018年11月下旬
作者:cryil_先森
以朱有鵬課程中uboot-samsung-dev為分析物件
******************************************************************************/
/*******************************************************************************
u-boot.lds中找到start.s的入口
整個程式的入口取決於連結指令碼u-boot.lds中ENTRY宣告的地方。
start符號所在的檔案是整個程式的起始檔案,_start所在的程式碼是整個程式的起始程式碼。
找到start.s檔案,他是整個程式的起始檔案。目錄:u-boot-samsung-dev\cpu\s5pc11x
.globl相當於C語言中的Extern,宣告此變數,並且告訴連結器此變數是全域性的,外部可以訪問
uboot啟動的階段的技巧是小範圍內有限條件下的輾轉騰挪(不斷地在設定棧,變換儲存的位置)
*******************************************************************************/
start.s的分析
***主要的工作:
1.構建異常向量表;
2.設定CPU為SVC模式;
3.關看門狗,開發板的供電索存(lowlevel.inti.s中完成,在start.s中重複進行);
4.時鐘的初始化,DDR的初始化(lowlevel.inti.s中完成);
5.除錯的重要依據:串列埠初始化並列印“OK”;
6.重定位,並且設定了uboot啟動第二部分在DDR中的起始地址;
7.建立對映表並開啟MMU;
8.設定三次不同用途的棧;
9.跳轉至uboot啟動的第二階段繼續進行。
***標頭檔案:
#include <config.h> config.h檔案在include目錄下,在配置時自動生成,詳見mkconfig指令碼
其實config.h檔案包含了一個頭檔案 include/configs/smdkv210onenand.h標頭檔案。
#include <version.h>
include/version.h中包含了include/versionn_autogeneraled.h。
這個檔案在配置時自動生成,裡面定義的巨集 U-BOOT-VERSION是一個字串,代表版本號。
U-BOOT-VERSION來源於主Makefile開頭位置的配置值,這個巨集在程式之中被呼叫,在開機啟動時會打印出我們的所配置的板子的資訊。
#include <asm/proc/domain.h>
asm目錄不是uboot的原生目錄,是配置時建立的一個符號連結,指向asm-arm目錄。
asm/proc/domain.h實際的檔案是 include/asm-arm/proc/domain.h
#include <regs.h>
regs.h檔案在/include目錄下,實際指向/include/s5pc110.h($6系統晶片.h)檔案。
***程式部分分析:
**1.啟動程式碼的16位元組頭部:
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif
分析:.word是GNU的偽指令,實際上定義了16位元組的內容,這16位元組是SD/NAND啟動時整個映象開頭所需的16位元組校驗頭。
在uboot啟動時:在開頭位置放置了16位元組的填充佔位,這個佔位的16位元組只是保證image的頭部確實有16位元組,但是這16位元組裡面的內容是不正確的,需要後面去計算校驗然後重新填充(類似及裸機之中的mkv210_image.c檔案)
**2.異常向量表:
_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
分析:異常向量表由硬體決定,軟體進行參考設計實現。
異常向量表中的每一個異常都應該被處理,否則遇到這種異常就會跑飛。
.balignl 16,0xdeadbeef:
讓當前地址對其排布,如果當前地址不對齊則自動向後走直到地址對齊,並且後面的記憶體用0xdeadbeef填充 (deadbeef:壞牛肉,表示可以被丟棄的內容)
**3. _TEXT_BASE:
.word TEXT_BASE
分析:和主Makefile中定義的TEXT_BASE相同,是我們連結時指定的連結地址,也就是c3e00000
**4. _TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE
分析:CFG_PHY_UBOOT_BASE=0x20000000+3e00000=0x23e00000 也是DDR之中的實體地址
***5.
#if defined(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
分析:uboot中與中斷有關的設定,一般情況下會被刪除。
**6.進行復位模式的設定,整個程式真正入口的地方
reset:
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
@;mrs r0,cpsr
@;bic r0,r0,#0x1f
@;orr r0,r0,#0xd3
@;msr cpsr,r0
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
設定CPU當前工作的狀態模式:禁止FIQ IRQ ARM狀態,SVC模式
分析:將CPU設定為禁止FIQ IRQ ARM狀態,SVC模式。(uboot工作時CPU移植處在SVC)
**7.CPU的一些初始化操作:
cpu_init_crit:
#ifndef CONFIG_EVT1 判斷是否是定義了CONFIG_EVT1,後面的if~endif 可以刪除
#if 0
bl v7_flush_dcache_all
#else
bl disable_l2cache
mov r0, #0x0 @
mov r1, #0x0 @ i
mov r3, #0x0
mov r4, #0x0
lp1:
mov r2, #0x0 @ j
lp2:
mov r3, r1, LSL #29 @ r3 = r1(i) <<29
mov r4, r2, LSL #6 @ r4 = r2(j) <<6
orr r4, r4, #0x2 @ r3 = (i<<29)|(j<<6)|(1<<1)
orr r3, r3, r4
mov r0, r3 @ r0 = r3
bl CoInvalidateDCacheIndex
add r2, #0x1 @ r2(j)++
cmp r2, #1024 @ r2 < 1024
bne lp2 @ jump to lp2
add r1, #0x1 @ r1(i)++
cmp r1, #8 @ r1(i) < 8
bne lp1 @ jump to lp1
bl set_l2cache_auxctrl
bl enable_l2cache
#endif
#endif
bl disable_l2cache 禁止12cache
bl set_l2cache_auxctrl_cycle 12cash 相關初始化
bl enable_l2cache 使能 12 cache
/*
* 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
總結:以上都是關於和CPU相關的cach mmu有關的,不用細看。
識別並暫時儲存啟動介質的選擇:
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
分析:從哪裡啟動由SOC的OM5~OM0這6個引腳的高低電平決定。
在210內有一個暫存器(地址是0xE0000004),這個暫存器是硬體根據OM引腳的設定而自動設定的,這個值(r2)反映了OM引腳的接法(電平的高低),從而確定了啟動介質是NAND還是SD其他的。
**9.進行板子的資訊匹配
#ifdef CONFIG_VOGUES
/* PS_HOLD(GPH0_0) set to output high 設定輸出高*/
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x00000001
str r1, [r0, #GPH0CON_OFFSET]
ldr r1, =0x5500
str r1, [r0, #GPH0PUD_OFFSET]
ldr r1, =0x01
str r1, [r0, #GPH0DAT_OFFSET]
#endif
分析:將板子與VOGUES進行比較 一致的話則進行下面的操作 (與我們的板子不同 刪除)
**10. 設定板子啟動的方式:
/* 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]
分析:通過給r2賦值,確定板子的啟動方式:nand,onenand,sd,nor flash
如果啟動失敗的話則對r2重新賦值,更換啟動的方式。
**11.初始化第一個棧(SRAM內)
ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */ //設定暫存器的值
sub sp, sp, #12 /* set stack */
mov fp, #0
分析:第一次設定棧,這次設定是在SRAM中設定的,因為當前整個程式碼還是執行在SRAM中,DDR還未初始化。在呼叫函式之前初始化棧主要原因是在被呼叫的函式內部還會再次呼叫函式,而BL只會返回地址儲存到LR之中,但是我們只有一個LR,所以在第二層呼叫函式之前將LR入棧,否則函式返回時第一層的返回地址就丟了。
**12.一個重要的函式:lowlevel_init
bl lowlevel_init /* go setup pll,mux,memory */
在其他部落格內會有具體的介紹。
**13.開發板的供電索存
ldr r0, =0xE010E81C /* PS_HOLD_CONTROL register */
ldr r1, =0x00005301 /* PS_HOLD output high */
str r1, [r0]
在lowlevel_init中初始化過了(可刪除)。
**14.第二次設定棧(DDR內)
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
分析:由於我們的DDR已經被初始化,所以此次設定棧為記憶體分配更大的空間,原來SRAM記憶體的空間受限,過多時易發生溢位現象,及時的將它遷移至DDR。
**15.再次判斷當前地址以決定是否重定位
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 */
分析:本次判斷是為了決定是否執行uboot程式碼的重定位。
(而在lowlevel_init也有判斷,以決定是否執行初始化時鐘和DDR的程式碼)
uboot啟動的階段:
1.冷啟動部分:uboot的前一部分,開機時自動從SD卡載入至SRAM中去執行;
2.第二部分(實際是整個部分):此時整個uboot還在SD卡的某個扇區開頭的N個扇區中,在uboot第一階段結束之前必須將它載入至DDR中連結地址0x23e00000處,這個過程就是重定位
**16 再次對啟動方式進行判斷:
#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
最終跳轉至mmcsd_boot函式之中進行執行。
**17.重定位:
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
進行一個判斷之後通過呼叫movi_bl2_copy函式程式來完成。執行完之後再跳轉至after_copy函式進行執行。
而movi_bl2_copy函式在uboot/cpu/s5pc11x/Movi.c檔案之中。
在這個函式值裡面其實呼叫了另外一個函式:copy_bl2:
copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0)
分析引數:2:表示通道; MOVI_BL2_POS:是uboot的第二部分在SD卡的開始扇區,這個扇區必數字必須和燒錄時的位置相同;
MOVI_BL2_BLKCNT:是uboot的長度所佔據的扇區數;
CFG_PHY_UBOOT_BASE:重定位時將uboot的第二部分複製到DDR之中的起始地址。(0x23e00000)
**18.設定虛擬地址對映:
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
分析:得到轉換表的基地址並將其放置在c2之中。
然後將cp15的c1暫存器賦值為1,開啟MMU,
注:上層軟體的地址就必須通過轉換表才能發給下層的物理層去執行。
CONFIG_ENABLE_MMU在smdkv210onenand.h中有定義,這是一個和虛擬地址對映有關的關鍵字
cp15協處理器內部有c0~c15共計6個暫存器,我們通過mrc和mcr指令來訪問這些暫存器
操作CPU協處理器其實就是操作cp15暫存器。c3在mmu之中作用就是控制域訪問,控制域訪問和mmu的訪問控制有關。
**19 Set the TTB register:轉換表基地址 (從此處開始,用的都是虛擬地址)
轉換表是建立虛擬地址對映的關鍵,轉換表分為表索引和表項兩部分,表索引對應虛擬地址對映,表項對應實體地址,一個表索引和表項構成一個轉換單元,能夠對一個記憶體塊進行虛擬地址轉換。(記憶體管理和對映以快為單位)轉換表放置在記憶體之中,放置時要求起始地址在記憶體之中要xx位對齊,轉換表不需要軟體的支援,而是將基地址TTB設定到cp15的c2暫存器之中,然後MMU工作時會自動查詢轉換表。
巨集觀上整個轉換表可以看做是一個int型的陣列,陣列的元素值就是表項,元素的下標就是表索引。在uboot/board/samsung/smdkc110/lowlever_init.s中對TTB轉換表基地址有著具體的定義。
/* form a first-level section entry */
.macro FL_SECTION_ENTRY base,ap,d,c,b
.word (\base << 20) | (\ap << 10) | \
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
.endm
.section .mmudata, "a"
.align 14
// the following alignment creates the mmu table at address 0x4000.
.globl mmu_table
mmu_table:
.set __base,0
// Access for iRAM
.rept 0x100
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
// Not Allowed
.rept 0x200 - 0x100
.word 0x00000000
.endr
.set __base,0x200
// should be accessed
.rept 0x600 - 0x200
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
.rept 0x800 - 0x600
.word 0x00000000
.endr
.set __base,0x800
// should be accessed
.rept 0xb00 - 0x800
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
/* .rept 0xc00 - 0xb00
.word 0x00000000
.endr */
.set __base,0xB00
.rept 0xc00 - 0xb00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
.set __base,0x200
// 256MB for SDRAM with cacheable
.rept 0xD00 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
// access is not allowed.
@.rept 0xD00 - 0xC80
@.word 0x00000000
@.endr
.set __base,0xD00
// 1:1 mapping for debugging with non-cacheable
.rept 0x1000 - 0xD00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
#else // CONFIG_MCP_AC, CONFIG_MCP_H, CONFIG_MCP_B
分析:設定了具體的分割槽方式和虛擬地址對映的關係。
核心:虛擬地址對映只是將虛擬地址的c0000000開頭的256MB對映到了DMC0的30000000開頭的256MB實體記憶體之中去,其他的虛擬地址沒有發生變化,對映的還是原來的實體地址。
注:DRAM的有效範圍:
DMC0:0x30000000~0x3FFFFFFF
DMC1:0X40000000~0X4FFFFFFF
**20 第三次設定棧(DDR之中),清除bss段程式碼。
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
本次設定棧主要是為了設定一個更加安全的棧,設定了棧的起始位置,設定了棧的大小:
棧的起始位置:CFG_UBOOT_BASE + CFG_UBOOT_SIZE:uboot起始地址上方2MB處
棧的大小:CFG_UBOOT_BASE + CFG_UBOOT_SIZE-0x1000 約為1.8MB
這次設定使得這個棧離我們uboot的原始碼儲存的地方儘量的近,從而緊湊而不浪費的使用了記憶體空間。(uboot中的棧是滿減棧,向下進行儲存,所以設定的棧在減去uboot原始碼的空間之後有著巨大的空間可以供開發使用)
而bss段的開頭和結尾地址的符號是從連結指令碼u-boot.lds得來的。
**21 第一階段的結束,與第二階段進行銜接
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
分析:實際上start_armboot是一個指標,指向的是uboot啟動原始碼第二部分在DDR之中存放的地址。它存放在uboot/lib_arm/board.c中。
相當於進行一個遠端的跳轉指令,直接跳轉至DDR中的第二階段開始的地址處。這個遠跳轉是uboot啟動過程第一階段和第二階段的分界線。
虛擬地址對映的簡單介紹:
***1.實體地址:
物理裝置設計生產時所被賦予的地址,是硬體編碼的,在設計出廠之後就無法 改(只能通過資料手冊進行查詢並應用)
***2.虛擬地址對映層:
在軟體操作和硬體操作之間增加的一個便於程式設計人員進行開發的層次。
對映層建立了一個從虛擬地址到實體地址的對映關係,當軟體執行時可以通過訪問虛擬地址來訪問相應的實體地址,再控制硬體的操作。
(軟體程式之中使用的都是虛擬地址,而和硬體直接相關的是實體地址,虛擬地址到實體地址的對映是通過硬體實現的)
**訪問控制:
在記憶體管理時同時對記憶體進行分塊處理,然後對每一塊進行單獨的虛擬地址對映,並同時對每一塊實現訪問控制(只讀,只寫。。。。)
例:在C語言之中出現的警告錯誤:segmentation fault (段錯誤)
原因就是訪問控制導致的,當前程式只能操作自己有權訪問的地址範圍(若干個記憶體塊),如果當前程式指標訪問了不屬於自己的記憶體塊,就會觸發段錯誤。
**cache:
快速快取,速度比CPU慢,比DDR快。相當於兩者之間的介質。
CPU在訪問DDR之前會先訪問cache之中快取的內容,然後訪問DDDR之中的內容。
CPU的速度遠高於記憶體,當CPU直接從記憶體中存取資料時要等待一定時間週期,而Cache則可以儲存CPU剛用過或迴圈使用的一部分資料,如果CPU需要再次使用該部分資料時可從Cache中直接呼叫,這樣就避免了重複存取資料,減少了CPU的等待時間,因而提高了系統的效率。
***3.MMU單元:
記憶體管理單元,實際上是SOC中的一個硬體單元,主要實現虛擬地址到實體地址的對映。
MMU在cp15協處理器中被進行控制,所以對於虛擬地址對映的操控就是對cp15協處理器的暫存器進行程式設計。
lowlever_init函式的分析
/*******************************************************************************
lowlever_init函式存在的地方:/board/samsunug/smdkc110(board)/lowlever_init.s
具體的作用:檢查復位狀態,進行IO恢復,關閉看門狗、開發板的供電索存、判斷程式碼的執行狀態、時鐘的初始化、DDR的初始化、後口初始化並列印“O”、tzpc初始化、列印“K”。
最核心的是:1.判斷當前程式碼的工作區域,即DDR or SRAM,從而設定不同的啟動方式;
2.通過串列埠的方式在uboot的啟動最開始打印出“OK”可以作為很重要除錯方式,初步排查問題的地方。
**********************************************************************************/
***具體的分析:
A.與啟動主線無多大影響的部分:
**1.壓棧操作
lowlevel_init:
push {lr}
分析:進行壓棧操作,以免因為後面ld呼叫其他的函式而導致資料的丟失
**2.檢查復位的狀態:
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfff6ffff
cmp r1, #0x10000
beq wakeup_reset_pre
cmp r1, #0x80000
beq wakeup_reset_from_didle
複雜CPU的復位方式情況很複雜,所以在復位程式碼啟動之前要去檢測復位的狀態。
例如:冷上電時DDR需要初始化,熱啟動時(休眠、低功耗)不需要初始化DDR。
3.對IO和看門狗進行設定
/* IO Retention release **/
ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
ldr r1, [r0]
ldr r2, =IO_RET_REL
orr r1, r1, r2
str r1, [r0]
/* Disable Watchdog /
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
分析:對io相關的出處理,並關掉看門狗
4.對一些SRAM SROM GPIO進行設定以及供電索存(與主線啟動程式碼無關)
/* SRAM(2MB) init for SMDKC110 */
/* GPJ1 SROM_ADDR_16to21 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, [r0, #GPJ1CON_OFFSET]
bic r1, r1, #0xFFFFFF
ldr r2, =0x444444
orr r1, r1, r2
str r1, [r0, #GPJ1CON_OFFSET]
ldr r1, [r0, #GPJ1PUD_OFFSET]
ldr r2, =0x3ff
bic r1, r1, r2
str r1, [r0, #GPJ1PUD_OFFSET]
/* GPJ4 SROM_ADDR_16to21 */
ldr r1, [r0, #GPJ4CON_OFFSET]
bic r1, r1, #(0xf<<16)
ldr r2, =(0x4<<16)
orr r1, r1, r2
str r1, [r0, #GPJ4CON_OFFSET]
ldr r1, [r0, #GPJ4PUD_OFFSET]
ldr r2, =(0x3<<8)
bic r1, r1, r2
str r1, [r0, #GPJ4PUD_OFFSET]
/* PS_HOLD pin(GPH0_0) set to high */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
ldr r1, [r0]
orr r1, r1, #0x300
orr r1, r1, #0x1
str r1, [r0]
B.真正有意義的地方:
**1.判定當前程式碼的執行位置(SRAM or DDR)
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
分析: r1 =pc & ~(0xff000fff)清零pc的特定位
注:pc載入的是執行地址的現在地址
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
分析:_TEXT_BASE是我們的連結地址,是DRAM、DDR中的內容,
bic r2, r2, r0 /* r0 <- current base addr of code */
分析:清零r2的特定位
cmp r1, r2 /* compare r0, r1 */
分析:將r2與r1進行比較,以確定我們的啟動方式是熱啟動還是冷啟動。相同的話當前執行在SRAM之中,則依次執行下面的程式碼;如果不同的話當前執行在DDR之中,則向下跳轉至“1:”位置執行其相應的程式碼。
注:在裸機啟動時,僅僅只是比較_start的執行地址和連結地址是否相同而確定我們當前程式碼的執行地址,從而確定啟動的方式。而在uboot之中,pc獲取的是執行地址的現在地址,顯然和我們的連結地址的首地址(_TEXT_BASE)不相同,則將_TEXT_BASE和pc的地址均取相同的位進行比較。
beq 1f /* r0 == r1 then skip sdram init */
/* init PMIC chip */
bl PMIC_InitIp
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init
分析:相同時直接先進行時鐘和記憶體(DDR動態記憶體)的初始化(後面會有具體的函式),不相同時直接跳轉“1:”執行。
通過分析mem_ctrl_asm_init(DDR動態記憶體)可知在uboot中DMC0、DMC1都工作了 。 它可用的實體地址範圍是0x30000000~0x4fffffff 一共是512MB,
其中30000000~3fffffff是DMC0,40000000~4FFFFFFF是DMC1。
(時鐘和記憶體的初始化的一些地址在s5pc110.h檔案裡有定義,如果想更改時鐘的初始化只需要更改.h檔案裡面的定義即可。)
1:
/* for UART */
bl uart_asm_init
bl tzpc_init
分析:初始化串列埠(函式在後面的程式之中有具體的定義),在初始化完畢後,傳送一個“o”;接著初始化可信任區域。
**2.接下來通過if判斷執行一些初始化,最重要的是在最後程式正確執行完畢之後列印了“K”,這和之前列印的“O”組合一起,列印“OK”,是否打印出OK可以作為uboot啟動過程的一個除錯方法。
#if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
/* check reset status */
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfffeffff
cmp r1, #0x10000
beq wakeup_reset_pre
/* ABB disable */
ldr r0, =0xE010C300
orr r1, r1, #(0x1<<23)
str r1, [r0]
/* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
pop {pc}
**3.一些基本的的初始化操作
wakeup_reset_from_didle:
/* Wait when APLL is locked */
ldr r0, =ELFIN_CLOCK_POWER_BASE
lockloop:
ldr r1, [r0, #APLL_CON0_OFFSET]
and r1, r1, #(1<<29)
cmp r1, #(1<<29)
bne lockloop
beq exit_wakeup
wakeup_reset_pre:
mrc p15, 0, r1, c1, c0, 1 @Read CP15 Auxiliary control register
and r1, r1, #0x80000000 @Check L2RD is disable or not
cmp r1, #0x80000000
bne wakeup_reset @if L2RD is not disable jump to wakeup_reset
bl disable_l2cache
bl v7_flush_dcache_all
/* L2 cache enable at sleep.S of kernel
* bl enable_l2cache
*/
wakeup_reset:
/* init system clock */
bl system_clock_init
bl mem_ctrl_asm_init
bl tzpc_init
#if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif
#if defined(CONFIG_NAND)
bl nand_asm_init
#endif
exit_wakeup:
/*Load return address and jump to kernel*/
ldr r0, =(INF_REG_BASE+INF_REG0_OFFSET)
ldr r1, [r0] /* r1 = physical address of s5pc110_cpu_resume function*/
mov pc, r1 /*Jump to kernel */
nop
nop
**4.最後就是一系列的初始化原始碼:
system_clock_init: uart_asm_init: nand_asm_init: tzpc_init: onenandcon_init:
完結。。。。。。。。。。。。。
時間:2018年11月下旬
作者:cryil_先森ID:qq_41464499
轉載需備註來源