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命令等