天天看點

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

分類:

Android Bootloader(68)

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

LK是什麼

           LK 代碼 在 bootable/bootloadler/lk 目錄下

           LK 代碼結構

           +app            // 應用相關

           +arch           // arm 體系 

           +dev            // 裝置相關

           +include      // 頭檔案

           +kernel        // lk系統相關   

           +platform    // 相關驅動

           +projiect     // makefile檔案

           +scripts      // Jtag 腳本

           +target        // 具體闆子相關

LK 流程分析

          在 bootable/bootloadler/lk/arch/arm/ssystem-onesegment.ld 連接配接檔案中 ENTRY(_start)指定 LK 從_start 函數開始,_start

在 lk/arch/crt0.S中 。crt0.S 主要做一些基本的 CPU 的初始化再通過 bl  kmain ;跳轉到 C 代碼中。

          kmain 在 lk/kernel/main.c 中

kmain()

            kmain 主要做兩件事:1、本身 lk 這個系統子產品的初始化;2、boot 的啟動初始化動作。

            kmain 源碼分析:

             void kmain()

          {

           1.初始化程序(lk

中的簡單程序)相關結構體。

             thread_init_early();

           2.做一些如 關閉 cache,使能 mmu 的 arm 相關工作。

            arch_early_init();

           3.相關平台的早期初始化

            platform_early_init();

           4.現在就一個函數跳轉,初始化UART(闆子相關)

            target_early_init();

           5.構造函數相關初始化

            call_constructors();

           6.lk系統相關的堆棧初始化

            heap_init();

           7.簡短的初始化定時器對象

            thread_init();

           8.lk系統控制器初始化(相關事件初始化)

            dpc_init();

           9.初始化lk中的定時器

            timer_init();

           10.建立線程入口函數 bootstrap2 用于boot 工作(重點)

           thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

         }

   以上與 boot 啟動初始化相關函數是 arch_early_init、  platform_early_init 、bootstrap2,這些是啟動的重點,我們下面慢慢來看。

arch_early_init()

         1.關閉cache

         arch_disable_cache(UCACHE);

         2.設定向量基位址(中斷相關)

         set_vector_base(MEMBASE);

         3.初始化MMU

         arm_mmu_init();

         4.初始化MMU映射__平台相關

         platform_init_mmu_mappings();

         5.開啟cache         

         arch_enable_cache(UCACHE)

         6.使能 cp10 和 cp11

         __asm__ volatile("mrc    p15, 0, %0, c1, c0, 2" : "=r" (val));

         val |= (3<<22)|(3<<20);

         __asm__ volatile("mcr    p15, 0, %0, c1, c0, 2" :: "r" (val));

        7.設定使能 fpexc 位 (中斷相關)

        __asm__ volatile("mrc  p10, 7, %0, c8, c0, 0" : "=r" (val));

        val |= (1<<30);

        __asm__ volatile("mcr  p10, 7, %0, c8, c0, 0" :: "r" (val));

        8.使能循環計數寄存器

        __asm__ volatile("mrc    p15, 0, %0, c9, c12, 0" : "=r" (en));

        en &= ~(1<<3); /*循環計算每個周期*/

        en |= 1; 

        __asm__ volatile("mcr    p15, 0, %0, c9, c12, 0" :: "r" (en));

       9.使能循環計數器

       en = (1<<31);

       __asm__ volatile("mcr    p15, 0, %0, c9, c12, 1" :: "r" (en));

platform_early_init()

       平台相關初始化不同平台不同的初始化下面是msm7x30

        1.初始化中斷

        platform_init_interrupts();

        2.初始化定時器

        platform_init_timer();

bootstrap2 

         bootstrap2 在kmain的末尾以線程方式開啟。主要分三步:platform_init、target_init、apps_init。

        1.platform_init

               platform_init 中主要是函數 acpu_clock_init。

               在 acpu_clock_init 對

arm11 進行系統時鐘設定,超頻 

        2.target_init

              針對硬體平台進行設定。主要對 arm9 和 arm11 的分區表進行整合,初始化flash和讀取FLASH資訊

        3.apps_init  

