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",