little kernel做为Android系统的bootloader,最早用google工程师实现,其后由高通,MTK等芯片厂商做了各自平台的适配。
lk的主要功能:

  • 初始化硬件模块,如时钟,中断,UART,USB,LCD,PMIC,eMMC/UFS等。
  • 更新cmdline。其中重要的是区分启动模式。
  • 选择和更新device tree。
  • 设置好系统状态,跳转到kernel。 MMU = off, D-cache = off, I-cache = on or off,x0 = physical address to the FDT blob。
  • fastboot功能。
  • 鉴权。

追踪代码可以看到c语言的入口函数为kmain,定义在lk/kernel/main.c中,然后用apps_init()函数调用lk中的另一个关键的c语言函数aboot_init()。由aboot_init()调用boot_linux_from_mmc()
再由boot_linux_from_mmc()调用boot_linux()
最后由boot_linux()调用entry()函数(32位kernel)或者scm_elexec_call()(64位kernel),完成lk到kernel的跳转。
lk初始化调用流程如下图:

lk/arch/crt0.s中的_start函数为入口函数,crt0.s主要初始化CPU,然后长跳转(bl)到lk/kernel/main.c中kmain函数。

#define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5
#define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5.section ".text.boot"
.globl _start                     // 声明全局符号_start
_start:                           // _start中值即为当前地址 /*设置异常向量表,从0地址开始,存放在8*4字节的连续内存中。需要将协处理CP15中的c1控制寄存器的中的V位配为0*/b   reset                     //跳转到resetb   arm_undefinedb   arm_syscallb   arm_prefetch_abortb   arm_data_abortb   arm_reservedb   arm_irqb   arm_fiqreset:#ifdef ENABLE_TRUSTZONE/*Add reference to TZ symbol so linker includes it in final image */ldr r7, =_binary_tzbsp_tzbsp_bin_start
#endif/* do some cpu setup */
#if ARM_WITH_CP15/* Read SCTLR */mrc     p15, 0, r0, c1, c0, 0/* XXX this is currently for arm926, revist with armv6 cores *//* new thumb behavior, low exception vectors, i/d cache disable, mmu disabled */bic     r0, r0, #(1<<15| 1<<13 | 1<<12)bic     r0, r0, #(1<<2 | 1<<0)/* enable alignment faults */orr     r0, r0, #(1<<1)/* Write SCTLR */mcr     p15, 0, r0, c1, c0, 0
#ifdef ENABLE_TRUSTZONE/*nkazi: not needed ? Setting VBAR to location of new vector table : 0x80000      */ldr             r0, =0x00080000mcr             p15, 0, r0, c12, c0, 0
#endif
#endif#if WITH_CPU_EARLY_INIT/* call platform/arch/etc specific init code */
#ifndef ENABLE_TRUSTZONE/* Not needed when TrustZone is the first bootloader that runs.*/bl __cpu_early_init
#endif/* declare return address as global to avoid using stack */
.globl _cpu_early_init_complete_cpu_early_init_complete:#endif#if (!ENABLE_NANDWRITE)
#if WITH_CPU_WARM_BOOTldr     r0, warm_boot_tagcmp     r0, #1/* if set, warm boot */ldreq   pc, =BASE_ADDRmov     r0, #1str r0, warm_boot_tag
#endif
#endif/* see if we need to relocate */      //判断是否需要代码重定位mov     r0, pcsub     r0, r0, #(.Laddr - _start)      //计算出_start的内存地址,保存在r0
.Laddr:ldr     r1, =_start        //加载_start的代码地址到r1cmp     r0, r1beq     .Lstack_setup/* we need to relocate ourselves to the proper spot */ldr     r2, =__data_end .Lrelocate_loop:     //进行循环拷贝,将代码段拷贝到代码地址处ldr     r3, [r0], #4str     r3, [r1], #4cmp     r1, r2                    //判断拷贝是否完成bne     .Lrelocate_loop          //跳转到代码段的.Lstack_setup,继续执行/* we're relocated, jump to the right address */ldr     r0, =.Lstack_setupbx      r0.ltorg
#if WITH_CPU_WARM_BOOT
warm_boot_tag:.word 0              //分配一个32bit的内存,并初始化为0
#endif.Lstack_setup:/* set up the stack for irq, fiq, abort, undefined, system/user, and lastly supervisor mode */mrs     r0, cpsrbic     r0, r0, #0x1fldr     r2, =abort_stack_toporr     r1, r0, #0x12 // irqmsr     cpsr_c, r1ldr     r13, =irq_save_spot     /* save a pointer to a temporary dumping spot used during irq delivery */           // 将全局符号irq_save_spot的地址赋给r13orr     r1, r0, #0x11 // fiq    0b10001msr     cpsr_c, r1    // 设置fiq模式mov     sp, r2        //设置fiq模式的堆栈orr     r1, r0, #0x17 // abortmsr     cpsr_c, r1mov     sp, r2orr     r1, r0, #0x1b // undefinedmsr     cpsr_c, r1mov     sp, r2orr     r1, r0, #0x1f // systemmsr     cpsr_c, r1mov     sp, r2orr     r1, r0, #0x13 // supervisormsr     cpsr_c, r1mov     sp, r2/* copy the initialized data segment out of rom if necessary */ldr     r0, =__data_start_romldr     r1, =__data_startldr     r2, =__data_endcmp     r0, r1beq     .L__do_bss //比较__data_start_rom和__data_start的内存地址是否相等,如果相等则跳转到.L__do_bss处,否则继续执行.L__copy_loop:cmp     r1, r2ldrlt   r3, [r0], #4strlt   r3, [r1], #4blt     .L__copy_loop        //完成数据段的拷贝.L__do_bss:/* clear out the bss */ldr     r0, =__bss_startldr     r1, =_endmov     r2, #0
.L__bss_loop:cmp     r0, r1strlt   r2, [r0], #4blt     .L__bss_loop      //完成bss段的清零#ifdef ARM_CPU_CORTEX_A8DSBISB
#endifbl      kmain   //跳转到kmain(lk代码kernel/main.c中)处继续执行b       ..ltorg.bss
.align 2/* the abort stack is for unrecoverable errors.* also note the initial working stack is set to here.* when the threading system starts up it'll switch to a new * dynamically allocated stack, so we don't need it for very long*/
abort_stack:.skip 1024     //异常堆栈的大小1024字节
abort_stack_top:

