Android启动流程分析之一:Bootloader(基于高通芯片) 【mtk lk阶段有类似】

http://blog.csdn.net/ly890700/article/details/54586448

注:很多内容和MTK  LK阶段相同,可以借鉴学习。aboot.c内容差异很大。

 

本文以C6的bootlader代码为例,

 

 

 

一  kmain

1 crt0.S

手机上电后,硬件会从固定的地址(固化在ROM中)加载bootloader到RAM,然后跳转到bootloader的入口函数开始执行,对于mido,它的入口函数是:

bootable/bootloader/lk/arch/arm/crt0.S Collapse source

#define DSB .byte 0x4f0xf00x7f0xf5

#define ISB .byte 0x6f0xf00x7f0xf5

.section ".text.boot"

.globl _start

_start:

    b   reset

    b   arm_undefined

    b   arm_syscall

    b   arm_prefetch_abort

 

.....

.....

.....

 

#ifdef ARM_CPU_CORTEX_A8

    DSB

    ISB

#endif

    bl      kmain

    b       .

在_start中先主要完成CPU初始化,禁用mmu,禁用cache,初始化异常向量表等操作,最后将直接跳转到函数kmain中

2 kmain

kmain的代码位于

bootable/bootloader/lk/kernel/main.c Collapse source

/* 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();

    // early arch stuff

    arch_early_init();

    // 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);

    // deal with any static constructors

    dprintf(SPEW, "calling constructors\n");

    call_constructors();

    // bring up the kernel heap

    dprintf(SPEW, "initializing heap\n");

    heap_init();

    __stack_chk_guard_setup();

    // initialize the threading system

    dprintf(SPEW, "initializing threads\n");

    thread_init();

    // initialize the dpc system

    dprintf(SPEW, "initializing dpc\n");

    dpc_init();

    // 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));

    // enable interrupts

    exit_critical_section();

    // become the idle thread

    thread_become_idle();

#else

        bootstrap_nandwrite();

#endif

}

在kmain中,

2.1 调用thread_init_early初始化线程系统

2.2 调用arch_early_init中判断如果存在mmu就初始化,设置异常向量基地址,使能中断相关寄存器

2.3 在platform_early_init中完成初始化硬件时钟、手机的主板等操作,这个函数每种cpu的实现都不一样,定义在bootable\bootloader\lk\platform\{cpu型号}\platform.c下

2.4 target_early_init中完成初始化uart端口的操作,这个函数的实现在bootable\bootloader\lk\target\{cpu型号}\init.c

2.5 首先输出调试信息"welcome to lk\n\n",在bootable/bootloader/lk/include/debug.h中将INFO定义为了类似kernel中使用的debug level,同时定义了dprintf

bootable/bootloader/lk/include/debug.h Collapse source

44/* debug levels */

45#define CRITICAL 0

46#define ALWAYS 0

47#define INFO 1

48#define SPEW 2

49

50/* output */

51void _dputc(char c); // XXX for now, platform implements

52int _dputs(const char *str);

53int _dprintf(const char *fmt, ...) __PRINTFLIKE(1, 2);

54int _dvprintf(const char *fmt, va_list ap);

55

56#define dputc(level, str) do if ((level) <= DEBUGLEVEL) { _dputc(str); } } while (0)

57#define dputs(level, str) do if ((level) <= DEBUGLEVEL) { _dputs(str); } } while (0)

58#define dprintf(level, x...) do if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0)

59#define dvprintf(level, x...) do if ((level) <= DEBUGLEVEL) { _dvprintf(x); } } while (0)

然后调用bs_set_timestamp设置启动状态为BS_BL_START,启动状态的其他值定义在bootable/bootloader/lk/platform/msm_shared/include/boot_stats.h中:

bootable/bootloader/lk/platform/msm_shared/include/boot_stats.h Collapse source

33/* The order of the entries in this enum does not correspond to bootup order.

34 * It is mandated by the expected order of the entries in imem when the values

35 * are read in the kernel.

36 */

