转载请注明来源:cuixiaolei的技术博客

这篇文章是lk启动流程分析(以高通为例),将会详细介绍下面的内容:

1).正常开机引导流程

2).recovery引导流程

3).fastboot引导流程

4).ffbm引导流程

5).lk向kernel传参

start----------------------------------------

在bootable/bootloader/lk/arch/arm/crt0.S文件中有下面代码,所以从kmain()开始介绍

bl        kmain

kmain函数位于bootable/bootloader/lk/kernel/main.c

/* 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();          //初始化线程上下文 #ifdef FEATURE_AFTER_SALE_LOG_LK
// do console early init
console_init_early();          //初始化控制台
#endif // 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();                     //线程初始化 #ifdef FEATURE_AFTER_SALE_LOG_LK
// initialize the console layer dprintf(SPEW, "initializing console layer\n");
console_init();           //初始化控制台
#endif // 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");
thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));     //创建一个线程初始化系统 // enable interrupts
exit_critical_section();       //使能中断 // become the idle thread
thread_become_idle();      //本线程切换成idle线程,idle为空闲线程,当没有更高优先级的线程时才执行
#else
bootstrap_nandwrite();
#endif
}
arch_early_init()负责使能内存管理单元mmu
bootable/bootloader/lk/arch/arm/arch.c
void arch_early_init(void)
{
/* turn off the cache */
arch_disable_cache(UCACHE);      //关闭cache /* 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();       //使能mmu #endif /* turn the cache back on */
arch_enable_cache(UCACHE);      //打开cache #if ARM_WITH_NEON
/* enable cp10 and cp11 */
uint32_t val;
__asm__ volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (val));
val |= (<<)|(<<);
__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 |= (<<);
__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 &= ~(<<); /* cycle count every cycle */
en |= ; /* enable all performance counters */
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en)); /* enable cycle counter */
en = (<<);
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en));
#endif
}
platform_early_init()平台早期初始化,初始化平台的时钟和主板
bootable\bootloader\lk\platform\msm8952\platform.c
void platform_early_init(void)
{
board_init(); //主板初始化
platform_clock_init(); //时钟初始化
qgic_init();
qtimer_init();
}

从代码可知,会创建一个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();            //目标设备初始化,主要初始化Flash,整合分区表等 dprintf(SPEW, "calling apps_init()\n");
apps_init();           //应用功能初始化,主要调用boot_init,启动kernel,加载boot/recovery镜像等 return ;
}

apps_init()通过下面方式进入aboot_init()函数
APP_START(aboot)
.init = aboot_init,
APP_END

bootable/bootloader/lk/app/app.cvoid 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) == ) {
start_app(app);
}
}
}

从这里开始是这篇文章的重点,分析aboot.c文件。每个项目的文件可能会有不同,但是差别会很小。

bootable/bootloader/lk/app/aboot/aboot.c

