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