37enum bs_entry {

38  BS_BL_START = 0,

39  BS_KERNEL_ENTRY,

40  BS_SPLASH_SCREEN_DISPLAY,

41  BS_KERNEL_LOAD_TIME,

42  BS_KERNEL_LOAD_START,

43  BS_KERNEL_LOAD_DONE,

44  BS_MAX,

45};

2.6 SPEW同样是在bootable/bootloader/lk/include/debug.h中定义的debug level,函数call_constructors完成相关构造函数的初始化。

2.7 调用函数heap_init完成内核堆栈的初始化,用与kmalloc等函数的内存分配。

2.8  __stack_chk_guard_setup()其实是个宏,定义在bootable/bootloader/lk/include/debug.h中,

#define __stack_chk_guard_setup() do { __stack_chk_guard = get_canary(); } while(0)
get_canary函数定义在bootable/bootloader/lk/platform/msm_shared/scm.c中,返回一个生成的随机数,最终保存在全局变量__stack_chk_guard中。

2.9  在thread_init函数中初始化定时器

2.10 初始化延迟过程调用(delay procedure call)

2.11 调用timer_init初始化内核定时器

2.12 如果没有定义ENABLE_NANDWRITE,就创建出一个名为bootstrap2的线程,然后运行这个线程。退出临界区,开中断。全局变量critical_section_count在定义时取值为1,在exit_critical_section中会将critical_section_count先减1,如果减一后为0就使能中断。

bootable/bootloader/lk/include/kernel/thread.h Collapse source

136static inline __ALWAYS_INLINE void exit_critical_section(void)

137{

138 critical_section_count--;

139 if (critical_section_count == 0)

140     arch_enable_ints();

141}

最后调用thread_become_idle将本线程切换到idle状态

2.13 如果定义了ENABLE_NANDWRITE,在timer_init之后将执行bootstrap_nandwrite。我们接下来先以没有定义ENABLE_NANDWRITE来进行分析。

 

二  bootstrap2

bootstrap2线程会执行bootstrap2函数

bootable/bootloader/lk/kernel/kmain.c Collapse source

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();

    // initialize the target

    dprintf(SPEW, "initializing target\n");

    target_init();

    dprintf(SPEW, "calling apps_init()\n");

    apps_init();

    return 0;

}

1 arch_init

 

arch_init函数目前实现为空。如果定义了WITH_LIB_BIO和WITH_LIB_FS,就分别执行bio_init和fs_init。函数platform_init的实现也跟cpu关联,但基本都是输出调试信息。

2 target_init

target_init的实现也与cpu型号有关,对于mido,其cpu型号为msm8953,target_init的实现为:

bootable/bootloader/lk/target/msm8953/init.c Collapse source

void target_init(void)

{

#if VERIFIED_BOOT

#if !VBOOT_MOTA

    int ret = 0;

#endif

#endif

    dprintf(INFO, "target_init()\n");

    spmi_init(PMIC_ARB_CHANNEL_NUM, PMIC_ARB_OWNER_ID);

    target_keystatus();

    target_sdc_init();

    if (partition_read_table())

    {

        dprintf(CRITICAL, "Error reading the partition table info\n");

        ASSERT(0);

    }

#if LONG_PRESS_POWER_ON

    shutdown_detect();

#endif

#if PON_VIB_SUPPORT

    vib_timed_turn_on(VIBRATE_TIME);

#endif

 

    if (target_use_signed_kernel())

        target_crypto_init_params();

#if VERIFIED_BOOT

#if !VBOOT_MOTA

    clock_ce_enable(CE1_INSTANCE);

    /* Initialize Qseecom */

    ret = qseecom_init();

    if (ret < 0)

    {

        dprintf(CRITICAL, "Failed to initialize qseecom, error: %d\n", ret);

        ASSERT(0);

    }

    /* Start Qseecom */

    ret = qseecom_tz_init();

    if (ret < 0)

    {

        dprintf(CRITICAL, "Failed to start qseecom, error: %d\n", ret);

        ASSERT(0);

    }

    if (rpmb_init() < 0)

    {

        dprintf(CRITICAL, "RPMB init failed\n");

        ASSERT(0);

    }

    /*

     * Load the sec app for first time

     */

    if (load_sec_app() < 0)

    {

        dprintf(CRITICAL, "Failed to load App for verified\n");

        ASSERT(0);

    }

#endif

#endif

#if SMD_SUPPORT

    rpm_smd_init();

#endif

}

