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

来源:互联网 发布:linux重启mysql数据库 编辑:程序博客网 时间:2024/05/01 08:25

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的启动过程。其博大精深处还待我等慢慢发现。

0 0
原创粉丝点击