void aboot_init(const struct app_descriptor *app)
{
unsigned reboot_mode = ;
unsigned restart_reason = ;
unsigned hard_reboot_mode = ;
bool boot_into_fastboot = false;
uint8_t pon_reason = pm8950_get_pon_reason(); //pm8950_get_pon_reason() 获取开机原因 /* Setup page size information for nv storage */
if (target_is_emmc_boot())             //检测是emmc还是flash存储,并设置页大小,一般是2048
{
page_size = mmc_page_size();
page_mask = page_size - ;
}
else
{
page_size = flash_page_size();
page_mask = page_size - ;
} ASSERT((MEMBASE + MEMSIZE) > MEMBASE);           //断言,如果内存基地址+内存大小小于内存基地址,则直接终止错误 read_device_info(&device);                 //从devinfo分区表read data到device结构体            
read_allow_oem_unlock(&device);            //devinfo分区里记录了unlock状态,从device中读取此信息 /* Display splash screen if enabled */
if (!check_alarm_boot()) {           
dprintf(SPEW, "Display Init: Start\n");
target_display_init(device.display_panel);          //显示splash,Splash也就是应用程序启动之前先启动一个画面,上面简单的介绍应用程序的厂商,厂商的LOGO,名称和版本等信息,多为一张图片     
dprintf(SPEW, "Display Init: Done\n");
} #ifdef FEATURE_LOW_POWER_DISP_LK
if(is_low_voltage) {           //如果电量低,则显示关机动画,并关闭设备
mdelay();
//target_uninit();
target_display_shutdown();
shutdown_device();
}
#endif is_alarm_boot = check_alarm_boot();                           //检测开机原因是否是由于关机闹钟导致 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;
dprintf(ALWAYS, "pon_reason=0x%02x\n", pon_reason); /* Check if we should do something other than booting up */
if ( (pon_reason & USB_CHG)                 //启动原因是插上USB,并且用户同时按住了音量上下键,进入下载模式
&& (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))) { display_dloadimage_on_screen();          //显示下载模式图片
volume_keys_init();             //初始化音量按键
int i = ;
int j = ;
int k = ;
dload_flag = ;
while()            //进入下载模式后,通过不同的按键组合进入不同的模式,下面的代码逻辑很简单,就不介绍了
{
thread_sleep();
//dprintf(ALWAYS, "in while circle\n");
if ( check_volume_up_key() && !check_volume_down_key() && !check_power_key() )
{
/* Hold volume_up_key 3 sec to download mode, if not enough, need to hold another 3 sec. */
for(i = ;i < ;++i)
{
thread_sleep();
if (!check_volume_up_key())
{
dprintf(ALWAYS, "press volume_up not enough time\n");
break;
}
}
if(i == )
{
break;
}
}
else if (check_power_key() && !check_volume_up_key() && !check_volume_down_key())
{
/* Hold power_key 1 sec to normal boot, if not enough, need to hold another 1 sec. */
for(j = ;j < ;++j)
{
thread_sleep();
if (!check_power_key())
{
//dprintf(ALWAYS, "press power_key not enough time\n");
break;
}
}
if(j == )
{
goto normal_boot;
}
}
else if (!check_volume_down_key() && !check_volume_up_key() && !check_power_key())
{
/* Hold no key and go to normal boot 30 sec later. */
for(k = ;k < ;++k)
{
thread_sleep();
if (check_power_key() || check_volume_up_key())
{
//dprintf(ALWAYS, "press nothing\n");
break;
}
}
if(k == )
{
//dprintf(ALWAYS, "goto normal_boot\n");
goto normal_boot;
}
}
} dprintf(CRITICAL,"dload mode key sequence detected\n");
if (set_download_mode(EMERGENCY_DLOAD))
{
dprintf(CRITICAL,"dload mode not supported by target\n");
}
else
{
reboot_device(DLOAD);
dprintf(ALWAYS,"Failed to reboot into dload mode\n");
}
boot_into_fastboot = true;         //下载模式本质上是进入fastboot
}
if (!boot_into_fastboot)    //如果不是通过usb+上下键进入下载模式
{
if (keys_get_state(KEY_HOME) || (keys_get_state(KEY_VOLUMEUP) && !keys_get_state(KEY_VOLUMEDOWN))) //上键+电源键 进入recovery模式
{
boot_into_recovery = ;
struct recovery_message msg;
strcpy(msg.recovery, "recovery\n--show_text"); } if (!boot_into_recovery &&
(keys_get_state(KEY_BACK) || (keys_get_state(KEY_VOLUMEDOWN) && !keys_get_state(KEY_VOLUMEUP))))   //下键+back键进入fastboot模式,我的手机是有back实体键的
boot_into_fastboot = true;
} reboot_mode = check_reboot_mode();                          //检测开机原因,并且修改相应的标志位
hard_reboot_mode = check_hard_reboot_mode();
if (reboot_mode == RECOVERY_MODE ||
hard_reboot_mode == RECOVERY_HARD_RESET_MODE) {
boot_into_recovery = ;
} 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; }
else if (reboot_mode == DM_VERITY_ENFORCING)
{
device.verity_mode = ;
write_device_info(&device);
} else if(reboot_mode == DM_VERITY_LOGGING) {
device.verity_mode = ;
write_device_info(&device);
} else if(reboot_mode == DM_VERITY_KEYSCLEAR) {
if(send_delete_keys_to_tz())
ASSERT();
} normal_boot:
if(dload_flag){
display_image_on_screen();                 //显示界面,上面提到过
}
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(); /* initialize and start fastboot */
fastboot_init(target_get_scratch_address(), target_get_max_flash_size());     //初始化fastboot
#if FBCON_DISPLAY_MSG
display_fastboot_menu_thread();         //显示fastboot界面
#endif
}

