Beagleboneblack的MLO文件幹了些啥

Beagleboneblack在啓動linux以前還有三個啓動階段:linux

ROM code  -->  MLO  -->  u-boot --> kernel緩存

先看看ROM code幹了些什麼

ROM code是TI固化在芯片內部的,處理器上電以後會先跑到這裏執行一部分代碼,看看這部分代碼在哪:less

Boot ROM一共有128K+48K,其中後48K具備可讀可執行屬性,那估計就應該在0x4002_0000的地方了.這48K的空間又被分紅了這樣子:函數

不過程序不該該是在RAM裏運行的嗎,怎麼能夠在ROM裏運行呢.回想之前用STM32時,程序在靜態時確定被燒寫到內部的Flash上了,上電以後也是直接從Flash裏面執行代碼,以後也只是把部分代碼分散加載到RAM裏,那這樣說來程序也不是在RAM裏運行的.spa

這個問題讓我很困惑,我只能這麼解釋:若是CPU能直接取到指令,那它就能譯碼,執行對於這些芯片內部的Flash也好,ROM也好,RAM也好,這些存儲器都是直接掛在CPU三總線上的,CPU能夠直接從這些地方取到指令,而後執行,只是有的存儲器不能寫.而對於有些存儲器,如EEPROM,SD卡,它們通常並不直接掛在總線上,中間還要有控制器和相應的驅動時序,因此CPU不能直接取到指令,程序也就不能在那裏面運行.若是我說的不對,網友們必定予以指正.debug

回到這個ROM code上來,看看它幹了些什麼:3d

它說上電以後CPU執行公共端的初始化和堆棧設置,而後配置WDT1爲3分鐘,執行系統時鐘配置,最終跳到booting的處理程序中.code

首先根據軟件配置或者SYSBOOY引腳生成啓動設備列表,若是不按卡旁邊的按鈕,啓動順序是MMC1(eMMC) -> MMC0 -> UART0 -> USB0,按下則爲SPI0 -> MMC0(SD card) -> USB0 -> UART0,而後根據設備類型執行存儲器啓動或者是外設啓動初始化,假如咱們從SD卡啓動,orm

初始化MMC/SD控制器 -> 檢測設備 -> 判斷啓動模式 -> 獲取啓動文件,即找到SD卡上的MLO文件,而後加載到SRAM去,同時跳到MLO程序中.至於它是怎麼加載的,想必是讀取MLO文件頭信息,經過目的地址和文件大小複製過去的吧.blog

因此ROM code就作了這麼些事情,那MLO程序從哪一個地方開始執行呢,前面說過MLO文件的編譯過程,在編譯SPL程序時連接器的腳本是u-boot-spl.lds

MEMORY { .sram : ORIGIN = 0x402F0400, LENGTH = (0x4030B800 - 0x402F0400) }

其中給出了SRAM的起始地址和大小(109K),並且在makefile的配置文件autoconf.mk中,也給出了基地址:

CONFIG_SPL_TEXT_BASE=0x402F0400

因此MLO程序就從0x4020F0400的地方開始執行,再看看SRAM的內存分佈:

109K的Image空間,6K的stack空間,上面還有xxxx,個人u-boot(2014.04)在編譯以後MLO有76.5K,仍是裝得下的,u-boot.img有329K,天然就裝不下的,因此纔要MLO來引導u-boot.

接下來看看MLO程序幹了些什麼:

從start.S開始,位於arch/arm/cpu/armv7下,首先跳到reset:

_start: b    reset

跳到save_boot_params:

reset:
    bl    save_boot_params

save_boot_params具備weak屬性,在start.S中有定義,若是其餘地方沒有定義的話,它就直接返回,因爲這裏是SPL程序,想必仍是要save一下的,實際上在arch/arm/cpu/armv7/am33xx/lowlevel_init.S中有它的定義:

ENTRY(save_boot_params)
    ldr    r1, =OMAP_SRAM_SCRATCH_BOOT_PARAMS
    str    r0, [r1]
    bx    lr
ENDPROC(save_boot_params)

將R0的值保存在這個地方OMAP_SRAM_SCRATCH_BOOT_PARAMS,即0x4030B824處,位於6K的stack空間中,R0是什麼東西呢:

