little kernel 是小内核小操作系统,简称lk,主要用来引导运行OS系统,lk启动后根据一些参数值,引导启动进入不同模式。其实Android手机有四种启动方式,四种方式分别为:
下面就以高通代码为例,分析下这四种启动方式分别是在什么条件下启动的手机上电后,会从固定的地址(固化在ROM中)加载bootloader到RAM,然后跳转到bootloader的入口函数开始执行
bootable/bootloader/lk/arch/arm/crt0.S
blkmain
kmain的代码位于bootable/bootloader/lk/kernel/main.c
/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void kmain(void)
{
thread_t *thr;
// get us into some sort of thread context
thread_init_early(); //初始化线程上下文
// early arch stuff
arch_early_init(); //架构初始化,如关闭cache,使能mmu
// 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(); //lk系统控制器初始化
// initialize kernel timers
dprintf(SPEW, "initializing timers\n");
timer_init(); //kernel时钟初始化
#if (!ENABLE_NANDWRITE)
// create a thread to complete system initialization
dprintf(SPEW, "creating bootstrap completion thread\n");
thr = thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if (!thr)
{
panic("failed to create thread bootstrap2\n");
}
thread_resume(thr); //创建一个线程初始化系统
// enable interrupts
exit_critical_section(); //使能中断
// become the idle thread
thread_become_idle();//本线程切换成idle线程,idle为空闲线程,当没有更高优先级的线程时才执行
#else
bootstrap_nandwrite();
#endif
}
如果定义了ENABLE_NANDWRITE在timer_init之后将执行bootstrap_nandwrite。我们接下来先以没有定义ENABLE_NANDWRITE来进行分析。从代码可知,会创建一个bootstrap2线程,并使能中断
bootable/bootloader/lk/kernel/main.c
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(); //目标设备初始化,主要初始化Flash,整合分区表等
dprintf(SPEW, "calling apps_init()\n");
apps_init(); //应用功能初始化,主要调用boot_init,启动kernel,加载boot/recovery镜像等
return 0;
}
在app.c中定义了apps_init如下:
bootable/bootloader/lk/app/app.c
/* 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);
}
}
}
static void start_app(const struct app_descriptor *app)
{
thread_t *thr;
printf("starting app %s\n", app->name);
thr = thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if(!thr)
{
return;
}
thread_resume(thr);
}
__apps_start和__apps_end都是在连接脚本中定义的, 在 bootable/bootloader/lk/arch/arm/system-onesegment.ld中有:
__apps_start = .;
KEEP (*(.apps))
__apps_end = .;
__apps_start和__apps_end都代表两个链接地址,KEEP (*(.apps))表示所有.apps段都链接在__apps_start和__apps_end之间。
因为在bootable/bootloader/lk/include/app.h中有定义
bootable/bootloader/lk/include/app.h
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };
所以这里将会执行在APP_START(appname)中定义的appname函数。在代码里搜索APP_START,会发现在bootable/bootloader/lk/app/aboot/aboot.c中调用APP_START来执行aboot_init函数
bootable/bootloader/lk/app/aboot/aboot.c
APP_START(aboot)
.init = aboot_init,
APP_END
接下来就是这篇文章的重点aboot.c中的aboot_init,如下:
void aboot_init(const struct app_descriptor *app)
{
unsigned reboot_mode = 0;
unsigned hard_reboot_mode = 0;//bug 8046
int ret;
/* Initialise wdog to catch early lk crashes */
#if WDOG_SUPPORT
msm_wdog_init();
#endif
/* Setup page size information for nv storage */
if (target_is_emmc_boot()) ////检测是emmc还是flash存储,并设置页大小,一般是2048
{
page_size = mmc_page_size();
page_mask = page_size - 1;
mmc_blocksize = mmc_get_device_blocksize();
mmc_blocksize_mask = mmc_blocksize - 1;
}
else
{
page_size = flash_page_size();
page_mask = page_size - 1;
}
ASSERT((MEMBASE + MEMSIZE) > MEMBASE); //断言,如果内存基地址+内存大小小于内存基地址,则直接终止错误
read_device_info(&device); //从devinfo分区表read data到device结构体
read_allow_oem_unlock(&device); //devinfo分区里记录了unlock状态,从device中读取此信息
if(target_enter_ffbm_mode()) //判断是否进入ffbm模式
{
boot_into_ffbm = true;
dprintf(ALWAYS," gpio24 lcd te pull up ------boot_into_ffbm----\n");
}
/* Display splash screen if enabled */
#if DISPLAY_SPLASH_SCREEN
#if NO_ALARM_DISPLAY
if (!check_alarm_boot()) { //检测开机原因是否是由于关机闹钟导致
#endif
dprintf(SPEW, "Display Init: Start\n");
#if DISPLAY_HDMI_PRIMARY
if (!strlen(device.display_panel))
strlcpy(device.display_panel, DISPLAY_PANEL_HDMI,
sizeof(device.display_panel));
#endif
#if ENABLE_WBC
/* Wait if the display shutdown is in progress */
while(pm_app_display_shutdown_in_prgs());
if (!pm_appsbl_display_init_done())
target_display_init(device.display_panel); //显示splash,Splash也就是应用程序启动之前先启动一个画面,上面简单的介绍应用程序的厂商,厂商的LOGO,名称和版本等信息,多为一张图片
else
display_image_on_screen();
#else
target_display_init(device.display_panel);
#endif
dprintf(SPEW, "Display Init: Done\n");
#if NO_ALARM_DISPLAY
}
#endif
#endif
target_serialno((unsigned char *) sn_buf);
dprintf(SPEW,"serial number: %s\n",sn_buf);
memset(display_panel_buf, '\0', MAX_PANEL_BUF_SIZE);
/*
* Check power off reason if user force reset,
* if yes phone will do normal boot.
*/
if (is_user_force_reset()) //如果强制重启,直接进入normal_boot
goto normal_boot;
/* Check if we should do something other than booting up */
if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))//同时按住音量上下键进入fastboot 模式
{
dprintf(ALWAYS,"dload mode key sequence detected\n");
reboot_device(EMERGENCY_DLOAD);
dprintf(CRITICAL,"Failed to reboot into dload mode\n");
boot_into_fastboot = true;
}
if (keys_get_state(KEY_VOLUMEUP)) //按住音量上键进入recovery 模式
{
dprintf(ALWAYS,"KEY_VOLUMEUP------------------\n");
boot_into_recovery = true;
}
if (keys_get_state(KEY_VOLUMEDOWN)) //按住音量下键进入fastboot 模式
{
dprintf(ALWAYS,"KEY_VOLUMEDOWN------------------\n");
boot_into_fastboot = true;
}
if (!boot_into_fastboot)
{
if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))
boot_into_recovery = 1;
if (!boot_into_recovery &&
(keys_get_state(KEY_BACK) /*|| keys_get_state(KEY_VOLUMEDOWN)*/))
boot_into_fastboot = true;
}
#if NO_KEYPAD_DRIVER
if (fastboot_trigger())
boot_into_fastboot = true;
#endif
hard_reboot_mode = check_hard_reboot_mode();
reboot_mode = check_reboot_mode();//检查重启原因
dprintf(CRITICAL,"aboot_init reboot_mode=%x,hard_reboot_mode=%x\n",reboot_mode,hard_reboot_mode);
if (reboot_mode == RECOVERY_MODE || hard_reboot_mode == RECOVERY_HARD_RESET_MODE)
{
boot_into_recovery = 1;
}
else if(reboot_mode == FASTBOOT_MODE || hard_reboot_mode == FASTBOOT_HARD_RESET_MODE)
{
boot_into_fastboot = true;
}
else if(reboot_mode == ALARM_BOOT || hard_reboot_mode == RTC_HARD_RESET_MODE)
{
boot_reason_alarm = true;
}
normal_boot:
if (!boot_into_fastboot) //如果不是fastboot模式
{
if (target_is_emmc_boot())
{
if(emmc_recovery_init())
dprintf(ALWAYS,"error in emmc_recovery_init\n");
if(target_use_signed_kernel())
{
if((device.is_unlocked) || (device.is_tampered))
{
#ifdef TZ_TAMPER_FUSE
set_tamper_fuse_cmd();
#endif
#if USE_PCOM_SECBOOT
set_tamper_flag(device.is_tampered);
#endif
}
}
boot_linux_from_mmc(); //程序会跑到这里,又一个重点内容,下面会独立分析这个函数。
}
else
{
recovery_init();
#if USE_PCOM_SECBOOT
if((device.is_unlocked) || (device.is_tampered))
set_tamper_flag(device.is_tampered);
#endif
boot_linux_from_flash();
}
dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
"to fastboot mode.\n");
}
//下面的代码是fastboot的准备工作,从中可以看出,进入fastboot模式是不启动kernel的
/* We are here means regular boot did not happen. Start fastboot. */
/* register aboot specific fastboot commands */
aboot_fastboot_register_commands(); //注册fastboot命令,建议看下此函数的源码,此函数是fastboot支持的命令,如flash、erase等等
/* dump partition table for debug info */
partition_dump();
dprintf(0,"run in fastboot %s %s\r\n",__DATE__,__TIME__);
display_image_on_screen_fastboot(); //显示fastboot界面
/* initialize and start fastboot */
fastboot_init(target_get_scratch_address(), target_get_max_flash_size());
#if FBCON_DISPLAY_MSG
display_fastboot_menu();
#endif
}
从上面可以看出boot_init主要工作如下:
1、确定page_size大小;
2、从devinfo分区获取devinfo信息;
3、根据条件判断进入不同模式,设置对应标志位boot_into_xxx;
4、进入fastboot模式,初始化fastboot命令等