I.MX6 recovery mode hacking

/********************************************************************************
 *                      I.MX6 recovery mode hacking
 * 说明:
 *     看一下i.MX6 Recovery模式是怎么工作的。
 *
 *                                              2017-6-12 深圳 龙华樟坑村 曾剑锋
 *******************************************************************************/


一、参考文档:
    1. Booting into recovery from terminal [duplicate]
        https://android.stackexchange.com/questions/50366/booting-into-recovery-from-terminal
    2. Enter recovery mode from command line
        https://android.stackexchange.com/questions/21050/enter-recovery-mode-from-command-line
    3. How to Support Recovery Mode for POR Reboot Based on i.MX6 Android R13.4.1
        https://community.nxp.com/docs/DOC-93964

二、U-Boot Hacking

    /************************************************************************
     *
     * This is the next part if the initialization sequence: we are now
     * running from RAM and have a "normal" C environment, i. e. global
     * data can be written, BSS has been cleared, the stack size in not
     * that critical any more, etc.
     *
     ************************************************************************
     */

    void board_init_r(gd_t *id, ulong dest_addr)
    {
        ulong malloc_start;
    #if !defined(CONFIG_SYS_NO_FLASH)
        ulong flash_size;
    #endif

        gd->flags |= GD_FLG_RELOC;    /* tell others: relocation done */
        bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");

        monitor_flash_len = (ulong)&__rel_dyn_end - (ulong)_start;

        /* Enable caches */
        enable_caches();

        debug("monitor flash len: %08lX\n", monitor_flash_len);
        board_init();    /* Setup chipselects */
        /*
         * TODO: printing of the clock inforamtion of the board is now
         * implemented as part of bdinfo command. Currently only support for
         * davinci SOC's is added. Remove this check once all the board
         * implement this.
         */
    #ifdef CONFIG_CLOCKS
        set_cpu_clk_info(); /* Setup clock information */
    #endif
        serial_initialize();

        debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

    #ifdef CONFIG_LOGBUFFER
        logbuff_init_ptrs();
    #endif
    #ifdef CONFIG_POST
        post_output_backlog();
    #endif

        /* The Malloc area is immediately below the monitor copy in DRAM */
        malloc_start = dest_addr - TOTAL_MALLOC_LEN;
        mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);

    #ifdef CONFIG_ARCH_EARLY_INIT_R
        arch_early_init_r();
    #endif
        power_init_board();

    #if !defined(CONFIG_SYS_NO_FLASH)
        puts("Flash: ");

        flash_size = flash_init();
        if (flash_size > 0) {
    # ifdef CONFIG_SYS_FLASH_CHECKSUM
            print_size(flash_size, "");
            /*
             * Compute and print flash CRC if flashchecksum is set to 'y'
             *
             * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
             */
            if (getenv_yesno("flashchecksum") == 1) {
                printf("  CRC: %08X", crc32(0,
                    (const unsigned char *) CONFIG_SYS_FLASH_BASE,
                    flash_size));
            }
            putc('\n');
    # else    /* !CONFIG_SYS_FLASH_CHECKSUM */
            print_size(flash_size, "\n");
    # endif /* CONFIG_SYS_FLASH_CHECKSUM */
        } else {
            puts(failed);
            hang();
        }
    #endif

    #if defined(CONFIG_CMD_NAND)
        puts("NAND:  ");
        nand_init();        /* go init the NAND */
    #endif

    #if defined(CONFIG_CMD_ONENAND)
        onenand_init();
    #endif

    #ifdef CONFIG_GENERIC_MMC
        puts("MMC:   ");
        mmc_initialize(gd->bd);
    #endif

    #ifdef CONFIG_CMD_SCSI
        puts("SCSI:  ");
        scsi_init();
    #endif

    #ifdef CONFIG_HAS_DATAFLASH
        AT91F_DataflashInit();
        dataflash_print_info();
    #endif

        /* initialize environment */
        if (should_load_env())
            env_relocate();
        else
            set_default_env(NULL);

    #if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
        arm_pci_init();
    #endif

        stdio_init();    /* get the devices list going. */

        jumptable_init();

    #if defined(CONFIG_API)
        /* Initialize API */
        api_init();
    #endif

        console_init_r();    /* fully init console as a device */

    #ifdef CONFIG_DISPLAY_BOARDINFO_LATE
    # ifdef CONFIG_OF_CONTROL
        /* Put this here so it appears on the LCD, now it is ready */
        display_fdt_model(gd->fdt_blob);
    # else
        checkboard();
    # endif
    #endif

    #if defined(CONFIG_ARCH_MISC_INIT)
        /* miscellaneous arch dependent initialisations */
        arch_misc_init();
    #endif
    #if defined(CONFIG_MISC_INIT_R)
        /* miscellaneous platform dependent initialisations */
        misc_init_r();
    #endif

         /* set up exceptions */
        interrupt_init();
        /* enable exceptions */
        enable_interrupts();

        /* Initialize from environment */
        load_addr = getenv_ulong("loadaddr", 16, load_addr);

    #ifdef CONFIG_BOARD_LATE_INIT
        board_late_init();
    #endif

    #ifdef CONFIG_FSL_FASTBOOT
        fastboot_setup();          ----------------------------------------------+
    #endif                                                                       |
                                                                                 |
    #ifdef CONFIG_BITBANGMII                                                     |
        bb_miiphy_init();                                                        |
    #endif                                                                       |
    #if defined(CONFIG_CMD_NET)                                                  |
        puts("Net:   ");                                                         |
        eth_initialize(gd->bd);                                                  |
    #if defined(CONFIG_RESET_PHY_R)                                              |
        debug("Reset Ethernet PHY\n");                                           |
        reset_phy();                                                             |
    #endif                                                                       |
    #endif                                                                       |
                                                                                 |
    #ifdef CONFIG_POST                                                           |
        post_run(NULL, POST_RAM | post_bootmode_get(0));                         |
    #endif                                                                       |
                                                                                 |
    #if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)                        |
        /*                                                                       |
         * Export available size of memory for Linux,                            |
         * taking into account the protected RAM at top of memory                |
         */                                                                      |
        {                                                                        |
            ulong pram = 0;                                                      |
            uchar memsz[32];                                                     |
                                                                                 |
    #ifdef CONFIG_PRAM                                                           |
            pram = getenv_ulong("pram", 10, CONFIG_PRAM);                        |
    #endif                                                                       |
    #ifdef CONFIG_LOGBUFFER                                                      |
    #ifndef CONFIG_ALT_LB_ADDR                                                   |
            /* Also take the logbuffer into account (pram is in kB) */           |
            pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;                     |
    #endif                                                                       |
    #endif                                                                       |
            sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);        |
            setenv("mem", (char *)memsz);                                        |
        }                                                                        |
    #endif                                                                       |
                                                                                 |
    #ifdef CONFIG_FSL_FASTBOOT                                                   |
        check_fastboot();                                                        |
    #endif                                                                       |
                                                                                 |
        /* main_loop() can return to retry autoboot, if so just run it again. */ |
        for (;;) {                                                               |
            main_loop();                                                         |
        }                                                                        |
                                                                                 |
        /* NOTREACHED - no way out of command loop except booting */             |
    }                                                                            |
                                                                                 |
    void fastboot_setup(void)               <------------------------------------+
    {
        struct tag_serialnr serialnr;
        char serial[17];

        get_board_serial(&serialnr);
        sprintf(serial, "%u%u", serialnr.high, serialnr.low);
        g_dnl_set_serialnumber(serial);

        /*execute board relevant initilizations for preparing fastboot */
        board_fastboot_setup();

        /*get the fastboot dev*/
        _fastboot_setup_dev();

        /*check if we need to setup recovery*/
    #ifdef CONFIG_ANDROID_RECOVERY
        check_recovery_mode();           -----------------------+
    #endif                                                      |
                                                                |
        /*load partitions information for the fastboot dev*/    |
        _fastboot_load_partitions();                            |
                                                                |
        parameters_setup();                                     |
    }                                                           |
                                                                |
    /* export to lib_arm/board.c */                             |
    void check_recovery_mode(void)          <-------------------+
    {
        if (check_key_pressing()) {
            puts("Fastboot: Recovery key pressing got!\n");
            setup_recovery_env();
        } else if (check_recovery_cmd_file()) {    --------------+
            puts("Fastboot: Recovery command file found!\n");    |
            setup_recovery_env();                  --------------*-------------------+
    #ifdef CONFIG_BCB_SUPPORT                                    |                   |
        } else if (recovery_check_and_clean_command()) {         |                   |
            puts("Fastboot: BCB command found\n");               |                   |
            setup_recovery_env();                                |                   |
    #endif                                                       |                   |
        } else {                                                 |                   |
            puts("Fastboot: Normal\n");                          |                   |
        }                                                        |                   |
    }                                                            |                   |
                                                                 |                   |
    int check_recovery_cmd_file(void)        <-------------------+                   |
    {                                                                                |
        int button_pressed = 0;                                                      |
        int recovery_mode = 0;                                                       |
                                                                                     |
    #ifdef CONFIG_SBC7112                                                            |
        return recovery_mode || button_pressed;                                      |
    #endif                                                                           |
        recovery_mode = recovery_check_and_clean_flag();    -----------------------+ |
                                                                                   | |
        /* Check Recovery Combo Button press or not. */                            | |
        imx_iomux_v3_setup_multiple_pads(recovery_key_pads,                        | |
                ARRAY_SIZE(recovery_key_pads));                                    | |
                                                                                   | |
        gpio_direction_input(GPIO_VOL_DN_KEY);                                     | |
                                                                                   | |
        if (gpio_get_value(GPIO_VOL_DN_KEY) == 0) { /* VOL_DN key is low assert */ | |
            button_pressed = 1;                                                    | |
            printf("Recovery key pressed\n");                                      | |
        }                                                                          | |
                                                                                   | |
        return recovery_mode || button_pressed;                                    | |
    }                                                                              | |
                                                                                   | |
    int recovery_check_and_clean_flag(void)             <--------------------------+ |
    {                                                                                |
        int flag_set = 0;                                                            |
        u32 reg;                                                                     |
        reg = readl(SNVS_BASE_ADDR + SNVS_LPGPR);                                    |
                                                                                     |
        flag_set = !!(reg & ANDROID_RECOVERY_BOOT);                                  |
        printf("check_and_clean: reg %x, flag_set %d\n", reg, flag_set);             |
        /* clean it in case looping infinite here.... */                             |
        if (flag_set) {                                                              |
            reg &= ~ANDROID_RECOVERY_BOOT;                                           |
            writel(reg, SNVS_BASE_ADDR + SNVS_LPGPR);            --------------------*-+
        }                                                                            | |
                                                                                     | |
        return flag_set;                                                             | |
    }                                                                                | |
                                                                                     | |
    void setup_recovery_env(void)                <-----------------------------------+ |
    {                                                                                  |
        board_recovery_setup();     --------+                                          |
    }                                       |                                          |
                                            |                                          |
    void board_recovery_setup(void)  <------+                                          |
    {                                                                                  |
        int bootdev = get_boot_device();                                               |
                                                                                       |
        switch (bootdev) {                                                             |
    #if defined(CONFIG_FASTBOOT_STORAGE_SATA)                                          |
        case SATA_BOOT:                                                                |
            if (!getenv("bootcmd_android_recovery"))                                   |
                setenv("bootcmd_android_recovery",                                     |
                    "boota sata recovery");                                            |
            break;                                                                     |
    #endif /*CONFIG_FASTBOOT_STORAGE_SATA*/                                            |
    #if defined(CONFIG_FASTBOOT_STORAGE_MMC)                                           |
        case SD2_BOOT:                                                                 |
        case MMC2_BOOT:                                                                |
            if (!getenv("bootcmd_android_recovery"))                                   |
                setenv("bootcmd_android_recovery",                                     |
                    "boota mmc0 recovery");                                            |
            break;                                                                     |
        case SD3_BOOT:                                                                 |
        case MMC3_BOOT:                                                                |
            if (!getenv("bootcmd_android_recovery"))                                   |
                setenv("bootcmd_android_recovery",                                     |
                    "boota mmc1 recovery");                                            |
            break;                                                                     |
        case MMC4_BOOT:                                                                |
            if (!getenv("bootcmd_android_recovery"))                                   |
                setenv("bootcmd_android_recovery",                                     |
                    "boota mmc2 recovery");                                            |
            break;                                                                     |
    #endif /*CONFIG_FASTBOOT_STORAGE_MMC*/                                             |
        default:                                                                       |
            printf("Unsupported bootup device for recovery: dev: %d\n",                |
                bootdev);                                                              |
            return;                                                                    |
        }                                                                              |
                                                                                       |
        printf("setup env for recovery..\n");                                          |
        setenv("bootcmd", "run bootcmd_android_recovery");                             |
    }                                                                                  |
                                                                                       |