关于device_info,这里多说一点

devinfo     Device information including:iis_unlocked, is_tampered, is_verified, charger_screen_enabled, display_panel, bootloader_version, radio_version
All these attirbutes are set based on some specific conditions and written on devinfo partition.

devinfo是一个独立的分区,里面存放了下面的一些信息,上面是高通对这个分区的介绍。
struct device_info
{
unsigned char magic[DEVICE_MAGIC_SIZE];
bool is_unlocked;
bool is_tampered;
bool is_verified;
bool charger_screen_enabled;
char display_panel[MAX_PANEL_ID_LEN];
char bootloader_version[MAX_VERSION_LEN];
char radio_version[MAX_VERSION_LEN];
};

从上面的分析,我们大致可以知道boot_init()主要工作

1).确定page_size大小;

2).从devinfo分区获取devinfo信息;

3).通过不同按键选择设置对应标志位boot_into_xxx;

4).如果进入fastboot模式,初始化fastboot命令等。

5).进入boot_linux_from_mmc()函数。

下面分析lk启动过程中另一个重要的函数boot_linux_from_mmc();它主要负责根据boot_into_xxx从对应的分区内读取相关信息并传给kernel,然后引导kernel。

程序走到这,说成没有进入fastboot模式,可能的情况有:正常启动,进入recovery,开机闹钟启动。

boot_linux_from_mmc()主要做下面的事情

1).程序会从boot分区或者recovery分区的header中读取地址等信息,然后把kernel、ramdisk加载到内存中。

2).程序会从misc分区中读取bootloader_message结构体,如果有boot-recovery,则进入recovery模式

3).更新cmdline,然后把cmdline写到tags_addr地址,把参数传给kernel,kernel起来以后会到这个地址读取参数。