aboot_init

        1.設定NAND/ EMMC讀取資訊頁面大小

        if (target_is_emmc_boot())

        {

                  page_size = 2048;

                  page_mask = page_size - 1;

        }

       else

       {

                 page_size = flash_page_size();

                 page_mask = page_size - 1;

      2.讀取按鍵資訊,判斷是正常開機,還是進入 fastboot ,還是進入recovery 模式

       。。。。。。。。。

      通過一系列的 if (keys_get_state() == XXX) 判斷

      3.從 nand 中加載 核心

      boot_linux_from_flash();

      partition_dump();

      sz = target_get_max_flash_size();

      fastboot_init(target_get_scratch_address(), sz);

      udc_start(); // 開始 USB 協定

boot_linux_from_flash

             主要是核心的加載過程,我們的 boot.img 包含:kernel 頭、kernel、ramdisk、second stage(可以沒有)。

           1.讀取boot 頭部

           flash_read(p, offset, raw_header, 2048) 

           offset += 2048;

           2.讀取 核心    

           memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)

           n = (hdr->kernel_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));

           flash_read(p, offset, (void*) hdr->kernel_addr, n)

           offset += n;

           3.讀取 ramdisk

           n = (hdr->ramdisk_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));

           flash_read(p, offset, (void*) hdr->ramdisk_addr, n)

           offset += n;

            4.啟動核心,

                boot_linux();//在boot_linux

中entry(0,machtype,tags);從kernel加載在核心中的位址開始運作了。

        到這裡LK的啟動過程就結束了。

Android Boot loader 的 code 在 bootable/bootloader/lk 底下, LK 是 Little Kernel 的縮寫, 是 andriod bootloader 的核心精神.

入口函數在 kernel/main.c 中的 kmain(), 以下就來讀讀這一段 code.

void kmain(void)  

{  

    // get us into some sort of thread context   

    thread_init_early();  

    // early arch stuff   

    arch_early_init();  

    // do any super early platform initialization   

    platform_early_init();  

    // do any super early target initialization   

    target_early_init();  

    dprintf(INFO, "welcome to lk/n/n");  

    // deal with any static constructors   

    dprintf(SPEW, "calling constructors/n");  

    call_constructors();  

    // bring up the kernel heap   

    dprintf(SPEW, "initializing heap/n");  

    heap_init();  

    // initialize the threading system   

    dprintf(SPEW, "initializing threads/n");  

    thread_init();  

    // initialize the dpc system   

    dprintf(SPEW, "initializing dpc/n");  

    dpc_init();  

    // initialize kernel timers   

    dprintf(SPEW, "initializing timers/n");  

    timer_init();  

#if (!ENABLE_NANDWRITE)   

    // create a thread to complete system initialization   

    dprintf(SPEW, "creating bootstrap completion thread/n");  

    thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));  

    // enable interrupts   

    exit_critical_section();  

    // become the idle thread   

    thread_become_idle();  

#else   

        bootstrap_nandwrite();  

#endif   

}  

In include/debug.h: 我們可以看到 dprintf 的第一個參數是代表 debug level.

/* debug levels */ 

#define CRITICAL 0  

#define ALWAYS 0  

#define INFO 1  

#define SPEW 2  

In include/debug.h: 

#define dprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0)  

是以 dprintf 會依 DEBUGLEVEL 來判斷是否輸出資訊.

來看第一個 call 的函數: thread_init_early, define in thread.c

void thread_init_early(void)  

        int i;  

        /* initialize the run queues */  

        for (i=0; i < NUM_PRIORITIES; i++)  

                list_initialize(&run_queue[i]);  

        /* initialize the thread list */  

        list_initialize(&thread_list);  

        /* create a thread to cover the current running state */  

        thread_t *t = &bootstrap_thread;  

        init_thread_struct(t, "bootstrap");  

        /* half construct this thread, since we're already running */  

        t->priority = HIGHEST_PRIORITY;  

        t->state = THREAD_RUNNING;  

        t->saved_critical_section_count = 1;  

        list_add_head(&thread_list, &t->thread_list_node);  

        current_thread = t;  

