MTK 驱动(73)---MTK 6761平台 android O bootloader启动之 Pre-loader -> Lk

MTK 6761平台 android O bootloader启动之 Pre-loader -> Lk

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
    jump_arg = (u32)&(g_dram_buf->boottag);
#else
    jump_arg = (u32)&bootarg;
#endif
 /* 64S3,32S1,32S1 (MTK_ATF_BOOT_OPTION = 0)
	 * re-loader jump to LK directly and then LK jump to kernel directly */
 
  
#if CFG_ATF_SUPPORT
    pal_log_info("%s Others, jump to ATF\n", MOD);
    bldr_jump64(jump_addr, jump_arg, sizeof(boot_arg_t));
#else
    bldr_jump(jump_addr, jump_arg, sizeof(boot_arg_t));
#endif
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);
    }

...
}
Pre-loader 到 Lk的源码分析到这就完成了.

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值