int boot_linux_from_mmc(void)                                  
{
struct boot_img_hdr *hdr = (void*) buf;       //************buf和hdr指向相同的地址,可以理解为buf就是hdr
struct boot_img_hdr *uhdr;
unsigned offset = ;
int rcode;
unsigned long long ptn = ;
int index = INVALID_PTN; unsigned char *image_addr = ;
unsigned kernel_actual;
unsigned ramdisk_actual;
unsigned imagesize_actual;
unsigned second_actual = ; unsigned int dtb_size = ;
unsigned int out_len = ;
unsigned int out_avai_len = ;
unsigned char *out_addr = NULL;
uint32_t dtb_offset = ;
unsigned char *kernel_start_addr = NULL;
unsigned int kernel_size = ;
int rc; #if DEVICE_TREE                    
struct dt_table *table;
struct dt_entry dt_entry;
unsigned dt_table_offset;
uint32_t dt_actual;
uint32_t dt_hdr_size;
unsigned char *best_match_dt_addr = NULL;
#endif
struct kernel64_hdr *kptr = NULL; if (check_format_bit())                        //查找bootselect分区,查看分区表,没有此分区,所以返回值为false
boot_into_recovery = ; if (!boot_into_recovery) {                     //此时有两种可能,正常开机/进入ffbm工厂测试模式,进入工厂测试模式是正行启动,但是向kernel传参会多一个字符串"androidboot.mode='ffbm_mode_string'"
memset(ffbm_mode_string, '\0', sizeof(ffbm_mode_string));     //ffbm_mode_string = ""
rcode = get_ffbm(ffbm_mode_string, sizeof(ffbm_mode_string));  //从misc分区0地址中读取sizeof(ffbm_mode_string)的内容,如果内容是"ffbm-",返回1,否则返回0
if (rcode <= ) {
boot_into_ffbm = false;
if (rcode < )
dprintf(CRITICAL,"failed to get ffbm cookie");
} else
boot_into_ffbm = true;
} else                                     //boot_into_recovery=true
boot_into_ffbm = false;
uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;           //uhdr指向boot分区header地址,header是什么东西,下面会详细介绍
if (!memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {      //检查uhdr->magic 是否等于 "ANDROID!",不知到为什么要这么做,觉的没有什么作用
dprintf(INFO, "Unified boot method!\n");
hdr = uhdr;
goto unified_boot;
}
if (!boot_into_recovery) {    //如果不是recovery模式,可能是正常启动或者进入ffbm,再次生命ffbm和正常启动流程一样启动kernel,只是kernel起来以后,init.c文件会读取是否有"ffbm-"
index = partition_get_index("boot");         //读取boot分区
ptn = partition_get_offset(index);      //读取boot分区的偏移量
if(ptn == ) {
dprintf(CRITICAL, "ERROR: No boot partition found\n");
return -;
}
}
else {
index = partition_get_index("recovery");        //进入recovery模式,读取recovery分区,并获得recovery分区的偏移量。recovery.img和boot.img的组成是一样的,下面有介绍
ptn = partition_get_offset(index);
if(ptn == ) {
dprintf(CRITICAL, "ERROR: No recovery partition found\n");
return -;
}
}
/* Set Lun for boot & recovery partitions */
mmc_set_lun(partition_get_lun(index));         if (mmc_read(ptn + offset, (uint32_t *) buf, page_size)) {                 //从boot/recovery分区读取1字节的内容到buf(hdr)中,我们知道在boot/recovery中开始的1字节存放的是hdr的内容,下面有详细的介绍。
dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");
return -;
} if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {                   //上面已经从boot/recovery分区读取了header到hdr,这里对比magic是否等于"ANDROID!",如果不是,则表明读取的header是错误的,也算是校验吧
dprintf(CRITICAL, "ERROR: Invalid boot image header\n");
return -;
} if (hdr->page_size && (hdr->page_size != page_size)) {                   //比较也的大小是否相同,应该都是相同的2048字节 if (hdr->page_size > BOOT_IMG_MAX_PAGE_SIZE) {
dprintf(CRITICAL, "ERROR: Invalid page size\n");
return -;
}
page_size = hdr->page_size;
page_mask = page_size - ;
} /* ensure commandline is terminated */
hdr->cmdline[BOOT_ARGS_SIZE-] = ;          kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, page_mask);          //kernel所占的页的总大小       例如kernel大小0x01,kernel_actual = 2048
ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);          //ramdisk所占的页的总大小 image_addr = (unsigned char *)target_get_scratch_address();             #if DEVICE_TREE
dt_actual = ROUND_TO_PAGE(hdr->dt_size, page_mask);     //dt所占的页的大小
imagesize_actual = (page_size + kernel_actual + ramdisk_actual + dt_actual);          //image占的页的总大小
#else
imagesize_actual = (page_size + kernel_actual + ramdisk_actual);
#endif #if VERIFIED_BOOT
boot_verifier_init();   //校验boot
#endif if (check_aboot_addr_range_overlap((uint32_t) image_addr, imagesize_actual))       //校验image_addr是否被覆盖
{
dprintf(CRITICAL, "Boot image buffer address overlaps with aboot addresses.\n");
return -;
} /*
* Update loading flow of bootimage to support compressed/uncompressed
* bootimage on both 64bit and 32bit platform.
* 1. Load bootimage from emmc partition onto DDR.
* 2. Check if bootimage is gzip format. If yes, decompress compressed kernel
* 3. Check kernel header and update kernel load addr for 64bit and 32bit
* platform accordingly.
* 4. Sanity Check on kernel_addr and ramdisk_addr and copy data.
*/ dprintf(INFO, "Loading boot image (%d): start\n", imagesize_actual);
bs_set_timestamp(BS_KERNEL_LOAD_START); /* Read image without signature */
if (mmc_read(ptn + offset, (void *)image_addr, imagesize_actual))        //读取boot/recovery分区到image_addr
{
dprintf(CRITICAL, "ERROR: Cannot read boot image\n");
return -;
} dprintf(INFO, "Loading boot image (%d): done\n", imagesize_actual);
bs_set_timestamp(BS_KERNEL_LOAD_DONE); /* Authenticate Kernel */
dprintf(INFO, "use_signed_kernel=%d, is_unlocked=%d, is_tampered=%d.\n",
(int) target_use_signed_kernel(),
device.is_unlocked,
device.is_tampered); if(target_use_signed_kernel() && (!device.is_unlocked))               //这里是false ,感兴趣可以追target_use_signed_kernel(),会发现这个函数返回的是0
{
offset = imagesize_actual;uhdr->magic
if (check_aboot_addr_range_overlap((uint32_t)image_addr + offset, page_size))
{
dprintf(CRITICAL, "Signature read buffer address overlaps with aboot addresses.\n");
return -;
} /* Read signature */
if(mmc_read(ptn + offset, (voidffbm_mode_string *)(image_addr + offset), page_size))
{
dprintf(CRITICAL, "ERROR: Cannot read boot image signature\n");
return -;
} verify_signed_bootimg((uint32_t)image_addr, imagesize_actual);
} else {
second_actual = ROUND_TO_PAGE(hdr->second_size, page_mask);     
#ifdef TZ_SAVE_KERNEL_HASH
aboot_save_boot_hash_mmc((uint32_t) image_addr, imagesize_actual);
#endif /* TZ_SAVE_KERNEL_HASH */ #if VERIFIED_BOOT
if(boot_verify_get_state() == ORANGE)    //校验boot
{
#if FBCON_DISPLAY_MSG
display_bootverify_menu_thread(DISPLAY_MENU_ORANGE);
wait_for_users_action();
#else
dprintf(CRITICAL,
"Your device has been unlocked and can't be trusted.\nWait for 5 seconds before proceeding\n");
mdelay();
#endif
set_root_flag(ORANGE,);
}
#endif #ifdef MDTP_SUPPORT
{
/* Verify MDTP lock.
* For boot & recovery partitions, MDTP will use boot_verifier APIs,
* since verification was skipped in aboot. The signature is not part of the loaded image.
*/
mdtp_ext_partition_verification_t ext_partition;
ext_partition.partition = boot_into_recovery ? MDTP_PARTITION_RECOVERY : MDTP_PARTITION_BOOT;
ext_partition.integrity_state = MDTP_PARTITION_STATE_UNSET;
ext_partition.page_size = page_size;
ext_partition.image_addr = (uint32)image_addr;
ext_partition.image_size = imagesize_actual;
ext_partition.sig_avail = FALSE;
mdtp_fwlock_verify_lock(&ext_partition);
}
#endif /* MDTP_SUPPORT */
} #if VERIFIED_BOOT
#if !VBOOT_MOTA
// send root of trust
if(!send_rot_command((uint32_t)device.is_unlocked))
ASSERT();
#endif
#endif
/*
* Check if the kernel image is a gzip package. If yes, need to decompress it.
* If not, continue booting.
*/
       //检测kernel image是否是gzip的包,如果是,解压,如果不是,继续boot。得到kernel的起始地址和大小