#define NUM_PRIORITIES 32 in include/kernel/thread.h

list_initialize() defined in include/list.h: initialized a list

static inline void list_initialize(struct list_node *list)  

        list->prev = list->next = list;  

run_queue 是 static struct list_node run_queue[NUM_PRIORITIES]

thread_list 是 static struct list_node thread_list

再來要 call  的函數是: arch_early_init() defined in arch/arm/arch.c

void arch_early_init(void)  

    /* turn off the cache */  

    arch_disable_cache(UCACHE);  

    /* set the vector base to our exception vectors so we dont need to double map at 0 */ 

#if ARM_CPU_CORTEX_A8   

    set_vector_base(MEMBASE);  

#endif  

#if ARM_WITH_MMU   

    arm_mmu_init();  

    platform_init_mmu_mappings();  

    /* turn the cache back on */  

    arch_enable_cache(UCACHE);  

#if ARM_WITH_NEON   

    /* enable cp10 and cp11 */  

    uint32_t val;  

    __asm__ volatile("mrc   p15, 0, %0, c1, c0, 2" : "=r" (val));  

    val |= (3<<22)|(3<<20);  

    __asm__ volatile("mcr   p15, 0, %0, c1, c0, 2" :: "r" (val));  

    /* set enable bit in fpexc */  

    val = (1<<30);  

    __asm__ volatile("mcr  p10, 7, %0, c8, c0, 0" :: "r" (val));  

現代作業系統普遍采用虛拟記憶體管理(Virtual Memory Management)機制,這需要處理器中的MMU(Memory Management Unit,

記憶體管理單元)提供支援。

CPU執行單元發出的記憶體位址将被MMU截獲,從CPU到MMU的位址稱為虛拟位址(Virtual Address,以下簡稱VA),而MMU将這個地

址翻譯成另一個位址發到CPU晶片的外部位址引腳上,也就是将VA映射成PA

MMU将VA映射到PA是以頁(Page)為機關的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個映射項将VA的一頁

0xb7001000~0xb7001fff映射到PA的一頁0x2000~0x2fff,如果CPU執行單元要通路虛拟位址0xb7001008,則實際通路到的實體地

址是0x2008。實體記憶體中的頁稱為實體頁面或者頁幀(Page Frame)。虛拟記憶體的哪個頁面映射到實體記憶體的哪個頁幀是通過頁

表(Page Table)來描述的,頁表儲存在實體記憶體中,MMU會查找頁表來确定一個VA應該映射到什麼PA。

作業系統和MMU是這樣配合的:

1. 作業系統在初始化或配置設定、釋放記憶體時會執行一些指令在實體記憶體中填寫頁表,然後用指令設定MMU,告訴MMU頁表在實體記憶體中

   的什麼位置。

2. 設定好之後,CPU每次執行通路記憶體的指令都會自動引發MMU做查表和位址轉換操作,位址轉換操作由硬體自動完成,不需要用指令

    控制MMU去做。

MMU除了做位址轉換之外,還提供記憶體保護機制。各種體系結構都有使用者模式(User Mode)和特權模式(Privileged Mode)之分,

作業系統可以在頁表中設定每個記憶體頁面的通路權限,有些頁面不允許通路,有些頁面隻有在CPU處于特權模式時才允許通路,有些頁面

在使用者模式和特權模式都可以通路,通路權限又分為可讀、可寫和可執行三種。這樣設定好之後,當CPU要通路一個VA時,MMU會檢查

CPU目前處于使用者模式還是特權模式,通路記憶體的目的是讀資料、寫資料還是取指令,如果和作業系統設定的頁面權限相符,就允許訪

問,把它轉換成PA,否則不允許通路,産生一個異常(Exception)

常見的 segmentation fault 産生的原因:

使用者程式要通路一段 VA, 經 MMU 檢查後無權通路, MMU 會産生異常, CPU 從使用者模式切換到特權模式, 跳轉到核心代碼中執行異常服務程式.

核心就會把這個異常解釋為 segmentation fault, 将引發異常的程式終止.

簡單的講一下 NEON: NEON technology can accelerate multimedia and signal processing algorithms such as video encode/decode,

