漫谈android系统(2)androidLK启动过程1

andoid的启动过程

通过audio口所吐出的log,我们可以发现android的启动还是非常之有趣的,因而在此对它的启动做相应的分析。

在此分析LK的启动过程,LK就是bootloader。

现在只截取一部分的Log

[0] welcome to lk
[10] platform_init()
[10] target_init()
[60] SDHC Running in HS400 mode
[60] Done initialization of the card
[70] pm8x41_get_is_cold_boot: Warm boot [70] Unsupported platform id

现在可以根据这部分的log来看LK是如何加载的。

当android加载完fileware时,LK会通过crt0.s的跳转命令,跳转到main.c中的kmain函数中去。

#ifdef ARM_CPU_CORTEX_A8
    DSB
    ISB
#endif
    bl      kmain
    b       .
.ltorg

那么kmain又做了什么?

/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
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");
    bs_set_timestamp(BS_BL_START);
    // deal with any static constructors
    dprintf(SPEW, "calling constructors\n");
    call_constructors();
    // bring up the kernel heap
    dprintf(SPEW, "initializing heap\n");
    heap_init();
    __stack_chk_guard_setup();
    // 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
}

在这里,我们充分看到了Lk在kmain()中做了大量的初始化动作,其中thread_init_early()主要的工作是初始化线程系统,就是创建队列,然后系统根据CPU的不同架构进入不同的init线程

/** * @brief Initialize threading system * * This function is called once, from kmain() */
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;
}

arch_early_init()函数,一般采用的高通的芯片,因而会导向lk/arch/arm/arch.c,那么其主要的工作设置基本CPU属性

    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();
    #endif
        /* 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));
        isb();
        /* set enable bit in 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));
    #endif
    #if ARM_CPU_CORTEX_A8
        /* enable the cycle count register */
        uint32_t en;
        __asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (en));
        en &= ~(1<<3); /* cycle count every cycle */
        en |= 1; /* enable all performance counters */
        __asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en));
        /* enable cycle counter */
        en = (1<<31);
        __asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en));
    #endif
    }

/lk/platform/msmtitanium/platform.c中的platform_early_init()首先会调用board_init(),它会去调用lk/platform/msm_shared/board.c中的Platform_detect函数来检测平台信息,平台信息包含主要信息(format_major)与次要信息(format_minor)。通过信息来初始化平台版本\hw\type,lk/target/msmtitanium/init.c中target_detect()不做任何事情,而target_baseband_detect函数会测试modem type

void platform_early_init(void)
{
    board_init();
    platform_clock_init();
    qgic_init();
    qtimer_init();
    scm_init();
}

platform_clock_init会初始化一些时钟信息,qgic_init(void)初始化qgic,qtimer_init()提供初始化频率,scm_init()是scm的初始化函数

target_early_init()函数是对串口的初始化,因而此时可以看到lk的第一条log:welcome to lk,后面的几个函数就是对一些时序,状态等等的初始化。

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

这个函数是重点,它创建了一个子线程bootstrap2来进行初始化。调用如下函数:

static int bootstrap2(void *arg)
{
    dprintf(SPEW, "top of bootstrap2()\n");
    arch_init();
    // XXX put this somewhere else
#if WITH_LIB_BIO
    bio_init();
#endif
#if WITH_LIB_FS
    fs_init();
#endif
    // 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;
}

arch_init()里面什么都没做,然后调用/lk/platform/msmtitanium/platform.c下的platfrom_init(),其中只是打印了一条platform_init()log,

然后调用lk/target/xxxx/init.c的target_init()函数。这个函数可以做许多的定制,如将部分GPIO设定,与设置使用哪一个DTSI文件等等。

void target_init(void)
{
#if VERIFIED_BOOT
#if !VBOOT_MOTA
    int ret = 0;
#endif
#endif
    dprintf(INFO, "target_init()\n");
    spmi_init(PMIC_ARB_CHANNEL_NUM, PMIC_ARB_OWNER_ID);
    target_keystatus();
    target_sdc_init();
    if (partition_read_table())
    {
        dprintf(CRITICAL, "Error reading the partition table info\n");
        ASSERT(0);
    }
#if LONG_PRESS_POWER_ON
    shutdown_detect();
#endif
#if PON_VIB_SUPPORT
    vib_timed_turn_on(VIBRATE_TIME);
#endif
    if (target_use_signed_kernel())
        target_crypto_init_params();
#if VERIFIED_BOOT
#if !VBOOT_MOTA
    clock_ce_enable(CE1_INSTANCE);
    /* Initialize Qseecom */
    ret = qseecom_init();
    if (ret < 0)
    {
        dprintf(CRITICAL, "Failed to initialize qseecom, error: %d\n", ret);
        ASSERT(0);
    }
    /* Start Qseecom */
    ret = qseecom_tz_init();
    if (ret < 0)
    {
        dprintf(CRITICAL, "Failed to start qseecom, error: %d\n", ret);
        ASSERT(0);
    }
    if (rpmb_init() < 0)
    {
        dprintf(CRITICAL, "RPMB init failed\n");
        ASSERT(0);
    }
    /* * Load the sec app for first time */
    if (load_sec_app() < 0)
    {
        dprintf(CRITICAL, "Failed to load App for verified\n");
        ASSERT(0);
    }
#endif
#endif
#if SMD_SUPPORT
    rpm_smd_init();
#endif
}