if (is_gzip_package((unsigned char *)(image_addr + page_size), hdr->kernel_size))
{
out_addr = (unsigned char *)(image_addr + imagesize_actual + page_size);
out_avai_len = target_get_max_flash_size() - imagesize_actual - page_size;
dprintf(INFO, "decompressing kernel image: start\n");
rc = decompress((unsigned char *)(image_addr + page_size),
hdr->kernel_size, out_addr, out_avai_len,
&dtb_offset, &out_len);
if (rc)
{
dprintf(CRITICAL, "decompressing kernel image failed!!!\n");
ASSERT();
} dprintf(INFO, "decompressing kernel image: done\n");
kptr = (struct kernel64_hdr *)out_addr;
kernel_start_addr = out_addr;
kernel_size = out_len;
} else {
kptr = (struct kernel64_hdr *)(image_addr + page_size);
kernel_start_addr = (unsigned char *)(image_addr + page_size);   //kernel_start起始地址
kernel_size = hdr->kernel_size; //kernel大小
} /*
* Update the kernel/ramdisk/tags address if the boot image header
* has default values, these default values come from mkbootimg when
* the boot image is flashed using fastboot flash:raw
*/
update_ker_tags_rdisk_addr(hdr, IS_ARM64(kptr)); //更新kernel/tags/ramdisk地址   /* Get virtual addresses since the hdr saves physical addresses. */
hdr->kernel_addr = VA((addr_t)(hdr->kernel_addr));        //保存虚拟地址(mmu)
hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));
hdr->tags_addr = VA((addr_t)(hdr->tags_addr)); kernel_size = ROUND_TO_PAGE(kernel_size, page_mask);
/* Check if the addresses in the header are valid. */
if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_size) ||                      //检测kernel/ramdisk/tags地址是否超出emmc地址
check_aboot_addr_range_overlap(hdr->ramdisk_addr, ramdisk_actual))
{
dprintf(CRITICAL, "kernel/ramdisk addresses overlap with aboot addresses.\n");
return -;
} #ifndef DEVICE_TREE
if (check_aboot_addr_range_overlap(hdr->tags_addr, MAX_TAGS_SIZE))
{
dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
return -;
}
#endif /* Move kernel, ramdisk and device tree to correct address */
memmove((void*) hdr->kernel_addr, kernel_start_addr, kernel_size);       //把kernel/ramdisk放在相应的地址上
memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size); #if DEVICE_TREE   //读取设备树信息,放在相应的地址上
if(hdr->dt_size) {
dt_table_offset = ((uint32_t)image_addr + page_size + kernel_actual + ramdisk_actual + second_actual);
table = (struct dt_table*) dt_table_offset; if (dev_tree_validate(table, hdr->page_size, &dt_hdr_size) != ) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -;
} /* Find index of device tree within device tree table */
if(dev_tree_get_entry_info(table, &dt_entry) != ){
dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
return -;
} if (is_gzip_package((unsigned char *)dt_table_offset + dt_entry.offset, dt_entry.size))
{
unsigned int compressed_size = ;
out_addr += out_len;
out_avai_len -= out_len;
dprintf(INFO, "decompressing dtb: start\n");
rc = decompress((unsigned char *)dt_table_offset + dt_entry.offset,
dt_entry.size, out_addr, out_avai_len,
&compressed_size, &dtb_size);
if (rc)
{
dprintf(CRITICAL, "decompressing dtb failed!!!\n");
ASSERT();
} dprintf(INFO, "decompressing dtb: done\n");
best_match_dt_addr = out_addr;
} else {
best_match_dt_addr = (unsigned char *)dt_table_offset + dt_entry.offset;
dtb_size = dt_entry.size;
} /* Validate and Read device device tree in the tags_addr */
if (check_aboot_addr_range_overlap(hdr->tags_addr, dtb_size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -;
} memmove((void *)hdr->tags_addr, (char *)best_match_dt_addr, dtb_size);
} else {
/* Validate the tags_addr */
if (check_aboot_addr_range_overlap(hdr->tags_addr, kernel_actual))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -;
}
/*
* If appended dev tree is found, update the atags with
* memory address to the DTB appended location on RAM.
* Else update with the atags address in the kernel header
*/
void *dtb;
dtb = dev_tree_appended((void*)(image_addr + page_size),
hdr->kernel_size, dtb_offset,
(void *)hdr->tags_addr);
if (!dtb) {
dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
return -;
}
}
#endif if (boot_into_recovery && !device.is_unlocked && !device.is_tampered)
target_load_ssd_keystore(); unified_boot: boot_linux((void *)hdr->kernel_addr, (void *)hdr->tags_addr,           //进入boot_linux函数,此函数比较简单,更新cmdline。
(const char *)hdr->cmdline, board_machtype(),
(void *)hdr->ramdisk_addr, hdr->ramdisk_size); return ;
}