2D/3D graphics, gaming, audio and speech processing, image processing, telephony, and sound synthesis.

platform_early_init() defined in platform/<your-platform>/platform.c

void platform_early_init(void)  

    uart_init();  

    platform_init_interrupts();  

    platform_init_timer();  

uart_init.c defined in platform/<your-platform>/uart.c 所有用到的變數,也都定義在 uart.c

void uart_init(void)  

    uwr(0x0A, UART_CR);  /* disable TX and RX */  

    uwr(0x30, UART_CR);  /* reset error status */  

    uwr(0x10, UART_CR);  /* reset receiver */  

    uwr(0x20, UART_CR);  /* reset transmitter */ 

#if PLATFORM_QSD8K   

    /* TCXO */  

    uwr(0x06, UART_MREG);  

    uwr(0xF1, UART_NREG);  

    uwr(0x0F, UART_DREG);  

    uwr(0x1A, UART_MNDREG);  

    /* TCXO/4 */  

    uwr(0xC0, UART_MREG);  

    uwr(0xAF, UART_NREG);  

    uwr(0x80, UART_DREG);  

    uwr(0x19, UART_MNDREG);      

    uwr(0x10, UART_CR);  /* reset RX */  

    uwr(0x20, UART_CR);  /* reset TX */  

    uwr(0x40, UART_CR);  /* reset RX break */  

    uwr(0x70, UART_CR);  /* rest? */  

    uwr(0xD0, UART_CR);  /* reset */  

    uwr(0x7BF, UART_IPR); /* stale timeout = 630 * bitrate */  

    uwr(0, UART_IMR);  

    uwr(115, UART_RFWR); /* RX watermark = 58 * 2 - 1 */  

    uwr(10, UART_TFWR);  /* TX watermark */  

    uwr(0, UART_RFWR);   

    uwr(UART_CSR_115200, UART_CSR);  

    uwr(0, UART_IRDA);  

    uwr(0x1E, UART_HCR);  

//  uwr(0x7F4, UART_MR1); /* RFS/ CTS/ 500chr RFR */   

    uwr(16, UART_MR1);  

    uwr(0x34, UART_MR2); /* 8N1 */  

    uwr(0x05, UART_CR); /* enable TX & RX */  

    uart_ready = 1;  

platform_init_interrupts: defined in platform/msm8x60/interrupts.c

void platform_init_interrupts(void)  

    platform_gic_dist_init();  

    platform_gic_cpu_init();  

GIC 指的是 Generic Interrupt Controller. The gic-cpu and gic-dist are two subcomponents of GIC.

platform_init_timer(): defined in platform/<your-platform>/timer.c

void platform_init_timer(void)  

    writel(0, DGT_ENABLE);  

DGT: Digital Game Timer: presents the countdowns of two players, it is also called chess timer or chess clock.

target_early_init(): defined in target/init.c

/* 

 * default implementations of these routines, if the target code 

 * chooses not to implement. 

 */  

__WEAK void target_early_init(void)  

__WEAK void target_init(void)  

call_constructors() is defined in kernel/main.c:

static void call_constructors(void)  

    void **ctor;  

    ctor = &__ctor_list;  

    while(ctor != &__ctor_end) {  

        void (*func)(void);  

        func = (void (*)())*ctor;  

        func();  

        ctor++;  

    }  

heap_init is defined in lib/heap/heap.c:

void heap_init(void)  

    LTRACE_ENTRY;  

    // set the heap range   

    theheap.base = (void *)HEAP_START;  

    theheap.len = HEAP_LEN;  

    LTRACEF("base %p size %zd bytes/n", theheap.base, theheap.len);  

    // initialize the free list   

    list_initialize(&theheap.free_list);  

    // create an initial free chunk   

    heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));  

    // dump heap info   

//  heap_dump();   

//  dprintf(INFO, "running heap tests/n");   

//  heap_test();   

thread_init is defined in kernel/thread.c but nothing coded, 先記下.

void thread_init(void)  

dpc_init() is defined in kernel/dpc.c:

void dpc_init(void)  

    event_init(&dpc_event, false, 0);  

    thread_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE));  