该函数有对sim卡的GPIO的初始化,初始化spmi,target_keystatus();函数是对音量上下键的初始化,target_sdc_init初始化了mmc.包含了struct mmc_device *mmc_init(struct mmc_config_data *data)->uint32_t mmc_card_init(struct mmc_device *dev)可以看到mmc的选择模式

    /* Enable high speed mode in the follwing order:
         * 1. HS400 mode if supported by host & card
         * 1. HS200 mode if supported by host & card
         * 2. DDR mode host, if supported by host & card
         * 3. Use normal speed mode with supported bus width
         */
        if (host->caps.hs400_support && mmc_card_supports_hs400_mode(card))
        {
            dprintf(INFO, "SDHC Running in HS400 mode\n");
            mmc_return = mmc_set_hs400_mode(host, card, bus_width);
            if (mmc_return)
            {
                dprintf(CRITICAL, "Failure to set HS400 mode for Card(RCA:%x)\n",
                                  card->rca);
                return mmc_return;
            }
        }

此时执行完mmc_init,那么Done initialization of the card也就执行完成。
通过bootable/bootloader/lk/platfrom/msm_shared.c下partition_read_table()函数开始检查partition table。

unsigned int partition_read_table()
{
    unsigned int ret;
    uint32_t block_size;
    block_size = mmc_get_device_blocksize();
    /* Allocate partition entries array */
    if(!partition_entries)
    {
        partition_entries = (struct partition_entry *) calloc(NUM_PARTITIONS, sizeof(struct partition_entry));
        ASSERT(partition_entries);
    }
    /* Read MBR of the card */
    ret = mmc_boot_read_mbr(block_size);
    if (ret) {
        dprintf(CRITICAL, "MMC Boot: MBR read failed!\n");
        return 1;
    }
    /* Read GPT of the card if exist */
    if (gpt_partitions_exist) {
        ret = mmc_boot_read_gpt(block_size);
        if (ret) {
            dprintf(CRITICAL, "MMC Boot: GPT read failed!\n");
            return 1;
        }
    }
    return 0;
}

首先先获取mmc block的大小,会分配内存给partition_entries,然后从mmc中读取mbr,并将parse都初始化好。
随后会执行shutdown_detect();

*
 * Function to support for shutdown detection
 * If below condition is met, the function will shut down
 * the device. Otherwise it will do nothing and return to
 * normal boot.
 * condition:
 * 1. it is triggered by power key &&
 * 2. the power key is released before
 * (PWRKEY_LONG_PRESS_COUNT/MPM_SLEEP_TIMETICK_COUNT) seconds.
 */
void shutdown_detect()
{
    /*
     * If it is booted by power key tirigger.
     * Initialize pon_timer and call long_press_pwrkey_timer_func
     * function to check if the power key is last press long enough.
     */
    if (is_pwrkey_pon_reason()) {
        if(!pm8x41_get_pwrkey_is_pressed()){
            shutdown_device();
        }
        timer_initialize(&pon_timer);
        timer_set_oneshot(&pon_timer, 0,(timer_callback)long_press_pwrkey_timer_func, NULL);
        /*
         * Wait until long press power key timeout
         *
         * It will be confused to end users if we shutdown the device
         * after the splash screen displayed. But it can be moved the
         * wait here if the boot time is much more considered.
         */
        wait_for_long_pwrkey_pressed();
    }
}
/*
 * Function to check if the power on reason is power key triggered.
 * Return 1 if it is triggered by power key.
 * Return 0 if it is not triggered by power key.
 */
static uint32_t is_pwrkey_pon_reason()
{
#if PMI_CONFIGURED
 return target_is_pwrkey_pon_reason();
#else
    uint8_t pon_reason = pm8x41_get_pon_reason();
    if (pm8x41_get_is_cold_boot() && (pon_reason == KPDPWR_N))
 return 1;
    else
 return 0;
#endif
}
uint8_t pm8x41_get_is_cold_boot()
{
    if (REG_READ(PON_WARMBOOT_STATUS1) || REG_READ(PON_WARMBOOT_STATUS2)) {
        dprintf(INFO,"%s: Warm boot\n", __func__);
 return 0;
    }
    dprintf(INFO,"%s: cold boot\n", __func__);
 return 1;
}

后面将会调用apps_init();

/* 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 */
    for (app = &__apps_start; app != &__apps_end; app++) {
        if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
            start_app(app);
        }
    }
}

从注释中已经不难看出LK现在需要去启动boot了。

本文只是简单地介绍了LK的启动过程。其博大精深处还待我等慢慢发现。

你可能感兴趣的:(android,bootloader)