如果misc分区的0地址内容是"ffbm-",则boot_into_ffbm=true

int get_ffbm(char *ffbm, unsigned size)
{
const char *ffbm_cmd = "ffbm-";
uint32_t page_size = get_page_size();
char *ffbm_page_buffer = NULL;
int retval = ;
if (size < FFBM_MODE_BUF_SIZE || size >= page_size)
{
dprintf(CRITICAL, "Invalid size argument passed to get_ffbm\n");
retval = -;
goto cleanup;
}
ffbm_page_buffer = (char*)malloc(page_size);
if (!ffbm_page_buffer)
{
dprintf(CRITICAL, "Failed to alloc buffer for ffbm cookie\n");
retval = -;
goto cleanup;
}
if (read_misc(, ffbm_page_buffer, page_size))
{
dprintf(CRITICAL, "Error reading MISC partition\n");
retval = -;
goto cleanup;
}
ffbm_page_buffer[size] = '\0';
if (strncmp(ffbm_cmd, ffbm_page_buffer, strlen(ffbm_cmd)))
{
retval = ;
goto cleanup;
}
else
{
if (strlcpy(ffbm, ffbm_page_buffer, size) <
FFBM_MODE_BUF_SIZE -)
{
dprintf(CRITICAL, "Invalid string in misc partition\n");
retval = -;
}
else
retval = ;
}
cleanup:
if(ffbm_page_buffer)
free(ffbm_page_buffer);
return retval;
}

