blob: 6960967c62e79570a576b3551cff539c0be9f6f7 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <config.h>
#include <common.h>
#include <linux/kernel.h>
#include <amlogic/aml_efuse.h>
#include <amlogic/cpu_id.h>
#include <amlogic/storage.h>
#include <partition_table.h>
#include <fastboot.h>
#include <emmc_partitions.h>
#ifndef IS_FEAT_BOOT_VERIFY
#define IS_FEAT_BOOT_VERIFY() 0
#endif// #ifndef IS_FEAT_BOOT_VERIFY
int __attribute__((weak)) store_logic_read(const char *name, loff_t off, size_t size, void *buf)
{ return store_read(name, off, size, buf);}
typedef boot_img_hdr_t boot_img_hdr;
#define debugP(fmt...) //printf("[DbgBootSta]L%d:", __LINE__),printf(fmt)
#define errorP(fmt...) printf("ErrBootSta(L%d):", __LINE__),printf(fmt)
#define wrnP(fmt...) printf("wrn:"fmt)
#define MsgP(fmt...) printf("[BootSta]"fmt)
//check SWPL-31296 for details
static int do_get_bootloader_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
bool printStat = false;
bool saveenv = false;
debugP("Initial value for argc=%d\n", argc);
while (argc > 1 && **(argv + 1) == '-') {
char *arg = *++argv;
--argc;
while (*++arg) {
switch (*arg) {
case 'p': /* print */
printStat = true;
break;
case 's': /* saveenv */
saveenv = true;
break;
default:
return CMD_RET_USAGE;
}
}
}
debugP("Final value for argc=%d\n", argc);
//1,forUpgrade_socType, cpu familyId
const cpu_id_t cpuid = get_cpu_id();
const int familyId = cpuid.family_id;
env_set_hex("forUpgrade_socType", familyId);
//2,forUpgrade_secureBoot
const bool secureboot = IS_FEAT_BOOT_VERIFY();
env_set("forUpgrade_secureBoot", secureboot ? "true" : "false");
//3,forUpgrade_robustOta
bool supportRobustOta = false;
switch (familyId) {
case MESON_CPU_MAJOR_ID_SC2:
case MESON_CPU_MAJOR_ID_S4:
supportRobustOta = true;
break;
default:break;
}
env_set("forUpgrade_robustOta", supportRobustOta ? "true" : "false");
//4,forUpgrade_flashType
const char* BootDevices[] = {
"BOOT_EMMC", "BOOT_SD",
"BOOT_NAND_NFTL", "BOOT_NAND_MTD",
"BOOT_SNAND", "BOOT_SNOR",
};
const char* bootDevice = "BOOT_NONE";
enum boot_type_e bootType = store_get_type();
int i = 0;
for (; i < ARRAY_SIZE(BootDevices); ++i) {
if ((1<<i) != bootType) continue;
bootDevice = BootDevices[i];
break;
}
env_set("forUpgrade_flashType", bootDevice);
//5,forUpgrade_bootloaderCopies, how many copies supported
int bootCopies = 1;
switch (bootType) {
case BOOT_EMMC: bootCopies = 3; break;
default:break;
}
env_set_ulong("forUpgrade_bootloaderCopies", bootCopies);
//6,forUpgrade_bootloaderIndex
//for emmc, 0/1/2 is user/boot0/boot1
const int bootCpyIndex = store_bootup_bootidx("bootloader");
env_set_ulong("forUpgrade_bootloaderIndex", bootCpyIndex);
//7,get first boot index, for defendkey
const int firstBootCpyIndex = store_boot_copy_start();
env_set_ulong("forUpgrade_1stBootIndex", firstBootCpyIndex);
if (printStat) run_command("printenv forUpgrade_socType forUpgrade_secureBoot "
" forUpgrade_robustOta forUpgrade_flashType forUpgrade_bootloaderCopies "
" forUpgrade_bootloaderIndex forUpgrade_1stBootIndex", 0);
if (saveenv) run_command("saveenv", 0);
return CMD_RET_SUCCESS;
}
static void run_recovery_from_flash(void) {
env_set("dolby_status","0");
run_command("run init_display", 0);
run_command("run storeargs", 0);
run_command("run recovery_from_flash", 0);
}
static void run_recovery_from_cache(void) {
char *loadaddr_kernel = env_get("loadaddr_kernel");
if (loadaddr_kernel != NULL) {
env_set("loadaddr",loadaddr_kernel);
} else {
env_set("loadaddr","0x01080000");
}
env_set("dolby_status","0");
run_command("run init_display", 0);
run_command("run storeargs", 0);
run_command("if ext4load mmc 1:2 ${dtb_mem_addr} /recovery/dtb.img; then echo cache dtb.img loaded; fi;", 0);
run_command("if ext4load mmc 1:2 ${loadaddr} /recovery/recovery.img; then echo cache recovery.img loaded; fi;", 0);
env_set("check_result","recovery_succ");
run_command("bootm ${loadaddr}", 0);
env_set("check_result","recovery_fail");
env_set("reboot_status","reboot_recovery");
run_command("saveenv", 0);
run_command("reboot", 0);//need reboot old bootloader
}
int write_bootloader_back(const char* bootloaderindex, int dstindex) {
int iRet = 0;
int copy = 0;
int ret = -1;
unsigned char* buffer = NULL;
if (strcmp(bootloaderindex, "1") == 0) {
copy = 1;
} else if (strcmp(bootloaderindex, "2") == 0) {
copy = 2;
} else if (strcmp(bootloaderindex, "0") == 0) {
copy = 0;
}
buffer = (unsigned char *)malloc(0x2000 * 512);
if (!buffer)
{
printf("ERROR! fail to allocate memory ...\n");
goto exit;
}
memset(buffer, 0, 0x2000 * 512);
iRet = store_boot_read("bootloader", copy, 0, buffer);
if (iRet) {
errorP("Fail read bootloader from rsv with sz\n");
goto exit;
}
iRet = store_boot_write("bootloader", dstindex, 0, buffer);
if (iRet) {
printf("Failed to write bootloader\n");
goto exit;
} else {
ret = 0;
}
exit:
if (buffer)
{
free(buffer);
buffer = NULL;
}
return ret;
}
static void aml_recovery(void) {
char *mode = NULL;
char command[32];
char miscbuf[4096] = {0};
run_command("get_rebootmode", 0);
//get reboot_mode
mode = env_get("reboot_mode");
if (mode == NULL) {
wrnP("can not get reboot mode, so skip recovery check\n");
} else {
if ((!strcmp(mode, "factory_reset")) || (!strcmp(mode, "update"))) {
env_set("dolby_status","0");
}
}
#ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK
int ret = 0;
extern int boot_info_open_partition(char *miscbuf);
ret = boot_info_open_partition(miscbuf);
if (ret != 0) {
wrnP("open misc partition failed, so skip recovery check");
return;
}
#endif
//if run recovery, need disable dolby
memcpy(command, miscbuf, 32);
if (!memcmp(command, "boot-recovery", strlen("boot-recovery"))) {
env_set("dolby_status","0");
return;
}
}
static int do_secureboot_check(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
int code_boot = 0;
int match_flag = 0;
char *rebootstatus = NULL;
char *checkresult = NULL;
char *bootloaderindex = NULL;
char *expect_index = NULL;
char *robustota = NULL;
char *mode = NULL;
char *update_env = NULL;
char *rebootmode = NULL;
int ret = -1;
#ifdef CONFIG_MMC_MESON_GX
struct mmc *mmc = find_mmc_device(1);
#endif
//if recovery mode, need disable dv, if factoryreset, need default uboot env
aml_recovery();
run_command("get_rebootmode", 0);
rebootmode = env_get("reboot_mode");
printf("rebootmode is %s\n", rebootmode);
if (rebootmode && (strcmp(rebootmode, "rescueparty") == 0)) {
printf("rebootmode is rescueparty, need rollback\n");
char *slot;
#ifdef CONFIG_MMC_MESON_GX
if (mmc)
ret = aml_gpt_valid(mmc);
#endif
#ifdef CONFIG_FASTBOOT
struct misc_virtual_ab_message message;
set_mergestatus_cancel(&message);
#endif
slot = env_get("slot-suffixes");
if (!slot) {
run_command("get_valid_slot", 0);
slot = env_get("slot-suffixes");
}
if (strcmp(slot, "0") == 0) {
if (ret != 0) {
wrnP("normal mode\n");
write_bootloader_back("2", 0);
env_set("expect_index", "0");
} else {
wrnP("gpt mode\n");
env_set("expect_index", "2");
}
wrnP("back to slot b\n");
run_command("set_active_slot b", 0);
} else if (strcmp(slot, "1") == 0) {
if (ret != 0) {
wrnP("normal mode\n");
write_bootloader_back("1", 0);
env_set("expect_index", "0");
} else {
wrnP("gpt mode\n");
env_set("expect_index", "1");
}
wrnP("back to slot a\n");
run_command("set_active_slot a", 0);
}
env_set("update_env", "1");
env_set("reboot_status", "reboot_next");
run_command("saveenv", 0);
run_command("reset", 0);
}
//check_result init
checkresult = env_get("check_result");
if (checkresult == NULL) {
env_set("check_result","succ");
}
//reboot_status init
rebootstatus = env_get("reboot_status");
if (rebootstatus == NULL) {
env_set("reboot_status","reboot_init");
rebootstatus = env_get("reboot_status");
}
if (rebootstatus == NULL) {
printf("rebootstatus is NULL, skip check\n");
return -1;
}
//no secure check need
if (!strcmp(rebootstatus, "reboot_init")) {
printf("rebootstatus is reboot_init, skip check\n");
return -1;
}
//check reboot_end
if (!strcmp(rebootstatus, "reboot_end")) {
env_set("reboot_status","reboot_init");
run_command("saveenv", 0);
}
//get reboot_mode
mode = env_get("reboot_mode");
if (mode == NULL) {
wrnP("can not get reboot mode, so skip secure check\n");
return -1;
}
code_boot = strcmp(mode, "cold_boot");
if (code_boot == 0) {
wrnP("not support code_boot for check\n");
//env_set("reboot_status","reboot_init");
//run_command("saveenv", 0);
//return -1;
}
//get boot status
run_command("amlbootsta -p -s", 0);
//get forUpgrade_robustOta and check if support robustota
robustota = env_get("forUpgrade_robustOta");
if ((robustota == NULL) || !strcmp(robustota, "false")) {
return -1;
}
//get bootloader index
bootloaderindex = env_get("forUpgrade_bootloaderIndex");
if (bootloaderindex == NULL) {
wrnP("can not get bootloader index, so skip secure check\n");
return -1;
}
//get expect index
expect_index = env_get("expect_index");
if (expect_index == NULL) {
wrnP("can not get expect index, so skip secure check\n");
return -1;
}
match_flag = strcmp(bootloaderindex, expect_index);
//first reboot, command from recovery, need reboot next
if (!strcmp(rebootstatus,"reboot_next")) {
wrnP("--secure check reboot_next---\n");
//bootloader index, expect == current, no need reboot next
if (match_flag == 0) {
wrnP("current index is expect, no need reboot next, run ceche recovery\n");
if (has_boot_slot == 1) {
wrnP("ab mode\n");
update_env = env_get("update_env");
if (strcmp(update_env, "1") == 0) {
printf("ab mode, default all uboot env\n");
run_command("env default -a;saveenv;", 0);
env_set("update_env","0");
}
} else {
run_recovery_from_cache();
return 0;
}
} else {
wrnP("now ready start reboot next\n");
if (has_boot_slot == 1) {
#ifdef CONFIG_MMC_MESON_GX
if (mmc != NULL)
ret = aml_gpt_valid(mmc);
#endif
if (ret == 0) {
wrnP("gpt mode\n");
env_set("reboot_status","reboot_finish");
run_command("saveenv", 0);
run_command("get_rebootmode", 0);
run_command("if test ${reboot_mode} = quiescent; then reboot next,quiescent; else reboot next; fi;", 0);
} else {
write_bootloader_back(bootloaderindex, 0);
#ifdef CONFIG_FASTBOOT
struct misc_virtual_ab_message message;
set_mergestatus_cancel(&message);
#endif
if (strcmp(bootloaderindex, "1") == 0) {
wrnP("back to slot a\n");
run_command("set_active_slot a", 0);
} else if (strcmp(bootloaderindex, "2") == 0) {
wrnP("back to slot b\n");
run_command("set_active_slot b", 0);
}
env_set("update_env","1");
env_set("reboot_status","reboot_next");
env_set("expect_index","0");
run_command("saveenv", 0);
run_command("reset", 0);
}
} else {
env_set("reboot_status","reboot_finish");
run_command("saveenv", 0);
run_command("reboot next", 0);
}
return 0;
}
} else if (!strcmp(rebootstatus,"reboot_finish")) {//second reboot, reboot next from uboot
wrnP("--secure check reboot_finish---\n");
env_set("reboot_status","reboot_end");
run_command("saveenv", 0);
if (match_flag == 0) {
wrnP("reboot next succ, bootloader secure check pass......\n");
if (has_boot_slot == 1) {
printf("ab mode, default all uboot env\n");
update_env = env_get("update_env");
if (strcmp(update_env, "1") == 0) {
printf("ab mode, default all uboot env\n");
run_command("env default -a;", 0);
env_set("update_env","0");
if (strcmp(bootloaderindex, "2") == 0) {
wrnP("rom always boot as boot0--> boot1\n");
wrnP("So if boot1 is ok, write it to boot0\n");
run_command("copy_slot_bootable 2 1", 0);
}
run_command("saveenv", 0);
}
} else {
run_recovery_from_cache();
return 0;
}
} else {
//bootloader check failed, run recovery show error
wrnP("reboot next fail, bootloader secure check fail(curr:%s, expect:%s)......\n",bootloaderindex, expect_index);
env_set("check_result","bootloader_fail");
run_command("saveenv", 0);
if (has_boot_slot == 1) {
wrnP("ab mode\n");
#ifdef CONFIG_FASTBOOT
struct misc_virtual_ab_message message;
set_mergestatus_cancel(&message);
#endif
if (strcmp(bootloaderindex, "1") == 0) {
wrnP("back to slot a\n");
run_command("set_active_slot a", 0);
} else if (strcmp(bootloaderindex, "2") == 0) {
wrnP("back to slot b\n");
run_command("set_active_slot b", 0);
}
#ifdef CONFIG_MMC_MESON_GX
if (mmc != NULL)
ret = aml_gpt_valid(mmc);
#endif
if (ret == 0) {
wrnP("gpt mode\n");
env_set("update_env","0");
env_set("reboot_status","reboot_init");
run_command("saveenv", 0);
} else {
write_bootloader_back(bootloaderindex, 0);
env_set("update_env","1");
env_set("reboot_status","reboot_next");
env_set("expect_index","0");
run_command("saveenv", 0);
run_command("reset", 0);
}
} else {
run_recovery_from_flash();
return 0;
}
}
} else if (!strcmp(rebootstatus,"reboot_recovery")) {
//recovery check failed, run recovery show error
wrnP("--secure check reboot_recovery---\n");
env_set("reboot_status","reboot_end");
run_command("saveenv", 0);
run_recovery_from_flash();
return 0;
} else {
env_set("check_result","succ");
}
return 0;
}
U_BOOT_CMD_COMPLETE(
amlbootsta, 3, 0, do_get_bootloader_status,
"get bootloader status in env",
"[-p] print bootloader status\n"
"[-s] saveenv after generate bootloader status\n",
var_complete
);
U_BOOT_CMD_COMPLETE(
amlsecurecheck, 1, 0, do_secureboot_check,
"try bootloader/dtb/recovery secure check",
""
"",
var_complete
);