Bootloader(Vivi)源代碼分析
----基于S 3C 2410處理器
作者:forkchen
Email:[email protected]
QQ:56155986
歡迎廣大嵌入式之友多多交流
目 錄
1.1 Vivi簡介... 2
1.2 vivi的配置與編譯... 3
1.2.1 建立交叉開發環境... 3
1.2.2 配置和編譯vivi 4
1.3 vivi代碼分析... 4
1.4 vivi的運作... 5
1.4.1 vivi的第一階段... 5
1.4.2 vivi的第二階段... 15
1.5 啟動代碼執行流程圖... 17
1.6 vivi的配置檔案... 19
1.1 Vivi簡介
Vivi 是南韓mizi 公司開發的bootloader, 适用于ARM9處理器。 Vivi有兩種工作模式:啟動加載模式和下載下傳模式。啟動加載模式可以在一段時間後(這個時間可更改)自行啟動linux核心,這時vivi的預設模式。在下載下傳模式下,vivi為使用者提供一個指令行接口,通過接口可以使用vivi提供的一些指令,見下表:
指令 | 功能 |
Load | 把二進制檔案載入Flash或RAM |
Part | 操作MTD分區資訊。顯示、增加、删除、複位、儲存MTD分區 |
Param | 設定參數 |
Boot | 啟動系統 |
Flash | 管理Flash,如删除Flash的資料 |
1.2 vivi的配置與編譯
1.2.1 建立交叉開發環境
1、在主控端上安裝标準Linux 作業系統:Redhat 9.0 ( 主機系統為win2000,用虛拟機vmware安裝的Redhat 9.0,核心版本為 2.4.18 ) 。
2、主控端上安裝交叉編譯器。
我這邊的2410開發闆提供的CD光牒上已附交叉編譯器工具:arm-linux-gcc-2.95.3(源碼為cross-2.95.3.tar.bz2)。
先以root 使用者的身份登陸到linux 下。
進入/usr/local 目錄,建立名為arm的目錄:
cd /usr/local
mkdir arm
将CD光牒提供的cross-2.95.3.tar.bz2解壓到/usr/local/arm目錄:
tar jxvf cross-2.95.3.tar.bz2 –C /usr/local/arm
然後修改修改PATH 變量:為了可以友善使用arm-linux-gcc編譯器系統, 把arm-linux工具鍊目錄加入到環境變量PATH中:
修改/etc/profile檔案,添加pathmunge /usr/local/arm/2.95.3/bin即可。
# Path manipulation
if [ `id -u` = 0 ]; then
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local/sbin
pathmunge /usr/local/arm/2.95.3/bin
fi
pathmunge /usr/X11R6/bin after
設定環境變量後,最好是重新開機或登出一下,這樣設定的環境變量才能生效。
1.2.2 配置和編譯vivi
如果vivi的源代碼已根據開發闆作了相應改動,則需要對源代碼進行配置和編譯,以生成燒入flash的vivi 二進制映象檔案。
由于vivi要用到kernel的一些頭檔案,是以需要kernel的源代碼,是以先要把linux的kernel準備好。将vivi和kernel都解到相應目錄下(例如我将CD光牒提供的vivi源代碼解壓到/home/chenjun目錄下,CD光牒提供的Linux kernel源碼kernel-h2410eb.041024.tar.gz也解壓到/home/chenjun目錄下,解壓後的檔案名為kerne-h2410eb)。
然後需修改/vivi/Makefile裡的一些變量設定:
Ø LINUX_INCLUDE_DIR = /kernel/include/
(LINUX_INCLUDE_DIR 為kernel/include的對應目錄,我的是/home/chen/kerne-h2410eb /include/)
是以修改為:
LINUX_INCLUDE_DIR = /home/chenjun/ kerne-h2410eb/include/
Ø CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
(CROSS_COMPILE 為arm-linux安裝的相應目錄,我的是/usr/local/arm/2.95.3/bin/arm-linux-)
是以修改為:
CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
Ø ARM_GCC_LIBS = /usr/local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.3
(需根據你arm-linux的安裝目錄修改,我的是/usr/local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.3)
進入/vivi目錄執行make distclean。(目的是確定編譯的有效性,在編譯之前将vivi裡所有的“*.o”和“*.o.flag”檔案删掉)
進入/vivi目錄裡,輸入“make menuconfig”,開始選擇配置。可以Load一個寫好的配置檔案也可以自己修改試試。注意Exit時一定要選“Yes”儲存配置。
再輸入“make”正式開始編譯,一會兒就完了。如果不報錯,在/vivi裡面就有你自己的“vivi”了。這個就是後面要燒寫到flash中的bootloader。
mvn r2, #0xff000000
str r2, [r1, #oLOCKTIME]
@ldr r2, mpll_50mhz
@str r2, [r1, #oMPLLCON]
#ifndef CONFIG_S 3C 2410_MPORT1 ;滿足條件,向下執行
@ 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 ;CPU的頻率是200MHz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_200mhz
str r2, [r1, #oMPLLCON]
#else
@ 1:2:2
mov r1, #CLK_CTL_BASE
ldr r2, clock_clkdivn
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 100 Mhz ;CPU的頻率是100MHz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_100mhz
str r2, [r1, #oMPLLCON]
#endif
bl memsetup ;跳轉到memsetup函數
.long vBANKCON0
.long vBANKCON1
.long vBANKCON2
.long vBANKCON3
.long vBANKCON4
.long vBANKCON5
.long vBANKCON6
.long vBANKCON7
.long vREFRESH
.long vBANKSIZE
.long vMRSRB6
.long vMRSRB7
********************/
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b ;循環操作,直到13個寄存器指派完成
mov pc, lr
*******************************/
#ifdef CONFIG_PM ;vivi考慮不需要使用電源管理
@ Check if this is a wake-up from sleep
ldr r1, PMST_ADDR
ldr r0, [r1]
tst r0, #(PMST_SMR)
bne WakeupStart ;檢視狀态,判斷是否需要跳轉到WakeupStart
#endif
#ifdef CONFIG_S 3C 2410_SMDK ;SMDK開發闆使用
@ All LED on ;點亮開發闆上的LED
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F ;LED使用GPIOF組的管腳
ldr r2,=0x55aa ;使能EINT0,EINT1,EINT2,EINT3,
;另四個管腳配置成輸出,屏蔽EINT4,5,6,7
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP] ;disable the pull-up function
mov r2, #0x00
str r2, [r1, #oGPIO_DAT]
#endif
#if 0
@ SVC
mrs r0, cpsr
bic r0, r0, #0xdf
orr r1, r0, #0xd3
msr cpsr_all, r1
#endif
@ set GPIO for UART ;設定序列槽
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_H ;設定GPIO_H組管腳為序列槽
ldr r2, gpio_con_uart
str r2, [r1, #oGPIO_CON]
ldr r2, gpio_up_uart
str r2, [r1, #oGPIO_UP]
gpio_up_uart:
.long Vgphup ;同上#define vGPHUP 0x000007ff
;The pull-up function is disabled.
************************/
bl InitUART ;跳轉到InitUART序列槽初始化函數
/****************************************************
@ Initialize UART
@
@ r0 = number of UART port
InitUART:
ldr r1, SerBase
/*******************
.align 4 ;預設情況下在vivi中隻初始化了UART0
SerBase:
#if defined(CONFIG_SERIAL_UART0)
.long UART0_CTL_BASE ;基位址在/vivi/include/s
3c
2410.h中定義
#elif defined(CONFIG_SERIAL_UART1)
.long UART1_CTL_BASE
#elif defined(CONFIG_SERIAL_UART2)
.long UART2_CTL_BASE
#else
#error not defined base address of serial
#endif
********************/
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
****************************************************/
#ifdef CONFIG_DEBUG_LL ;列印調試資訊,預設未定義
@ Print current Program Counter
ldr r1, SerBase
mov r0, #'/r'
bl PrintChar
mov r0, #'/n'
bl PrintChar
mov r0, #'@'
bl PrintChar
mov r0, pc
bl PrintHexWord
#endif
#ifdef CONFIG_BOOTUP_MEMTEST
@ simple memory test to find some DRAM flaults.
bl memtest
#endif
#ifdef CONFIG_S
3C
2410_NAND_BOOT ;從NAND Flash啟動
bl copy_myself ;跳轉到copy_myself函數
/**********************************************
@
@ 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]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy vivi to RAM
ldr r0, =VIVI_RAM_BASE
/*********在/vivi/linux/platform/smdk2410.h中定義
#define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
***************************************/
mov r1, #0x0
mov r2, #0x20000 ;0x20000-〉128k位元組
bl nand_read_ll ;nand_read_ll在/vivi/arch/s
3c
2410/nand_read.c中定義
;r0,r1,r2分别為函數的三個參數
;從NANDFlash的0位址拷貝128k到SDRAM指定處
tst r0, #0x0
beq ok_nand_read
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
1: b 1b @ infinite loop
#endif
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
@ verify
mov r0, #0
ldr r1, =0x
33f
00000
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq done_nand_read
bne go_next
notmatch:
#ifdef CONFIG_DEBUG_LL
sub r0, r0, #4
ldr r1, SerBase
bl PrintHexWord
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
#endif
1: b 1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
mov pc, r10 ;vivi拷貝到SDRAM完成,函數傳回
*********************************/
@ jump to ram
ldr r1, =on_the_ram
add pc, r1, #0
nop
nop
1: b 1b @ infinite loop
on_the_ram:
#endif
#ifdef CONFIG_DEBUG_LL
ldr r1, SerBase
ldr r0, STR_STACK
bl PrintWord
ldr r0, DW_STACK_START
bl PrintHexWord
#endif
@ get read to call C functions
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
mov a2, #0 @ set argv to NULL
bl main @ call main
mov pc, #FLASH_BASE @ otherwise, reboot
@
@ End VIVI head
@
1.4.2 vivi的第二階段
vivi的第二階段是從main()函數開始,同一般的C語言程式一樣,該函數在/init/main.c檔案中,總共可以分為8個步驟。
(1) 函數開始,通過putstr(vivi_banner)列印出vivi的版本。Vivi_banner在/init/version.c檔案中定義
(2) 對開發闆進行初始化(board_init函數),board_init是與開發闆緊密相關的,這個函數在/arch/s
3c
2410/smdk.c檔案中。開發闆初始化主要完成兩個功能,時鐘初始化(init_time())和通用IO口設定(set_gpios())。
void set_gpios(void)
{
GPACON = vGPACON;
GPBCON = vGPBCON;
GPBUP = vGPBUP;
GPCCON = vGPCCON;
GPCUP = vGPCUP;
GPDCON = vGPDCON;
GPDUP = vGPDUP;
GPECON = vGPECON;
GPEUP = vGPEUP;
GPFCON = vGPFCON;
GPFUP = vGPFUP;
GPGCON = vGPGCON;
GPGUP = vGPGUP;
GPHCON = vGPHCON;
GPHUP = vGPHUP;
EXTINT0 = vEXTINT0;
EXTINT1 = vEXTINT1;
EXTINT2 = vEXTINT2;
}
其中,GPIO口在smdk2410.h(/vivi/include/platform/目錄下)檔案中定義。
(3) 記憶體映射初始化和記憶體管理單元的初始化工作:
mem_map_init();
mmu_init();
這兩個函數都在/arch/s
3c
2410/mmu.c檔案中。
void mem_map_init(void)
{
#ifdef CONFIG_S
3C
2410_NAND_BOOT
mem_map_nand_boot();
#else
mem_map_nor();
#endif
cache_clean_invalidate();
tlb_invalidate();
}
如果配置vivi時使用了NAND作為啟動裝置,則執行mem_map_nand_boot(),否則執行mem_map_nor()。這裡要注意的是,如果使用NOR啟動,則必須先把vivi代碼複制到RAM中。這個過程是由copy_vivi_to_ram()函數來完成的。代碼如下:
static void copy_vivi_to_ram(void)
{
putstr_hex("Evacuating 1MB of Flash to DRAM at 0x", VIVI_RAM_BASE);
memcpy((void *)VIVI_RAM_BASE, (void *)VIVI_ROM_BASE, VIVI_RAM_SIZE);
}
VIVI_RAM_BASE、VIVI_ROM_BASE、VIVI_RAM_SIZE這些值都可以在smdk2410.h中查到,并且這些值必須根據自己開發闆的RAM實際大小修改。這也是在移植vivi的過程中需要注意的一個地方。
mmu_init()函數中執行了arm920_setup函數。這段代碼是用彙編語言實作的,針對arm920t核的處理器。
(4) 初始化堆棧,heap_init()。(定義在/vivi/lib/heap.c檔案中)
int heap_init(void)
{
return mmalloc_init((unsigned char *)(HEAP_BASE), HEAP_SIZE);
}
(5) 初始化mtd裝置,mtd_dev_init()。
int mtd_init(void)
{
int ret;
#ifdef CONFIG_MTD_CFI
ret = cfi_init();
#endif
#ifdef CONFIG_MTD_SMC
ret = smc_init();
#endif
#ifdef CONFIG_S
3C
2410_AMD_BOOT
ret = amd_init();
#endif
if (ret) {
mymtd = NULL;
return ret;
}
return 0;
}
這幾個函數可以在/drivers/mtd/maps/s
3c
2410_flash.c裡找到。
(6) 初始化私有資料,init_priv_data()。(定義在/vivi/lib/priv_data/rw.c檔案中)
(7) 初始化内置指令,init_builtin_cmds()。
通過add_command函數,加載vivi内置的幾個指令。
(8) 啟動boot_or_vivi()。
啟動成功後,将通過vivi_shell()啟動一個shell(如果配置了CONFIG_SERIAL_TERM),此時vivi的任務完成。
1.5 啟動代碼執行流程圖
(1)head.s代碼執行流程 (2)main.c代碼執行流程
1.6 vivi的配置檔案
Vivi的初始配置檔案位置:/vivi/arch/def-configs/smkd2410, 通過make menuconfig 修改後的配置儲存在這個檔案中,我們也可以載入一個自己的配置檔案來進行編譯。