1515 lines
39 KiB
C
1515 lines
39 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* common/cmd_bootctl_vab.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <environment.h>
|
|
#include <malloc.h>
|
|
#include <asm/byteorder.h>
|
|
#include <config.h>
|
|
#include <asm/arch/io.h>
|
|
#include <partition_table.h>
|
|
#include <libavb.h>
|
|
#include <version.h>
|
|
#include <fb_mmc.h>
|
|
#include <mmc.h>
|
|
#include <amlogic/aml_mmc.h>
|
|
#include <u-boot/sha1.h>
|
|
#include <emmc_partitions.h>
|
|
#include <android_vab.h>
|
|
|
|
#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 BOOTLOADER_OFFSET 512
|
|
#define BOOTLOADER_MAX_SIZE (4 * 1024 * 1024)
|
|
|
|
#define BOOTLOADER_BOOT0_OFFSET_IN_RSV 0
|
|
#define BOOTLOADER_BOOT1_OFFSET_IN_RSV (4 * 1024 * 1024)
|
|
#define BOOTLOADER_BACKUP_RSV_SIZE (4 * 1024 * 1024)
|
|
|
|
#define MERGE_STATE_FILE "/ota/state"
|
|
|
|
extern int store_read_ops(
|
|
unsigned char *partition_name,
|
|
unsigned char * buf, uint64_t off, uint64_t size);
|
|
extern int store_write_ops(
|
|
unsigned char *partition_name,
|
|
unsigned char * buf, uint64_t off, uint64_t size);
|
|
|
|
#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;
|
|
|
|
enum UpdateState {
|
|
None = 0,
|
|
Initiated,
|
|
Unverified,
|
|
Merging,
|
|
MergeNeedsReboot,
|
|
MergeCompleted,
|
|
MergeFailed,
|
|
Cancelled
|
|
};
|
|
|
|
/* 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.
|
|
u8 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 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.
|
|
*/
|
|
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. */
|
|
u8 roll_flag;
|
|
u8 reserved1[1];
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
}
|
|
|
|
void dump_boot_info(bootloader_control* boot_ctrl)
|
|
{
|
|
int loglevel = getenv_ulong("loglevel", 10, 0);
|
|
|
|
if (loglevel >= 5) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
static bool slot_is_bootable(slot_metadata* slot) {
|
|
return slot->tries_remaining != 0;
|
|
}
|
|
|
|
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 u8 *buf, size_t size)
|
|
{
|
|
static u32 crc_table[256];
|
|
u32 ret = -1;
|
|
u32 i, j, crc, mask;
|
|
size_t l;
|
|
|
|
// Compute the CRC-32 table only once.
|
|
if (!crc_table[1]) {
|
|
for (i = 0; i < 256; ++i) {
|
|
crc = i;
|
|
|
|
for (j = 0; j < 8; ++j) {
|
|
mask = -(crc & 1);
|
|
|
|
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
|
}
|
|
crc_table[i] = crc;
|
|
}
|
|
}
|
|
|
|
for (l = 0; l < size; ++l)
|
|
ret = (ret >> 8) ^ crc_table[(ret ^ buf[l]) & 0xFF];
|
|
|
|
return ~ret;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int boot_info_open_partition(char *miscbuf)
|
|
{
|
|
char *partition = "misc";
|
|
|
|
printf("Start read %s partition datas!\n", partition);
|
|
if (store_read_ops((unsigned char *)partition,
|
|
(unsigned char *)miscbuf, 0, MISCBUF_SIZE) < 0) {
|
|
printf("failed to store read %s.\n", partition);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
bool boot_info_save(bootloader_control *info, char *miscbuf)
|
|
{
|
|
char *partition = "misc";
|
|
|
|
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);
|
|
store_write_ops((unsigned char *)partition, (unsigned char *)miscbuf, 0, MISCBUF_SIZE);
|
|
return true;
|
|
}
|
|
|
|
int write_bootloader(int copy, int dstindex)
|
|
{
|
|
int ret = -1;
|
|
unsigned char *buffer = NULL;
|
|
char str[128];
|
|
u64 addr;
|
|
u64 size = 0x2000 * 512 - 512;
|
|
|
|
buffer = (unsigned char *)malloc(size);
|
|
if (!buffer) {
|
|
printf("ERROR! fail to allocate memory ...\n");
|
|
goto exit;
|
|
}
|
|
memset(buffer, 0, size);
|
|
addr = (unsigned long)buffer;
|
|
|
|
if (copy == 0) {
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
} else if (copy == 1) {
|
|
sprintf(str, "amlmmc switch 1 boot0");
|
|
ret = run_command(str, 0);
|
|
} else if (copy == 2) {
|
|
sprintf(str, "amlmmc switch 1 boot1");
|
|
ret = run_command(str, 0);
|
|
}
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
|
|
sprintf(str, "amlmmc read 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
|
|
switch (dstindex) {
|
|
case 0: {
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc write 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
break;
|
|
}
|
|
case 1: {
|
|
sprintf(str, "amlmmc switch 1 boot0");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc write 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
break;
|
|
}
|
|
case 2: {
|
|
sprintf(str, "amlmmc switch 1 boot1");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc write 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
goto exit;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
|
|
exit:
|
|
if (buffer) {
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int write_bootloader_up(int i)
|
|
{
|
|
unsigned char *buffer = NULL;
|
|
int capacity_boot = 0x2000 * 512;
|
|
int ret = 0;
|
|
char partname[32] = {0};
|
|
char *slot_name = NULL;
|
|
int bootloader_index = 0;
|
|
int size = BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET;
|
|
|
|
#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("capacity_boot: 0x%x\n", capacity_boot);
|
|
buffer = (unsigned char *)malloc(capacity_boot);
|
|
if (!buffer) {
|
|
printf("ERROR! fail to allocate memory ...\n");
|
|
return -1;
|
|
}
|
|
memset(buffer, 0, capacity_boot);
|
|
|
|
slot_name = getenv("active_slot");
|
|
if (slot_name && (strcmp(slot_name, "_a") == 0))
|
|
strcpy((char *)partname, "bootloader_a");
|
|
else if (slot_name && (strcmp(slot_name, "_b") == 0))
|
|
strcpy((char *)partname, "bootloader_b");
|
|
else
|
|
strcpy((char *)partname, "bootloader_up");
|
|
|
|
ret = store_read_ops((unsigned char *)partname, buffer, 0, size);
|
|
if (ret) {
|
|
printf("Fail to read 0x%xB from part[%s] at offset 0\n",
|
|
size, "bootloader_up");
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
if (i == 0)
|
|
bootloader_index = AML_BL_BOOT0;
|
|
else
|
|
bootloader_index = AML_BL_BOOT1;
|
|
|
|
ret = amlmmc_write_bootloader(1, bootloader_index, size, buffer);
|
|
if (ret) {
|
|
printf("Failed to write boot0\n");
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
void backup_boot(void)
|
|
{
|
|
int ret = -1;
|
|
unsigned char *buffer = NULL;
|
|
u64 addr;
|
|
u64 size = 0x2000 * 512 - 512;
|
|
char *partname = "rsv";
|
|
char str[128] = {0};
|
|
char rsvbuf[BOOTLOADER_BACKUP_RSV_SIZE - 512] = {0};
|
|
|
|
buffer = (unsigned char *)malloc(size);
|
|
if (!buffer) {
|
|
printf("ERROR! fail to allocate memory ...\n");
|
|
return;
|
|
}
|
|
//1.Read boot0 (old bootloader)
|
|
memset(buffer, 0, size);
|
|
addr = (unsigned long)buffer;
|
|
sprintf(str, "amlmmc switch 1 boot0");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc read 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
//2.The old Boot0 is written to the beginning of rsv with offset 0
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
memcpy(rsvbuf, buffer, sizeof(rsvbuf));
|
|
ret = store_write_ops((unsigned char *)partname, (unsigned char *)rsvbuf,
|
|
0, BOOTLOADER_BACKUP_RSV_SIZE - 512);
|
|
if (ret < 0) {
|
|
printf("\n failed to write boot0 to rsv!\n");
|
|
goto exit;
|
|
}
|
|
//3.Read out new boot1
|
|
sprintf(str, "amlmmc switch 1 boot1");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc read 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
|
|
//4.The new boot1 is written to offset 4M of rsv
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
memcpy(rsvbuf, buffer, sizeof(rsvbuf));
|
|
store_write_ops((unsigned char *)partname, (unsigned char *)rsvbuf,
|
|
BOOTLOADER_BOOT1_OFFSET_IN_RSV,
|
|
BOOTLOADER_BACKUP_RSV_SIZE - 512);
|
|
|
|
//5.Read boot0 (old Uboot) in rsv so that it can be written to boot1 and rolled back for use
|
|
memset(buffer, 0, size);
|
|
ret = store_read_ops((unsigned char *)partname, buffer, 0,
|
|
BOOTLOADER_BACKUP_RSV_SIZE - 512);
|
|
if (ret) {
|
|
printf("Fail to read 0x%xB from part[%s] at offset 0\n",
|
|
BOOTLOADER_BACKUP_RSV_SIZE - 512, "rsv");
|
|
goto exit;
|
|
}
|
|
|
|
//6.Write boot1 (new uboot) to boot0 (old uboot) so that after changing to gpt,
|
|
//the default is to start from boot0 and become the new uboot.
|
|
write_bootloader(2, 1);
|
|
|
|
//7.Write boot0 (old uboot) saved in rsv to boot1
|
|
sprintf(str, "amlmmc switch 1 boot1");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
sprintf(str, "amlmmc write 1 0x%llx 0x1 0x%llx", addr, size / 512);
|
|
printf("command: %s\n", str);
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
|
|
//8.Switch to user so that sore env
|
|
sprintf(str, "amlmmc switch 1 user");
|
|
ret = run_command(str, 0);
|
|
if (ret != 0) {
|
|
printf("amlmmc cmd %s failed\n", str);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (buffer) {
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
}
|
|
|
|
void bootloader_update_check(void)
|
|
{
|
|
int ret = 0;
|
|
char *writeboot = getenv("write_boot");
|
|
char *rebootmode = getenv("reboot_mode");
|
|
|
|
if (writeboot && !strcmp(writeboot, "1")) {
|
|
if (has_boot_slot == 0) {
|
|
printf("non ab for kernel 5.15 update boot0 & boot1 from bootloader_up\n");
|
|
ret = write_bootloader_up(0);
|
|
ret += write_bootloader_up(1);
|
|
} else {
|
|
printf("ab for kernel 5.15 update boot0 from bootloader_a or _b\n");
|
|
ret = write_bootloader_up(0);
|
|
}
|
|
if (ret != 0) {
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
}
|
|
|
|
setenv("write_boot", "0");
|
|
setenv("upgrade_step", "1");
|
|
run_command("saveenv", 0);
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
}
|
|
#ifdef CONFIG_AML_GPT
|
|
if (dts_to_gpt && has_boot_slot == 1) {
|
|
printf("after R dts to U gpt, update dts from boot_a or b\n");
|
|
//update dts from boot_a or b
|
|
run_command("imgread dtb ${boot_part} ${dtb_mem_addr}", 0);
|
|
run_command("emmc dtb_write ${dtb_mem_addr} 0", 0);
|
|
//for bootloader partition will be write gpt, so will bootup from
|
|
//boot0, but boot0 is bootloader of R, so need write sync boot0
|
|
//from boot1, boot1 is bootloader of U.
|
|
char *slot_name = getenv("active_slot");
|
|
|
|
if (slot_name && (strcmp(slot_name, "_a") == 0)) {
|
|
//b ota to a, user and boot0 are updated in bootctrl.
|
|
//boot1 is old, no need to update boot1
|
|
//write_bootloader(1, 2);
|
|
printf("now, slot b is upgraded to slot a.\n");
|
|
}
|
|
else if (slot_name && (strcmp(slot_name, "_b") == 0))
|
|
//A ota to B, user and boot1 are updated in bootctrl.
|
|
//boot0 is the old uboot.Back up the old boot0 and new boot1 to rsv here,
|
|
//then read old boot0 and write it to boot1. boot1 is old
|
|
//rollback, write boot1 to boot0.
|
|
backup_boot();
|
|
|
|
dts_to_gpt = false;
|
|
setenv("upgrade_step", "1");
|
|
run_command("saveenv", 0);
|
|
|
|
//erase dts of bootloader partition, offset:7168(0x1c00)blocks
|
|
run_command("mmc dev 1", 0);
|
|
run_command("mmc erase 0x1c00 0x50", 0);
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int is_f2fs_by_name(char *name)
|
|
{
|
|
int ret = -1;
|
|
#ifdef CONFIG_AML_SD_EMMC
|
|
struct partitions *partition = NULL;
|
|
|
|
partition = find_mmc_partition_by_name(name);
|
|
if (!partition)
|
|
return ret;
|
|
ret = (partition->mask_flags >> 12) & 0x1;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
*get merge status
|
|
*/
|
|
int check_mergestatus(const char *filename)
|
|
{
|
|
if (!filename)
|
|
return -1;
|
|
|
|
#ifdef CONFIG_AML_SD_EMMC
|
|
int part_no = -1;
|
|
char cmd[256] = {0};
|
|
|
|
part_no = get_partition_num_by_name("metadata");
|
|
if (part_no < 0) {
|
|
printf("fail find part index for metadata\n");
|
|
return -1;
|
|
}
|
|
|
|
void *loadaddr = (void *)simple_strtoul("0x1080000", NULL, 16);
|
|
|
|
sprintf(cmd, "ext4load mmc 1:0x%X 0x1080000 %s",
|
|
part_no,
|
|
filename);
|
|
|
|
if (run_command(cmd, 0)) {
|
|
printf("command[%s] failed\n", cmd);
|
|
return -1;
|
|
}
|
|
|
|
char *pData = (char *)loadaddr;
|
|
|
|
printf("merge_state: %d\n", pData[1]);
|
|
|
|
if (pData[1] == Initiated || pData[1] == Unverified)
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void update_after_failed_rollback(void)
|
|
{
|
|
run_command("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;
|
|
char str_count[16];
|
|
block_dev_desc_t *dev_desc;
|
|
|
|
if (argc != 1)
|
|
return cmd_usage(cmdtp);
|
|
|
|
dev_desc = get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
|
|
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
|
|
error("invalid mmc device");
|
|
return -1;
|
|
}
|
|
|
|
//recovery mode, need disable dolby
|
|
run_command("get_rebootmode", 0);
|
|
char *rebootmode = getenv("reboot_mode");
|
|
|
|
if ((!strcmp(rebootmode, "factory_reset")) || (!strcmp(rebootmode, "update")))
|
|
setenv("dolby_status", "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. 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 recovery mode, need disable dolby
|
|
if (!memcmp(miscbuf, "boot-recovery", strlen("boot-recovery"))) {
|
|
printf("recovery mode, need disable dolby\n");
|
|
setenv("dolby_status", "0");
|
|
}
|
|
|
|
if (AB_mode == 1) {
|
|
slot = get_active_slot_normalAB(&info);
|
|
bootable_a = slot_is_bootable_normalAB(&info.slots[0]);
|
|
bootable_b = slot_is_bootable_normalAB(&info.slots[1]);
|
|
if (has_boot_slot == 1) {
|
|
sprintf(str_count, "%d", info.slots[0].tries_remaining);
|
|
setenv("retry-count_a", str_count);
|
|
sprintf(str_count, "%d", info.slots[1].tries_remaining);
|
|
setenv("retry-count_b", str_count);
|
|
}
|
|
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_ctrl.roll_flag = info.roll_flag;
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
slot = get_active_slot(&boot_ctrl);
|
|
} else {
|
|
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 (has_boot_slot == 1) {
|
|
sprintf(str_count, "%d", boot_ctrl.slot_info[0].tries_remaining);
|
|
setenv("retry-count_a", str_count);
|
|
sprintf(str_count, "%d", boot_ctrl.slot_info[1].tries_remaining);
|
|
setenv("retry-count_b", str_count);
|
|
}
|
|
}
|
|
|
|
if (dynamic_partition)
|
|
setenv("partition_mode", "dynamic");
|
|
else
|
|
setenv("partition_mode", "normal");
|
|
|
|
if (vendor_boot_partition) {
|
|
setenv("vendor_boot_mode", "true");
|
|
printf("set vendor_boot_mode true\n");
|
|
} else {
|
|
setenv("vendor_boot_mode", "false");
|
|
printf("set vendor_boot_mode false\n");
|
|
}
|
|
|
|
if (slot == 0) {
|
|
if (bootable_a) {
|
|
if (has_boot_slot == 1) {
|
|
setenv("active_slot", "_a");
|
|
setenv("boot_part", "boot_a");
|
|
setenv("recovery_part", "recovery_a");
|
|
setenv("slot-suffixes", "0");
|
|
} else {
|
|
setenv("active_slot", "normal");
|
|
setenv("boot_part", "boot");
|
|
setenv("recovery_part", "recovery");
|
|
setenv("slot-suffixes", "-1");
|
|
}
|
|
} else if (bootable_b) {
|
|
#ifdef CONFIG_FASTBOOT
|
|
struct misc_virtual_ab_message message;
|
|
|
|
set_mergestatus_cancel(&message);
|
|
#endif
|
|
boot_ctrl.roll_flag = 1;
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
run_command("set_active_slot b", 0);
|
|
setenv("default_env", "1");
|
|
run_command("saveenv", 0);
|
|
#ifdef CONFIG_AML_GPT
|
|
printf("rollback, erase gpt first!\n");
|
|
erase_gpt_part_table(dev_desc);
|
|
if (write_bootloader(2, 1) == 0) {
|
|
#else
|
|
if (write_bootloader(2, 0) == 0) {
|
|
#endif
|
|
printf("rollback ok\n");
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
} else {
|
|
printf("rollback failed\n");
|
|
rollback_failure_handler();
|
|
}
|
|
} 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) {
|
|
setenv("active_slot", "_b");
|
|
setenv("boot_part", "boot_b");
|
|
setenv("recovery_part", "recovery_b");
|
|
setenv("slot-suffixes", "1");
|
|
} else {
|
|
setenv("active_slot", "normal");
|
|
setenv("boot_part", "boot");
|
|
setenv("recovery_part", "recovery");
|
|
setenv("slot-suffixes", "-1");
|
|
}
|
|
} else if (bootable_a) {
|
|
#ifdef CONFIG_FASTBOOT
|
|
struct misc_virtual_ab_message message;
|
|
|
|
set_mergestatus_cancel(&message);
|
|
#endif
|
|
boot_ctrl.roll_flag = 1;
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
run_command("set_active_slot a", 0);
|
|
setenv("default_env", "1");
|
|
run_command("saveenv", 0);
|
|
|
|
#ifdef CONFIG_AML_GPT
|
|
printf("rollback, erase gpt first!\n");
|
|
erase_gpt_part_table(dev_desc);
|
|
if (write_bootloader(2, 1) == 0) {
|
|
#else
|
|
if (write_bootloader(1, 0) == 0) {
|
|
#endif
|
|
printf("rollback ok\n");
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
} else {
|
|
printf("rollback failed\n");
|
|
rollback_failure_handler();
|
|
}
|
|
} else {
|
|
boot_info_reset(&boot_ctrl);
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
rollback_failure_handler();
|
|
}
|
|
}
|
|
|
|
if (rebootmode && (strcmp(rebootmode, "rescueparty") == 0)) {
|
|
printf("rebootmode is rescueparty, need rollback\n");
|
|
|
|
#ifdef CONFIG_FASTBOOT
|
|
struct misc_virtual_ab_message message;
|
|
|
|
set_mergestatus_cancel(&message);
|
|
#endif
|
|
if (slot == 0) {
|
|
boot_ctrl.roll_flag = 1;
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
run_command("set_active_slot b", 0);
|
|
setenv("default_env", "1");
|
|
run_command("saveenv", 0);
|
|
#ifdef CONFIG_AML_GPT
|
|
printf("rollback, erase gpt first!\n");
|
|
erase_gpt_part_table(dev_desc);
|
|
if (write_bootloader(2, 1) == 0) {
|
|
#else
|
|
if (write_bootloader(2, 0) == 0) {
|
|
#endif
|
|
printf("rollback ok\n");
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
} else {
|
|
printf("rollback failed\n");
|
|
rollback_failure_handler();
|
|
}
|
|
} else if (slot == 1) {
|
|
boot_ctrl.roll_flag = 1;
|
|
boot_info_save(&boot_ctrl, miscbuf);
|
|
run_command("set_active_slot a", 0);
|
|
setenv("default_env", "1");
|
|
run_command("saveenv", 0);
|
|
#ifdef CONFIG_AML_GPT
|
|
printf("rollback, erase gpt first!\n");
|
|
erase_gpt_part_table(dev_desc);
|
|
if (write_bootloader(2, 1) == 0) {
|
|
#else
|
|
if (write_bootloader(1, 0) == 0) {
|
|
#endif
|
|
printf("rollback ok\n");
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
} else {
|
|
printf("rollback failed\n");
|
|
rollback_failure_handler();
|
|
}
|
|
}
|
|
}
|
|
|
|
bootloader_update_check();
|
|
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) {
|
|
setenv("active_slot", "_a");
|
|
setenv("slot-suffixes", "0");
|
|
setenv("boot_part", "boot_a");
|
|
setenv("recovery_part", "recovery_a");
|
|
printf("set active slot a\n");
|
|
boot_info_set_active_slot(&info, 0);
|
|
} else if (strcmp(argv[1], "b") == 0) {
|
|
setenv("active_slot", "_b");
|
|
setenv("slot-suffixes", "1");
|
|
setenv("boot_part", "boot_b");
|
|
setenv("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 (info.roll_flag == 1) {
|
|
printf("if null gpt, write dtb back when rollback\n");
|
|
run_command("imgread dtb ${boot_part} ${dtb_mem_addr}", 0);
|
|
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 update_flag = 0;
|
|
int success_flag = -1;
|
|
int merge_ret = -1;
|
|
char *rebootmode = getenv("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);
|
|
|
|
//unsupport update dt in boothal, update dt in uboot
|
|
run_command("update_dt;", 0);
|
|
|
|
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 (is_f2fs_by_name("metadata") == 0) {
|
|
printf("metadata is ext4\n");
|
|
merge_ret = check_mergestatus(MERGE_STATE_FILE);
|
|
} else if (boot_ctrl.merge_flag == SNAPSHOTTED) {
|
|
printf("merge_flag is SNAPSHOTTED\n");
|
|
merge_ret = 1;
|
|
}
|
|
|
|
if (rebootmode && (!strcmp(rebootmode, "fastboot") ||
|
|
!strcmp(rebootmode, "kernel_panic")) &&
|
|
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("%s boot_ctrl.roll_flag = %d\n", __func__, boot_ctrl.roll_flag);
|
|
if (boot_ctrl.roll_flag == 1)
|
|
setenv("rollback_flag", "1");
|
|
|
|
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; fastboot;", 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_Get_slot_state(
|
|
cmd_tbl_t *cmdtp,
|
|
int flag,
|
|
int argc,
|
|
char * const argv[])
|
|
{
|
|
char miscbuf[MISCBUF_SIZE] = {0};
|
|
bootloader_control info;
|
|
|
|
if (argc != 3)
|
|
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[2], "suc_stete") == 0) {
|
|
if (strcmp(argv[1], "a") == 0) {
|
|
if (info.slot_info[0].successful_boot == 1)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[1], "b") == 0) {
|
|
if (info.slot_info[1].successful_boot == 1)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (strcmp(argv[2], "boot_state") == 0) {
|
|
if (strcmp(argv[1], "a") == 0) {
|
|
if (slot_is_bootable(&info.slot_info[0]))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (strcmp(argv[1], "b") == 0) {
|
|
if (slot_is_bootable(&info.slot_info[1]))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int do_GetSystemMode (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
#ifdef CONFIG_SYSTEM_AS_ROOT
|
|
setenv("system_mode", "1");
|
|
#else
|
|
setenv("system_mode", "0");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_UpdateDt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
char *update_dt = getenv("update_dt");
|
|
char *part_changed = getenv("part_changed");
|
|
char *rebootmode = getenv("reboot_mode");
|
|
|
|
if (update_dt && (!strcmp(update_dt, "1"))) {
|
|
printf("write dtb\n");
|
|
run_command("imgread dtb ${boot_part} ${dtb_mem_addr}", 0);
|
|
run_command("emmc dtb_write ${dtb_mem_addr} 0", 0);
|
|
|
|
setenv("update_dt", "0");
|
|
run_command("saveenv", 0);
|
|
|
|
if (part_changed && (!strcmp(part_changed, "1"))) {
|
|
setenv("part_changed", "0");
|
|
run_command("saveenv", 0);
|
|
|
|
if (rebootmode && (strcmp(rebootmode, "quiescent") == 0))
|
|
run_command("reboot quiescent", 0);
|
|
else
|
|
run_command("reboot", 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */
|
|
|
|
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_slot_state, 3, 2, do_Get_slot_state,
|
|
"get_slot_state a suc_stete",
|
|
"\nThis command will get slot state\n"
|
|
"you can run: get_slot_state a suc_stete/boot_state"
|
|
);
|
|
|
|
|
|
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"
|
|
);
|
|
|