recovery

昨天大家問recovery怎麼工作的,差不多花了倆小時看了下
這個東西肯定是要bootloader支持的,因爲bootloader要選擇啓動哪個kernel和ramdisk
所以是平臺相關的。

這裏可以從上往下看,也可以從下往上看。
我們先從上往下吧

多年不做,好在以前做bootloader和kernel的基礎還在。
還比較容易找。

Setting裏面我們可以選擇恢復出廠設置,recovery
Power.reboot("recovery");

參數表示reboot的原因

然後會到JNI
static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason)
{
    sync();
#ifdef HAVE_ANDROID_OS
    if (reason == NULL) {
        reboot(RB_AUTOBOOT);
    } else {
        const char *chars = env->GetStringUTFChars(reason, NULL);
        __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                 LINUX_REBOOT_CMD_RESTART2, (char*) chars);
        env->ReleaseStringUTFChars(reason, chars);  // In case it fails.
    }
    jniThrowIOException(env, errno);
#endif
}


這裏會調用到c庫裏面的函數

__reboot:
    .save   {r4, r7}
    stmfd   sp!, {r4, r7}
    ldr     r7, =__NR_reboot
    swi     #0
    ldmfd   sp!, {r4, r7}
    movs    r0, r0
    bxpl    lr
    b       __set_syscall_errno
    .fnend


c庫實際上到最底下就是系統調用的封裝了,
一般都是sys_reboot的實現了,
不過Qualcomm這裏用了宏來定義的。

調用了系統調用,kernel裏面實現,我們就到了kernel裏面

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        void __user *, arg)



    switch (cmd) {
    case LINUX_REBOOT_CMD_RESTART:
        kernel_restart(NULL);
        break;

    case LINUX_REBOOT_CMD_CAD_ON:
        C_A_D = 1;
        break;

    case LINUX_REBOOT_CMD_CAD_OFF:
        C_A_D = 0;
        break;

    case LINUX_REBOOT_CMD_HALT:
        kernel_halt();
        unlock_kernel();
        do_exit(0);
        break;

    case LINUX_REBOOT_CMD_POWER_OFF:
        kernel_power_off();
        unlock_kernel();
        do_exit(0);
        break;

    case LINUX_REBOOT_CMD_RESTART2:
        if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
            unlock_kernel();
            return -EFAULT;
        }
        buffer[sizeof(buffer) - 1] = '\0';

        kernel_restart(buffer);


走到kernel_restart
void kernel_restart(char *cmd)
{
    kernel_restart_prepare(cmd);
    if (!cmd)
        printk(KERN_EMERG "Restarting system.\n");
    else
        printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
    machine_restart(cmd);
}



void machine_restart(char * __unused)
{
    arm_pm_restart(reboot_mode);
}

    arm_pm_restart(reboot_mode);
這個函數是要每個target自己定義的,
以Qualcomm來說
static void msm_pm_restart(char str)
{
    msm_rpcrouter_close();
    msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);

    for (;;)
        ;
}

這裏的restart reason是recovery

static int msm_reboot_call
    (struct notifier_block *this, unsigned long code, void *_cmd)
{
    if ((code == SYS_RESTART) && _cmd) {
        char *cmd = _cmd;
        if (!strcmp(cmd, "bootloader")) {
            restart_reason = 0x77665500;
        } else if (!strcmp(cmd, "recovery")) {
            restart_reason = 0x77665502;
        } else if (!strcmp(cmd, "eraseflash")) {
            restart_reason = 0x776655EF;
        } else if (!strncmp(cmd, "oem-", 4)) {
            unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
            restart_reason = 0x6f656d00 | code;
        } else {
            restart_reason = 0x77665501;
        }
    }
    return NOTIFY_DONE;
}

會把這個原因寫到smem裏面去

下次啓動的時候怎麼管用呢?

bootloader下次起來的時候會去讀這個值

Qualcomm的bootloader是appsboot.mbn
void aboot_init(const struct app_descriptor *app)
{
    unsigned reboot_mode = 0;
    unsigned disp_init = 0;
    #if DISPLAY_SPLASH_SCREEN
    display_init();
    dprintf(INFO, "Diplay initialized\n");
    disp_init = 1;
    #endif
    page_size = flash_page_size();
    page_mask = page_size - 1;
    if (keys_get_state(KEY_HOME) != 0)
            boot_into_recovery = 1;
    if (keys_get_state(KEY_BACK) != 0)
        goto fastboot;
    if (keys_get_state(KEY_CLEAR) != 0)
        goto fastboot;
    if (keys_get_state(KEY_VOLUMEUP) != 0)
        goto fastboot;
    if (keys_get_state(KEY_CAMERA) != 0)
        goto fastboot;
    if (keys_get_state(KEY_VOLUMEDOWN) != 0)
            boot_into_recovery = 1;
        //goto fastboot;

    reboot_mode = check_reboot_mode();
        if (reboot_mode == RECOVERY_MODE){
            boot_into_recovery = 1;
        }else if(reboot_mode == FASTBOOT_MODE){
            goto fastboot;
        }
    recovery_init();
    boot_linux_from_flash();
    dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
        "to fastboot mode.\n");

