blob: 5a8ba5bb0c7fa531f405f30a8ccf8bf0cbf28cac [file] [log] [blame] [edit]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <command.h>
#include <env.h>
#include <malloc.h>
#ifdef CONFIG_AML_MTD
#include <linux/mtd/mtd.h>
#endif
#include <asm/byteorder.h>
#include <config.h>
#include <asm/amlogic/arch/io.h>
#include <amlogic/partition_table.h>
#include <amlogic/libavb/libavb.h>
#include <version.h>
#include <amlogic/storage.h>
#include <fastboot.h>
#include <u-boot/sha1.h>
#include <asm/amlogic/arch/efuse.h>
#include <amlogic/emmc_partitions.h>
#if (IS_ENABLED(CONFIG_UNIFY_BOOTLOADER))
#include "cmd_bootctl_wrapper.h"
#endif
#include "cmd_bootctl_utils.h"
#include <amlogic/store_wrapper.h>
#include <asm/amlogic/arch/secure_apb.h>
#if defined(CONFIG_EFUSE_OBJ_API) && defined(CONFIG_CMD_EFUSE)
extern efuse_obj_field_t efuse_field;
#endif//#ifdef CONFIG_EFUSE_OBJ_API
#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;
// Reserved for further use.
uint8_t reserved : 7;
} 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 reserved1[5];
// 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
#define BOOT_ATTEMPTS 4
/* 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 7
/* 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.
*/
uint8_t priority;
/* Number of times left attempting to boot this slot ranging from 0
* to AVB_AB_MAX_TRIES_REMAINING.
*/
uint8_t tries_remaining;
/* Non-zero if this slot has booted successfully, 0 otherwise. */
uint8_t successful_boot;
/* Reserved for future use. */
uint8_t reserved[1];
} 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. */
uint8_t magic[AVB_AB_MAGIC_LEN];
/* Version of on-disk struct - see AVB_AB_{MAJOR,MINOR}_VERSION. */
uint8_t version_major;
uint8_t version_minor;
/* Padding to ensure |slots| field start eight bytes in. */
uint8_t reserved1[2];
/* Per-slot metadata. */
AvbABSlotData slots[2];
/* Reserved for future use. */
uint8_t reserved2[12];
/* CRC32 of all 28 bytes preceding this field. */
uint32_t crc32;
}AvbABData;
#ifdef CONFIG_UNIFY_BOOTLOADER
bootctl_func_handles vab_cmd_bootctrl_handles = {0};
#endif
static bool boot_info_validate(bootloader_control *info)
{
if (info->magic != BOOT_CTRL_MAGIC) {
printf("Magic 0x%x is incorrect.\n", info->magic);
return false;
}
return true;
}
bool boot_info_validate_normalAB(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;
}
void boot_info_reset(bootloader_control* boot_ctrl)
{
int slot;
memset(boot_ctrl, '\0', sizeof(bootloader_control));
memcpy(boot_ctrl->slot_suffix, "_a", 2);
boot_ctrl->magic = BOOT_CTRL_MAGIC;
boot_ctrl->version = BOOT_CTRL_VERSION;
boot_ctrl->nb_slot = 2;
boot_ctrl->roll_flag = 0;
boot_ctrl->merge_flag = -1;
boot_ctrl->firstboot = 1;
boot_ctrl->boot_tries_remaining = BOOT_ATTEMPTS;
for (slot = 0; slot < 4; ++slot) {
slot_metadata entry = {};
if (slot < boot_ctrl->nb_slot) {
entry.priority = 7;
entry.tries_remaining = 0;
entry.successful_boot = 0;
} else {
entry.priority = 0; // Unbootable
entry.tries_remaining = 0;
entry.successful_boot = 0;
}
boot_ctrl->slot_info[slot] = entry;
}
boot_ctrl->slot_info[0].priority = 15;
boot_ctrl->slot_info[0].tries_remaining = BOOT_ATTEMPTS;
boot_ctrl->slot_info[0].successful_boot = 1;
boot_ctrl->recovery_tries_remaining = 0;
}
static void dump_boot_info(bootloader_control *boot_ctrl)
{
#if 0
int slot;
printf("boot_ctrl->slot_suffix = %s\n", boot_ctrl->slot_suffix);
printf("boot_ctrl->magic = 0x%x\n", boot_ctrl->magic);
printf("boot_ctrl->version = %d\n", boot_ctrl->version);
printf("boot_ctrl->nb_slot = %d\n", boot_ctrl->nb_slot);
printf("boot_ctrl->firstboot = %d\n", boot_ctrl->firstboot);
printf("boot_ctrl->boot_tries_remaining = %d\n",
boot_ctrl->boot_tries_remaining);
for (slot = 0; slot < 4; ++slot) {
printf("boot_ctrl->slot_info[%d].priority = %d\n",
slot, boot_ctrl->slot_info[slot].priority);
printf("boot_ctrl->slot_info[%d].tries_remaining = %d\n",
slot, boot_ctrl->slot_info[slot].tries_remaining);
printf("boot_ctrl->slot_info[%d].successful_boot = %d\n",
slot, boot_ctrl->slot_info[slot].successful_boot);
}
printf("boot_ctrl->recovery_tries_remaining = %d\n",
boot_ctrl->recovery_tries_remaining);
#endif
}
static bool slot_is_bootable(slot_metadata* slot) {
return slot->tries_remaining != 0;
}
static int get_active_slot(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_normalAB(AvbABSlotData* slot) {
return slot->priority > 0 &&
(slot->successful_boot || (slot->tries_remaining > 0));
}
int get_active_slot_normalAB(AvbABData* info) {
if (info->slots[0].priority > info->slots[1].priority)
return 0;
else
return 1;
}
static uint32_t vab_crc32(const uint8_t *buf, size_t size)
{
static uint32_t crc_table[256];
uint32_t ret = -1;
// Compute the CRC-32 table only once.
if (!crc_table[1]) {
for (uint32_t i = 0; i < 256; ++i) {
uint32_t crc = i;
for (uint32_t j = 0; j < 8; ++j) {
uint32_t mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
crc_table[i] = crc;
}
}
for (size_t i = 0; i < size; ++i)
ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
return ~ret;
}
static int boot_info_set_active_slot(bootloader_control *bootctrl, int slot)
{
int i;
// Set every other slot with a lower priority than the new "active" slot.
const unsigned int kActivePriority = 15;
const unsigned int kActiveTries = 6;
for (i = 0; i < bootctrl->nb_slot; ++i) {
if (i != slot) {
//bootctrl->slot_info[i].priority -= 1;
if (bootctrl->slot_info[i].priority >= kActivePriority)
bootctrl->slot_info[i].priority = kActivePriority - 1;
}
printf("bootctrl->slot_info[%d].priority = %d\n", i,
bootctrl->slot_info[i].priority);
}
// Note that setting a slot as active doesn't change the successful bit.
// The successful bit will only be changed by setSlotAsUnbootable().
bootctrl->slot_info[slot].priority = kActivePriority;
bootctrl->slot_info[slot].tries_remaining = kActiveTries;
printf("bootctrl->slot_info[%d].priority = %d\n", slot,
bootctrl->slot_info[slot].priority);
printf("bootctrl->slot_info[%d].tries_remaining = %d\n",
slot, bootctrl->slot_info[slot].tries_remaining);
// Setting the current slot as active is a way to revert the operation that
// set *another* slot as active at the end of an updater. This is commonly
// used to cancel the pending update. We should only reset the verity_corrpted
// bit when attempting a new slot, otherwise the verity bit on the current
// slot would be flip.
if (slot != get_active_slot(bootctrl))
bootctrl->slot_info[slot].verity_corrupted = 0;
dump_boot_info(bootctrl);
return 0;
}
static bool boot_info_load(bootloader_control *out_info, char *miscbuf)
{
memcpy(out_info, miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, sizeof(bootloader_control));
dump_boot_info(out_info);
return true;
}
bool boot_info_load_normalAB(AvbABData *out_info, char *miscbuf)
{
memcpy(out_info, miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, AVB_AB_DATA_SIZE);
return true;
}
static bool boot_info_save(bootloader_control *info, char *miscbuf)
{
char *partition = "misc";
int ret = 0;
printf("save boot-info\n");
info->crc32_le = vab_crc32((const uint8_t *)info,
sizeof(bootloader_control) - sizeof(uint32_t));
memcpy(miscbuf + AB_METADATA_MISC_PARTITION_OFFSET, info, sizeof(bootloader_control));
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 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 set_ddr_size(void)
{
char ddr_size_str[32];
ulong ddr_size = (readl(SYSCTRL_SEC_STATUS_REG4) & ~0xfffffUL) << 4;
memset(ddr_size_str, 0, 32);
ddr_size = ddr_size / SZ_1M;
sprintf(ddr_size_str, "%lu%s", ddr_size, "MB");
printf("ddr_size_str = %s\n", ddr_size_str);
env_set("ddr_size", ddr_size_str);
}
static void update_after_failed_rollback(void)
{
run_command("run init_display; run storeargs; run enter_fastboot;", 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);
set_ddr_size();
boot_info_open_partition(miscbuf);
boot_info_load(&boot_ctrl, miscbuf);
if (!boot_info_validate(&boot_ctrl)) {
printf("boot-info virtual ab is invalid. Try normal ab.\n");
boot_info_load_normalAB(&info, miscbuf);
if (!boot_info_validate_normalAB(&info)) {
printf("boot-info is invalid. Resetting.\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
} else {
printf("update from normal ab to virtual ab\n");
env_set("normal_to_virtual", "1");
AB_mode = 1;
}
}
if (AB_mode == 1) {
slot = get_active_slot_normalAB(&info);
printf("active slot = %d\n", slot);
bootable_a = slot_is_bootable_normalAB(&info.slots[0]);
bootable_b = slot_is_bootable_normalAB(&info.slots[1]);
boot_info_reset(&boot_ctrl);
boot_ctrl.slot_info[0].successful_boot = info.slots[0].successful_boot;
boot_ctrl.slot_info[1].successful_boot = info.slots[1].successful_boot;
boot_info_set_active_slot(&boot_ctrl, slot);
boot_info_save(&boot_ctrl, miscbuf);
slot = get_active_slot(&boot_ctrl);
} else {
slot = get_active_slot(&boot_ctrl);
printf("active slot = %d\n", slot);
bootable_a = slot_is_bootable(&boot_ctrl.slot_info[0]);
bootable_b = slot_is_bootable(&boot_ctrl.slot_info[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");
boot_ctrl.roll_flag = 1;
boot_info_save(&boot_ctrl, 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))
run_command("reboot quiescent", 0);
else
run_command("reboot", 0);
} else {
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
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");
boot_ctrl.roll_flag = 1;
boot_info_save(&boot_ctrl, 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))
run_command("reboot quiescent", 0);
else
run_command("reboot", 0);
} else {
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
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};
bootloader_control 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_SetRollFlag
(cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
bootloader_control 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");
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_SetUpdateTries(
cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
bootloader_control boot_ctrl;
bool bootable_a, bootable_b;
int slot;
int ret = -1;
bool nocs_mode = false;
int update_flag = 0;
int success_flag = -1;
int merge_ret = -1;
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(&boot_ctrl, miscbuf);
if (!boot_info_validate(&boot_ctrl)) {
printf("boot-info is invalid. Resetting\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
}
slot = get_active_slot(&boot_ctrl);
bootable_a = slot_is_bootable(&boot_ctrl.slot_info[0]);
bootable_b = slot_is_bootable(&boot_ctrl.slot_info[1]);
if (slot == 0) {
if (bootable_a) {
if (boot_ctrl.slot_info[0].successful_boot == 0) {
success_flag = 0;
if (rebootmode && strcmp(rebootmode, "fastboot")) {
boot_ctrl.slot_info[0].tries_remaining -= 1;
update_flag = 1;
}
}
}
}
if (slot == 1) {
if (bootable_b) {
if (boot_ctrl.slot_info[1].successful_boot == 0) {
success_flag = 0;
if (rebootmode && strcmp(rebootmode, "fastboot")) {
boot_ctrl.slot_info[1].tries_remaining -= 1;
update_flag = 1;
}
}
}
}
if (boot_ctrl.firstboot == 1) {
if (rebootmode && strcmp(rebootmode, "fastboot")) {
boot_ctrl.boot_tries_remaining -= 1;
update_flag = 1;
}
}
printf("boot_ctrl.merge_flag = %d\n", boot_ctrl.merge_flag);
if (update_flag == 1)
boot_info_save(&boot_ctrl, miscbuf);
if (boot_ctrl.merge_flag == SNAPSHOTTED) {
printf("merge_flag is SNAPSHOTTED\n");
merge_ret = 1;
}
if (rebootmode && (!strcmp(rebootmode, "fastboot")) &&
success_flag == 0 &&
merge_ret == 1) {
printf("reboot bootloader during merge, rollback\n");
if (slot == 0 && bootable_a) {
if (boot_ctrl.slot_info[0].successful_boot == 0)
boot_ctrl.slot_info[0].tries_remaining = 0;
} else if (slot == 1 && bootable_b) {
if (boot_ctrl.slot_info[1].successful_boot == 0)
boot_ctrl.slot_info[1].tries_remaining = 0;
}
boot_info_save(&boot_ctrl, miscbuf);
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
run_command("reboot quiescent", 0);
else
run_command("reboot", 0);
}
printf("do_SetUpdateTries boot_ctrl.roll_flag = %d\n", boot_ctrl.roll_flag);
if (boot_ctrl.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 (boot_ctrl.slot_info[slot].successful_boot == 1) {
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);
/*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");
}
}
}
}
bootable_a = slot_is_bootable(&boot_ctrl.slot_info[0]);
bootable_b = slot_is_bootable(&boot_ctrl.slot_info[1]);
if ((!bootable_a && !bootable_b) ||
(boot_ctrl.firstboot == 1 && boot_ctrl.boot_tries_remaining <= 0)) {
printf("both a & b can't bootup, enter fastboot\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
run_command("run init_display; run storeargs; run enter_fastboot;", 0);
}
return 0;
}
static int do_CheckABState(cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
bootloader_control boot_ctrl;
bool bootable_a, bootable_b;
int slot;
int retry_times = 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(&boot_ctrl, miscbuf);
if (!boot_info_validate(&boot_ctrl)) {
printf("boot-info is invalid. Resetting\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
}
slot = get_active_slot(&boot_ctrl);
bootable_a = slot_is_bootable(&boot_ctrl.slot_info[0]);
bootable_b = slot_is_bootable(&boot_ctrl.slot_info[1]);
if ((slot == 0 && bootable_a &&
boot_ctrl.slot_info[0].successful_boot == 1) ||
(slot == 1 && bootable_b &&
boot_ctrl.slot_info[1].successful_boot == 1) ||
(!bootable_a && !bootable_b))
return 0;
if (slot == 0 && bootable_a &&
boot_ctrl.slot_info[0].successful_boot == 0)
retry_times = boot_ctrl.slot_info[0].tries_remaining;
if (slot == 1 && bootable_b &&
boot_ctrl.slot_info[1].successful_boot == 0)
retry_times = boot_ctrl.slot_info[1].tries_remaining;
printf("ab update mode, try %d times again\n", retry_times + 1);
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
run_command("reboot quiescent", 0);
else
run_command("reboot", 0);
return 0;
}
static int do_CopySlot(
cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[MISCBUF_SIZE] = {0};
bootloader_control boot_ctrl;
int copy = -1;
int dest = -1;
boot_info_open_partition(miscbuf);
boot_info_load(&boot_ctrl, miscbuf);
if (!boot_info_validate(&boot_ctrl)) {
printf("boot-info is invalid. Resetting\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);
}
if (strcmp(argv[1], "1") == 0)
copy = 1;
else if (strcmp(argv[1], "2") == 0)
copy = 2;
else if (strcmp(argv[1], "0") == 0)
copy = 0;
if (strcmp(argv[2], "1") == 0)
dest = 1;
else if (strcmp(argv[2], "2") == 0)
dest = 2;
else if (strcmp(argv[2], "0") == 0)
dest = 0;
char *fastboot_step = env_get("fastboot_step");
if (copy == 1) {
if ((fastboot_step && (strcmp(fastboot_step, "2") == 0)) ||
boot_ctrl.slot_info[0].successful_boot == 1)
write_bootloader(copy, dest);
} else if (copy == 2) {
if ((fastboot_step && (strcmp(fastboot_step, "2") == 0)) ||
boot_ctrl.slot_info[1].successful_boot == 1) {
write_bootloader(copy, dest);
} else {
env_set("update_env", "1");
env_set("reboot_status", "reboot_next");
env_set("expect_index", "2");
run_command("saveenv", 0);
}
}
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");
char *rebootmode = env_get("reboot_mode");
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
printf("part changes, reset\n");
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
run_command("reboot quiescent", 0);
else
run_command("reboot", 0);
}
}
return 0;
}
#endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */
#ifdef CONFIG_UNIFY_BOOTLOADER
bootctl_func_handles *get_bootctl_cmd_func_vab(void)
{
vab_cmd_bootctrl_handles.do_GetValidSlot_func = do_GetValidSlot;
vab_cmd_bootctrl_handles.do_SetActiveSlot_func = do_SetActiveSlot;
vab_cmd_bootctrl_handles.do_SetRollFlag_func = do_SetRollFlag;
vab_cmd_bootctrl_handles.do_CopySlot_func = do_CopySlot;
vab_cmd_bootctrl_handles.do_SetUpdateTries_func = do_SetUpdateTries;
vab_cmd_bootctrl_handles.do_GetSystemMode_func = do_GetSystemMode;
vab_cmd_bootctrl_handles.do_UpdateDt_func = do_UpdateDt;
vab_cmd_bootctrl_handles.do_CheckABState_func = do_CheckABState;
return &vab_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
(copy_slot_bootable, 3, 1, do_CopySlot,
"copy_slot_bootable",
"\nThis command will set active slot\n"
"So you can execute command: copy_slot_bootable 2 1"
);
U_BOOT_CMD
(check_ab, 2, 0, do_CheckABState,
"check_ab",
"\nThis command will check ab sate\n"
"So you can execute command: check_ab"
);
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
int set_successful_boot(void) {
char miscbuf[MISCBUF_SIZE] = {0};
AvbABData info;
int slot_num = 0;
bootloader_control boot_ctrl;
bool bootable = 0;
int AB_mode = 0;
boot_info_open_partition(miscbuf);
boot_info_load(&boot_ctrl, miscbuf);
if (!boot_info_validate(&boot_ctrl)) {
printf("boot-info virtual ab is invalid. Try normal ab.\n");
boot_info_load_normalAB(&info, miscbuf);
if (!boot_info_validate_normalAB(&info)) {
printf("boot-info is invalid.\n");
/*printf("boot-info is invalid. Resetting.\n");
boot_info_reset(&boot_ctrl);
boot_info_save(&boot_ctrl, miscbuf);*/
} else {
printf("update from normal ab to virtual ab\n");
AB_mode = 1;
}
}
if (AB_mode == 1) {
slot_num = get_active_slot_normalAB(&info);
printf("active slot = %d\n", slot_num);
bootable = slot_is_bootable_normalAB(&info.slots[slot_num]);
//bootable_b = slot_is_bootable_normalAB(&info.slots[1]);
/*boot_info_reset(&boot_ctrl);
boot_ctrl.slot_info[0].successful_boot = info.slots[0].successful_boot;
boot_ctrl.slot_info[1].successful_boot = info.slots[1].successful_boot;
boot_info_set_active_slot(&boot_ctrl, slot);
boot_info_save(&boot_ctrl, miscbuf);
slot = get_active_slot(&boot_ctrl);*/
} else {
slot_num = get_active_slot(&boot_ctrl);
printf("active slot = %d\n", slot_num);
bootable = slot_is_bootable(&boot_ctrl.slot_info[slot_num]);
//bootable_b = slot_is_bootable(&boot_ctrl.slot_info[1]);
}
if (bootable) {
printf("slots[%d] is bootable\n", slot_num);
return 0;
}
return -1;
}