boot.img和recovery.img的组成是一样的,所以lk加载方式一样,只是读取的地址和大小不同而已。

我们看下boot.img和recovery.img镜像里有什么,理解了这个再看lk加载boot.img/recovery.img就知道是怎么回事了:

** +-----------------+
** | boot header | 1 page
** +-----------------+
** | kernel | n pages
** +-----------------+
** | ramdisk | m pages
** +-----------------+
** | second stage | o pages
** +-----------------+
** | device tree | p pages
** +-----------------+
  
分析boot_img_hdr结构提
  kernel_size  kernel表示zImage的实际大小
  kernel_addr  kernel的zImage载入内存的物理地址,也是bootloader要跳转的地址
  ramdisk_size  ramdisk的实际大小
  ramdisk_addr  ramdisk加载到内存的实际物理地址,之后kernel会解压并把它挂载成根文件系统,我们的中枢神经-init.rc就隐藏于内
  tags_addr    tags_addr是传参数用的物理内存地址,它作用是把bootloader中的参数传递给kernel,参数放在这个地址上
  page_size    page_size是存储芯片(ram/emmc)的页大小,取决与存储芯片
  cmdline      command line它可以由bootloader向kernel传参的内容,存放在tag_addr地址
  second     可选
bootable/bootloader/lk/app/aboot/bootimg.h

