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