以上汇编初始化代码最终跳转到kmain函数中,进入C代码,kmain函数定义在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 contextthread_init_early();          //初始化lk的线程系统// early arch stuffarch_early_init();            //架构相关早期初始化,如使能mmu,cache等// do any super early platform initializationplatform_early_init();       //平台相关早期初始化,如获取板级信息,初始化时钟、中断、定时器等// do any super early target initializationtarget_early_init();        //初始化目标,其中只初始化了串口dprintf(INFO, "welcome to lk\n\n");bs_set_timestamp(BS_BL_START);      //设置bootloader初始的时间戳// deal with any static constructorsdprintf(SPEW, "calling constructors\n");call_constructors();                 //构造函数相关初始化// bring up the kernel heapdprintf(SPEW, "initializing heap\n");heap_init();                    //堆初始化,用于malloc等函数的内存分配// initialize the threading systemdprintf(SPEW, "initializing threads\n");thread_init();              //仅简单的初始化了定时器对象// initialize the dpc systemdprintf(SPEW, "initializing dpc\n");dpc_init();             //delayed procedure call 延迟过程调用// initialize kernel timersdprintf(SPEW, "initializing timers\n");timer_init();            //初始化定时器#if (!ENABLE_NANDWRITE)// create a thread to complete system initializationdprintf(SPEW, "creating bootstrap completion thread\n");thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));    //创建并唤醒bootstrap2线程,用于进一步完成bootloader工作// enable interruptsexit_critical_section();   //使能中断,执行后critical_section_count等于0// become the idle threadthread_become_idle();           //将本线程切换到idle状态
#elsebootstrap_nandwrite();
#endif
}

其中 thread_init_early 函数


/*** @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++)     //NUM_PRIORITIES=32list_initialize(&run_queue[i]);   //初始化32个运行队列/* initialize the thread list */list_initialize(&thread_list);         //初始化运行队列/* create a thread to cover the current running state */thread_t *t = &bootstrap_thread;   //bootstrap_thread是一个全局静态thread_t结构体init_thread_struct(t, "bootstrap");  //对bootstrap_thread进行清零,设置thread魔数,设置thread name为bootstrap/* half construct this thread, since we're already running */t->priority = HIGHEST_PRIORITY;         //设置bootstrap优先级为最高优先级t->state = THREAD_RUNNING;              //设置bootstrap状态为正在运行t->saved_critical_section_count = 1;list_add_head(&thread_list, &t->thread_list_node); //将bootstrap加入thread链表中current_thread = t;
}

arch_early_init 函数


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_A8set_vector_base(MEMBASE);    //armv8重新设置异常向量表到MEMBASE
#endif#if ARM_WITH_MMUarm_mmu_init();      //初始化mmu#endif/* turn the cache back on */arch_enable_cache(UCACHE);    //使能cache/*NEON 技术是 ARM Cortex™-A 系列处理器的 128 位 SIMD(单指令,多数据)架构扩展,旨在为消费性多媒体应用程序提供灵活、强大的加速功能,从而显著改善用户体验。*/
#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));/* 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
}

platform_early_init 函数,在目标平台中定义,这里以mdm9x25为例:


void platform_early_init(void)
{/* Initialize board identifier data */board_init();       //从共享内存中读取板级信息/* Initialize clock driver */platform_clock_init();     //初始化时钟/* Initialize interrupt controller */qgic_init();         //初始化ARM GIC中断控制器/* timer */qtimer_init();   //初始化timer,只是获取了计时器的频率
}

