自己動手寫bootloader

                                            自己動手寫bootloader

    爲了寫一個bootloader讓板子跑起來,首先我們要知道bootloader是個什麼東東才行。簡單的說,bootloader就是一個引導內核啓動的一段小代碼,也就是說當啓動完內核之後,它的使命就已經結束。

bootloader生命週期:初始化硬件==》設置啓動參數==》跳到Linux內核的首地址==》消亡。那麼它是怎麼啓動內核的呢或者說在啓動內核之前它究竟完成了哪些工作呢?

寫代碼之前還是先了解下內核啓動的硬性條件:

    1.CPU寄存器的設置: r0=0,r1=機器類型ID(linux/arch/arm/tools/mach-types),r2=啓動參數標記列表在RAM中的基地址。

    2.CPU工作模式:設置爲管理模式(因爲SUV模式的權限最大),且必須禁止中斷IRQ、FIQ。

    3.cache和MMU的設置:MMU必須關閉,數據cache必須關閉,指令cache可以開啓可以關閉,不過建議開啓,因爲可以加快bootloader的運行速度

    bootloader的工作分爲兩個階段,由彙編語言和C語言各自掌管:

    第一階段完成功能(彙編語言):包括模式設置,關閉看門狗(防止系統定時重啓),關閉中斷,關cache,設置時鐘頻率,初始化硬件(nand flash和sdram),初始化堆棧(因爲要進入到C語言中執行),跳轉到第二階段代碼的C入口點。

    第二階段完成功能(C語言):重定位從flash裏把程序本身複製到sdram裏去,清bss段,爲內核設置啓動參數,調用內核。

註釋:bootloader的代碼分爲前4K和4K後,bootloader運行時前4K代碼會由硬件自動的拷貝到sram中,然後前4K代碼需要將bootloader拷貝到sdram中運行(拷貝4K後的代碼還是全部分具體情況而定),因爲nand flash是不能直接運行代碼的。

完成一個簡單的具有基本功能的bootloader

start.S(bootloader入口)
.globl _start
_start:
    /*1.1 設置CPU的工作模式爲SVC*/
    mrs r0 , cpsr
    bic r0 , r0 , #0x1f
    orr r0 , r0 , #0xd3 
    msr cpsr , r0
    /*1.2 關閉看門狗*/
    ldr r0, =0x53000000
    ldr r1, =0
    str r1, [r0]
    /*1.3 關閉中斷*/
    ldr r0 , =0x4a000008
    ldr r1 , =0xffffffff
    str r1 , [r0]
    ldr r0 , =0x4a00001c
    ldr r1 , =0x7fff
    str r1 , [r0] 
    /*1.4使能icache 關閉data cache*/
    mrc p15, 0, r2, c1, c0, 0
    orr r2, r2, #(1<<12)
    mcr p15, 0, r2, c1, c0, 0
    /*1.5禁用MMU和緩存*/
    mrc p15,0,r0,c1,c0,0
    bic r0,r0,#0x00002300 @clear bits 13,9:8(--V- --RS)
    bic r0,r0,#0x00000087 @clear bits 7,2:0(B--- -CAM)
    orr r0,r0,#0x00000002 @set bit 1(A)Align
    orr r0,r0,#0x00001000 @set bit 12(I)I-Cache
    mcr p15,0,r0,c1,c0,0
    /*1.6 設置堆棧.判斷是nor啓動還是nand啓動初始化sp指針*/
    /*判斷原理:
     * 無論是從NOR Flash還是從NAND Flash啓動,
     * 地址0處爲指令"b Reset", 機器碼爲0xEA00000B,
     * 對於從NAND Flash啓動的情況,其開始4KB的代碼會複製到CPU內部4K內存中,即SRAM
     * 因此在NAND啓動時往裏寫數據的時候實際上就是寫SRAM,而SRAM是直接可讀可寫的      
     * 對於從NOR Flash啓動的情況,NOR Flash的開始地址即爲0。
     * 對於NOR Flash,必須通過一定的命令序列才能寫數據,

     * 所以可以根據這點差別來分辨是從NAND Flash還是NOR Flash啓動:
     * 向地址0寫入一個數據,然後讀出來,如果沒有改變的話就是Nand Flash
     */
    /* NAND啓動時,片內內存從0開始, sp=4096 */
    /* NOR啓動時,片內內存從0x40000000開始, sp=0x40000000+4096 */
 
    ldr r1 ,=0 
    str r1, [r1] /* 在0地址寫入0 */
    ldr r1, [r1] /* 從0地址讀出數據 */
    cmp r1, #0
    ldreq sp, =4096
    ldrne sp, =(0x40000000+4096)
 
    /***************************詳見附件************************************************/
    *1.7 時鐘初始化,主要是MPLLCON、CLKDIVN寄存器的設置,特別注意將總線模式改爲異步總線模式*/
    bl clock_init
    /*1.8. 初始化SDRAM,對於13個寄存器的設置 */
    bl sdram_init
    /*1.9. 初始化nand*/
    bl nand_init
    /***************************詳見附件************************************************/

    /* 2.1 重定位: 從flash裏把程序本身複製到sdram裏去 */
    /*重定位(Relocate)的概念:調試u-boot時,開始一般是讓它在RAM中運行,當RAM運行通過後纔將其固化到FLASH中;這樣如果我們當前的代碼是通過BDI2000等Load到內存直接運行的話,
    u-boot就不需要去將自己從FLASH搬移到內存了;而如果u-boot是固化在FLASH中在CPU復位後由第一個片選信號指向開始執行的話,則又一個從FLASH搬移到內存的過程。*/
    /*拷貝函數*/
    bl CopyCode2Ram
    /* 2.2. 清bss段(初始值爲0、無初始值的全局變量、靜態變量放在BSS段 ),意味着給這些變量開闢空間,因爲bootloader是不給bss段分配空間的*/
    ldr r0,= __bss_start
    ldr r1, =__bss_end
    cmp r0, r1
    beq setup_stack
    mov r2, #0
    clear_loop:
    str r2, [r0], #4
    cmp r0, r1
    bne clear_loop
    /* 2.3調用c函數 */
 

relocate: /* relocate U-Boot to RAM */ 
    mov r0, #0
    ldr r1, =__start
    ldr r2, =__bss_start
    sub r2, r2, r1
    setup_stack:
     /* 前面已經設置sp, 這裏不用再設置 */
    ldr pc, =main /* 跳到sdram裏去 */
main.c
int main(void)
{
 void (*theKernel)(int zero, int machine_id, int params_addr);
 /*初始化串口: 內核啓動時會用到串口輸出一些信息 */
 uart_init();
 puts("============BOOT for S3C2440=============\n\rCopy kernel for flash to sdram ...\n\r");
 /*從NAND FLASH裏把內核讀到SDRAM裏去 */
 nand_read(0x30108000, 0x60000, 0x300000);
 puts("OK\n\r"); 
 /*設置參數 */ 
 puts("Set parametes ...");
 set_params();
 puts("OK\n\r"); 
 /*啓動內核 */ 
 puts("Boot kernel ...\n\r");
 theKernel = 0x30108040;
 theKernel(0, 362, 0x30000100);/*帶三個參數的指向內核首地址的指針*/ 
 puts("\nerror!\n\r");
 return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章