天天看點

Bootloader(Vivi)源代碼分析1.1 Vivi簡介1.2 vivi的配置與編譯1.5 啟動代碼執行流程圖1.6 vivi的配置檔案

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 修改後的配置儲存在這個檔案中,我們也可以載入一個自己的配置檔案來進行編譯。

繼續閱讀