R0保存了Booting Parameters Structure這樣一個結構體的地址,包括啓動設備描述符,當前啓動設備,重啓緣由這些,這個結構體在哪,啓動設備描述符又是個什麼東西我也不知道了.

  回到start.S中,接着關中斷,切換值SVC模式:

複製代碼
    mrs    r0, cpsr
    and    r1, r0, #0x1f        @ mask mode bits
    teq    r1, #0x1a        @ test for HYP mode
    bicne    r0, r0, #0x1f        @ clear all mode bits
    orrne    r0, r0, #0x13        @ set SVC mode
    orr    r0, r0, #0xc0        @ disable FIQ and IRQ
    msr    cpsr,r0
複製代碼

設置中斷向量表:

複製代碼
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
    /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
    mrc    p15, 0, r0, c1, c0, 0    @ Read CP15 SCTRL Register
    bic    r0, #CR_V        @ V = 0
    mcr    p15, 0, r0, c1, c0, 0    @ Write CP15 SCTRL Register

    /* Set vector address in CP15 VBAR register */
    ldr    r0, =_start
    mcr    p15, 0, r0, c12, c0, 0    @Set VBAR
#endif
複製代碼

底層初始化:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl    cpu_init_cp15
    bl    cpu_init_crit
#endif

在編譯SPL程序時,沒有定義CONFIG_SKIP_LOWLEVEL_INIT這個宏,因此下面兩個函數會執行,而編譯u-boot時,會跳過,這也比較合理,只須要初始化一次嘛.

  cpu_init_cp15主要是操做CP15寄存器,關掉緩存,關掉MMU,而後跳到cpu_init_crit:

複製代碼
ENTRY(cpu_init_crit)
    /*
     * Jump to board specific initialization...
     * The Mask ROM will have already initialized
     * basic memory. Go here to bump up clock rate and handle
     * wake up conditions.
     */
    b    lowlevel_init        @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
複製代碼

跳到lowlevel_init中,這裏是"b"指令跳轉,不會改變lr寄存器的值:

複製代碼
ENTRY(lowlevel_init)
    /*
     * Setup a temporary stack
     */
    ldr    sp, =CONFIG_SYS_INIT_SP_ADDR
    bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_BUILD
    ldr    r9, =gdata
#else
    sub    sp, sp, #GD_SIZE
    bic    sp, sp, #7
    mov    r9, sp
#endif
    /*
     * Save the old lr(passed in ip) and the current lr to stack
     */
    push    {ip, lr}

    /*
     * go setup pll, mux, memory
     */
    bl    s_init
    pop    {ip, pc}
ENDPROC(lowlevel_init)
複製代碼

設置sp=0x4030FF40,即將運行C代碼,讓R9指向gdata,gdata是寄存器變量,在arch/arm/include/asm/global_data.h中聲明,定義在arch/arm/lib/spl.c中,位於data段

#define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("r9")

而後講ip,lr寄存器入棧跳到s_init中,位於arch/arm/cpu/am33xx/board.c中,主要調用函數以下:

-> save_omap_boot_params();    
-> watchdog_disable();       
-> timer_init();          
-> set_uart_mux_conf();      
-> setup_clocks_for_console();  
-> uart_soft_reset();       

-> gd = &gdata;
-> preloader_console_init();

-> prcm_init();
-> set_mux_conf_regs();

-> rtc32k_enable();

-> sdram_init();

這些函數就再也不一一展開了,由於展開看了一下發現很難看懂,C語言功底遠遠不夠(⊙﹏⊙)b

值得注意的是save_omap_boot_params函數,它將前面說的R0所保存的啓動信息保存到gdata結構體中,以加載u-boot鏡像文件.

preloader_console_init函數,位於common/spl/spl.c中,初始化了一個串口,輸出了第一行信息,即u-boot版本號,編譯時間

複製代碼
void preloader_console_init(void)  
{  
    gd->bd = &bdata;  
    gd->baudrate = CONFIG_BAUDRATE;  
  
    serial_init();      /* serial communications setup */  
  
    gd->have_console = 1;  
  
    puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \  
            U_BOOT_TIME ")\n");//wlg: now we print our first information  
#ifdef CONFIG_SPL_DISPLAY_PRINT  
    spl_display_print();  
