| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 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 <version.h> |
| #include <amlogic/store_wrapper.h> |
| |
| #define COMMANDBUF_SIZE 32 |
| #define STATUSBUF_SIZE 32 |
| #define RECOVERYBUF_SIZE 768 |
| |
| #define BOOTINFO_OFFSET 864 |
| #define SLOTBUF_SIZE 32 |
| #define BCB_INFO_SZ 1024 |
| |
| #define DbgP(fmt...) //printf("D[BCB]L%d:", __LINE__),printf(fmt) |
| #define ErrP(fmt...) printf("E[BCB]L%d:", __LINE__),printf(fmt) |
| #define MsgP(fmt...) printf("[BCB]"fmt) |
| |
| #pragma pack(push, 4) |
| struct recovery_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]; |
| }; |
| |
| typedef struct BrilloSlotInfo { |
| uint8_t bootable; |
| uint8_t online; |
| uint8_t reserved[2]; |
| } BrilloSlotInfo; |
| |
| #define MAX_SLOT_NUM 4 |
| 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; |
| |
| uint8_t attemp_times; |
| uint8_t maxTryTimes; |
| uint8_t numSlots;//slots number, >=2 if support multiple system switch |
| |
| // Information about each slot. |
| BrilloSlotInfo slot_info[MAX_SLOT_NUM]; //at last and can extend to any number you need |
| |
| } BrilloBootInfo; |
| |
| struct bootloader_control_msg { |
| struct recovery_message recovery; |
| struct BrilloBootInfo bootInfo; |
| }; |
| #pragma pack(pop) |
| |
| static 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 >= MAX_SLOT_NUM) |
| return false; |
| return true; |
| } |
| |
| static void dump_boot_info(BrilloBootInfo* info) |
| { |
| MsgP("info->active_slot = %u\n", info->active_slot); |
| MsgP("info->attemp_times = %u / %u\n", info->attemp_times, info->maxTryTimes); |
| MsgP("info->slot_info[0].bootable = %u\n", info->slot_info[0].bootable); |
| MsgP("info->slot_info[0].online = %u\n", info->slot_info[0].online); |
| MsgP("info->slot_info[1].bootable = %u\n", info->slot_info[1].bootable); |
| MsgP("info->slot_info[1].online = %u\n", info->slot_info[1].online); |
| } |
| |
| /* |
| * code flow: |
| * if recovery_message->command valid, yes then run into upgrade mode |
| * if info->numSlots == 1, then NOT a/b system mode, just set boot_part as 'boot' |
| * if info->numSlots > 1, then IS a/b system mode |
| * if attemp_times >= maxTryTimes; then |
| * try info[active_slot] |
| * else try next slot |
| * */ |
| static int parse_and_update_bcb(struct bootloader_control_msg* bcb, int* needUpdateMisc) |
| { |
| struct recovery_message* recoveryInfo = &(bcb->recovery); |
| BrilloBootInfo* info = &(bcb->bootInfo); |
| |
| *needUpdateMisc = 1; |
| DbgP("command:%s, recovery:%s\n", recoveryInfo->command, recoveryInfo->recovery); |
| if (!memcmp(recoveryInfo->command, "boot-recovery", strlen("boot-recovery"))) { |
| MsgP("need boot recovery, maybe upgrading not completed\n"); |
| run_command("run storeargs; run update", 0); |
| return 0; |
| } |
| |
| if (!boot_info_validate(info)) { |
| MsgP("boot-info is invalid. Resetting.\n"); |
| return CMD_RET_FAILURE; |
| } |
| dump_boot_info(info); |
| |
| const int numSlots = info->numSlots; |
| if (numSlots < 1) { |
| ErrP("slots num %d invalid\n", numSlots); |
| return CMD_RET_FAILURE; |
| } |
| if (numSlots < 2) { |
| MsgP("slots num %d < 2, so don't support multiple system switch\n", numSlots); |
| env_set("active_slot","normal"); |
| env_set("boot_part","boot"); |
| *needUpdateMisc = 0; |
| return 0; |
| } |
| |
| int slotIndex = info->active_slot; |
| MsgP("active slot = %d \n", slotIndex); |
| BrilloSlotInfo* slotInf = info->slot_info + slotIndex; |
| |
| int attemp_times = info->attemp_times; |
| MsgP("attemp_times = %d \n", attemp_times); |
| do { |
| if (slotInf->bootable) { |
| MsgP("current slot %d already bootable, just boot it\n", slotIndex); |
| *needUpdateMisc = 0; |
| break; |
| } |
| |
| //current active slot NOT bootable, try until system marked bootable |
| if (attemp_times > info->maxTryTimes) { |
| MsgP("Try next as slot[%d] tried times %d > max %d!!!\n", slotIndex, attemp_times, (int)(info->maxTryTimes)); |
| attemp_times = 0; |
| slotIndex += 1; |
| slotIndex = slotIndex < numSlots ? slotIndex : (slotIndex - numSlots); |
| slotInf = info->slot_info + slotIndex; |
| continue; |
| } |
| attemp_times += 1; |
| break; |
| }while(1); |
| info->attemp_times = attemp_times; |
| info->active_slot = slotIndex; |
| |
| char bootName[8]; |
| const int iPostfix = 4; |
| const int nlen = strlen("bootA"); |
| memcpy(bootName, "bootA", nlen + 1); |
| if (slotIndex > 0) bootName[iPostfix] += slotIndex; |
| env_set("active_slot", bootName + iPostfix);//"_a" or "-b" |
| env_set("boot_part", bootName); |
| run_command("setenv dsp_part dsp${active_slot}", 0); |
| memcpy(info->bootctrl_suffix, bootName + iPostfix, iPostfix); |
| |
| char* slotSuffix = bootName + iPostfix;//"a" or "b" |
| slotSuffix[0] = slotSuffix[0] - 'A' + '0'; //"0" or "1" |
| env_set("slot-suffixes", slotSuffix); |
| run_command("printenv slot-suffixes boot_part active_slot dsp_part", 0); |
| |
| return 0; |
| } |
| |
| static int do_GetValidSlot(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
| { |
| struct bootloader_control_msg* loadaddr = NULL; |
| const char* bcbPart = argc > 1 ? argv[1] : "misc"; |
| if ( argc < 3 ) {//default address is env $loadaddr |
| loadaddr = (struct bootloader_control_msg*)env_get_hex("loadaddr", 0x4000); |
| } else { |
| loadaddr = (struct bootloader_control_msg*)simple_strtoul(argv[2], NULL, 16); |
| } |
| DbgP("bcbPart %s, loadaddr %p\n", bcbPart, loadaddr); |
| int rc = store_logic_read(bcbPart, 0, BCB_INFO_SZ, loadaddr); |
| if (rc) { |
| ErrP("Fail read bcb from part %s\n", bcbPart); |
| return CMD_RET_FAILURE; |
| } |
| |
| int needUpdateMisc = 0; |
| rc = parse_and_update_bcb(loadaddr, &needUpdateMisc); |
| if (rc) { |
| ErrP("Fail parse bcb from part %s\n", bcbPart); |
| return CMD_RET_FAILURE; |
| } |
| if (!needUpdateMisc) return CMD_RET_SUCCESS; |
| |
| rc = store_erase(bcbPart, 0, 0, 0);//erase whole misc part |
| if (rc) { |
| ErrP("Fail erase bcb from part %s\n", bcbPart); |
| return CMD_RET_FAILURE; |
| } |
| rc = store_logic_write(bcbPart, 0, BCB_INFO_SZ, loadaddr); |
| if (rc) { |
| ErrP("Fail read bcb from part %s\n", bcbPart); |
| return CMD_RET_FAILURE; |
| } |
| return CMD_RET_SUCCESS; |
| } |
| |
| U_BOOT_CMD( |
| get_boot_part, 3, 0, do_GetValidSlot, |
| "get part name to load the bootable part", |
| "\nThis command will get which partititon name should be used for booting\n" |
| " argv: get_boot_part <bcbPartName> <loadaddr>\n" //usage |
| ); |
| |