首页 > 代码库 > I.MX6 recovery mode hacking

I.MX6 recovery mode hacking

/******************************************************************************** *                      I.MX6 recovery mode hacking * 说明: *     看一下i.MX6 Recovery模式是怎么工作的。 * *                                              2017-6-12 深圳 龙华樟坑村 曾剑锋 *******************************************************************************/一、参考文档:    1. 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);    }

 

I.MX6 recovery mode hacking