#endif  
}
複製代碼

最後將DRAM初始化,便於加載u-boot到DRAM中.

以後返回到lowlevel_init.S中,

push {ip, lr}

pop {ip, pc}

將以前的lr彈給pc,也就是跳到前一個bl的下面,也就是start.S中:

    bl    _main

繼續跳到_main中,位於arch/arm/lib/crt0.S中:

複製代碼
ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr    sp, =(CONFIG_SPL_STACK)
#else
    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
    bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    sub    sp, sp, #GD_SIZE    /* allocate one GD above SP */
    bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    mov    r9, sp        /* GD is above SP */
    mov    r0, #0
    bl    board_init_f
複製代碼

再次賦值sp,仍然指向0x4030FF40,而後向下留出gd結構體的空間,並讓R9指向這裏,跳到board_init_f,位於arch/arm/lib/spl.c中,具備weak屬性,在u-boot階段有不一樣的實現:

複製代碼
void __weak board_init_f(ulong dummy)
{
    /* Clear the BSS. */
    memset(__bss_start, 0, __bss_end - __bss_start);

    /* Set global data pointer. */
    gd = &gdata;

    board_init_r(NULL, 0);
}
複製代碼

清BSS段,再次讓gd,即R9指向gdata,跳到board_init_r,位於common/spl/spl.c中,主要執行函數以下:

-> spl_board_init();

-> boot_device = spl_boot_device();

根據boot_device的值在相應的地方加載鏡像文件,好比是MMC/SD的話,將執行spl_mmc_load_image();位於common/spl/spl_mmc.c中,主要執行函數以下:

-> mmc_initialize(gd->bd);

-> mmc = find_mmc_device(0);

-> err = mmc_init(mmc);

-> boot_mode = spl_boot_mode();

根據boot_mode的值使用不一樣的方式讀取鏡像,好比是FAT模式,將執行一下分支:

複製代碼
else if (boot_mode == MMCSD_MODE_FAT) {
        debug("boot mode - FAT\n");
#ifdef CONFIG_SPL_OS_BOOT
        if (spl_start_uboot() || spl_load_image_fat_os(&mmc->block_dev,
                                CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION))
#endif
        err = spl_load_image_fat(&mmc->block_dev,
                    CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION,
                    CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME);
#endif
複製代碼

spl_load_image_fat這個函數將u-boot.img文件讀到它該去的地方,即DRAM中,具體是怎麼實現的我也不太懂了,執行完畢以後回到spl.c中,根據鏡像文件的類型執跳到u-boot中或者linux中:

複製代碼
    switch (spl_image.os) {
    case IH_OS_U_BOOT:
        debug("Jumping to U-Boot\n");
        break;
#ifdef CONFIG_SPL_OS_BOOT
    case IH_OS_LINUX:
        debug("Jumping to Linux\n");
        spl_board_prepare_for_linux();
        jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
    default:
        debug("Unsupported OS image.. Jumping nevertheless..\n");
    }
    jump_to_image_no_args(&spl_image);
複製代碼

SPL程序天然是跳到u-boot中了,執行下面的jump_to_image_no_args(&spl_image),位於arch/arm/cpu/armv7/omap-common/boot-common中:

複製代碼
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
{
    typedef void __noreturn (*image_entry_noargs_t)(u32 *);
    image_entry_noargs_t image_entry =
            (image_entry_noargs_t) spl_image->entry_point;

    debug("image entry point: 0x%X\n", spl_image->entry_point);
    /* Pass the saved boot_params from rom code */
    image_entry((u32 *)&gd->arch.omap_boot_params);
}
複製代碼

貌似是跳到了image_entry那個地方,具體是怎麼來的我就不曉得了╮(╯▽╰)╭

直接用file命令看一下最後生成的u-boot.img鏡像文件:

u-boot.img: 
u-boot legacy uImage, U-Boot 2014.04 for am335x board, Firmware/ARM, Firmware Image (Not compressed),

329036 bytes, Tue Feb 28 05:56:24 2017, Load Address: 0x80800000, Entry Point: 0x00000000, Header CRC: 0xBA1D5896, Data CRC: 0x697BF780

看到加載地址和入口點,加載地址貌似比DRAM的基址高出了8M.

相關文章
相關標籤/搜索