主要完成的操作有:

2.1从共享内存中读取xbl提供的pmic信息(pmic_info_populate)

2.2初始化spmi总线,用于cpu与pmic通信(spmi_init)

2.3初始化ap与rpm通信通道(rpm_glink_init)
2.4初始化按键(target_keystatus)

2.5判断内核是否签名,当使用的是签名的kernel时,需要初始加密解密引擎(target_crypto_init_params)

2.6判断是从usf还是emmc启动(platform_boot_dev_isemmc)

2.7获取分区表信息(mmc_read_partition_table)

2.8判断电池电压是否过低,过低则进入预充电(pm_appsbl_chg_check_weak_battery_status)

2.9和tz通信(qseecom_tz_init)

2.10初始化emmc或ufs中的rpmb用户加解密认证分区(rpmb_init)

2.11运行keymaster(load_sec_app)

 

三  apps_init

在bootstrap2线程中最后执行apps_init,完成一些应用功能的初始化。

 1 apps_init

apps_init的实现在:

bootable/bootloader/lk/app/app.c Collapse source

/* 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++) {

        if (app->init)

            app->init(app);

    }

    /* 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);

        }

    }

}

static int app_thread_entry(void *arg)

{

    const struct app_descriptor *app = (const struct app_descriptor *)arg;

    app->entry(app, NULL);

    return 0;

}

static void start_app(const struct app_descriptor *app)

{

    thread_t *thr;

    printf("starting app %s\n", app->name);

    thr = thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);

    if(!thr)

    {

        return;

    }

    thread_resume(thr);

}

__apps_start和__apps_end都是在连接脚本中定义的, 在  bootable/bootloader/lk/arch/arm/system-onesegment.ld中有:

__apps_start = .;
KEEP (*(.apps))
__apps_end = .;

__apps_start和__apps_end都代表两个链接地址,KEEP (*(.apps))表示所有.apps段都链接在__apps_start和__apps_end之间。

因为在bootable/bootloader/lk/include/app.h中有定义

bootable/bootloader/lk/include/app.h Collapse source

/* app support api */

void apps_init(void); /* one time setup */

/* app entry point */

struct app_descriptor;

typedef void (*app_init)(const struct app_descriptor *);

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

/* app startup flags */

#define APP_FLAG_DONT_START_ON_BOOT 0x1

/* each app needs to define one of these to define its startup conditions */

struct app_descriptor {

    const char *name;

    app_init  init;

    app_entry entry;

    unsigned int flags;

};

#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,

#define APP_END };

 所以这里将会执行在APP_START(appname)中定义的appname函数。在代码里搜索APP_START,会发现在bootable/bootloader/lk/app/aboot/aboot.c中调用APP_START来执行aboot_init函数

2 aboot_init

aboot_init的定义为:

bootable/bootloader/lk/app/aboot/aboot.c Collapse source

void aboot_init(const struct app_descriptor *app)

