1. 程式人生 > >uboot移植之啟動過程詳解1

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

     轉載需備註來源