dpc 為 Delayed Procedure Call 延遲過程調用的縮寫.

timer_init() is defined in kernel/timer.c:

void timer_init(void)  

    list_initialize(&timer_queue);  

    /* register for a periodic timer tick */  

    platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */  

執行 thread_resume 或是 bootstrap_nandwrite

In kermel/main.c:

static int bootstrap2(void *arg)  

    dprintf(SPEW, "top of bootstrap2()/n");  

    arch_init();  

    // initialize the rest of the platform   

    dprintf(SPEW, "initializing platform/n");  

    platform_init();  

    // initialize the target   

    dprintf(SPEW, "initializing target/n");  

    target_init();  

    dprintf(SPEW, "calling apps_init()/n");  

    apps_init();  

    return 0;  

}  

#if (ENABLE_NANDWRITE)   

void bootstrap_nandwrite(void)  

    dprintf(SPEW, "calling nandwrite_init()/n");  

    nandwrite_init();  

continue to see apps_init(): app/app.c:void apps_init(void)

#include <app.h>  

#include <kernel/thread.h>   

extern const struct app_descriptor __apps_start;  

extern const struct app_descriptor __apps_end;  

static void start_app(const struct app_descriptor *app);  

/* one time setup */  

void apps_init(void)  

    const struct app_descriptor *app;  

    /* call all the init routines */  

    for (app = &__apps_start; app != &__apps_end; app++) {  

        if (app->init)  

            app->init(app);  

    /* start any that want to start on boot */  

        if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {  

            start_app(app);  

        }  

static int app_thread_entry(void *arg)  

    const struct app_descriptor *app = (const struct app_descriptor *)arg;  

    app->entry(app, NULL);  

static void start_app(const struct app_descriptor *app)  

    printf("starting app %s/n", app->name);  

    thread_resume(thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));     

至于會有那些 app 被放入 boot thread section, 則定義在 include/app.h 中的 APP_START(appname)

#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname, 

#define APP_END };  

在 app 中隻要像 app/aboot/aboot.c 指定就會在 bootloader bootup 時放入 thread section 中被執行.

APP_START(aboot)  

        .init = aboot_init,  

APP_END  

在我的 bootloader 中有 app/aboot/aboot.c, app/tests/tests.c, app/shell/shell.c, 及 app/stringtests/string_tests.c 皆有此聲明.

接下來關注: aboot.c 中的 aboot_init()