{

    unsigned reboot_mode = 0;

    /* Initialise wdog to catch early lk crashes */

#if WDOG_SUPPORT

    msm_wdog_init();

#endif

    /* Setup page size information for nv storage */

    if (target_is_emmc_boot())

    {

        page_size = mmc_page_size();

        page_mask = page_size - 1;

        mmc_blocksize = mmc_get_device_blocksize();

        mmc_blocksize_mask = mmc_blocksize - 1;

    }

    else

    {

        page_size = flash_page_size();

        page_mask = page_size - 1;

    }

    ASSERT((MEMBASE + MEMSIZE) > MEMBASE);

    read_device_info(&device);

    read_allow_oem_unlock(&device);

    /* Display splash screen if enabled */

#if DISPLAY_SPLASH_SCREEN

#if NO_ALARM_DISPLAY

    if (!check_alarm_boot()) {

#endif

        dprintf(SPEW, "Display Init: Start\n");

#if ENABLE_WBC

        /* Wait if the display shutdown is in progress */

        while(pm_app_display_shutdown_in_prgs());

        if (!pm_appsbl_display_init_done())

            target_display_init(device.display_panel);

        else

            display_image_on_screen();

#else

        target_display_init(device.display_panel);

#endif

        dprintf(SPEW, "Display Init: Done\n");

#if NO_ALARM_DISPLAY

    }

#endif

#endif

    target_serialno((unsigned char *) sn_buf);

    dprintf(SPEW,"serial number: %s\n",sn_buf);

    sprintf(secureboot_buf, "%s", is_secure_boot_enable() ? "1":"0");

    memset(display_panel_buf, '\0', MAX_PANEL_BUF_SIZE);

    /*

     * Check power off reason if user force reset,

     * if yes phone will do normal boot.

     */

    if (is_user_force_reset())

        goto normal_boot;

    dprintf(CRITICAL,"fastboot: is_unlocked = %d\n",device.is_unlocked);

    /* Check if we should do something other than booting up */

    if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))

    {

        /***************add by xiangchao.zhong for fastboot*********************/

        if(device.is_unlocked == 0){

            dprintf(CRITICAL,"fastboot: because devices has locked, so goto normal boot!\n");

            goto normal_boot;

        }

        else{

        /***************add by xiangchao.zhong for fastboot*********************/

            dprintf(ALWAYS,"dload mode key sequence detected\n");

            reboot_device(EMERGENCY_DLOAD);

            dprintf(CRITICAL,"Failed to reboot into dload mode\n");

            boot_into_fastboot = true;

        }

    }

        if (!boot_into_fastboot)

        {

            if(target_build_variant_user())

            {

            if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))

                boot_into_recovery = 1;

            if (!boot_into_recovery &&

                (keys_get_state(KEY_BACK) || keys_get_state(KEY_VOLUMEDOWN)))

                boot_into_fastboot = true;

            }else{

            if (keys_get_state(KEY_VOLUMEUP))

            {

                snprintf((char *)boot_reason_buf, 20, "%s""boot_with_factory");

                dprintf(SPEW,"boot_reason_buf: %s\n", boot_reason_buf);

            }else

            if (keys_get_state(KEY_VOLUMEDOWN))

                boot_into_fastboot = true;

            }

        }

        #if NO_KEYPAD_DRIVER

        if (fastboot_trigger())

            boot_into_fastboot = true;

        #endif

    #if USE_PON_REBOOT_REG

        reboot_mode = check_hard_reboot_mode();

    #else

        reboot_mode = check_reboot_mode();

    #endif

        if (reboot_mode == RECOVERY_MODE)

        {

            boot_into_recovery = 1;

        }

        else if(reboot_mode == FASTBOOT_MODE)

        {

            boot_into_fastboot = true;

        }

        else if(reboot_mode == ALARM_BOOT)

        {

            boot_reason_alarm = true;

        }

    #if VERIFIED_BOOT

    #if !VBOOT_MOTA

        else if (reboot_mode == DM_VERITY_ENFORCING)

        {

            device.verity_mode = 1;

            write_device_info(&device);

        }

        else if (reboot_mode == DM_VERITY_LOGGING)

        {

            device.verity_mode = 1;

            write_device_info(&device);

        }

        else if (reboot_mode == DM_VERITY_KEYSCLEAR)

        {

            if(send_delete_keys_to_tz())

                ASSERT(0);

        }

    #endif

    #endif