#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_ typedef struct boot_img_hdr boot_img_hdr; #define BOOT_MAGIC "ANDROID!"
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512
#define BOOT_IMG_MAX_PAGE_SIZE 4096 struct boot_img_hdr
{
unsigned char magic[BOOT_MAGIC_SIZE]; unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */ unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */ unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */ unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned dt_size; /* device_tree in bytes */
unsigned unused; /* future expansion: should be 0 */ unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ unsigned char cmdline[BOOT_ARGS_SIZE]; unsigned id[]; /* timestamp / checksum / sha1 / etc */
}; /*
** +-----------------+
** | boot header | 1 page
** +-----------------+
** | kernel | n pages
** +-----------------+
** | ramdisk | m pages
** +-----------------+
** | second stage | o pages
** +-----------------+
** | device tree | p pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
** p = (dt_size + page_size - 1) / page_size
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
** 2. second is optional (second_size == 0 -> no second)
** 3. load each element (kernel, ramdisk, second) at
** the specified physical address (kernel_addr, etc)
** 4. prepare tags at tag_addr. kernel_args[] is
** appended to the kernel commandline in the tags.
** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
** 6. if second_size != 0: jump to second_addr
** else: jump to kernel_addr
*/ boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
void *ramdisk, unsigned ramdisk_size,
void *second, unsigned second_size,
unsigned page_size,
unsigned *bootimg_size); void bootimg_set_cmdline(boot_img_hdr *hdr, const char *cmdline); #define KERNEL64_HDR_MAGIC 0x644D5241 /* ARM64 */ struct kernel64_hdr
{
uint32_t insn;
uint32_t res1;
uint64_t text_offset;
uint64_t res2;
uint64_t res3;
uint64_t res4;
uint64_t res5;
uint64_t res6;
uint32_t magic_64;
uint32_t res7;
}; #endif

lk启动流程详细分析的更多相关文章

  1. 海思uboot启动流程详细分析(转)

    海思uboot启动流程详细分析(一) 海思uboot启动流程详细分析(二) 海思uboot启动流程详细分析(三)  

  2. 海思uboot启动流程详细分析(三)【转】

    1. 前言 书接上文(u-boot启动流程分析(二)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_init_r所代表的. ...

  3. 海思uboot启动流程详细分析(二)

    1. 第二个start.S 从start_armboot开始,在startup.c中有包含#include <config.h> 在config.h中: /* Automatically ...

  4. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  5. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  6. 海思uboot启动流程详细分析(一)

    第一阶段 start.S 首先我们可以在u-boot.lds中看到ENTRY(_start),即指定了入口_start,_start也就是整个start.S的最开始: 1. reset 在arch\a ...

  7. Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

  8. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动

    Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  9. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

    Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...

随机推荐

  1. notepad++汉字突然横过来了

    修改notepad++,汉字突然横过来了,如图, 百度了一下,原来是因为选择的字体"@微软雅黑"前面的@符号惹的祸,改成"微软雅黑"就没事了.

  2. codeforces 630A Again Twenty Five!

    A. Again Twenty Five! time limit per test 0.5 seconds memory limit per test 64 megabytes input stand ...

  3. CSS构造超链接

    超链接边框 派生超链接 属性选择器超链接 动态超链接 图像翻转超链接 CSS工具提示 1.给链接加上边框     A:link {         Color: #f00;         Text- ...

  4. zookeeper的配置项

    1 tickTime:CS通信心跳数 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳.tickTime以毫秒为单位. tick ...

  5. 创建类模式(三):创建者(Builder)

    定义 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.这使得构件算法和组装方式可以独立应对变化:复用同样的构建算法可以创建不同的表示,不同的构建过程可以复用相同的部件组装方式 ...

  6. Java学习笔记(七):内部类、静态类和泛型

    内部类 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类.下面就先来了解一下这四种 ...

  7. Model First:创建实体数据模型(ADO.NET 实体数据模型)

    Microsoft Entity Framework是一个对象关系映射工具(Object Relational Mapping ,O/RM)工具.它可以让你从一个数据库自动地生成数据接入层.实体框架免 ...

  8. (转)HTML5实战与剖析之触摸事件(touchstart、touchmove和touchend)

    HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主 ...

  9. windows 下实现函数打桩:拦截API方式

    windows 下实现函数打桩:拦截API方式            近期由于工作须要,開始研究函数打桩的方法. 由于不想对project做过多的改动,于是放弃了使用Google gmock的想法. ...

  10. 从零开始学android开发- 应用程序窗体显示状态操作requestWindowFeature

    我们在开发程序是经常会需要软件全屏显示.自定义标题(使用按钮等控件)和其他的需求,今天这一讲就是如何控制Android应用程序的窗体显示. 首先介绍一个重要方法那就是requestWindowFeat ...