Files
BPI-CM9-Linux/aml-comp/uboot/bl33/v2015/common/cmd_bootctl.c
T

579 lines
13 KiB
C

/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* common/cmd_bootctl.c
*
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
*
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <malloc.h>
#ifdef CONFIG_AML_MTD
#include <linux/mtd/mtd.h>
#endif
#include <asm/byteorder.h>
#include <config.h>
#include <asm/arch/io.h>
#include <partition_table.h>
#include <mmc.h>
#include <amlogic/aml_mmc.h>
#include <version.h>
#ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK
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 COMMANDBUF_SIZE 32
#define STATUSBUF_SIZE 32
#define RECOVERYBUF_SIZE 768
#define BOOTINFO_OFFSET 864
#define SLOTBUF_SIZE 32
#define MISCBUF_SIZE 1088
#define BOOTLOADER_OFFSET 512
#define BOOTLOADER_MAX_SIZE (4 * 1024 * 1024)
struct bootloader_message {
char command[32];
char status[32];
char recovery[768];
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
char slot_suffix[32];
char reserved[192];
};
typedef struct BrilloSlotInfo {
uint8_t bootable;
uint8_t online;
uint8_t reserved[2];
} BrilloSlotInfo;
typedef struct BrilloBootInfo {
// Used by fs_mgr. Must be NUL terminated.
char bootctrl_suffix[4];
// Magic for identification - must be 'B', 'C', 'c' (short for
// "boot_control copy" implementation).
uint8_t magic[3];
// Version of BrilloBootInfo struct, must be 0 or larger.
uint8_t version;
// Currently active slot.
uint8_t active_slot;
// Information about each slot.
BrilloSlotInfo slot_info[2];
uint8_t attemp_times;
uint8_t reserved[14];
} BrilloBootInfo;
bool boot_info_validate(BrilloBootInfo* info)
{
if (info->magic[0] != 'B' ||
info->magic[1] != 'C' ||
info->magic[2] != 'c')
return false;
if (info->active_slot >= 2)
return false;
return true;
}
void boot_info_reset(BrilloBootInfo* info)
{
memset(info, '\0', SLOTBUF_SIZE);
info->magic[0] = 'B';
info->magic[1] = 'C';
info->magic[2] = 'c';
info->active_slot = 0;
info->slot_info[0].bootable = 1;
info->slot_info[0].online = 1;
info->slot_info[1].bootable = 0;
info->slot_info[1].online = 0;
info->attemp_times = 0;
memcpy(info->bootctrl_suffix, "_a", 2);
}
void dump_boot_info(BrilloBootInfo* info)
{
printf("info->attemp_times = %u\n", info->attemp_times);
printf("info->active_slot = %u\n", info->active_slot);
printf("info->slot_info[0].bootable = %u\n", info->slot_info[0].bootable);
printf("info->slot_info[0].online = %u\n", info->slot_info[0].online);
printf("info->slot_info[1].bootable = %u\n", info->slot_info[1].bootable);
printf("info->slot_info[1].online = %u\n", info->slot_info[1].online);
printf("info->attemp_times = %u\n", info->attemp_times);
}
int boot_info_get_attemp_times(BrilloBootInfo* info)
{
return info->attemp_times;
}
int boot_info_change_online_slot(BrilloBootInfo* info)
{
BrilloSlotInfo tmp_info;
memcpy(&tmp_info, &info->slot_info[0], sizeof(BrilloSlotInfo));
memcpy(&info->slot_info[0], &info->slot_info[1], sizeof(BrilloSlotInfo));
memcpy(&info->slot_info[1], &tmp_info, sizeof(BrilloSlotInfo));
info->attemp_times = 0;
return 0;
}
int boot_info_get_online_slot(BrilloBootInfo* info)
{
if (info->slot_info[0].online == 1)
return 0;
if (info->slot_info[1].online == 1)
return 1;
return 0;
}
int boot_info_set_active_slot(BrilloBootInfo* info, int slot)
{
if (slot == 0) {
info->active_slot = 0;
info->slot_info[0].bootable = 1;
info->slot_info[0].online = 1;
info->slot_info[1].bootable = 0;
info->slot_info[1].online = 0;
info->attemp_times = 0;
memcpy(info->bootctrl_suffix, "_a", 2);
} else {
info->active_slot = 1;
info->slot_info[0].bootable = 0;
info->slot_info[0].online = 0;
info->slot_info[1].bootable = 1;
info->slot_info[1].online = 1;
info->attemp_times = 0;
memcpy(info->bootctrl_suffix, "_b", 2);
}
dump_boot_info(info);
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(BrilloBootInfo *out_info, char *miscbuf)
{
memcpy(out_info, miscbuf + BOOTINFO_OFFSET, SLOTBUF_SIZE);
dump_boot_info(out_info);
return true;
}
bool boot_info_save(BrilloBootInfo *info, char *miscbuf)
{
char *partition = "misc";
printf("save boot-info\n");
memcpy(miscbuf + BOOTINFO_OFFSET, info, SLOTBUF_SIZE);
dump_boot_info(info);
#ifdef CONFIG_AML_MTD
if (NAND_BOOT_FLAG == device_boot_flag || SPI_NAND_FLAG == device_boot_flag) {
int ret = 0;
ret = run_command("store erase partition misc", 0);
if (ret != 0) {
printf("erase partition misc failed!\n");
return false;
}
}
#endif
store_write_ops((unsigned char *)partition, (unsigned char *)miscbuf, 0, MISCBUF_SIZE);
return true;
}
static int write_bootloader_up(int i);
void bootloader_update_check(void)
{
int ret = 0;
char *writeboot = getenv("write_boot");
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)
run_command("reboot", 0);
setenv("write_boot", "0");
setenv("upgrade_step", "1");
run_command("saveenv", 0);
run_command("reboot", 0);
}
}
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;
}
static int do_GetValidSlot(
cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[4096] = {0};
BrilloBootInfo info;
int attemp_times;
int slot;
int ret = 0;
char str_count[16];
if (argc != 1)
return cmd_usage(cmdtp);
//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");
ret = boot_info_open_partition(miscbuf);
if (ret != 0)
return -1;
boot_info_load(&info, miscbuf);
#ifndef CONFIG_AB_SYSTEM
char command[32];
memcpy(command, miscbuf, 32);
if (!memcmp(command, "boot-recovery", strlen("boot-recovery"))) {
setenv("dolby_status", "0");
run_command("run init_display", 0);
run_command("run storeargs", 0);
if (run_command("run recovery_from_flash", 0) < 0) {
printf("run_command for cmd:run recovery_from_flash failed.\n");
return -1;
}
printf("run command:run recovery_from_flash successful.\n");
return 0;
}
#endif
if (!boot_info_validate(&info)) {
printf("boot-info is invalid. Resetting.\n");
boot_info_reset(&info);
boot_info_save(&info, miscbuf);
}
attemp_times = boot_info_get_attemp_times(&info);
printf("attemp_times = %d\n", attemp_times);
if (attemp_times == -1)
boot_info_change_online_slot(&info);
slot = info.active_slot;
printf("active slot = %d\n", slot);
if (has_boot_slot == 1) {
attemp_times = boot_info_get_attemp_times(&info);
sprintf(str_count, "%d", attemp_times);
setenv("retry-count_a", str_count);
setenv("retry-count_b", str_count);
}
#ifdef CONFIG_AML_MTD
//check if boot_a/b on nand
if (device_boot_flag == NAND_BOOT_FLAG) {
struct mtd_info *nand;
nand = get_mtd_device_nm("boot_a");
if (!IS_ERR(nand))
has_boot_slot = 1;
else
has_boot_slot = 0;
}
#endif
if (slot == 0) {
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 (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");
}
}
if (dynamic_partition)
setenv("partition_mode", "dynamic");
else
setenv("partition_mode", "normal");
bootloader_update_check();
return 0;
}
static int do_SetActiveSlot(
cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[4096] = {0};
BrilloBootInfo info;
int ret = 0;
if (argc != 2)
return cmd_usage(cmdtp);
if (has_boot_slot == 0) {
printf("device is not ab mode\n");
return -1;
}
ret = boot_info_open_partition(miscbuf);
if (ret != 0)
return -1;
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("boot_part", "boot_a");
setenv("recovery_part", "recovery_a");
setenv("slot-suffixes", "0");
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("boot_part", "boot_b");
setenv("recovery_part", "recovery_b");
setenv("slot-suffixes", "1");
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);
return 0;
}
static int do_Get_slot_state(
cmd_tbl_t *cmdtp,
int flag,
int argc,
char * const argv[])
{
char miscbuf[4096] = {0};
BrilloBootInfo 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].bootable == 1)
return 1;
else
return 0;
}
if (strcmp(argv[1], "b") == 0) {
if (info.slot_info[1].bootable == 1)
return 1;
else
return 0;
}
}
if (strcmp(argv[2], "boot_state") == 0) {
if (strcmp(argv[1], "a") == 0) {
if (info.slot_info[0].bootable == 1)
return 1;
else
return 0;
}
if (strcmp(argv[1], "b") == 0) {
if (info.slot_info[1].bootable == 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;
}
#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(
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"
);