little kernel分析 [LK]

http://blog.csdn.net/viewsky11/article/details/53906070

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                     //跳转到reset
    b   arm_undefined
    b   arm_syscall
    b   arm_prefetch_abort
    b   arm_data_abort
    b   arm_reserved
    b   arm_irq
    b   arm_fiq

reset:

#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, =0x00080000
 mcr             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_BOOT
    ldr     r0, warm_boot_tag
    cmp     r0, #1

    /* if set, warm boot */
    ldreq   pc, =BASE_ADDR

    mov     r0, #1
    str r0, warm_boot_tag
#endif
#endif

    /* see if we need to relocate */      //判断是否需要代码重定位
    mov     r0, pc
    sub     r0, r0, #(.Laddr - _start)      //计算出_start的内存地址,保存在r0
.Laddr:
    ldr     r1, =_start        //加载_start的代码地址到r1
    cmp     r0, r1
    beq     .Lstack_setup

    /* we need to relocate ourselves to the proper spot */
    ldr     r2, =__data_end 

.Lrelocate_loop:     //进行循环拷贝,将代码段拷贝到代码地址处
    ldr     r3, [r0], #4
    str     r3, [r1], #4
    cmp     r1, r2                    //判断拷贝是否完成
    bne     .Lrelocate_loop          //跳转到代码段的.Lstack_setup,继续执行

    /* we're relocated, jump to the right address */
    ldr     r0, =.Lstack_setup
    bx      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, cpsr
    bic     r0, r0, #0x1f

    ldr     r2, =abort_stack_top
    orr     r1, r0, #0x12 // irq
    msr     cpsr_c, r1
    ldr     r13, =irq_save_spot     /* save a pointer to a temporary dumping spot used during irq delivery */           // 将全局符号irq_save_spot的地址赋给r13

    orr     r1, r0, #0x11 // fiq    0b10001
    msr     cpsr_c, r1    // 设置fiq模式
    mov     sp, r2        //设置fiq模式的堆栈

    orr     r1, r0, #0x17 // abort
    msr     cpsr_c, r1
    mov     sp, r2

    orr     r1, r0, #0x1b // undefined
    msr     cpsr_c, r1
    mov     sp, r2

    orr     r1, r0, #0x1f // system
    msr     cpsr_c, r1
    mov     sp, r2

    orr     r1, r0, #0x13 // supervisor
    msr     cpsr_c, r1
    mov     sp, r2

    /* copy the initialized data segment out of rom if necessary */
    ldr     r0, =__data_start_rom
    ldr     r1, =__data_start
    ldr     r2, =__data_end

    cmp     r0, r1
    beq     .L__do_bss //比较__data_start_rom和__data_start的内存地址是否相等,如果相等则跳转到.L__do_bss处,否则继续执行


.L__copy_loop:
    cmp     r1, r2
    ldrlt   r3, [r0], #4
    strlt   r3, [r1], #4
    blt     .L__copy_loop        //完成数据段的拷贝

.L__do_bss:
    /* clear out the bss */
    ldr     r0, =__bss_start
    ldr     r1, =_end
    mov     r2, #0
.L__bss_loop:
    cmp     r0, r1
    strlt   r2, [r0], #4
    blt     .L__bss_loop      //完成bss段的清零

#ifdef ARM_CPU_CORTEX_A8
    DSB
    ISB
#endif

    bl      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 context
    thread_init_early();          //初始化lk的线程系统

    // early arch stuff
    arch_early_init();            //架构相关早期初始化,如使能mmu,cache等

    // 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);      //设置bootloader初始的时间戳

    // deal with any static constructors
    dprintf(SPEW, "calling constructors\n");
    call_constructors();                 //构造函数相关初始化

    // bring up the kernel heap
    dprintf(SPEW, "initializing heap\n");
    heap_init();                    //堆初始化,用于malloc等函数的内存分配

    // initialize the threading system
    dprintf(SPEW, "initializing threads\n");
    thread_init();              //仅简单的初始化了定时器对象

    // initialize the dpc system
    dprintf(SPEW, "initializing dpc\n");
    dpc_init();             //delayed procedure call 延迟过程调用

    // 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));    //创建并唤醒bootstrap2线程,用于进一步完成bootloader工作

    // enable interrupts
    exit_critical_section();   //使能中断,执行后critical_section_count等于0

    // become the idle thread
    thread_become_idle();           //将本线程切换到idle状态
#else
        bootstrap_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=32
        list_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_A8
    set_vector_base(MEMBASE);    //armv8重新设置异常向量表到MEMBASE
#endif

#if ARM_WITH_MMU
    arm_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_UART
    uart_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 range
    theheap.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 list
    list_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_event

    thread_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,做了三个动作,结构体清零,设置魔数,设置name

    t->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_CHECKS
    ASSERT(t->magic == THREAD_MAGIC);
    ASSERT(t->state != THREAD_DEATH);
#endif

    if (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资源,执行新的thread
    exit_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_BIO
    bio_init();
#endif
#if WITH_LIB_FS
    fs_init();
#endif

    // initialize the rest of the platform
    dprintf(SPEW, "initializing platform\n");
    platform_init();                //目前只打印了一句log

    // initialize the target
    dprintf(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为2048
        page_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_DRIVER
    if (fastboot_trigger())
        boot_into_fastboot = true;
    #endif

    reboot_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_FUSE
                    set_tamper_fuse_cmd();
                #endif
                #if USE_PCOM_SECBOOT
                    set_tamper_flag(device.is_tampered);
                #endif
                }
            }
            boot_linux_from_mmc();//从emmc/ufs加载boot.img,选择dts,设置cmdline,跳转到kernel
        }
        else
        {    //这里是从nanflash启动,
            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");
    }

    /* 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_TREE
    struct dt_table *table; //一个dt_table通常包含多个dt_entry
    struct dt_entry dt_entry;//需要根据硬件信息选择一个最合适的dt_entry传递给kernel
    unsigned dt_table_offset;
    uint32_t dt_actual;
    uint32_t dt_hdr_size;
#endif
    if (!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");
        } else
            boot_into_ffbm = true;
    } else
        boot_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_TREE
        dt_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;
        }
#else
        imagesize_actual = (page_size + kernel_actual + ramdisk_actual);

#endif

        dprintf(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读取除签名外的image
        if (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_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;  //获取device tree table

            if (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_TREE
        if(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_HASH
            aboot_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,将会在这个函数中跳转到kernel


    return 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_TREE
    int ret = 0;
#endif

    void (*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_TREE
    dprintf(INFO, "Updating device tree: start\n");

    /* Update the Device Tree */ 
     // 更新device tree内容,主要是三部分:memory,cmdline,ramdisk
    ret = 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);
#endif

    free(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_MMU
    arch_disable_mmu();      //关闭mmu
#endif
    bs_set_timestamp(BS_KERNEL_ENTRY);  //设置kernel入口时间戳
    entry(0, machtype, (unsigned*)tags_phys);//直接跳转到kernel入口函数执行;r0 = 0; r1 = machtype; r2 = device tree的物理内存地址
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值