void aboot_init(const struct app_descriptor *app)  

    unsigned reboot_mode = 0;  

    unsigned disp_init = 0;  

    unsigned usb_init = 0;  

    //test_ram();   

    /* Setup page size information for nand/emmc reads */  

    if (target_is_emmc_boot())  

    {  

        page_size = 2048;  

        page_mask = page_size - 1;  

    else  

        page_size = flash_page_size();  

    /* Display splash screen if enabled */ 

    #if DISPLAY_SPLASH_SCREEN   

    display_init();  

    dprintf(INFO, "Diplay initialized/n");  

    disp_init = 1;  

    diplay_image_on_screen();  

    #endif   

    /* Check if we should do something other than booting up */  

    if (keys_get_state(KEY_HOME) != 0)  

        boot_into_recovery = 1;  

    if (keys_get_state(KEY_BACK) != 0)  

        goto fastboot;  

    if (keys_get_state(KEY_CLEAR) != 0)  

        goto fastboot;  

    #if NO_KEYPAD_DRIVER   

    /* With no keypad implementation, check the status of USB connection. */  

    /* If USB is connected then go into fastboot mode. */  

    usb_init = 1;  

    udc_init(&surf_udc_device);  

    if (usb_cable_status())  

    init_vol_key();  

    if(voldown_press())  

    reboot_mode = check_reboot_mode();  

    if (reboot_mode == RECOVERY_MODE) {  

    } else if(reboot_mode == FASTBOOT_MODE) {  

        boot_linux_from_mmc();  

        recovery_init();  

        boot_linux_from_flash();  

    dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "  

        "to fastboot mode./n");  

fastboot:  

    if(!usb_init)  

        udc_init(&surf_udc_device);  

    fastboot_register("boot", cmd_boot);  

        fastboot_register("flash:", cmd_flash_mmc);  

        fastboot_register("erase:", cmd_erase_mmc);  

        fastboot_register("flash:", cmd_flash);  

        fastboot_register("erase:", cmd_erase);  

    fastboot_register("continue", cmd_continue);  

    fastboot_register("reboot", cmd_reboot);  

    fastboot_register("reboot-bootloader", cmd_reboot_bootloader);  

    fastboot_publish("product", TARGET(BOARD));  

    fastboot_publish("kernel", "lk");  

    fastboot_init(target_get_scratch_address(), 120 * 1024 * 1024);  

    udc_start();  

    target_battery_charging_enable(1, 0);  

target_is_emmc_boot() is defined in target/init.c: _EMMC_BOOT 是 compiler 時的 flags

__WEAK int target_is_emmc_boot(void)  

{  

#if _EMMC_BOOT   

    return 1;  

    return 0;  

check_reboot_mode is defined in target/<your-platform>/init.c:

unsigned check_reboot_mode(void)  

        unsigned restart_reason = 0;  

        void *restart_reason_addr = 0x401FFFFC;  

        /* Read reboot reason and scrub it */  

        restart_reason = readl(restart_reason_addr);  

        writel(0x00, restart_reason_addr);  

        return restart_reason;  

reboot mode in bootloader:

#define RECOVERY_MODE   0x77665502  

#define FASTBOOT_MODE   0x77665500  

再來就會執行 boot_linux_from_mmc():

int boot_linux_from_mmc(void)  

    struct boot_img_hdr *hdr = (void*) buf;  

    struct boot_img_hdr *uhdr;  

    unsigned offset = 0;  

    unsigned long long ptn = 0;  

    unsigned n = 0;  

    const char *cmdline;  

    uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;  

    if (!memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {  

        dprintf(INFO, "Unified boot method!/n");  

        hdr = uhdr;  

        goto unified_boot;  

    if(!boot_into_recovery)  

        ptn = mmc_ptn_offset("boot");  

        if(ptn == 0) {  

            dprintf(CRITICAL, "ERROR: No boot partition found/n");  

                    return -1;  

        ptn = mmc_ptn_offset("recovery");  

            dprintf(CRITICAL, "ERROR: No recovery partition found/n");  

    if (mmc_read(ptn + offset, (unsigned int *)buf, page_size)) {  

        dprintf(CRITICAL, "ERROR: Cannot read boot image header/n");  

                return -1;  

    if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {  

        dprintf(CRITICAL, "ERROR: Invaled boot image header/n");  

    if (hdr->page_size && (hdr->page_size != page_size)) {  

        page_size = hdr->page_size;  

    offset += page_size;  

    n = ROUND_TO_PAGE(hdr->kernel_size, page_mask);  

    if (mmc_read(ptn + offset, (void *)hdr->kernel_addr, n)) {  

        dprintf(CRITICAL, "ERROR: Cannot read kernel image/n");  

    offset += n;  

    n = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);  

    if (mmc_read(ptn + offset, (void *)hdr->ramdisk_addr, n)) {  

        dprintf(CRITICAL, "ERROR: Cannot read ramdisk image/n");  

unified_boot:  

    dprintf(INFO, "/nkernel  @ %x (%d bytes)/n", hdr->kernel_addr,  

        hdr->kernel_size);  

    dprintf(INFO, "ramdisk @ %x (%d bytes)/n", hdr->ramdisk_addr,  

        hdr->ramdisk_size);  

    if(hdr->cmdline[0]) {  

        cmdline = (char*) hdr->cmdline;  

    } else {  

        cmdline = DEFAULT_CMDLINE;  

    dprintf(INFO, "cmdline = '%s'/n", cmdline);  

    dprintf(INFO, "/nBooting Linux/n");  

    boot_linux((void *)hdr->kernel_addr, (void *)TAGS_ADDR,  

           (const char *)cmdline, board_machtype(),  

           (void *)hdr->ramdisk_addr, hdr->ramdisk_size);  

mmc_read() is defined in platform/<your-platform>/mmc.c:

unsigned int mmc_read (unsigned long long data_addr, unsigned int* out, unsigned int data_len)  

    int val = 0;  

    val = mmc_boot_read_from_card( &mmc_host, &mmc_card, data_addr, data_len, out);  

    return val;  

boot_linux(): 啟動 Linux, 看看函數定義

void boot_linux(void *kernel, unsigned *tags,   

        const char *cmdline, unsigned machtype,  

        void *ramdisk, unsigned ramdisk_size)  

    unsigned *ptr = tags;  

    unsigned pcount = 0;  

    void (*entry)(unsigned,unsigned,unsigned*) = kernel;  

    struct ptable *ptable;  

    int cmdline_len = 0;  

    int have_cmdline = 0;  

    int pause_at_bootup = 0;  

    /* CORE */  

    *ptr++ = 2;  

    *ptr++ = 0x54410001;  

    if (ramdisk_size) {  

        *ptr++ = 4;  

        *ptr++ = 0x54420005;  

        *ptr++ = (unsigned)ramdisk;  

        *ptr++ = ramdisk_size;  

    ptr = target_atag_mem(ptr);  

    if (!target_is_emmc_boot()) {  

        /* Skip NAND partition ATAGS for eMMC boot */  

        if ((ptable = flash_get_ptable()) && (ptable->count != 0)) {  

            int i;  

            for(i=0; i < ptable->count; i++) {  

                struct ptentry *ptn;  

                ptn =  ptable_get(ptable, i);  

                if (ptn->type == TYPE_APPS_PARTITION)  

                    pcount++;  

            }  

            *ptr++ = 2 + (pcount * (sizeof(struct atag_ptbl_entry) /  

                               sizeof(unsigned)));  

            *ptr++ = 0x4d534d70;  

            for (i = 0; i < ptable->count; ++i)  

                ptentry_to_tag(&ptr, ptable_get(ptable, i));  

    if (cmdline && cmdline[0]) {  

        cmdline_len = strlen(cmdline);  

        have_cmdline = 1;  

    if (target_is_emmc_boot()) {  

        cmdline_len += strlen(emmc_cmdline);  

    if (target_pause_for_battery_charge()) {  

        pause_at_bootup = 1;  

        cmdline_len += strlen(battchg_pause);  

    if (cmdline_len > 0) {  

        const char *src;  

        char *dst;  

        unsigned n;  

        /* include terminating 0 and round up to a word multiple */  

        n = (cmdline_len + 4) & (~3);  

        *ptr++ = (n / 4) + 2;  

        *ptr++ = 0x54410009;  

        dst = (char *)ptr;  

        if (have_cmdline) {  

            src = cmdline;  

            while ((*dst++ = *src++));  

        if (target_is_emmc_boot()) {  

            src = emmc_cmdline;  

            if (have_cmdline) --dst;  

            have_cmdline = 1;  

        if (pause_at_bootup) {  

            src = battchg_pause;  

        ptr += (n / 4);  

    /* END */  

    *ptr++ = 0;  

    dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d)/n",  

        kernel, ramdisk, ramdisk_size);  

    if (cmdline)  

        dprintf(INFO, "cmdline: %s/n", cmdline);  

    enter_critical_section();  

    platform_uninit_timer();  

    arch_disable_mmu();  

#if DISPLAY_SPLASH_SCREEN   

    display_shutdown();  

    entry(0, machtype, tags);  

配合 boot.img 來看會比較好了解.

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

由此可知 boot_img_hdr 中各成員值為:

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523
Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

TAGS_ADDR 如上 target/<your-platform>/rules.mk 所定義的 : 0x40200100, 是以 boot_linux(), 就是傳入TAGS_ADDR,

然後将資料寫入 tag, tag 的結構如下所示.

Android Bootloader LittleKernel的兩篇文章 【轉】轉自:http://blog.csdn.net/loongembedded/article/details/41747523

然後進入到 kernel 的入口函數: entry(0, machtype, tags)

【新浪微網誌】 張昺華--sky

【twitter】 @sky2030_

【facebook】 張昺華 zhangbinghua

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.

繼續閱讀