三、Linux Kernel Hacking                                                               |
                                                                                       |
    DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")             |
        /*                                                                             |
         * i.MX6Q/DL maps system memory at 0x10000000 (offset 256MiB), and             |
         * GPU has a limit on physical address that it accesses, which must            |
         * be below 2GiB.                                                              |
         */                                                                            |
        .dma_zone_size  = (SZ_2G - SZ_256M),                                           |
        .smp        = smp_ops(imx_smp_ops),                                            |
        .map_io     = imx6q_map_io,                                                    |
        .init_irq   = imx6q_init_irq,                                                  |
        .init_machine   = imx6q_init_machine,                                          |
        .init_late      = imx6q_init_late,                                             |
        .dt_compat   = imx6q_dt_compat,                                                |
        .reserve     = imx6q_reserve,                                                  |
        .restart    = mxc_restart,                ------------------+                  |
    MACHINE_END                                                     |                  |
                                                                    |                  |
    /*                                                              |                  |
     * Reset the system. It is called by machine_restart().         |                  |
     */                                                             |                  |
    void mxc_restart(enum reboot_mode mode, const char *cmd)  <-----+                  |
    {                                                                                  |
        unsigned int wcr_enable;                                                       |
                                                                                       |
        arch_reset_special_mode(mode, cmd);            -------------------------+      |
                                                                                |      |
        if (wdog_clk)                                                           |      |
            clk_enable(wdog_clk);                                               |      |
                                                                                |      |
        if (cpu_is_mx1())                                                       |      |
            wcr_enable = (1 << 0);                                              |      |
        /*                                                                      |      |
         * Some i.MX6 boards use WDOG2 to reset external pmic in bypass mode,   |      |
         * so do WDOG2 reset here. Do not set SRS, since we will                |      |
         * trigger external POR later. Use WDOG1 to reset in ldo-enable         |      |
         * mode. You can set it by "fsl,wdog-reset" in dts.                     |      |
         * For i.MX6SX we have to trigger wdog-reset to reset QSPI-NOR flash to |      |
         * workaround qspi-nor reboot issue whatever ldo-bypass or not.         |      |
         */                                                                     |      |
        else if ((wdog_source == 2 && (cpu_is_imx6q() || cpu_is_imx6dl() ||     |      |
                cpu_is_imx6sl())) || cpu_is_imx6sx() || cpu_is_imx7d()          |      |
                || cpu_is_imx6ul())                                             |      |
            wcr_enable = 0x14;                                                  |      |
        else                                                                    |      |
            wcr_enable = (1 << 2);                                              |      |
                                                                                |      |
        /* Assert SRS signal */                                                 |      |
        __raw_writew(wcr_enable, wdog_base);                                    |      |
        /*                                                                      |      |
         * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be     |      |
         * written twice), we add another two writes to ensure there must be at |      |
         * least two writes happen in the same one 32kHz clock period.  We save |      |
         * the target check here, since the writes shouldn't be a huge burden   |      |
         * for other platforms.                                                 |      |
         */                                                                     |      |
        __raw_writew(wcr_enable, wdog_base);                                    |      |
        __raw_writew(wcr_enable, wdog_base);                                    |      |
                                                                                |      |
        /* wait for reset to assert... */                                       |      |
        mdelay(500);                                                            |      |
                                                                                |      |
        pr_err("%s: Watchdog reset failed to assert reset\n", __func__);        |      |
                                                                                |      |
        /* delay to allow the serial port to show the message */                |      |
        mdelay(50);                                                             |      |
                                                                                |      |
        /* we'll take a jump through zero as a poor second */                   |      |
        soft_restart(0);                                                        |      |
    }                                                                           |      |
                                                                                |      |
    static void arch_reset_special_mode(char mode, const char *cmd)  <----------+      |
    {                                                                                  |
    #ifdef CONFIG_MXC_REBOOT_ANDROID_CMD                                               |
        if (cmd && strcmp(cmd, "recovery") == 0)                                       |
            do_switch_recovery();                          -----+                      |
        else if (cmd && strcmp(cmd, "bootloader") == 0)         |                      |
            do_switch_fastboot();                               |                      |
    #endif                                                      |                      |
    }                                                           |                      |
                                                                |                      |
    void do_switch_recovery(void)           <-------------------+                      |
    {                                                                                  |
        u32 reg;                                                                       |
        void *addr;                                                                    |
        struct clk *snvs_root;                                                         |
        if(cpu_is_imx6()){                                                             |
            addr = ioremap(MX6_SNVS_BASE_ADDR, MX6_SNVS_SIZE);                         |
            if (!addr) {                                                               |
                pr_warn("SNVS ioremap failed!\n");                                     |
                return;                                                                |
            }                                                                          |
            reg = __raw_readl(addr + MX6_SNVS_LPGPR);           <----------------------+
            reg |= ANDROID_RECOVERY_BOOT;
            __raw_writel(reg, (addr + MX6_SNVS_LPGPR));
        }else{
            snvs_root = clk_get_sys("imx-snvs.0", "snvs");
            addr = ioremap(MX7_SNVS_BASE_ADDR, MX7_SNVS_SIZE);
            if (!addr) {
                pr_warn("SNVS ioremap failed!\n");
                return;
            }
            clk_enable(snvs_root);
            reg = __raw_readl(addr + MX7_SNVS_LPGPR);
            reg |= ANDROID_RECOVERY_BOOT;
            __raw_writel(reg, (addr + MX7_SNVS_LPGPR));
            clk_disable(snvs_root);
        }
        iounmap(addr);
    }

 

posted on 2017-06-12 21:08  zengjf  阅读(767)  评论(0编辑  收藏  举报

导航