// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK #define COMMANDBUF_SIZE 32 #define STATUSBUF_SIZE 32 #define RECOVERYBUF_SIZE 768 #define BOOTINFO_OFFSET 864 #define RESERVED_OFFSET 896 #define SLOTBUF_SIZE 32 #define MISCBUF_SIZE 1088 #define CMD_WIPE_DATA "wipe_data" #define CMD_SYSTEM_CRASH "system_crash" #define CMD_RUN_RECOVERY "boot-recovery" #define CMD_RESIZE_DATA "resize2fs_data" #define CMD_FOR_RECOVERY "recovery_" #define CMD_FASTBOOTD "fastbootd" #define CMD_RECOVERY_WIPE "prompt_and_wipe_data" #define CMD_FACTORY "factory" #define CMD_BIST "bist" static const char * const temp_for_compile[] = {"__test1", "__test2", "__test3", NULL}; extern const char * const _env_list_execute_[0] __attribute__((weak, alias("temp_for_compile"))); struct bootloader_message { char command[32]; char status[32]; char recovery[768]; // The 'recovery' field used to be 1024 bytes. It has only ever // been used to store the recovery command line, so 768 bytes // should be plenty. We carve off the last 256 bytes to store the // stage string (for multistage packages) and possible future // expansion. char stage[32]; char slot_suffix[32]; char reserved[192]; }; #ifdef CONFIG_CMD_BCB static bool env_command_check(const char *cmd) { int index = 0; int envListNum = 0; char *s = NULL; char *v = NULL; char *strcopy = NULL; const char **envListArr = (const char **)_env_list_execute_; const char **pArr = (const char **)envListArr; while (*pArr++) { ++envListNum; } printf("envListNum = %d\n", envListNum); if (envListNum == 0) { return false; } strcopy = strdup(cmd); if (!strcopy) { return false; } s = strcopy; while ((v = strsep(&s, ";")) != NULL) { char *p = v; strsep(&p, " "); for (index = 0; index < envListNum; index++) { if (!strcmp(envListArr[index], v)) { break; } } if (index == envListNum) { printf("%s not in the white list.\n", v); free(strcopy); strcopy = NULL; return false; } } free(strcopy); strcopy = NULL; return true; } #endif /** * @usage: write data to storage device for nand flash * * @name: partition name, when it's null the target * will regards as whole device. * @off: offset to the 0 address of partition/device * @size: the amount of bytes to write * @buf: pointer of source buffer * * @return: result of the operation * 0 = success * other = fail */ int nand_store_write(const char *name, loff_t off, size_t size, void *buf) { int ret = -1; u64 rc = 0; unsigned char *buffer = NULL; u64 sz = 0; rc = store_logic_cap(name); if (rc == 1) { printf("Failed to get partition[%s] size\n", name); return ret; } sz = rc;// save the size buffer = (unsigned char *)malloc(rc); if (!buffer) { printf("ERROR! fail to allocate memory ...\n"); goto exit; } memset(buffer, 0, rc); /* 1. read all data from the partition table */ if (store_read(name, 0, rc, buffer) < 0) { printf("Fail to read %s partition\n", name); goto exit; } /* 2. update buffer data */ memcpy(buffer + off, buf, size); /* 3. erase partition */ rc = store_erase(name, 0, 0, 0); if (rc) { printf("Fail erase partition %s\n", name); goto exit; } // here the rc is 0, should use sz instead /* 4.write data back to the partitioned table */ if (store_write((const char *)name, 0, sz, (unsigned char *)buffer) < 0) { printf("failed to write data to %s.\n", name); goto exit; } else { ret = 0; } exit: if (buffer) free(buffer); return ret; } static int clear_misc_partition(char *clearbuf, int size) { char *partition = "misc"; memset(clearbuf, 0, size); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { if (nand_store_write((const char *)partition, 0, size, (unsigned char *)clearbuf) < 0) { printf("failed to clear %s for nand flash.\n", partition); return -1; } } else { if (store_write((const char *)partition, 0, size, (unsigned char *)clearbuf) < 0) { printf("failed to clear %s.\n", partition); return -1; } } return 0; } static int my_atoi(const char *str) { int result = 0; int signal = 1; if ((*str >= '0' && *str <= '9') || *str == '-' || *str == '+') { if (*str == '-' || *str == '+') { if (*str == '-') signal = -1; str++; } } else { return 0; } while (*str >= '0' && *str <= '9') result = result * 10 + (*str++ - '0'); return signal * result; } static int do_RunBcbCommand( cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int i = 0; char command[COMMANDBUF_SIZE] = {0}; char status[STATUSBUF_SIZE] = {0}; char recovery[RECOVERYBUF_SIZE] = {0}; char miscbuf[MISCBUF_SIZE] = {0}; char clearbuf[COMMANDBUF_SIZE+STATUSBUF_SIZE+RECOVERYBUF_SIZE] = {0}; char* RebootMode; char *Powermode; int remain_time = 0; if (argc != 2) { return cmd_usage(cmdtp); } printf("Command: "); for (i = 0; i < argc; i++) { printf("%s ", argv[i]); } printf("\n"); char *partition = "misc"; char *command_mark = (char *)argv[1]; if (strlen(command_mark) > sizeof(command)) { //printf("Bcb command mark range out of length(%d > %d).\n", //strlen(command_mark), sizeof(command)); goto ERR; } if (!memcmp(command_mark, CMD_WIPE_DATA, strlen(command_mark))) { printf("Start to write --wipe_data to %s\n", partition); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf+sizeof(command)+sizeof(status), "recovery\n--wipe_data", sizeof("recovery\n--wipe_data")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } } else if (!memcmp(command_mark, CMD_SYSTEM_CRASH, strlen(command_mark))) { printf("Start to write --system_crash to %s\n", partition); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf+sizeof(command)+sizeof(status), "recovery\n--system_crash", sizeof("recovery\n--system_crash")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } } else if (!memcmp(command_mark, CMD_RESIZE_DATA, strlen(command_mark))) { printf("Start to write --resize2fs_data to %s\n", partition); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf+sizeof(command)+sizeof(status), "recovery\n--resize2fs_data", sizeof("recovery\n--resize2fs_data")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } } else if (!memcmp(command_mark, CMD_FOR_RECOVERY, strlen(CMD_FOR_RECOVERY))) { memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); sprintf(recovery, "%s%s", "recovery\n--", command_mark); memcpy(miscbuf+sizeof(command)+sizeof(status), recovery, strlen(recovery)); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } return 0; } else if (!memcmp(command_mark, CMD_FASTBOOTD, strlen(command_mark))) { printf("write cmd to enter fastbootd \n"); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf+sizeof(command)+sizeof(status), "recovery\n--fastboot", sizeof("recovery\n--fastboot")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } return 0; } else if (IS_ENABLED(CONFIG_FACTORY_BURN_MODE) && !memcmp(command_mark, CMD_FACTORY, strlen(command_mark))) { printf("write cmd to enter factory mode\n"); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf + sizeof(command) + sizeof(status), "recovery\n--factory", sizeof("recovery\n--factory")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } env_set("factory_mode", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p factory_mode;", 0); #else run_command("saveenv;", 0); #endif run_command("reboot factory;", 0); } else if (IS_ENABLED(CONFIG_BIST_MODE) && !memcmp(command_mark, CMD_BIST, strlen(command_mark))) { printf("write cmd to enter bist mode\n"); memcpy(miscbuf, CMD_RUN_RECOVERY, sizeof(CMD_RUN_RECOVERY)); memcpy(miscbuf + sizeof(command) + sizeof(status), "recovery\n--bist", sizeof("recovery\n--bist")); if (store_get_type() == BOOT_SNAND || store_get_type() == BOOT_NAND_MTD) { nand_store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } else { store_write((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf); } env_set("bist_mode", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p bist_mode;", 0); #else run_command("saveenv;", 0); #endif run_command("reboot Diagnostic;", 0); } printf("Start read %s partition datas!\n", partition); if (store_read((const char *)partition, 0, sizeof(miscbuf), (unsigned char *)miscbuf) < 0) { printf("failed to store read %s.\n", partition); goto ERR; } #ifdef CONFIG_BOOTLOADER_SHOW_ERRCODE char rsvbuf[64]; memset(rsvbuf, 0, 64); strncpy(rsvbuf, miscbuf + RESERVED_OFFSET, 63); if (strlen(rsvbuf)) { char cmdbuf[256]; sprintf(cmdbuf, "errcode mode '%s'", rsvbuf); printf("cmdbuf:%s\n", cmdbuf); run_command(cmdbuf, 0); } #endif // judge misc partition whether has datas char tmpbuf[MISCBUF_SIZE]; memset(tmpbuf, 0, sizeof(tmpbuf)); if (!memcmp(tmpbuf, miscbuf, strlen(miscbuf))) { env_set("retry_recovery_times", "7"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p retry_recovery_times;", 0); #else run_command("saveenv;", 0); #endif printf("BCB hasn't any datas,exit!\n"); return 0; } memcpy(command, miscbuf, sizeof(command)); memcpy(status, miscbuf+sizeof(command), sizeof(status)); memcpy(recovery, miscbuf+sizeof(command)+sizeof(status), sizeof(recovery)); memcpy(clearbuf, miscbuf, sizeof(clearbuf)); printf("get bootloader message from misc partition:\n"); printf("[command:%s]\n[status:%s]\n[recovery:%s]\n", command, status, recovery); run_command("get_rebootmode", 0); RebootMode = env_get("reboot_mode"); if (RebootMode && strstr(RebootMode, "quiescent")) { printf("quiescent mode.\n"); run_command("run storeargs", 0); run_command("setenv bootconfig ${bootconfig} androidboot.quiescent=1;", 0); } Powermode = env_get("powermode"); if (RebootMode && !strcmp(RebootMode, "cold_boot")) { if (Powermode && !strcmp(Powermode, "standby")) { printf("cold_boot default standby, ignore recovery mode\n"); return 0; } } if (!memcmp(command, CMD_RUN_RECOVERY, strlen(CMD_RUN_RECOVERY))) { char *retry_times; char *retest_times; retry_times = env_get("retry_recovery_times"); if (retry_times) { printf("retry_time: %s\n", retry_times); remain_time = my_atoi(retry_times); printf("retry_time remain_time = %d\n", remain_time); } if (remain_time == 0) { printf("clear recovery cmds in misc\n"); if (clear_misc_partition(clearbuf, sizeof(clearbuf)) < 0) { printf("clear misc partition failed.\n"); goto ERR; } env_set("retry_recovery_times", "7"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p retry_recovery_times;", 0); #else run_command("saveenv;", 0); #endif run_command("run enter_fastboot", 0); } if (IS_ENABLED(CONFIG_FACTORY_BURN_MODE)) { char *factory_mode; factory_mode = env_get("factory_mode"); if (factory_mode && !strcmp(factory_mode, "1")) { printf("clear recovery cmds for factory mode\n"); if (clear_misc_partition(clearbuf, sizeof(clearbuf)) < 0) { printf("clear misc partition failed.\n"); goto ERR; } } } if (IS_ENABLED(CONFIG_BIST_MODE)) { char *bist_mode; bist_mode = env_get("bist_mode"); if (bist_mode && !strcmp(bist_mode, "1")) { printf("clear recovery cmds for bist mode\n"); if (clear_misc_partition(clearbuf, sizeof(clearbuf)) < 0) { printf("clear misc partition failed.\n"); goto ERR; } } } if (retry_times && remain_time > 0) { sprintf(retry_times, "%d", remain_time - 1); printf("retry_time: %s\n", retry_times); env_set("retry_recovery_times", retry_times); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p retry_recovery_times;", 0); #else run_command("saveenv;", 0); #endif } retest_times = env_get("retest_times"); if (strstr(recovery, CMD_RECOVERY_WIPE) && retest_times && (strcmp(retest_times, "2") == 0)) { char *rebootmode = env_get("reboot_mode"); printf("enter recovery first time, try again\n"); if (clear_misc_partition(clearbuf, sizeof(clearbuf)) < 0) { printf("clear misc partition failed.\n"); goto ERR; } run_command("set_retest_times 1;", 0); if (rebootmode && (strcmp(rebootmode, "quiescent") == 0)) run_command("reboot quiescent", 0); else run_command("reboot", 0); return 0; } #ifdef CONFIG_BOOTLOADER_SHOW_ERRCODE char cmdbuf[256]; char *reason = strstr(recovery, "reason"); if (reason) { sprintf(cmdbuf, "errcode misc %s", reason); run_command(cmdbuf, 0); } #endif if (run_command("run recovery_from_flash", 0) < 0) { printf("run_command for cmd:run recovery_from_flash failed.\n"); return -1; } printf("run command:run recovery_from_flash successful.\n"); return 0; } #ifdef CONFIG_CMD_BCB //uboot-command only valid once, not matter success or not if (clear_misc_partition(clearbuf, sizeof(clearbuf)) < 0) { printf("clear misc partition failed.\n"); goto ERR; } if (!memcmp(command_mark, command, strlen(command_mark))) { printf("%s\n", recovery); if (run_command((char *)recovery, 0) < 0) { printf("run_command for cmd:%s failed.\n", recovery); goto ERR; } printf("run command successful.\n"); } else if (!strncmp(command_mark, "uboot-command", strlen("uboot-command"))) { printf("uboot-command: %s\n", command); if (!env_command_check(command)) { printf("not all uboot-command in white-list\n"); goto ERR; } if (run_command((char *)command, 0) < 0) { printf("run_command for cmd:%s failed.\n", command); goto ERR; } printf("run uboot-command successful.\n"); } else { printf("command mark(%s) not match %s,don't execute.\n", command_mark, command); } #endif return 0; ERR: return -1; } #else static int do_RunBcbCommand( cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { if (argc != 2) { return cmd_usage(cmdtp); } // Do-Nothing! return 0; } #endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */ // BCB: Bootloader Control Block U_BOOT_CMD( bcb, 2, 0, do_RunBcbCommand, "bcb", "\nThis command will run some commands which saved in misc\n" "partition by mark to decide whether execute command!\n" "Command format:\n" " bcb bcb_mark\n" "Example:\n" " /dev/block/misc partition is saved some contents:\n" " uboot-command\n" // command mark " N/A\n" " setenv aa 11;setenv bb 22;setenv cc 33;saveenv;\n" // command "So you can execute command: bcb uboot-command" );