// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #ifdef CONFIG_AML_MTD #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #if (IS_ENABLED(CONFIG_UNIFY_BOOTLOADER)) #include "cmd_bootctl_wrapper.h" #endif #include "cmd_bootctl_utils.h" #include #include #include #include #include #include #if defined(CONFIG_EFUSE_OBJ_API) && defined(CONFIG_CMD_EFUSE) extern efuse_obj_field_t efuse_field; #endif//#ifdef CONFIG_EFUSE_OBJ_API #define BOOTLOADER_OFFSET 512 #define BOOTLOADER_MAX_SIZE (4 * 1024 * 1024) #define MIB_SIZE (1024 * 1024) #define GPT_OFFSET 0x3DFE00 #define DTS_GPT_OFFSET 0x3E0000 #ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK // Spaces used by misc partition are as below: // 0 - 2K For bootloader_message // 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used // as bootloader_message_ab struct) // 16K - 32K Used by uncrypt and recovery to store wipe_package for A/B devices // 32K - 64K System space, used for miscellanious AOSP features. See below. // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. #define BOOTLOADER_MESSAGE_OFFSET_IN_MISC 0 #define VENDOR_SPACE_OFFSET_IN_MISC 2 * 1024 #define WIPE_PACKAGE_OFFSET_IN_MISC 16 * 1024 #define SYSTEM_SPACE_OFFSET_IN_MISC 32 * 1024 #define SYSTEM_SPACE_SIZE_IN_MISC 32 * 1024 #define AB_METADATA_MISC_PARTITION_OFFSET 2048 #define MISCBUF_SIZE 2080 /* Bootloader Message (2-KiB) * * This structure describes the content of a block in flash * that is used for recovery and the bootloader to talk to * each other. * * The command field is updated by linux when it wants to * reboot into recovery or to update radio or bootloader firmware. * It is also updated by the bootloader when firmware update * is complete (to boot into recovery for any final cleanup) * * The status field was used by the bootloader after the completion * of an "update-radio" or "update-hboot" command, which has been * deprecated since Froyo. * * The recovery field is only written by linux and used * for the system to send a message to recovery or the * other way around. * * The stage field is written by packages which restart themselves * multiple times, so that the UI can reflect which invocation of the * package it is. If the value is of the format "#/#" (eg, "1/3"), * the UI will add a simple indicator of that status. * * We used to have slot_suffix field for A/B boot control metadata in * this struct, which gets unintentionally cleared by recovery or * uncrypt. Move it into struct bootloader_message_ab to avoid the * issue. */ 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]; // The 'reserved' field used to be 224 bytes when it was initially // carved off from the 1024-byte recovery field. Bump it up to // 1184-byte so that the entire bootloader_message struct rounds up // to 2048-byte. char reserved[1184]; }; /** * The A/B-specific bootloader message structure (4-KiB). * * We separate A/B boot control metadata from the regular bootloader * message struct and keep it here. Everything that's A/B-specific * stays after struct bootloader_message, which should be managed by * the A/B-bootloader or boot control HAL. * * The slot_suffix field is used for A/B implementations where the * bootloader does not set the androidboot.ro.boot.slot_suffix kernel * commandline parameter. This is used by fs_mgr to mount /system and * other partitions with the slotselect flag set in fstab. A/B * implementations are free to use all 32 bytes and may store private * data past the first NUL-byte in this field. It is encouraged, but * not mandatory, to use 'struct bootloader_control' described below. * * The update_channel field is used to store the Omaha update channel * if update_engine is compiled with Omaha support. */ struct bootloader_message_ab { struct bootloader_message message; char slot_suffix[32]; char update_channel[128]; // Round up the entire struct to 4096-byte. char reserved[1888]; }; #define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */ #define BOOT_CTRL_VERSION 1 typedef struct slot_metadata { // Slot priority with 15 meaning highest priority, 1 lowest // priority and 0 the slot is unbootable. uint8_t priority : 4; // Number of times left attempting to boot this slot. uint8_t tries_remaining : 3; // 1 if this slot has booted successfully, 0 otherwise. uint8_t successful_boot : 1; // 1 if this slot is corrupted from a dm-verity corruption, 0 // otherwise. uint8_t verity_corrupted : 1; // 0 init after ota; 1 slot boot success; 2 update boot1 ok uint8_t uboot_status : 2; // Reserved for further use. uint8_t reserved : 5; } slot_metadata; /* Bootloader Control AB * * This struct can be used to manage A/B metadata. It is designed to * be put in the 'slot_suffix' field of the 'bootloader_message' * structure described above. It is encouraged to use the * 'bootloader_control' structure to store the A/B metadata, but not * mandatory. */ typedef struct bootloader_control { // NUL terminated active slot suffix. char slot_suffix[4]; // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). uint32_t magic; // Version of struct being used (see BOOT_CTRL_VERSION). uint8_t version; // Number of slots being managed. uint8_t nb_slot : 3; // Number of times left attempting to boot recovery. uint8_t recovery_tries_remaining : 3; // Status of any pending snapshot merge of dynamic partitions. uint8_t merge_status : 3; // Ensure 4-bytes alignment for slot_info field. uint8_t roll_flag; // Per-slot information. Up to 4 slots. struct slot_metadata slot_info[4]; // Reserved for further use. uint8_t merge_flag; u8 firstboot; u8 boot_tries_remaining; u8 retest_times; u8 reserved1[4]; // CRC32 of all 28 bytes preceding this field (little endian // format). uint32_t crc32_le; } bootloader_control; #define MISC_VIRTUAL_AB_MESSAGE_VERSION 2 #define MISC_VIRTUAL_AB_MAGIC_HEADER 0x56740AB0 #ifndef CONFIG_UNIFY_BOOTLOADER unsigned int kDefaultBootAttempts = 4; #endif /* Magic for the A/B struct when serialized. */ #define AVB_AB_MAGIC "\0AB0" #define AVB_AB_MAGIC_LEN 4 /* Versioning for the on-disk A/B metadata - keep in sync with avbtool. */ #define AVB_AB_MAJOR_VERSION 1 #define AVB_AB_MINOR_VERSION 0 /* Size of AvbABData struct. */ #define AVB_AB_DATA_SIZE 32 /* Maximum values for slot data */ #define AVB_AB_MAX_PRIORITY 15 #define AVB_AB_MAX_TRIES_REMAINING 4 /* Struct used for recording per-slot metadata. * * When serialized, data is stored in network byte-order. */ typedef struct AvbABSlotData { /* Slot priority. Valid values range from 0 to AVB_AB_MAX_PRIORITY, * both inclusive with 1 being the lowest and AVB_AB_MAX_PRIORITY * being the highest. The special value 0 is used to indicate the * slot is unbootable. */ u8 priority; /* Number of times left attempting to boot this slot ranging from 0 * to AVB_AB_MAX_TRIES_REMAINING. */ u8 tries_remaining; /* Non-zero if this slot has booted successfully, 0 otherwise. */ u8 successful_boot; /* Reserved for future use. */ u8 uboot_status; } AvbABSlotData; /* Struct used for recording A/B metadata. * * When serialized, data is stored in network byte-order. */ typedef struct AvbABData { /* Magic number used for identification - see AVB_AB_MAGIC. */ u8 magic[AVB_AB_MAGIC_LEN]; /* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */ u8 version_major; u8 version_minor; /* Padding to ensure |slots| field start eight bytes in. */ u8 roll_flag; u8 firstboot; /* Per-slot metadata. */ AvbABSlotData slots[2]; /* Reserved for future use. */ u8 boot_tries_remaining; u8 reserved2[11]; /* CRC32 of all 28 bytes preceding this field. */ u32 crc32; } AvbABData; #ifdef CONFIG_UNIFY_BOOTLOADER bootctl_func_handles avb_cmd_bootctrl_handles = {0}; #endif static void reboot_by_reason(unsigned int r) { struct arm_smccc_res res; arm_smccc_smc(SET_REBOOT_REASON, r, 0, 0, 0, 0, 0, 0, &res); if (res.a0) printf("failed to set reboot reason\n"); run_command("wdt dev watchdog", 0); run_command("wdt expire", 0); } static bool boot_info_validate_VAB(bootloader_control *info) { if (info->magic != BOOT_CTRL_MAGIC) { printf("Magic 0x%x is incorrect.\n", info->magic); return false; } return true; } static bool boot_info_validate(AvbABData *info) { if (memcmp(info->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) { printf("Magic %s is incorrect.\n", info->magic); return false; } if (info->version_major > AVB_AB_MAJOR_VERSION) { printf("No support for given major version.\n"); return false; } return true; } static void boot_info_reset(AvbABData *info) { memset(info, '\0', sizeof(AvbABData)); memcpy(info->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN); info->version_major = AVB_AB_MAJOR_VERSION; info->version_minor = AVB_AB_MINOR_VERSION; info->firstboot = 1; info->boot_tries_remaining = AVB_AB_MAX_TRIES_REMAINING; info->slots[0].priority = AVB_AB_MAX_PRIORITY; info->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; info->slots[0].successful_boot = 1; info->slots[0].uboot_status = 1; info->slots[1].priority = AVB_AB_MAX_PRIORITY - 1; info->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; info->slots[1].successful_boot = 0; info->slots[1].uboot_status = 0; } static void dump_boot_info(AvbABData *info) { int loglevel = env_get_ulong("loglevel", 10, 0); char *rebootmode = env_get("reboot_mode"); if (loglevel >= 4 && (rebootmode && strcmp(rebootmode, "fastboot"))) { printf("info->magic = %s\n", info->magic); printf("info->version_major = %d\n", info->version_major); printf("info->version_minor = %d\n", info->version_minor); printf("info->firstboot = %d\n", info->firstboot); printf("info->boot_tries_remaining = %d\n", info->boot_tries_remaining); printf("info->slots[0].priority = %d\n", info->slots[0].priority); printf("info->slots[0].tries_remaining = %d\n", info->slots[0].tries_remaining); printf("info->slots[0].successful_boot = %d\n", info->slots[0].successful_boot); printf("info->slots[0].uboot_status = %d\n", info->slots[0].uboot_status); printf("info->slots[1].priority = %d\n", info->slots[1].priority); printf("info->slots[1].tries_remaining = %d\n", info->slots[1].tries_remaining); printf("info->slots[1].successful_boot = %d\n", info->slots[1].successful_boot); printf("info->slots[1].uboot_status = %d\n", info->slots[1].uboot_status); printf("info->crc32 = %d\n", info->crc32); } } void dump_boot_info_VAB(bootloader_control *boot_ctrl) { } static bool slot_is_bootable_VAB(slot_metadata *slot) { return slot->tries_remaining != 0; } int get_active_slot_VAB(bootloader_control *info) { if (info->slot_info[0].priority > info->slot_info[1].priority) { return 0; } else if (info->slot_info[0].priority == info->slot_info[1].priority) { if (info->slot_info[0].successful_boot == 1) return 0; else return 1; } else { return 1; } } static bool slot_is_bootable(AvbABSlotData *slot) { return slot->priority > 0 && (slot->successful_boot || (slot->tries_remaining > 0)); } static int get_active_slot(AvbABData *info) { if (info->slots[0].priority > info->slots[1].priority) return 0; else return 1; } static int boot_info_set_active_slot(AvbABData *info, int slot) { unsigned int other_slot_number; /* Make requested slot top priority, unsuccessful, and with max tries. */ info->slots[slot].priority = AVB_AB_MAX_PRIORITY; info->slots[slot].tries_remaining = AVB_AB_MAX_TRIES_REMAINING; //info->slots[slot].successful_boot = 0; /* Ensure other slot doesn't have as high a priority. */ other_slot_number = 1 - slot; //info->slots[other_slot_number].priority -= 1; if (info->slots[other_slot_number].priority == AVB_AB_MAX_PRIORITY) info->slots[other_slot_number].priority = AVB_AB_MAX_PRIORITY - 1; dump_boot_info(info); return 0; } bool boot_info_load_VAB(bootloader_control *out_info, char *miscbuf) { memcpy(out_info, miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, sizeof(bootloader_control)); dump_boot_info_VAB(out_info); return true; } static bool boot_info_load(AvbABData *out_info, char *miscbuf) { memcpy(out_info, miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, AVB_AB_DATA_SIZE); dump_boot_info(out_info); return true; } static bool boot_info_save(AvbABData *info, char *miscbuf) { char *partition = "misc"; int ret = 0; printf("save boot-info\n"); info->crc32 = avb_htobe32(avb_crc32((const uint8_t *)info, sizeof(AvbABData) - sizeof(uint32_t))); memcpy(miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, info, AVB_AB_DATA_SIZE); dump_boot_info(info); #ifdef CONFIG_AML_MTD enum boot_type_e device_boot_flag = store_get_type(); if (device_boot_flag == BOOT_NAND_NFTL || device_boot_flag == BOOT_NAND_MTD || device_boot_flag == BOOT_SNAND) { int ret = 0; ret = run_command("store erase misc 0 0x4000", 0); if (ret != 0) { printf("erase partition misc failed!\n"); return false; } } #endif ret = store_logic_write((const char *)partition, 0, MISCBUF_SIZE, (unsigned char *)miscbuf); if (ret) { printf("store logic write failed at %s\n", partition); return false; } return true; } static int check_bootloader(int slot) { int iRet = 0; int ret = 0; unsigned char *buffer_src = NULL; unsigned char *buffer_dest = NULL; int capacity_boot = 0; char back_partname[32] = {0}; struct blk_desc *dev_desc; gpt_header *gpt_h1; gpt_header *gpt_h2; int i = 0; #ifdef CONFIG_MMC_MESON_GX struct mmc *mmc = NULL; if (store_get_type() == BOOT_EMMC) mmc = find_mmc_device(1); if (mmc) capacity_boot = mmc->capacity_boot; #endif printf("check_bootloader: %x\n", capacity_boot); buffer_src = (unsigned char *)malloc(capacity_boot); if (!buffer_src) { printf("ERROR! fail to allocate memory ...\n"); goto exit; } memset(buffer_src, 0, capacity_boot); if (slot == 0) { strcpy((char *)back_partname, "bootloader_b"); } else if (slot == 1) { strcpy((char *)back_partname, "bootloader_a"); } iRet = store_boot_read("bootloader", 2, BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET, buffer_src); if (iRet) { printf("Fail read boot1\n"); goto exit; } buffer_dest = buffer_src; memset(buffer_dest, 0, capacity_boot); iRet = store_logic_read(back_partname, 0, BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET, buffer_dest); if (iRet) { printf("Fail read %s\n", back_partname); goto exit; } for (i = 512; i < capacity_boot; i++) { if (buffer_dest[i] != buffer_src[i]) ret = 1; } printf("boot1 & %s %s same\n", back_partname, ret ? "NOT" : "DO"); #ifdef CONFIG_MMC_MESON_GX dev_desc = blk_get_dev("mmc", 1); if (dev_desc && dev_desc->type != DEV_TYPE_UNKNOWN) { printf("valid mmc device, check gpt\n"); gpt_h1 = (void *)buffer_dest + GPT_OFFSET + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz); gpt_h2 = (void *)buffer_dest + DTS_GPT_OFFSET + (GPT_PRIMARY_PARTITION_TABLE_LBA * dev_desc->blksz); printf("gpt_h1 signature is: 0x%llX\n", le64_to_cpu(gpt_h1->signature)); printf("gpt_h2 signature is: 0x%llX\n", le64_to_cpu(gpt_h2->signature)); if (le64_to_cpu(gpt_h1->signature) == GPT_HEADER_SIGNATURE_UBOOT || le64_to_cpu(gpt_h2->signature) == GPT_HEADER_SIGNATURE_UBOOT) { printf("backup slot has gpt, set dts_to_gpt to 0\n"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) env_set("dts_to_gpt", "0"); run_command("update_env_part -p dts_to_gpt;", 0); #else run_command("defenv_reserve;setenv dts_to_gpt 0;saveenv;", 0); #endif if (ret) { printf("boot1 doesn't = %s, rewrite it\n", back_partname); iRet = store_boot_write("bootloader", 2, BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET, buffer_dest); if (iRet) { printf("Failed to write bootloader\n"); goto exit; } else { printf("write %s to boot1 ok\n", back_partname); ret = 0; } } } } #endif exit: if (buffer_src) { free(buffer_src); buffer_src = NULL; } return ret; } static int is_BootSame(int srcindex, int dstindex) { int iRet = 0; int ret = -1; unsigned char *buffer_src = NULL; unsigned char *buffer_dest = NULL; const int SHA1SUMLEN = 20; u8 gensum0[SHA1SUMLEN * 2]; u8 *gensum1 = gensum0 + SHA1SUMLEN; int capacity_boot = 0; #ifdef CONFIG_MMC_MESON_GX struct mmc *mmc = NULL; if (store_get_type() == BOOT_EMMC) mmc = find_mmc_device(1); if (mmc) capacity_boot = mmc->capacity_boot; #endif printf("is_BootSame_capacity_boot: %x\n", capacity_boot); buffer_src = (unsigned char *)malloc(capacity_boot); if (!buffer_src) { printf("ERROR! fail to allocate memory ...\n"); goto exit; } memset(buffer_src, 0, capacity_boot); iRet = store_boot_read("bootloader", srcindex, 0, buffer_src); if (iRet) { printf("Fail read bootloader %d\n", srcindex); goto exit; } sha1_csum(buffer_src, capacity_boot, gensum0); buffer_dest = buffer_src; memset(buffer_dest, 0, capacity_boot); iRet = store_boot_read("bootloader", dstindex, 0, buffer_dest); if (iRet) { printf("Fail read bootloader %d\n", dstindex); goto exit; } sha1_csum(buffer_dest, capacity_boot, gensum1); ret = memcmp(gensum0, gensum1, SHA1SUMLEN); printf("bootloader %d & %d %s same\n", srcindex, dstindex, ret ? "NOT" : "DO"); exit: if (buffer_src) { free(buffer_src); buffer_src = NULL; } return ret; } static int write_bootloader(int copy, int dstindex) { int iRet = 0; int ret = -1; unsigned char *buffer = NULL; int capacity_boot = 0; #ifdef CONFIG_MMC_MESON_GX struct mmc *mmc = NULL; if (store_get_type() == BOOT_EMMC) mmc = find_mmc_device(1); #endif if (mmc) capacity_boot = mmc->capacity_boot; printf("write_bootloader_capacity_boot: %x\n", capacity_boot); buffer = (unsigned char *)malloc(capacity_boot); if (!buffer) { printf("ERROR! fail to allocate memory ...\n"); goto exit; } memset(buffer, 0, capacity_boot); printf("copy from boot%d to boot%d\n", copy, dstindex); iRet = store_boot_read("bootloader", copy, 0, buffer); if (iRet) { printf("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 update_after_failed_rollback(void) { run_command("run init_display; run storeargs; run update;", 0); } void rollback_failure_handler(void) __attribute__((weak, alias("update_after_failed_rollback"))); static int do_GetValidSlot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char miscbuf[MISCBUF_SIZE] = {0}; bootloader_control boot_ctrl; AvbABData info; int slot; int AB_mode = 0; bool bootable_a, bootable_b; bool nocs_mode = false; char *rebootmode = env_get("reboot_mode"); if (argc != 1) return cmd_usage(cmdtp); boot_info_open_partition(miscbuf); boot_info_load(&info, miscbuf); if (!boot_info_validate(&info)) { printf("boot-info is invalid. Try VAB.\n"); boot_info_load_VAB(&boot_ctrl, miscbuf); if (!boot_info_validate_VAB(&boot_ctrl)) { printf("boot-info is invalid. Resetting.\n"); boot_info_reset(&info); boot_info_save(&info, miscbuf); } else { printf("rollback from R\n"); AB_mode = 1; } } if (AB_mode == 1) { slot = get_active_slot_VAB(&boot_ctrl); printf("active slot = %d\n", slot); printf("boot_ctrl.roll_flag = %d\n", boot_ctrl.roll_flag); bootable_a = slot_is_bootable_VAB(&boot_ctrl.slot_info[0]); bootable_b = slot_is_bootable_VAB(&boot_ctrl.slot_info[1]); boot_info_reset(&info); boot_info_set_active_slot(&info, slot); info.roll_flag = boot_ctrl.roll_flag; boot_info_save(&info, miscbuf); } else { slot = get_active_slot(&info); printf("active slot = %d\n", slot); bootable_a = slot_is_bootable(&info.slots[0]); bootable_b = slot_is_bootable(&info.slots[1]); } if (dynamic_partition) env_set("partition_mode", "dynamic"); else env_set("partition_mode", "normal"); if (gpt_partition) env_set("gpt_mode", "true"); else env_set("gpt_mode", "false"); if (vendor_boot_partition) { env_set("vendor_boot_mode", "true"); printf("set vendor_boot_mode true\n"); } else { env_set("vendor_boot_mode", "false"); printf("set vendor_boot_mode false\n"); } if (slot == 0) { if (bootable_a) { if (has_boot_slot == 1) { env_set("active_slot", "_a"); env_set("boot_part", "boot_a"); env_set("recovery_part", "recovery_a"); env_set("slot-suffixes", "0"); } else { env_set("active_slot", "normal"); env_set("boot_part", "boot"); env_set("recovery_part", "recovery"); env_set("slot-suffixes", "-1"); } return 0; } else if (bootable_b) { printf("slot a is unbootable, back to b\n"); if (gpt_partition || nocs_mode) check_bootloader(0); info.roll_flag = 1; boot_info_save(&info, miscbuf); run_command("set_active_slot b", 0); env_set("update_env", "1"); env_set("reboot_status", "reboot_next"); #if defined(CONFIG_EFUSE_OBJ_API) && defined(CONFIG_CMD_EFUSE) run_command("efuse_obj get FEAT_DISABLE_EMMC_USER", 0); if (*efuse_field.data == 1) nocs_mode = true; #endif//#ifdef CONFIG_EFUSE_OBJ_API if (gpt_partition || nocs_mode) { printf("gpt or nocs mode\n"); write_bootloader(2, 1); env_set("expect_index", "1"); } else { printf("normal mode\n"); write_bootloader(2, 0); env_set("expect_index", "0"); } run_command("saveenv", 0); if (rebootmode && (strcmp(rebootmode, "quiescent") == 0)) reboot_by_reason(AMLOGIC_QUIESCENT_REBOOT); else reboot_by_reason(AMLOGIC_NORMAL_BOOT); } else { rollback_failure_handler(); } } if (slot == 1) { if (bootable_b) { if (has_boot_slot == 1) { env_set("active_slot", "_b"); env_set("boot_part", "boot_b"); env_set("recovery_part", "recovery_b"); env_set("slot-suffixes", "1"); } else { env_set("active_slot", "normal"); env_set("boot_part", "boot"); env_set("recovery_part", "recovery"); env_set("slot-suffixes", "-1"); } return 0; } else if (bootable_a) { printf("slot b is unbootable, back to a\n"); if (gpt_partition || nocs_mode) check_bootloader(1); info.roll_flag = 1; boot_info_save(&info, miscbuf); run_command("set_active_slot a", 0); env_set("update_env", "1"); env_set("reboot_status", "reboot_next"); #if defined(CONFIG_EFUSE_OBJ_API) && defined(CONFIG_CMD_EFUSE) run_command("efuse_obj get FEAT_DISABLE_EMMC_USER", 0); if (*efuse_field.data == 1) nocs_mode = true; #endif//#ifdef CONFIG_EFUSE_OBJ_API if (gpt_partition || nocs_mode) { printf("gpt or nocs mode\n"); write_bootloader(2, 1); env_set("expect_index", "1"); } else { printf("normal mode\n"); write_bootloader(1, 0); env_set("expect_index", "0"); } run_command("saveenv", 0); if (rebootmode && (strcmp(rebootmode, "quiescent") == 0)) reboot_by_reason(AMLOGIC_QUIESCENT_REBOOT); else reboot_by_reason(AMLOGIC_NORMAL_BOOT); } else { rollback_failure_handler(); } } return 0; } static int do_SetActiveSlot (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char miscbuf[MISCBUF_SIZE] = {0}; AvbABData info; if (argc != 2) return cmd_usage(cmdtp); if (has_boot_slot == 0) { printf("device is not ab mode\n"); return -1; } boot_info_open_partition(miscbuf); boot_info_load(&info, miscbuf); if (!boot_info_validate(&info)) { printf("boot-info is invalid. Resetting.\n"); boot_info_reset(&info); boot_info_save(&info, miscbuf); } if (strcmp(argv[1], "a") == 0) { env_set("active_slot", "_a"); env_set("slot-suffixes", "0"); env_set("boot_part", "boot_a"); env_set("recovery_part", "recovery_a"); printf("set active slot a\n"); boot_info_set_active_slot(&info, 0); } else if (strcmp(argv[1], "b") == 0) { env_set("active_slot", "_b"); env_set("slot-suffixes", "1"); env_set("boot_part", "boot_b"); env_set("recovery_part", "recovery_b"); printf("set active slot b\n"); boot_info_set_active_slot(&info, 1); } else { printf("error input slot\n"); return -1; } boot_info_save(&info, miscbuf); printf("info.roll_flag = %d\n", info.roll_flag); if (!gpt_partition && info.roll_flag == 1) { printf("if null gpt, write dtb back when rollback\n"); if (run_command("imgread dtb ${boot_part} ${dtb_mem_addr}", 0)) { printf("Fail in load dtb\n"); } else { printf("write dtb back\n"); run_command("emmc dtb_write ${dtb_mem_addr} 0", 0); } } return 0; } static int do_SetUpdateTries (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char miscbuf[MISCBUF_SIZE] = {0}; AvbABData info; int slot; bool bootable_a, bootable_b; int ret = -1; bool nocs_mode = false; int update_flag = 0; char *rebootmode = env_get("reboot_mode"); if (has_boot_slot == 0) { printf("device is not ab mode\n"); return -1; } boot_info_open_partition(miscbuf); boot_info_load(&info, miscbuf); if (!boot_info_validate(&info)) { printf("boot-info is invalid. Resetting.\n"); boot_info_reset(&info); boot_info_save(&info, miscbuf); } slot = get_active_slot(&info); bootable_a = slot_is_bootable(&info.slots[0]); bootable_b = slot_is_bootable(&info.slots[1]); if (slot == 0) { if (bootable_a) { if (info.slots[0].successful_boot == 0) { info.slots[0].tries_remaining -= 1; update_flag = 1; } } } if (slot == 1) { if (bootable_b) { if (info.slots[1].successful_boot == 0) { info.slots[1].tries_remaining -= 1; update_flag = 1; } } } if (info.firstboot == 1) { if (rebootmode && strcmp(rebootmode, "fastboot") && strcmp(rebootmode, "factory_reset") && strcmp(rebootmode, "factory_reboot") && strcmp(rebootmode, "bist_reboot")) { info.boot_tries_remaining -= 1; update_flag = 1; } } if (update_flag == 1) boot_info_save(&info, miscbuf); printf("%s info.roll_flag = %d\n", __func__, info.roll_flag); if (info.roll_flag == 1) env_set("rollback_flag", "1"); #if defined(CONFIG_EFUSE_OBJ_API) && defined(CONFIG_CMD_EFUSE) run_command("efuse_obj get FEAT_DISABLE_EMMC_USER", 0); if (*efuse_field.data == 1) nocs_mode = true; #endif//#ifdef CONFIG_EFUSE_OBJ_API if (info.slots[slot].successful_boot == 1 && info.slots[slot].uboot_status != 2) { if (gpt_partition || nocs_mode) { char *bootloaderindex = NULL; printf("current slot %d is successful_boot\n", slot); bootloaderindex = env_get("forUpgrade_bootloaderIndex"); printf("bootloaderindex: %s\n", bootloaderindex); #ifdef CONFIG_BURN_KEY char *factorytest = env_get("factorytest"); if (factorytest && (strcmp(factorytest, "1") == 0)) { printf("factory burn mode, stop sync boot0 & boot1\n"); return 0; } #endif /*if boot from boot1, means boot0 is bab, don't need to copyback*/ if (bootloaderindex && strcmp(bootloaderindex, "2")) { printf("check if boot0 = boot1\n"); ret = is_BootSame(1, 2); if (ret) { printf("boot0 doesn't = boot1, write boot0 to boot1\n"); write_bootloader(1, 2); printf("after write boot0 to boot1\n"); } info.slots[slot].uboot_status = 2; boot_info_save(&info, miscbuf); } } } return 0; } static int do_SetRollFlag (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char miscbuf[MISCBUF_SIZE] = {0}; AvbABData info; if (has_boot_slot == 0) { printf("device is not ab mode\n"); return -1; } boot_info_open_partition(miscbuf); boot_info_load(&info, miscbuf); if (!boot_info_validate(&info)) { printf("boot-info is invalid. Resetting.\n"); return 0; } if (strcmp(argv[1], "1") == 0) info.roll_flag = 1; else info.roll_flag = 0; boot_info_save(&info, miscbuf); printf("set info.roll_flag = %d\n", info.roll_flag); return 0; } static int do_GetSystemMode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { #ifdef CONFIG_SYSTEM_AS_ROOT env_set("system_mode", "1"); #else env_set("system_mode", "0"); #endif return 0; } static int do_UpdateDt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char *update_dt = env_get("update_dt"); if (update_dt && (!strcmp(update_dt, "1"))) { printf("write dtb from ${boot_part}\n"); run_command("imgread dtb ${boot_part} ${dtb_mem_addr}", 0); run_command("emmc dtb_write ${dtb_mem_addr} 0", 0); env_set("update_dt", "0"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p update_dt;", 0); #else run_command("saveenv", 0); #endif char *part_changed = env_get("part_changed"); if (part_changed && (!strcmp(part_changed, "1"))) { env_set("part_changed", "0"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p part_changed;", 0); #else run_command("saveenv", 0); #endif run_command("reset", 0); } } return 0; } #endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */ #ifdef CONFIG_UNIFY_BOOTLOADER bootctl_func_handles *get_bootctl_cmd_func_avb(void) { avb_cmd_bootctrl_handles.do_GetValidSlot_func = do_GetValidSlot; avb_cmd_bootctrl_handles.do_SetActiveSlot_func = do_SetActiveSlot; avb_cmd_bootctrl_handles.do_SetRollFlag_func = do_SetRollFlag; avb_cmd_bootctrl_handles.do_SetUpdateTries_func = do_SetUpdateTries; avb_cmd_bootctrl_handles.do_GetSystemMode_func = do_GetSystemMode; avb_cmd_bootctrl_handles.do_UpdateDt_func = do_UpdateDt; return &avb_cmd_bootctrl_handles; } #else U_BOOT_CMD (get_valid_slot, 2, 0, do_GetValidSlot, "get_valid_slot", "\nThis command will choose valid slot to boot up which saved in misc\n" "partition by mark to decide whether execute command!\n" "So you can execute command: get_valid_slot" ); U_BOOT_CMD (set_active_slot, 2, 1, do_SetActiveSlot, "set_active_slot", "\nThis command will set active slot\n" "So you can execute command: set_active_slot a" ); U_BOOT_CMD (set_roll_flag, 2, 1, do_SetRollFlag, "set_roll_flag", "\nThis command will set active slot\n" "So you can execute command: set_active_slot a" ); U_BOOT_CMD (update_tries, 2, 0, do_SetUpdateTries, "update_tries", "\nThis command will change tries_remaining in misc\n" "So you can execute command: update_tries" ); U_BOOT_CMD (get_system_as_root_mode, 1, 0, do_GetSystemMode, "get_system_as_root_mode", "\nThis command will get system_as_root_mode\n" "So you can execute command: get_system_as_root_mode" ); U_BOOT_CMD (update_dt, 1, 0, do_UpdateDt, "update_dt", "\nThis command will update dt\n" "So you can execute command: update_dt" ); #endif static int get_bootloader_message_block(char * miscbuf, int size, AvbABData *info) { const char *misc_device = "misc"; printf("read misc for emmc device\n"); if (store_read(misc_device, 0, size, miscbuf) < 0) { printf("Error opening %s\n", misc_device); return -1; } memcpy(info, miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, AVB_AB_DATA_SIZE); if (!boot_info_validate(info)) { printf("boot-info is invalid.\n"); return -1; //boot_info_reset(&info); } dump_boot_info(info); return 0; } int set_successful_boot(void) { int ret = 0; char miscbuf[MISCBUF_SIZE] = {0}; AvbABData info; int slot = 0; ret = get_bootloader_message_block(miscbuf, MISCBUF_SIZE, &info); if (ret != 0) { printf("get_bootloader_message failed!\n"); return -1; } if (info.slots[1].priority > info.slots[0].priority) { printf("slot = 1\n"); slot = 1; } if (info.slots[slot].successful_boot) { printf("info.slots[%d].successful_boot\n", slot); return 0; } return -1; }