| // 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> |
| #ifdef CONFIG_UNIFY_BOOTLOADER |
| #include "cmd_bootctl_wrapper.h" |
| #endif |
| #include "cmd_bootctl_utils.h" |
| |
| //extern int nand_store_write(const char *name, loff_t off, size_t size, void *buf); |
| |
| #ifdef CONFIG_BOOTLOADER_CONTROL_BLOCK |
| #define COMMANDBUF_SIZE 32 |
| #define STATUSBUF_SIZE 32 |
| #define RECOVERYBUF_SIZE 768 |
| |
| #define BOOTINFO_OFFSET 864 |
| #define SLOTBUF_SIZE 32 |
| #define MISCBUF_SIZE 1088 |
| |
| 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 { |
| u8 bootable; |
| u8 online; |
| u8 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). |
| u8 magic[3]; |
| |
| // Version of BrilloBootInfo struct, must be 0 or larger. |
| u8 version; |
| |
| // Currently active slot. |
| u8 active_slot; |
| |
| // Information about each slot. |
| BrilloSlotInfo slot_info[2]; |
| u8 attemp_times; |
| |
| u8 reserved[14]; |
| } BrilloBootInfo; |
| |
| #if (IS_ENABLED(CONFIG_UNIFY_BOOTLOADER)) |
| bootctl_func_handles cmd_bootctrl_handles = {0}; |
| #endif |
| |
| 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; |
| } |
| |
| static 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) |
| { |
| pr_info("info->attemp_times = %u\n", info->attemp_times); |
| pr_info("info->active_slot = %u\n", info->active_slot); |
| pr_info("info->slot_info[0].bootable = %u\n", info->slot_info[0].bootable); |
| pr_info("info->slot_info[0].online = %u\n", info->slot_info[0].online); |
| pr_info("info->slot_info[1].bootable = %u\n", info->slot_info[1].bootable); |
| pr_info("info->slot_info[1].online = %u\n", info->slot_info[1].online); |
| pr_info("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; |
| } |
| |
| 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 |
| 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 |
| |
| store_write((const char *)partition, 0, MISCBUF_SIZE, (unsigned char *)miscbuf); |
| |
| return true; |
| } |
| |
| 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; |
| |
| if (argc != 1) |
| return cmd_usage(cmdtp); |
| |
| 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"))) { |
| 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)) { |
| pr_info("boot-info is invalid. Resetting.\n"); |
| boot_info_reset(&info); |
| boot_info_save(&info, miscbuf); |
| } |
| |
| attemp_times = boot_info_get_attemp_times(&info); |
| pr_info("attemp_times = %d\n", attemp_times); |
| |
| if (attemp_times == -1) |
| boot_info_change_online_slot(&info); |
| |
| //slot = boot_info_get_online_slot(&info); |
| slot = info.active_slot; |
| pr_info("active slot = %d\n", slot); |
| |
| #ifdef CONFIG_AML_MTD |
| //check if boot_a/b on nand |
| enum boot_type_e device_boot_flag = store_get_type(); |
| |
| if (device_boot_flag == BOOT_NAND_MTD) { |
| 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) { |
| 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"); |
| } |
| } else { |
| 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"); |
| } |
| } |
| |
| if (dynamic_partition) |
| env_set("partition_mode", "dynamic"); |
| else |
| env_set("partition_mode", "normal"); |
| |
| 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) { |
| pr_info("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)) { |
| pr_info("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("boot_part", "boot_a"); |
| env_set("recovery_part", "recovery_a"); |
| env_set("slot-suffixes", "0"); |
| pr_info("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("boot_part", "boot_b"); |
| env_set("recovery_part", "recovery_b"); |
| env_set("slot-suffixes", "1"); |
| pr_info("set active slot b\n"); |
| boot_info_set_active_slot(&info, 1); |
| } else { |
| pr_info("error input slot\n"); |
| return -1; |
| } |
| |
| boot_info_save(&info, miscbuf); |
| |
| 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; |
| } |
| |
| #endif /* CONFIG_BOOTLOADER_CONTROL_BLOCK */ |
| |
| #ifdef CONFIG_UNIFY_BOOTLOADER |
| bootctl_func_handles *get_bootctl_cmd_func(void) |
| { |
| cmd_bootctrl_handles.do_GetValidSlot_func = do_GetValidSlot; |
| cmd_bootctrl_handles.do_SetActiveSlot_func = do_SetActiveSlot; |
| cmd_bootctrl_handles.do_GetSystemMode_func = do_GetSystemMode; |
| cmd_bootctrl_handles.do_GetAvbMode_func = do_GetAvbMode; |
| |
| return &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 |
| (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" |
| ); |
| #endif |
| |