fastboot:
    if(!disp_init) {
        display_init();
    } else {
        fbcon_clear();
    }
    dprintf(INFO, "Diplay initialized\n");
    udc_init(&surf_udc_device);

    fastboot_register("boot", cmd_boot);
    fastboot_register("erase:", cmd_erase);
    fastboot_register("flash:", cmd_flash);
    fastboot_register("continue", cmd_continue);
    fastboot_register("reboot", cmd_reboot);
    fastboot_register("reboot-bootloader", cmd_reboot_bootloader);
    fastboot_publish("product", "swordfish");
    fastboot_publish("kernel", "lk");

    fastboot_init(target_get_scratch_address(), 150 * 1024 * 1024);
    udc_start();
        target_battery_charging_enable(1, 0);
}

APP_START(aboot)
    .init = aboot_init,
APP_END


函數很簡單,
有三種情況
1:如果按鍵就會進入recovery
2:如果check_boot_mode是recovery的時候就會做
3 : 如果recovery_init返回的是真的話就會進入recovery mode


第一種很好理解,標準就是
第二種會去讀smem
unsigned check_reboot_mode(void)
{
    unsigned mode[2] = {0, 0};
    unsigned int mode_len = sizeof(mode);
    unsigned smem_status;

    smem_status = smem_read_alloc_entry(SMEM_APPS_BOOT_MODE,
                    &mode, mode_len );
    if(smem_status)
    {
      dprintf(CRITICAL, "ERROR: unable to read shared memory for reboot mode\n");
      return 0;
    }
    return mode[0];
}


unsigned smem_read_alloc_entry(smem_mem_type_t type, void *buf, int len)
{
    struct smem_alloc_info *ainfo;
    unsigned *dest = buf;
    unsigned src;
    unsigned size;

    if (((len & 0x3) != 0) || (((unsigned)buf & 0x3) != 0))
        return 1;

    if (type < SMEM_FIRST_VALID_TYPE || type > SMEM_LAST_VALID_TYPE)
        return 1;

    /* TODO: Use smem spinlocks */
    ainfo = &smem->alloc_info[type];
    if (readl(&ainfo->allocated) == 0)
        return 1;

    if ((size = readl(&ainfo->size)) != (unsigned)len)
        return 1;

    src = MSM_SHARED_BASE + readl(&ainfo->offset);
    for (; size > 0; src += 4, size -= 4)
        *(dest++) = readl(src);

    return 0;
}


第三種纔是網絡上普遍說的尋找misc分區
這個我就不說了,可以參考網上的文檔。

http://blog.chinaunix.net/u/14459/showart_1911144.html

Qualcomm的機器有些沒有misc分區的,我見過的好像都沒有?
不過也都沒太注意。

這個文章只解釋如何進入recovery,至於進入recovery之後如何取cache,
就比較簡單了,系統都起來了,後面的recovery的過程就是cache分區的東西了。

實際上所有的Android的bsp都不是完全開源的,
包括Qualcomm的全系列,nvidia的tegra,ti的,freescale的,
都有一些不能開源的部分,比如tegra 的bootloader是單獨發佈的。沒開源。

我上面貼出來的代碼都是Qualcomm開源部分的代碼。
其他的部分都是合作伙伴纔有的,包括Tegra 2也是一樣的,大家能下載的
都只是開源的那個部分,私有的部分只有合作伙伴纔有。等等廠家都一樣。

不過大多數東西,應該說80%的東西都是開源的吧。

目前在做一些系統工程的東西,不過剛纔又有其他項目的臨時抽調任務。
疑難雜症性質的東西又來了。

關於Android雙屏的事情還一直有customer在問,
大體分多種
1:兩個屏幕是獨立的,各自跑各自的東西,就是俺之前做的那個東西,典型的應用是
大小屏應用。比如遊戲機等等
2:兩個屏幕是分離的,但是內容要跨界顯示,組合成一個屏幕,
也是可以做的了,KERNEL裏面做就好了,電子書屏幕的確是太小,不能摺疊還。
3:兩個屏幕是分離的,顯示東西是一樣的,這個就更好做了,類似HDMI

但是如果要硬件加速的話,整個系統上的工作會多很多
發佈了9 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章