1、bootloader到kernel启动总逻辑流程图
ARM架构中,EL0/EL1是必须实现,EL2/EL3是选配,ELx跟层级对应关系:
EL0 -- app
EL1 -- Linux kernel 、lk
EL2 -- hypervisor(虚拟化)
EL3 -- ARM trust firmware 、pre-loader
若平台未实现EL3(atf),pre-loader直接加载lk:
若平台实现EL3,则需要先加载完ATF再由ATF去加载lk:
bootloader 启动分两个阶段,一个是pre-loader加载lk(u-boot)阶段,另一个是lk加载kernel阶段。下面跟着流程图简述第一个阶段的加载流程。
1-3:设备上电起来后,跳转到Boot ROM(不是flash)中的boot code中执行把pre-loader加载起到ISRAM, 因为当前DRAM(RAM分SRAM跟DRAM,简单来说SRAM就是cache,DRAM就是普通内存)还没有准备好,所以要先把pre-loader load到芯片内部的ISRAM(Internal SRAM)中。
4-6:pre-loader初始化好DRAM后就将lk从flash(nand/emmc)中加载到DRAM中运行;
7-8:解压bootimage成ramdisk跟kernel并载入DRAM中,初始化dtb;
9-11:lk跳转到kernl初始化, kernel初始化完成后fork出init进程, 然后拉起ramdisk中的init程序,进入用户空间初始化,init进程fork出zygote进程..直到整个Android启动完成.
2、从pre-loader到lk(mt6771为例)
Pre-loader主要干的事情就是初始化某些硬件,比如: UART,GPIO,DRAM,TIMER,RTC,PMIC 等等,建立起最基本的运行环境,最重要的就是初始化DRAM.
时序图:
点击查看大图
源码流程如下:
\vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6761\src\init\init.s
.section .text.start .equ MODE_USR ,0x10 .equ MODE_FIQ ,0x11 .equ MODE_IRQ ,0x12 .equ MODE_SVC ,0x13 .equ MODE_MON ,0x16 .equ MODE_ABT ,0x17 .equ MODE_UNDEF ,0x1B .equ MODE_SYS ,0x1F .equ I_BIT ,0x80 .equ F_BIT ,0x40 .equ INT_BIT ,0xC0
.extern sys_stack .extern sys_stack_sz .extern bl31_base_addr .extern rst_vector_base_addr /* bldr argument address */ .globl bldr_args_addr bldr_args_addr: .word 0x0 .globl _start _start: b resethandler
bss1_start: .word _bss1_start bss1_end: .word _bss1_end bss2_start: .word _bss2_start bss2_end: .word _bss2_end stack: .long sys_stack stacksz: .long sys_stack_sz resethandler: LDR r6, =bldr_args_addr
STR r4, [r6] MOV r0, #0 MOV r1, #0 MOV r2, #0 MOV r3, #0 MOV r4, #0 MOV r5, #0 MOV r6, #0 MOV r7, #0 MOV r8, #0 MOV r9, #0 MOV r10, #0 MOV r11, #0 MOV r12, #0 MOV sp, #0 MOV lr, #0
/* CONFIG_ARM_ERRATA_826319 */ mrc p15, 0, r8, c1, c0, 0 @ Read System Control Register into Rt bic r8, r8, #0x4 @ disable D-Cache bic r8, r8, #0x1000 @ clear I-Cache mcr p15, 0, r8, c1, c0, 0 @ Write Rt to System Control Register
mrc p15, 1, r8, c15, c0, 0 @ Read L2ACTLR into Rt orr r8, r8, #0x8 @ Set Bit[3]=1, disable eviction transaction bic r8, r8, #0x1 << 14 @ Set Bit[14]]0 mcr p15, 1, r8, c15, c0, 0 @ Write Rt to L2ACTLR isb
/* CONFIG_ARM_ERRATA_836870 */ /** CONFIG_ARM_ERRATA_836870=y (for 6595/6752/6735, prior to r0p4) * Prog CatC, * Non-allocating reads might prevent a store exclusive from passing * worksround: set the CPUACTLR.DTAH bit. * The CPU Auxiliary Control Register can be written only when the system * is idle. ARM recommends that you write to this register after a powerup
* reset, before the MMU is enabled, and before any ACE or ACP traffic
* begins. **/ mrrc p15, 0, r8, r9, c15 @ Read CPUACTLR into Rt, Rt2 orr r8, r8, #0x1 << 24 @ Set Bit[24]=1, set the CPUACTLR.DTAH bit. mcrr p15, 0, r8, r9, c15 @ Write Rt, Rt2 to CPUACTLR isb
/* CONFIG_ARM_ERRATA_855873 */
mrrc p15, 0, r8, r9, c15 @ Read CPUACTLR into Rt(Low), Rt2(High)
orr r9, r9, #0x1 << 12 @ Set Bit[44]=1, set the CPUACTLR.ENDCCASCI bit.
mcrr p15, 0, r8, r9, c15 @ Write Rt, Rt2 to CPUACTLR
isb
/* set the cpu to SVC32 mode */ MRS r0,cpsr BIC r0,r0,#0x1f ORR r0,r0,#0xd3 MSR cpsr,r0
/* disable interrupt */ MRS r0, cpsr MOV r1, #INT_BIT ORR r0, r0, r1 MSR cpsr_cxsf, r0
/* * L2 write to write access 2T/3T programmable * Program before I/D bits enable * 0x0c53_0604 bit[21] set to 1鈥檅1 * 1 -> L2 write to write 2T (default) * 1 -> L2 write to write 3T */
ldr r0, =0x0C530604 ldr r1, [r0] orr r1, r1, #0x1 << 21 str r1, [r0] dsb isb
/* enable I+Z+SMP bits and disable D bit */ MRC p15, 0, ip, c1, c0, 0 ORR ip, ip, #0x1840 /* I+Z+SMP bits */ BIC ip, ip, #0x4 /* C bit */ MCR p15, 0, ip, c1, c0, 0
/* Make BROM connect to META mode more stable, request by Chandler.Li */ DSB ISB
clear_bss1 :
LDR r0, bss1_start /* find start of bss segment */ LDR r1, bss1_end /* stop here */ MOV r2, #0x00000000 /* clear */ CMP r0, r1 BEQ clear_bss2
/* clear loop... */
clbss1_l :
STR r2, [r0]
ADD r0, r0, #4
CMP r0, r1
BNE clbss1_l
clear_bss2 :
LDR r0, bss2_start /* find start of bss segment */
LDR r1, bss2_end /* stop here */
MOV r2, #0x00000000 /* clear */
CMP r0, r1
BEQ setup_stk
/* clear loop... */
clbss2_l :
STR r2, [r0]
ADD r0, r0, #4
CMP r0, r1
BNE clbss2_l
setup_stk :
/* setup stack */
LDR r0, stack
LDR r1, stacksz
/* buffer overflow detect pattern */
LDR r2, =0xDEADBEFF
STR r2, [r0]
LDR r1, [r1]
SUB r1, r1, #0x04
ADD r1, r0, r1
MOV sp, r1
entry :
LDR r0, =bldr_args_addr
B main
init.s 主要干的事情是切换系统到管理模式(svc)(如果平台有实现el3,那么pre-loader运行在el3,否则运行在el1),禁止irq/fiq,设置stack等, 然后jump到c代码main函数入口。
进入源码分析。
vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6761\src\core\main.c
vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6761\src\drivers\platform.c
vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6761\src\core\partition.c
vendor\mediatek\proprietary\bootable\bootloader\preloader\platform\mt6761\src\security\trustzone\inc\tz_init.c
void main(u32 *arg) { struct bldr_command_handler handler; u32 jump_addr, jump_arg; /* get the bldr argument */ p_bldr_param = &bldr_param; memcpy((void *)p_bldr_param, (void *)*arg, sizeof(bl_param_t)); mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE); /* * MT6761 only: adjust sramrom delsel
DRV_WriteReg32(0x1020D00C, 0x6C4E4F3); pal_log_info("sramrom delsel: 0x%X\n", DRV_Reg32(0x1020D00C)); /* * MT6761 only: adjust bandgap reference voltage */ DRV_WriteReg32(0x1000C600, 0x302012A8); pal_log_info("bandgap ref vol: 0x%X\n", DRV_Reg32(0x1000C600)); bldr_pre_process(); #ifdef HW_INIT_ONLY bldr_wait_forever(); #endif
handler.priv = NULL; handler.attr = 0; handler.cb = bldr_cmd_handler; BOOTING_TIME_PROFILING_LOG("before bldr_handshake"); bldr_handshake(&handler); BOOTING_TIME_PROFILING_LOG("bldr_handshake"); #if BOOTROM_INTEGRITY_CHECK /* if rom integrity check fails, device halts, so don't put it before bootloader handshake, this could make device bricked */ rom_integrity_check(); #endif
#if !CFG_FPGA_PLATFORM /* security check */ device_APC_dom_setup(); #endif BOOTING_TIME_PROFILING_LOG("sec_boot_check"); #if CFG_ATF_SUPPORT trustzone_pre_init(); BOOTING_TIME_PROFILING_LOG("trustzone pre init"); #endif #if defined(MTK_AB_OTA_UPDATER) ab_ota_boot_check(); #endif
#if !(CFG_BYPASS_LOAD_IMG_FORCE_ATF) /* Do not load ATF, lk, load by JTAG */ if (0 != bldr_load_images(&jump_addr)) { pal_log_err("%s Second Bootloader Load Failed\n", MOD); #if !CFG_BYPASS_EMI goto error; #endif } #else jump_addr = CFG_UBOOT_MEMADDR; #endif BOOTING_TIME_PROFILING_LOG("load images"); bldr_post_process(); #ifdef SLT mt_usb_phy_recover(); //mu3d_hal_rst_dev(); #endif
#if CFG_ATF_SUPPORT trustzone_post_init(); BOOTING_TIME_PROFILING_LOG("trustzone post init"); #endif
/* 跳转传入lk的参数,包括boot time/mode/reason 等,这些参数在
platform_set_boot_args 函数获取。
*/
#if CFG_BOOT_ARGUMENT_BY_ATAG
/* 64S3,32S1,32S1 (MTK_ATF_BOOT_OPTION = 0)
* re-loader jump to LK directly and then LK jump to kernel directly */
#if CFG_ATF_SUPPORTpal_log_info("%s Others, jump to ATF\n", MOD);
error: platform_error_handler(); }
main 函数小结:
1、各种硬件初始化(uart、pmic、wdt、timer、mem..);
2、获取系统启动模式等,保存在全局变量中;
3、Security check,跟secro.img相关;
4、如果系统已经实现el3,则进入tz初始化;
5、获取lk加载到DRAM的地址(固定值),然后从ROM中找到lk分区的地址, 如果没找到jump_addr,则 goto error;
6、battery check,如果没有电池就会陷入while(1);
7、jump到lk(如果有实现el3,则会先jump到el3,然后再回到lk)
3、重点函数分析
bldr_load_images
函数主要干的事情就是找到lk分区地址和lk加载到DRAM中的地址, 准备好jump到lk执行,如下源码分析:
static int bldr_load_images(u32 *jump_addr)
{
int ret = 0;
blkdev_t *bootdev;
u32 addr = 0;
char *name;
u32 size = 0;
u32 spare0 = 0;
u32 spare1 = 0;
...
/* 这个地址是一个固定值,可以查到定义在:
./bootloader/preloader/platform/mt6580/default.mak:95:
CFG_UBOOT_MEMADDR := 0x81E00000
从log中可以看到:
[BLDR] jump to 0x81E00000
*/
addr = CFG_UBOOT_MEMADDR;
/* 然后去ROM找到lk所在分区地址 */
ret = bldr_load_part("lk", bootdev, &addr, &size);
if (ret)
return ret;
*jump_addr = addr;
}
// 这个函数逻辑很简单,就不需要多说了.
int bldr_load_part(char *name, blkdev_t *bdev, u32 *addr, u32 *size)
{
part_t *part = part_get(name);
if (NULL == part) {
print("%s %s partition not found\n", MOD, name);
return -1;
}
return part_load(bdev, part, addr, 0, size);
}
// 真正的load实现是在part_load函数.
int part_load(blkdev_t *bdev, part_t *part, u32 *addr, u32 offset, u32 *size)
{
int ret;
img_hdr_t *hdr = (img_hdr_t *)img_hdr_buf;
part_hdr_t *part_hdr = &hdr->part_hdr;
gfh_file_info_t *file_info_hdr = &hdr->file_info_hdr;
/* specify the read offset */
u64 src = part->startblk * bdev->blksz + offset;
u32 dsize = 0, maddr = 0;
u32 ms;
// 检索分区头是否正确。
/* retrieve partition header. */
if (blkdev_read(bdev, src, sizeof(img_hdr_t), (u8*)hdr,0) != 0) {
print("[%s]bdev(%d) read error (%s)\n", MOD, bdev->type, part->name);
return -1;
}
if (part_hdr->info.magic == PART_MAGIC) {
/* load image with partition header */
part_hdr->info.name[31] = '\0';
/*
输出分区的各种信息,从log中可以看到:
[PART] Image with part header
[PART] name : lk
[PART] addr : FFFFFFFFh mode : -1
[PART] size : 337116
[PART] magic: 58881688h
*/
print("[%s]Img with part header\n", MOD);
print("[%s]name:%s\n", MOD, part_hdr->info.name);
print("[%s]addr:%xh\n", MOD, part_hdr->info.maddr);
print("[%s]size:%d\n", MOD, part_hdr->info.dsize);
print("[%s]magic:%xh\n", MOD, part_hdr->info.magic);
maddr = part_hdr->info.maddr;
dsize = part_hdr->info.dsize;
src += sizeof(part_hdr_t);
memcpy(part_info + part_num, part_hdr, sizeof(part_hdr_t));
part_num++;
} else {
print("[%s]%s img not exist\n", MOD, part->name);
return -1;
}
// 如果maddr没有定义,那么就使用前面传入的地址addr.
if (maddr == PART_HEADER_MEMADDR/*0xffffffff*/)
maddr = *addr;
if_overlap_with_dram_buffer((u32)maddr, ((u32)maddr + dsize));
ms = get_timer(0);
if (0 == (ret = blkdev_read(bdev, src, dsize, (u8*)maddr,0)))
*addr = maddr;
ms = get_timer(ms);
/* 如果一切顺利就会打印出关键信息:
[PART] load "lk" from 0x0000000001CC0200 (dev) to 0x81E00000 (mem) [SUCCESS]
[PART] load speed: 25324KB/s, 337116 bytes, 13ms
*/
print("\n[%s]load \"%s\" from 0x%llx(dev) to 0x%x (mem) [%s]\n", MOD,
part->name, src, maddr, (ret == 0) ? "SUCCESS" : "FAILED");
if( ms == 0 )
ms+=1;
print("[%s]load speed:%dKB/s,%d bytes,%dms\n", MOD, ((dsize / ms) * 1000) / 1024, dsize, ms);
return ret;
}
bldr_post_process
函数主要干的事情就是从pmic去检查是否有电池存在,如果没有就等待, 如下源码分析,比较简单:
// 就是包了一层而已.
static void bldr_post_process(void)
{
platform_post_init();
}
// 重点是这个函数:
void platform_post_init(void)
{
/* normal boot to check battery exists or not */
if (g_boot_mode == NORMAL_BOOT && !hw_check_battery() && usb_accessory_in()) {
...
pl_charging(1);
do {
mdelay(300);
/* 检查电池是否存在, 如果使用电源调试则需要修改此函数逻辑 */
if (hw_check_battery())
break;
/* 喂狗,以免超时被狗咬 */
platform_wdt_all_kick();
} while(1);
/* disable force charging mode */
pl_charging(0);
}
...
}