normal_boot:

     

    if (!boot_into_fastboot)

    {

        if (target_is_emmc_boot())

        {

            if(emmc_recovery_init())

                dprintf(ALWAYS,"error in emmc_recovery_init\n");

            if(target_use_signed_kernel())

            {

                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();

        }

        else

        {

            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 */

    aboot_fastboot_register_commands();

    /* dump partition table for debug info */

    partition_dump();

    /* initialize and start fastboot */

    fastboot_init(target_get_scratch_address(), target_get_max_flash_size());

//#if FBCON_DISPLAY_MSG

    //display_fastboot_menu();

    display_fastboot();

//#endif

}

在aboot_init中完成的操作主要有:

2.1 根据target_is_emmc_boot()判断是否是从emmc存储设备上启动,然后分别获取对应存储设备的页大小和页掩码

2.2 取得设备的device_info信息,保存到device变量中。device_info结构体定义为:

/bootable/bootloader/lk/app/aboot/devinfo.h Collapse source

#if VBOOT_MOTA

struct device_info

{

    unsigned char magic[DEVICE_MAGIC_SIZE];

    bool is_unlocked;

    bool is_tampered;

    bool is_verified;

    bool charger_screen_enabled;

    char display_panel[MAX_PANEL_ID_LEN];

    char bootloader_version[MAX_VERSION_LEN];

    char radio_version[MAX_VERSION_LEN];

    char sig[SIG_SIZE];

};

#else

struct device_info

{

    unsigned char magic[DEVICE_MAGIC_SIZE];

    bool is_unlocked;

    bool is_tampered;

    bool is_unlock_critical;

    bool charger_screen_enabled;

    char display_panel[MAX_PANEL_ID_LEN];

    char bootloader_version[MAX_VERSION_LEN];

    char radio_version[MAX_VERSION_LEN];

    bool verity_mode; // 1 = enforcing, 0 = logging

    char sig[SIG_SIZE];

};

#endif

这个结构体中包含的信息有:是否禁用fastboot,是否验证boot.img等。

2.3 调用target_display_init初始化lcd驱动,显示手机开机后的第一副图片。splash一般代表在系统启动阶段显示器用图形而非文本现实。

2.4 调用target_serialno获取emmc或者flash芯片的产品序列号,最后在启动kernel时通过cmdline中的androidboot.serialno参数传给内核。

2.5 调用memset清除屏幕

2.6 判断关机原因,如果是用户强制重启手机,这时在重启后跳转到normal_boot继续执行

2.7 判断音量上下键是否同时按下,之后如果进入下载模式失败,就设置fastboot模式标志boot_into_fastboot = true。

2.8 如果不是通过usb+上下键进入下载模式,判断如果home键和音量上键同时按下,设置recovery模式标志boot_into_recovery = 1, 如果back键和音量下键同时按下,设置fastboot模式标志。

2.9 如果没有按键驱动,设置boot_into_fastboot为true

2.10 根据硬件是否存在pon寄存器,调用check_hard_reboot_mode或者check_reboot_mode获取重启模式reboot_mode,设置相应的开机模式标志。以前使用的是共享内存记录重启模式,最新转为使用pmic的pon寄存器记录重启模式。

如果reboot_mode为RECOVERY_MODE,boot_into_recovery = 1

如果reboot_mode为FASTBOOT_MODE,boot_into_fastboot = 1

如果reboot_mode为ALARM_BOOT,boot_reason_alarm = true,这种情况时如果开启了闹钟并且设置了关机后闹钟有效,在关机后闹钟仍然会启动手机

2.11 在normal_boot:下,首先判断如果不是fastboot模式,然后通过target_is_emmc_boot判断是否从emmc或者ufs启动,

如果从emmc或者ufs启动,先执行emmc_recovery_init,emmc_recovery_init的实现主要在函数_emmc_recovery_init中,_emmc_recovery_init的定义在bootable/bootloader/lk//app/aboot/recovery.c中,主要工作为读取misc分区中的bcb,判断bcb的command域是否为boot-recovery,如果是update-radio还会检查radio的更新状态。 回到aboot_init中用target_use_signed_kernel判断是否使用了签名的kernel,最后执行boot_linux_from_mmc, 在boot_linux_from_mmc中将从emmc/ufs加载boot.img,选择dts,设置cmdline,跳转到kernel

如果从nand flash启动,调用boot_linux_from_flash去启动内核。

2.12 之后的代码是fasboot模式或者正常启动失败的情况下才会执行到的,首先调用aboot_fastboot_register_commands注册fastboot支持的命令,在partition_dump中打印分区表信息, 执行fastboot_init初始化并启动fastboot,在display_fastboot_menu_thread中为fastboot提供了一个简易图形显示。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值