target_early_init 函数,在目标平台定义:


void target_early_init(void)
{
#if WITH_DEBUG_UARTuart_dm_init(3, 0, MSM_UART2_BASE);  //初始化串口,lk的log可以通过串口打印到终端
#endif
}

heap_init 函数,定义在lk/lib/heap/heap.c中:


void heap_init(void)
{LTRACE_ENTRY;// set the heap rangetheheap.base = (void *)HEAP_START;  //lk bss段的结束地址theheap.len = HEAP_LEN;    //lk的内存空间结束地址减去lk bss段的结束地址LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);// initialize the free listlist_initialize(&theheap.free_list);  //初始化空闲的heap链表// create an initial free chunk // 现将所有heap可用内存创建一个大的chunk,然后将它插入空闲的heap链表中heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));// dump heap info
//  heap_dump();//  dprintf(INFO, "running heap tests\n");
//  heap_test();
}

dpc_init函数,定义在lk/kernel/dpc.c中:


void dpc_init(void)
{event_init(&dpc_event, false, 0); //初始化一个dpc_eventthread_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE));   //创建一个dpc thread,用于在thread退出时,做一些清除动作
}

timer_init 函数,定义在lk/kernel/timer.c中


void timer_init(void)
{list_initialize(&timer_queue);/* register for a periodic timer tick */platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */// 设置一个间隔10ms的timer中断,timer_tick为中断处理函数
}

exit_critical_section 函数,定义在lk/include/kernel/thread.h中:


static inline __ALWAYS_INLINE void exit_critical_section(void)
{critical_section_count--;if (critical_section_count == 0)arch_enable_ints();    //当critical_section_count为0时使能中断
}

thread_become_idle 函数


/*** @brief  Become an idle thread** This function marks the current thread as the idle thread -- the one which* executes when there is nothing else to do.  This function does not return.* This function is called once at boot time.*/
void thread_become_idle(void)
{thread_set_name("idle");  //将bootstrap线程改名为idle线程thread_set_priority(IDLE_PRIORITY);  //将优先级降为最低idle_thread = current_thread;   //idle_thread指向当前线程idle_thread_routine();  //进入cpu idle状态,等待中断唤醒
}

在kmain中完成初始化设置之后,从thread_resume函数创建bootstrap2线程,

thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

首先调用了thread_create 函数,该函数定义在lk/kernel/thread.c中:


/*** @brief  Create a new thread** This function creates a new thread.  The thread is initially suspended, so you* need to call thread_resume() to execute it.** @param  name        Name of thread* @param  entry       Entry point of thread* @param  arg         Arbitrary argument passed to entry()* @param  priority    Execution priority for the thread.* @param  stack_size  Stack size for the thread.** Thread priority is an integer from 0 (lowest) to 31 (highest).  Some standard* prioritys are defined in <kernel/thread.h>:**  HIGHEST_PRIORITY*  DPC_PRIORITY*  HIGH_PRIORITY*  DEFAULT_PRIORITY*  LOW_PRIORITY*  IDLE_PRIORITY*  LOWEST_PRIORITY** Stack size is typically set to DEFAULT_STACK_SIZE** @return  Pointer to thread object, or NULL on failure.*/
thread_t *thread_create(const char *name, thread_start_routine entry, void *arg, int priority, size_t stack_size)
{thread_t *t;t = malloc(sizeof(thread_t)); //从lk的heap中分配thread结构体内存if (!t)return NULL;init_thread_struct(t, name); //初始化thread,做了三个动作,结构体清零,设置魔数,设置namet->entry = entry;      //对thread入口函数指针进行赋值t->arg = arg;         //对thread入口函数参数进行赋值t->priority = priority; // 设置thread优先级t->saved_critical_section_count = 1; /* we always start inside a critical section */t->state = THREAD_SUSPENDED;  //设置thread的当前状态为挂起t->blocking_wait_queue = NULL;  //等待队列为空t->wait_queue_block_ret = NO_ERROR;/* create the stack */t->stack = malloc(stack_size); //为thread分配堆栈if (!t->stack) {free(t);return NULL;}t->stack_size = stack_size;   //记录堆栈大小/* inheirit thread local storage from the parent */int i;for (i=0; i < MAX_TLS_ENTRY; i++)t->tls[i] = current_thread->tls[i];/* set up the initial stack frame */arch_thread_initialize(t); //设置栈帧等/* add it to the global thread list */enter_critical_section();list_add_head(&thread_list, &t->thread_list_node); //将新创建的thread加入到lk的thread链表中exit_critical_section();return t;
}

其中使用了thread_t结构体,该结构体定义在lk/include/kernel/thread.h中:

typedef struct thread {int magic;                            //thread魔数struct list_node thread_list_node;   //thread链表/* active bits */struct list_node queue_node;          //运行队列链表int priority;                         //thread优先级enum thread_state state;             // thread状态,共6种int saved_critical_section_count;int remaining_quantum;/* if blocked, a pointer to the wait queue */struct wait_queue *blocking_wait_queue;  //等待队列status_t wait_queue_block_ret;            //等待队列唤醒时的状态/* architecture stuff */struct arch_thread arch;/* stack stuff */void *stack;                     //初始化时分配的堆栈空间指针        size_t stack_size;               //初始化时分配的堆栈空间大小/* entry point */thread_start_routine entry;        //thread入口函数void *arg;                        //thread入口函数的参数/* return code */int retcode;                   //thread退出时的返回值/* thread local storage */uint32_t tls[MAX_TLS_ENTRY];char name[32];    //线程名称
} thread_t;

thread_resume 函数,定义在lk/kernel/thread.c中:


/*** @brief  Make a suspended thread executable.** This function is typically called to start a thread which has just been* created with thread_create()** @param t  Thread to resume** @return NO_ERROR on success, ERR_NOT_SUSPENDED if thread was not suspended.*/
status_t thread_resume(thread_t *t)
{
#if THREAD_CHECKSASSERT(t->magic == THREAD_MAGIC);ASSERT(t->state != THREAD_DEATH);
#endifif (t->state == THREAD_READY || t->state == THREAD_RUNNING) //仅当thread为挂起状态才会进行唤醒return ERR_NOT_SUSPENDED;enter_critical_section();t->state = THREAD_READY;            //设置thread状态为就绪insert_in_run_queue_head(t);         //将thread插入运行队列的头部thread_yield();                    //让渡处cpu资源,执行新的threadexit_critical_section();return NO_ERROR;
}

thread切换的过程,先切换current_thread的指针内容,使其指向新的thread,进行thread切换时,保存旧thread的context_switch_frame的地址到旧thread的arch.sp中,同时从新thread的arch.sp读取context_switch_frame中的信息到相应寄存器中,然后跳转的新thread的lr指向的代码地址继续运行,如果新thread是第一次运行那么它的lr应该指向initial_thread_func,最后在initial_thread_func函数执行current_thread->entry(current_thread->arg)执行新thread的入口函数。

创建进程,进入bootstrap2函数中,该函数定义在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_BIObio_init();
#endif
#if WITH_LIB_FSfs_init();
#endif// initialize the rest of the platformdprintf(SPEW, "initializing platform\n");platform_init();                //目前只打印了一句log// initialize the targetdprintf(SPEW, "initializing target\n");target_init();                 //目标板级初始化,初始存储设备等dprintf(SPEW, "calling apps_init()\n");apps_init();               //应用功能初始化return 0;
}

其中重要的是target_init函数和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++) { //__apps_start和__apps_end都是在连接脚本中定义的if (app->init)  app->init(app);               // 调到aboot_init}/* 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);}}
}

其中app_descriptor结构体定义在lk/include/app.h中:


/* each app needs to define one of these to define its startup conditions */
struct app_descriptor {const char *name;         //app名字app_init  init;           //初始化函数app_entry entry;          //入口函数unsigned int flags;       //标志
};

app_descriptor 结构体中init和entry是两个函数,定义在lk/include/app.h中:

typedef void (*app_init)(const struct app_descriptor *);
typedef void (*app_entry)(const struct app_descriptor *, void *args);

在aboot.c中使用了APP_START宏来填充app_descriptor 结构体,来实现lk中app的初始化调用操作,APP_START 宏定义在lk/include/app.h中

/* app startup flags */
#define APP_FLAG_DONT_START_ON_BOOT 0x1
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };

查看lk/app/aboot.c中的APP_START调用:

APP_START(aboot).init = aboot_init,
APP_END

这相当于填充了一个app_descriptor 结构体,如下:

struct app_descriptor __app_aboot_init {const char *name = "aboot_init";app_init  init = aboot_init;app_entry entry = NULL;unsigned int flags = 0;
};

从这个结构体调用了aboot_init(),在lk的分区表的名称即为aboot,该函数定义在lk/app/aboot.c中:


void aboot_init(const struct app_descriptor *app)
{unsigned reboot_mode = 0;bool boot_into_fastboot = false;/* Setup page size information for nand/emmc reads */if (target_is_emmc_boot()){page_size = 2048;           // 获取mmc/ufs的页大小,ufs为4096,emmc为2048page_mask = page_size - 1;  // 获取mmc/ufs的页掩码}else{page_size = flash_page_size();page_mask = page_size - 1;}ASSERT((MEMBASE + MEMSIZE) > MEMBASE);read_device_info(&device);  //获取device_info信息,该函数决定fastboot是否被禁掉,boot.img是否需要鉴权等target_serialno((unsigned char *) sn_buf);//获取emmc/ufs的product serial number,fastboot和cmdline都会用到dprintf(SPEW,"serial number: %s\n",sn_buf);memset(display_panel_buf, '\0', MAX_PANEL_BUF_SIZE);//清除显示面板信息,lcd屏/* Check if we should do something other than booting up */if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))//定义的按键是否同时按下{dprintf(ALWAYS,"dload mode key sequence detected\n");if (set_download_mode(EMERGENCY_DLOAD)) //尝试进入9008下载模式{dprintf(CRITICAL,"dload mode not supported by target\n");}else{reboot_device(0);dprintf(CRITICAL,"Failed to reboot into dload mode\n");}boot_into_fastboot = true; //进入下载模式失败,设置fastboot模式标志}if (!boot_into_fastboot){if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))boot_into_recovery = 1; //如果定义的按键同时按下,设置fastboot模式标志if (!boot_into_recovery &&(keys_get_state(KEY_BACK) || keys_get_state(KEY_VOLUMEDOWN)))boot_into_fastboot = true;//如果定义的按键同时按下,设置recovery模式标志}#if NO_KEYPAD_DRIVERif (fastboot_trigger())boot_into_fastboot = true;#endifreboot_mode = check_reboot_mode();//获取重启原因,设置相应的开机模式标志,之前使用的是共享内存记录重启原因,最新转为使用pmic的pon寄存器记录重启原因if (reboot_mode == RECOVERY_MODE) {boot_into_recovery = 1;} else if(reboot_mode == FASTBOOT_MODE) {boot_into_fastboot = true;}if (!boot_into_fastboot)     //从这里进入非fastboot模式{if (target_is_emmc_boot())         //从emmc/ufs启动{if(emmc_recovery_init())       //recovery模式需要的一些初始化dprintf(ALWAYS,"error in emmc_recovery_init\n");if(target_use_signed_kernel())   //判断是否使用了签名的kernel(即boot.img){if((device.is_unlocked) || (device.is_tampered)){#ifdef TZ_TAMPER_FUSEset_tamper_fuse_cmd();#endif#if USE_PCOM_SECBOOTset_tamper_flag(device.is_tampered);#endif}}boot_linux_from_mmc();//从emmc/ufs加载boot.img,选择dts,设置cmdline,跳转到kernel}else{    //这里是从nanflash启动,recovery_init();#if USE_PCOM_SECBOOTif((device.is_unlocked) || (device.is_tampered))set_tamper_flag(device.is_tampered);#endifboot_linux_from_flash();}dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting ""to fastboot mode.\n");}/* We are here means regular boot did not happen. Start fastboot. *//* register aboot specific fastboot commands */ //fasboot模式或者正常启动失败才会执行到这里aboot_fastboot_register_commands();  //注册fastboot支持的命令/* dump partition table for debug info */partition_dump();                //打印分区表信息/* initialize and start fastboot */fastboot_init(target_get_scratch_address(), target_get_max_flash_size());//初始化并启动fastboot,参数1为fastboot使用的内存buffer的地址,参数2位内存buffer的大小
}

启动kernel,调用了boot_linux_from_mmc 该函数定义在lk/app/aboot.c中

int boot_linux_from_mmc(void)
{struct boot_img_hdr *hdr = (void*) buf;struct boot_img_hdr *uhdr;unsigned offset = 0;int rcode;unsigned long long ptn = 0;int index = INVALID_PTN;unsigned char *image_addr = 0;unsigned kernel_actual;unsigned ramdisk_actual;unsigned imagesize_actual;unsigned second_actual = 0;#if DEVICE_TREEstruct dt_table *table; //一个dt_table通常包含多个dt_entrystruct dt_entry dt_entry;//需要根据硬件信息选择一个最合适的dt_entry传递给kernelunsigned dt_table_offset;uint32_t dt_actual;uint32_t dt_hdr_size;
#endifif (!boot_into_recovery) {       //进入非recovery模式memset(ffbm_mode_string, '\0', sizeof(ffbm_mode_string));rcode = get_ffbm(ffbm_mode_string, sizeof(ffbm_mode_string));//跟据misc分区信息判断是否进入ffbm模式if (rcode <= 0) {boot_into_ffbm = false;if (rcode < 0)dprintf(CRITICAL,"failed to get ffbm cookie");} elseboot_into_ffbm = true;} elseboot_into_ffbm = false;uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR;//将指定地址的内存转为boot.img的头结构体类型if (!memcmp(uhdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {  //判断kernel是否已经加载dprintf(INFO, "Unified boot method!\n");hdr = uhdr;goto unified_boot;            //如果已加载,则直接跳转到kernel,这是非正常路径}if (!boot_into_recovery) {    //非recovery模式index = partition_get_index("boot");  //获取boot分区索引ptn = partition_get_offset(index);    //获取boot分区偏移if(ptn == 0) {dprintf(CRITICAL, "ERROR: No boot partition found\n");return -1;}}else {    //如果进入recovery模式index = partition_get_index("recovery"); //获取recovery分区索引ptn = partition_get_offset(index);      //获取recovery分区偏移if(ptn == 0) {dprintf(CRITICAL, "ERROR: No recovery partition found\n");return -1;}}if (mmc_read(ptn + offset, (unsigned int *) buf, page_size)) { //从emmc/ufs读取一页数据dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");return -1;}if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { //判断boot.img头结构体的魔数是否正确dprintf(CRITICAL, "ERROR: Invalid boot image header\n");return -1;}if (hdr->page_size && (hdr->page_size != page_size)) {   //判断是否需要更新页大小page_size = hdr->page_size;page_mask = page_size - 1;}/** 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);  //更新boot.img头结构体/* Get virtual addresses since the hdr saves physical addresses. */hdr->kernel_addr = VA((addr_t)(hdr->kernel_addr));  //转换为虚拟内存地址,即运行地址hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));hdr->tags_addr = VA((addr_t)(hdr->tags_addr));kernel_actual  = ROUND_TO_PAGE(hdr->kernel_size,  page_mask); //kernel大小向上页对齐ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask); //ramdisk大小向上页对齐/* Check if the addresses in the header are valid. */// 检查kernel和ramdisk的是否与aboot的内存空间有重叠if (check_aboot_addr_range_overlap(hdr->kernel_addr, kernel_actual) ||check_aboot_addr_range_overlap(hdr->ramdisk_addr, ramdisk_actual)){dprintf(CRITICAL, "kernel/ramdisk addresses overlap with aboot addresses.\n");return -1;}#ifndef DEVICE_TREE // 检查device tree的是否与aboot的内存空间有重叠if (check_aboot_addr_range_overlap(hdr->tags_addr, MAX_TAGS_SIZE)){dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");return -1;}
#endif/* Authenticate Kernel */ //是否使用签名的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))//使用签名的kernel,并且设备未解锁,将对boot.img进行鉴权{offset = 0;image_addr = (unsigned char *)target_get_scratch_address();//获取scratch内存地址#if DEVICE_TREEdt_actual = ROUND_TO_PAGE(hdr->dt_size, page_mask);  //device tree大小向上页对齐imagesize_actual = (page_size + kernel_actual + ramdisk_actual + dt_actual);//计算引导kernel所需完整内存大小if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_actual))//检查device tree的是否与aboot的内存空间有重叠{dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");return -1;}
#elseimagesize_actual = (page_size + kernel_actual + ramdisk_actual);#endifdprintf(INFO, "Loading boot image (%d): start\n", imagesize_actual);bs_set_timestamp(BS_KERNEL_LOAD_START);//设置kernel开始加载的时间戳,在kernel log中可以看到if (check_aboot_addr_range_overlap(image_addr, imagesize_actual))// 引导kernel所需完整内存是否与aboot的内存空间有重叠{dprintf(CRITICAL, "Boot image buffer address overlaps with aboot addresses.\n");return -1;}/* Read image without signature */// 从emmc/ufs读取除签名外的imageif (mmc_read(ptn + offset, (void *)image_addr, imagesize_actual)){dprintf(CRITICAL, "ERROR: Cannot read boot image\n");return -1;}dprintf(INFO, "Loading boot image (%d): done\n", imagesize_actual);bs_set_timestamp(BS_KERNEL_LOAD_DONE); //设置kernel结束加载的时间戳offset = imagesize_actual;if (check_aboot_addr_range_overlap(image_addr + offset, page_size)){dprintf(CRITICAL, "Signature read buffer address overlaps with aboot addresses.\n");return -1;}/* Read signature */// 从emmc/ufs读取image的签名if(mmc_read(ptn + offset, (void *)(image_addr + offset), page_size)){dprintf(CRITICAL, "ERROR: Cannot read boot image signature\n");return -1;}verify_signed_bootimg(image_addr, imagesize_actual);//对image进行鉴权/* Move kernel, ramdisk and device tree to correct address */memmove((void*) hdr->kernel_addr, (char *)(image_addr + page_size), hdr->kernel_size);    //将kernel移动到正确的地址memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);   //将ramdisk移动到正确的地址#if DEVICE_TREEif(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;  //获取device tree tableif (dev_tree_validate(table, hdr->page_size, &dt_hdr_size) != 0) {//检查device tree格式是否正确dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");return -1;}/* Find index of device tree within device tree table */ //从device tree表中选择一个最合适的device tree项if(dev_tree_get_entry_info(table, &dt_entry) != 0){dprintf(CRITICAL, "ERROR: Device Tree Blob cannot be found\n");return -1;}/* Validate and Read device device tree in the "tags_add */ // 检查device tree项是否与aboot的内存空间有重叠if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size)){dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");return -1;}memmove((void *)hdr->tags_addr, (char *)dt_table_offset + dt_entry.offset, dt_entry.size);   //将device tree移动到正确的地址} else {/** 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*) hdr->kernel_addr,hdr->kernel_size,(void *)hdr->tags_addr);if (!dtb) {dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");return -1;}}#endif}else   {       //不需要对boot.img进行鉴权的情况second_actual  = ROUND_TO_PAGE(hdr->second_size,  page_mask);dprintf(INFO, "Loading boot image (%d): start\n",kernel_actual + ramdisk_actual);bs_set_timestamp(BS_KERNEL_LOAD_START);offset = page_size;/* Load kernel */if (mmc_read(ptn + offset, (void *)hdr->kernel_addr, kernel_actual)) {dprintf(CRITICAL, "ERROR: Cannot read kernel image\n");return -1;}offset += kernel_actual;/* Load ramdisk */if(ramdisk_actual != 0){if (mmc_read(ptn + offset, (void *)hdr->ramdisk_addr, ramdisk_actual)) {dprintf(CRITICAL, "ERROR: Cannot read ramdisk image\n");return -1;}}offset += ramdisk_actual;dprintf(INFO, "Loading boot image (%d): done\n",kernel_actual + ramdisk_actual);bs_set_timestamp(BS_KERNEL_LOAD_DONE);if(hdr->second_size != 0) {offset += second_actual;/* Second image loading not implemented. */ASSERT(0);}#if DEVICE_TREEif(hdr->dt_size != 0) {/* Read the first page of device tree table into buffer */if(mmc_read(ptn + offset,(unsigned int *) dt_buf, page_size)) {dprintf(CRITICAL, "ERROR: Cannot read the Device Tree Table\n");return -1;}table = (struct dt_table*) dt_buf;if (dev_tree_validate(table, hdr->page_size, &dt_hdr_size) != 0) {dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");return -1;}table = (struct dt_table*) memalign(CACHE_LINE, dt_hdr_size);if (!table)return -1;/* Read the entire device tree table into buffer */if(mmc_read(ptn + offset,(unsigned int *) table, dt_hdr_size)) {dprintf(CRITICAL, "ERROR: Cannot read the Device Tree Table\n");return -1;}/* Find index of device tree within device tree table */if(dev_tree_get_entry_info(table, &dt_entry) != 0){dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");return -1;}/* Validate and Read device device tree in the "tags_add */if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size)){dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");return -1;}if(mmc_read(ptn + offset + dt_entry.offset,(void *)hdr->tags_addr, dt_entry.size)) {dprintf(CRITICAL, "ERROR: Cannot read device tree\n");return -1;}#ifdef TZ_SAVE_KERNEL_HASHaboot_save_boot_hash_mmc(hdr->kernel_addr, kernel_actual,hdr->ramdisk_addr, ramdisk_actual,ptn, offset, hdr->dt_size);#endif /* TZ_SAVE_KERNEL_HASH */} else {/** 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*) hdr->kernel_addr,kernel_actual,(void *)hdr->tags_addr);if (!dtb) {dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");return -1;}/* 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 -1;}}#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,(const char *)hdr->cmdline, board_machtype(),(void *)hdr->ramdisk_addr, hdr->ramdisk_size);//继续加载boot,将会在这个函数中跳转到kernelreturn 0;
}

讲过以上一系列的启动加载后,开始启动kernel,调用boot_linux函数,该函数定义在lk/app/aboot.c中:

void boot_linux(void *kernel, unsigned *tags,const char *cmdline, unsigned machtype,void *ramdisk, unsigned ramdisk_size)
{unsigned char *final_cmdline;
#if DEVICE_TREEint ret = 0;
#endifvoid (*entry)(unsigned, unsigned, unsigned*) = (entry_func_ptr*)(PA((addr_t)kernel));   //将kernel的起始内存地址转化成entry_func_ptr函数类型; uint32_t tags_phys = PA((addr_t)tags);ramdisk = PA(ramdisk);final_cmdline = update_cmdline((const char*)cmdline);//更新cmdline,这里可以动态增加修改默认cmdline的内容#if DEVICE_TREEdprintf(INFO, "Updating device tree: start\n");/* Update the Device Tree */ // 更新device tree内容,主要是三部分:memory,cmdline,ramdiskret = update_device_tree((void *)tags, final_cmdline, ramdisk, ramdisk_size);if(ret){dprintf(CRITICAL, "ERROR: Updating Device Tree Failed \n");ASSERT(0);}dprintf(INFO, "Updating device tree: done\n");
#else/* Generating the Atags */generate_atags(tags, final_cmdline, ramdisk, ramdisk_size);
#endiffree(final_cmdline);//由于cmdline内容已经打包进device tree中,这里可以释放cmdline临时占用的内存/* Perform target specific cleanup */target_uninit();   //完成目标板级清除动作dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d), tags/device tree @ %p\n",entry, ramdisk, ramdisk_size, tags_phys);enter_critical_section();  //关闭中断/* do any platform specific cleanup before kernel entry */platform_uninit();       //完成平台级清除动作arch_disable_cache(UCACHE);    //禁用cache#if ARM_WITH_MMUarch_disable_mmu();      //关闭mmu
#endifbs_set_timestamp(BS_KERNEL_ENTRY);  //设置kernel入口时间戳entry(0, machtype, (unsigned*)tags_phys);//直接跳转到kernel入口函数执行;r0 = 0; r1 = machtype; r2 = device tree的物理内存地址
}

little kernel分析相关推荐

  1. 海思hi3520dv400 kernel分析(3)——设备树支持

    概念: FDT:Flattened Device Tree (扁平设备树)是一种数据结构,用来描述设备的硬件配置信息,它源自开放固件使用的设备树格式. DTS:Device tree source(设 ...

  2. kaggle竞赛系列1----elo竞赛kernel分析1

    比赛介绍: https://www.kaggle.com/c/elo-merchant-category-recommendation kerne来源: https://www.kaggle.com/ ...

  3. ecos kernel 分析

    ecos kernel 是个典型的抢占式多任务的rtos,我这里想从代码上,把它的实现搭个框架出来.     分时的多任务系统是靠定时时间中断实现的,所以我从这里做切入点 有kernel 的ecos重 ...

  4. I.MX6ULL_Linux_系统篇(20) kernel分析-menuconfig

    linux内核的功能非常丰富,大多功能可以通过menuconfig图形界面来选择配置,但是我们面对众多的功能,往往不能快速确定配置项位置或无法理解配置项功能,本篇将对配置项做一下简要解析!(作者不建议 ...

  5. Linux kernel分析前的准备

    分析工具 "欲善其事,先利其器".Linux内核的代码量非常大,如果没有一个好的察看分析工具,那将是一件非常繁琐的事情. Vim+cscope cscope,如果你知道ctags, ...

  6. kernel停在Starting kernel 分析

    解决方法: 1.看门狗问题,可能会造成这种显现. 2.IMX6Q的PMU被bypass导致cpu没有供电而无法开机,只要donwload imx6q ldo版本的boot image即可. log: ...

  7. little kernel分析 [LK]

    http://blog.csdn.net/viewsky11/article/details/53906070 little kernel做为Android系统的bootloader,最早用googl ...

  8. [IMX6Q][Android5.1]移植笔记 --- kernel停在Starting kernel 分析

    platform: imx6q os: Android5.1 branch: l5.1.1_2.1.0-ga 解决方法: IMX6Q的PMU被bypass导致cpu没有供电而无法开机,只要donwlo ...

  9. uboot2015–启动流程分析 imx6q

    最近项目原因,要在uboot中增加内核验校和内核损坏修复功能,所以需要回头看看uboot.这次选择了uboot2015来进行分析 uboot是明远睿智提供的. 下载地址 链接:https://pan. ...

最新文章

  1. lua判断字符不为空或空格_Lua判断Table是否为空的方法(空的table即{})
  2. 总结数据库设计中的14个技巧
  3. python画太极八卦图_AI剪刀工具快速绘制太极八卦图
  4. fork join框架_Java中的Fork / Join框架的简要概述
  5. 【j2ee spring】30、巴巴荆楚网-综合hibernate4+spring4(5)分页
  6. aspectj 注解
  7. 设计师网站大全视野导航,灵感、素材统统搞定
  8. JavaScript的类的实现
  9. Linux 系统级开启文件句柄 调优
  10. 传奇脚本中提到的WIL序号是什么?在哪查看WIL序号?
  11. 汽车IC TPS7A6633QDGNRQ1应用 低压降线性稳压器
  12. BIP与Siebel系统集成
  13. 百度地图标注公司那家好
  14. 我的世界电脑服务器怎么显示键盘,我的世界基本操作按键 PC版基本操作详细介绍...
  15. 判断三维空间中三点是否共线
  16. [cesium] | 3d特效渲染
  17. 这就是你日日夜夜想要的docker!!!---------docker+consul+ nginx集成分布式的服务发现与注册架构
  18. 利用python,20行代码即可实现照片墙,还可以生成爱心形状哟
  19. 服务器如何解决被流量攻击
  20. 武汉体育学院计算机考试真题,武汉体育学院近几年考研的真题等资料

热门文章

  1. 抖音快手热门特效视频用手机软件怎么制作?
  2. 工作心得——后浪寄语
  3. 计算机毕业设计Java医院就诊预约(系统+源码+mysql数据库+Lw文档)
  4. 想知道未来你娃长啥样?这里有技术可以预测!
  5. Mysql中TO_DAYS函数——查询天数
  6. matlab有哪些课程设计,MATLAB课程设计报告
  7. Adapter类控件使用之ExpandableList(可折叠式列表)的基本使用
  8. 一分钟应对勒索病毒WannaCry
  9. 什么牌子的投影仪好家用的?什么牌子投影仪好
  10. 面试官答应给8000工资,新员工入职后变4千,签合同时愣了