移植嵌入式Linux到ARM處理器S3C2410:BootLoader
BootLoader指系統(tǒng)啟動后,在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序。通過BootLoader,我們可以初始化硬件設(shè)備、建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適的狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好正確的環(huán)境。通常,BootLoader是嚴(yán)重地依賴于硬件而實現(xiàn)的,特別是在嵌入式世界。因此,在嵌入式世界里建立一個通用的 BootLoader 幾乎是不可能的。盡管如此,我們?nèi)匀豢梢詫ootLoader歸納出一些通用的概念來,以指導(dǎo)用戶特定的BootLoader設(shè)計與實現(xiàn)。
BootLoader 的實現(xiàn)依賴于CPU的體系結(jié)構(gòu),因此大多數(shù) BootLoader 都分為stage1 和stage2 兩大部分。依賴于CPU體系結(jié)構(gòu)的代碼,比如設(shè)備初始化代碼等,通常都放在 stage1中,而且通常都用匯編語言來實現(xiàn),以達(dá)到短小精悍的目的。而stage2 則通常用C 語言來實現(xiàn),這樣可以實現(xiàn)更復(fù)雜的功能,而且代碼會具有更好的可讀性和可移植性。
BootLoader 的 stage1 通常包括以下步驟:
·硬件設(shè)備初始化;
·為加載Boot Loader的stage2準(zhǔn)備 RAM 空間;
·拷貝Boot Loader的stage2 到RAM空間中;
·設(shè)置好堆棧;
·跳轉(zhuǎn)到 stage2 的 C 入口點。
Boot Loader的stage2通常包括以下步驟:
·初始化本階段要使用到的硬件設(shè)備;
·檢測系統(tǒng)內(nèi)存映射(memory map);
·將kernel 映像和根文件系統(tǒng)映像從flash上讀到 RAM 空間中;
·為內(nèi)核設(shè)置啟動參數(shù);
·調(diào)用內(nèi)核。
本系統(tǒng)中的BootLoader參照韓國mizi公司的vivi進(jìn)行修改。
1.開發(fā)環(huán)境
我們購買了武漢創(chuàng)維特信息技術(shù)有限公司開發(fā)的具有自主知識產(chǎn)權(quán)的應(yīng)用于嵌入式軟件開發(fā)的集成軟、硬件開發(fā)平臺ADT(ARM Development Tools)它為基于ARM 核的嵌入式應(yīng)用提供了一整套完備的開發(fā)方案,包括程序編輯、工程管理和設(shè)置、程序編譯、程序調(diào)試等。
ADT嵌入式開發(fā)環(huán)境由ADT Emulator for ARM 和ADT IDE for ARM組成。ADT Emulator for ARM 通過JTAG 實現(xiàn)主機(jī)和目標(biāo)機(jī)之間的調(diào)試支持功能。它無需目標(biāo)存儲器,不占用目標(biāo)系統(tǒng)的任何端口資源。目標(biāo)程序直接在目標(biāo)板上運(yùn)行,通過ARM 芯片的JTAG 邊界掃描口進(jìn)行調(diào)試,屬于完全非插入式調(diào)試,其仿真效果接近真實系統(tǒng)。
ADT IDE for ARM 為用戶提供高效明晰的圖形化嵌入式應(yīng)用軟件開發(fā)環(huán)境,包括一整套完備的面向嵌入式系統(tǒng)的開發(fā)和調(diào)試工具:源碼編輯器、工程管理器、工程編譯器(編譯器、匯編器和連接器)、集成調(diào)試環(huán)境、ADT Emulator for ARM 調(diào)試接口等。其界面同Microsoft Visual Studio 環(huán)境相似,用戶可以在ADT IDE for ARM 集成開發(fā)環(huán)境中創(chuàng)建工程、打開工程,建立、打開和編輯文件,編譯、連接、設(shè)置、運(yùn)行、調(diào)試嵌入式應(yīng)用程序。
ADT嵌入式軟件開發(fā)環(huán)境采用主機(jī)-目標(biāo)機(jī)交叉開發(fā)模型。ADT IDE for ARM 運(yùn)行于主機(jī)端,而ADT Emulator for ARM 實現(xiàn)ADT IDE for ARM 與目標(biāo)機(jī)之間的連接。開發(fā)時,首先由ADT IDE for ARM 編譯連接生成目標(biāo)代碼,然后建立與ADT Emulator for ARM 之間的調(diào)試通道,調(diào)試通道建立成功后,就可以在ADT IDE for ARM 中通過ADT Emulator for ARM 控制目標(biāo)板實現(xiàn)目標(biāo)程序的調(diào)試,包括將目標(biāo)代碼下載到目標(biāo)機(jī)中,控制程序運(yùn)行,調(diào)試信息觀察等等。
2.ARM匯編
ARM本身屬于RISC指令系統(tǒng),指令條數(shù)就很少,而其編程又以C等高級語言為主,我們僅需要在Bootloader的第一階段用到少量匯編指令:
?。?)+-運(yùn)算
ADD r0, r1, r2
―― r0 := r1 + r2
SUB r0, r1, r2
―― r0 := r1 - r2
其中的第二個操作數(shù)可以是一個立即數(shù):
ADD r3, r3, #1
―― r3 := r3 + 1
第二個操作數(shù)還可以是位移操作后的結(jié)果:
ADD r3, r2, r1, LSL #3
―― r3 := r2 + 8.r1
?。?)位運(yùn)算
AND r0, r1, r2
―― r0 := r1 and r2
ORR r0, r1, r2
―― r0 := r1 or r2
EOR r0, r1, r2
―― r0 := r1 xor r2
BIC r0, r1, r2
―― r0 := r1 and not r2
?。?)寄存器搬移
MOV r0, r2
―― r0 := r2
MVN r0, r2
―― r0 := not r2
?。?)比較
CMP r1, r2
―― set cc on r1 - r2
CMN r1, r2
―― set cc on r1 + r2
TST r1, r2
―― set cc on r1 and r2
TEQ r1, r2
―― set cc on r1 or r2
這些指令影響CPSR寄存器中的 (N, Z, C, V) 位
(5)內(nèi)存操作
LDR r0, [r1]
―― r0 := mem [r1]
STR r0, [r1]
―― mem [r1] := r0
LDR r0, [r1, #4]
―― r0 := mem [r1+4]
LDR r0, [r1, #4] !
―― r0 := mem [r1+4] r1 := r1 + 4
LDR r0, [r1], #4
―― r0 := mem [r1] r1 := r1 +4
LDRB r0 , [r1]
―― r0 := mem8 [r1]
LDMIA r1, {r0, r2, r5}
―― r0 := mem [r1] r2 := mem [r1+4] r5 := mem [r1+8]
{..} 可以包括r0~r15中的所有寄存器,若包括r15 (PC)將導(dǎo)致程序的跳轉(zhuǎn)。
(6)控制流
例1:
MOV r0, #0 ; initialize counter
LOOP:
ADD r0, r0, #1 ; increment counter
CMP r0, #10 ; compare with limit
BNE LOOP ; repeat if not equal
例2:
CMP r0, #5
ADDNE r1, r1, r0
SUBNE r1, r1, r2
――
if (r0 != 5) {
r1 := r1 + r0 - r2
}
3.BootLoader第一階段
3.1硬件設(shè)備初始化
基本的硬件初始化工作包括:
屏蔽所有的中斷;
設(shè)置CPU的速度和時鐘頻率;
RAM初始化;
初始化LED
ARM的中斷向量表設(shè)置在0地址開始的8個字空間中,如下表:
每當(dāng)其中的某個異常發(fā)生后即將PC值置到相應(yīng)的中斷向量處,每個中斷向量處放置一個跳轉(zhuǎn)指令到相應(yīng)的中斷服務(wù)程序去進(jìn)行處理,中斷向量表的程序如下:
@ 0x00: Reset
b Reset
@ 0x04: Undefined instruction exception
UndefEntryPoint:
b HandleUndef
@ 0x08: Software interrupt exception
SWIEntryPoint:
b HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort)
PrefetchAbortEnteryPoint:
b HandlePrefetchAbort
@ 0x10: Data Access Memory Abort
DataAbortEntryPoint:
b HandleDataAbort
@ 0x14: Not used
NotUsedEntryPoint:
b HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception
IRQEntryPoint:
b HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception
FIQEntryPoint:
b HandleFIQ
復(fù)位時關(guān)閉看門狗定時器、屏蔽所有中斷:
Reset:
@ disable watch dog timer
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1]
@ disable all interrupts
mov r1, #INT_CTL_BASE
mov r2, #0xffffffff
str r2, [r1, #oINTMSK]
ldr r2, =0x7ff
str r2, [r1, #oINTSUBMSK]
設(shè)置系統(tǒng)時鐘:
@init clk
@ 1:2:4
mov r1, #CLK_CTL_BASE
mov r2, #0x3
str r2, [r1, #oCLKDIVN]
mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
orr r1, r1, #0xc0000000 @ Asynchronous
mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
@ now, CPU clock is 200 Mhz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_200mhz
str r2, [r1, #oMPLLCON]
點亮所有的用戶LED:
@ All LED on
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F
ldr r2,=0x55aa
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
mov r2, #0x00
str r2, [r1, #oGPIO_DAT]
設(shè)置(初始化)內(nèi)存映射:
ENTRY(memsetup)
@ initialise the static memory
@ set memory control registers
mov r1, #MEM_CTL_BASE
adrl r2, mem_cfg_val
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
mov pc, lr
設(shè)置(初始化)UART:
@ set GPIO for UART
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_H
ldr r2, gpio_con_uart
str r2, [r1, #oGPIO_CON]
ldr r2, gpio_up_uart
str r2, [r1, #oGPIO_UP]
bl InitUART
@ Initialize UART
@
@ r0 = number of UART port
InitUART:
ldr r1, SerBase
mov r2, #0x0
str r2, [r1, #oUFCON]
str r2, [r1, #oUMCON]
mov r2, #0x3
str r2, [r1, #oULCON]
ldr r2, =0x245
str r2, [r1, #oUCON]
#define UART_BRD ((50000000 / (UART_BAUD_RATE * 16)) - 1)
mov r2, #UART_BRD
str r2, [r1, #oUBRDIV]
mov r3, #100
mov r2, #0x0
1: sub r3, r3, #0x1
tst r2, r3
bne 1b
#if 0
mov r2, #'U'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
mov r2, #'0'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
#endif
mov pc, lr
此外,vivi還提供了幾個匯編情況下通過串口打印字符的函數(shù)PrintChar、PrintWord和PrintHexWord:
@ PrintChar : prints the character in R0
@ r0 contains the character
@ r1 contains base of serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintChar:
TXBusy:
ldr r2, [r1, #oUTRSTAT]
and r2, r2, #UTRSTAT_TX_EMPTY
tst r2, #UTRSTAT_TX_EMPTY
beq TXBusy
str r0, [r1, #oUTXHL]
mov pc, lr
@ PrintWord : prints the 4 characters in R0
@ r0 contains the binary word
@ r1 contains the base of the serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintWord:
mov r3, r0
mov r4, lr
bl PrintChar
mov r0, r3, LSR #8 /* shift word right 8 bits */
bl PrintChar
mov r0, r3, LSR #16 /* shift word right 16 bits */
bl PrintChar
mov r0, r3, LSR #24 /* shift word right 24 bits */
bl PrintChar
mov r0, #'r'
bl PrintChar
mov r0, #'n'
bl PrintChar
mov pc, r4
@ PrintHexWord : prints the 4 bytes in R0 as 8 hex ascii characters
@ followed by a newline
@ r0 contains the binary word
@ r1 contains the base of the serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintHexWord:
mov r4, lr
mov r3, r0
mov r0, r3, LSR #28
bl PrintHexNibble
mov r0, r3, LSR #24
bl PrintHexNibble
mov r0, r3, LSR #20
bl PrintHexNibble
mov r0, r3, LSR #16
bl PrintHexNibble
mov r0, r3, LSR #12
bl PrintHexNibble
mov r0, r3, LSR #8
bl PrintHexNibble
mov r0, r3, LSR #4
bl PrintHexNibble
mov r0, r3
bl PrintHexNibble
mov r0, #'r'
bl PrintChar
mov r0, #'n'
bl PrintChar
mov pc, r4
3.2Bootloader拷貝
配置為從NAND FLASH啟動,需要將NAND FLASH中的vivi代碼copy到RAM中:
#ifdef CONFIG_S3C2410_NAND_BOOT
bl copy_myself
@ jump to ram
ldr r1, =on_the_ram
add pc, r1, #0
nop
nop
1: b 1b @ infinite loop
#ifdef CONFIG_S3C2410_NAND_BOOT
@
@ copy_myself: copy vivi to ram
@
copy_myself:
mov r10, lr
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
1: add r3, r3, #0x1
cmp r3, #0xa
blt 1b
2: ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq 2b
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]