| /* |
| * Copyright (c) 2020 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <common.h> |
| |
| #include <asm/arch/io.h> |
| #include <asm/arch/secure_apb.h> |
| #include <command.h> |
| #include <environment.h> |
| #include <libabr/abr.h> |
| #include <libabr/data.h> |
| #include <libabr/util.h> |
| #include <zircon/zircon.h> |
| #include <zircon/zircon_abr_reg_util.h> |
| |
| #include "override_reboot_mode.h" |
| |
| // i.e. "_a", "_b" and "_r" |
| #define TPL_SLOT_NAME_LEN 2 |
| const char *g_tpl_slot = NULL; |
| |
| static int invalidate_abr_sticky_register(void) |
| { |
| abr_sticky_reg0_t reg0 = read_sticky_reg0(); |
| reg0.fields.valid = 0; |
| write_sticky_reg0(reg0); |
| return 0; |
| } |
| |
| static int get_abr_slot(void) |
| { |
| abr_sticky_reg1_t reg1 = read_sticky_reg1(); |
| AbrSlotIndex slot = reg1.fields.current_slot; |
| g_tpl_slot = AbrGetSlotSuffix(slot); |
| if (!g_tpl_slot || g_tpl_slot[0] == '\0') { |
| fprintf(stderr, "failed to get tpl slot\n"); |
| return -1; |
| } |
| printf("Active tpl slot is: %s\n", g_tpl_slot); |
| |
| return 0; |
| } |
| |
| static int sync_abr_reg_to_misc(void) |
| { |
| abr_sticky_reg0_t reg0 = read_sticky_reg0(); |
| abr_sticky_reg1_t reg1 = read_sticky_reg1(); |
| |
| // Check if we need to override reboot reason |
| if (force_recovery_bit_set()) { |
| // Set force recovery mode flag to indicate that this boot is due to |
| // force recovery. The abr booting logic will skip cross-validating |
| // abr metdata for slot matching. |
| set_force_recovery_mode(true); |
| set_override_rebootmode_val(reg0.fields.rebootmode); |
| // Clear force recovery. If there is another watchdog reset due to |
| // other reasons. The watchdog reset reason shall be reported as |
| // it is. |
| set_force_recovery_bit(false); |
| } |
| |
| if (!reg0.fields.valid) { |
| printf("sticky register does not contain abr metadata. " |
| "abr metadata in storage is up to date.\n"); |
| return 0; |
| } |
| |
| printf("flushing abr data in register to storage\n"); |
| |
| AbrData abr_data; |
| memset(&abr_data, 0, sizeof(abr_data)); |
| memcpy(abr_data.magic, kAbrMagic, kAbrMagicLen); |
| abr_data.version_major = kAbrMajorVersion; |
| abr_data.version_minor = kAbrMinorVersion; |
| |
| abr_data.slot_data[0].priority = reg0.fields.priority; |
| abr_data.slot_data[0].tries_remaining = reg0.fields.tries_remaining; |
| abr_data.slot_data[0].successful_boot = reg0.fields.successful; |
| abr_data.slot_data[1].priority = reg1.fields.priority; |
| abr_data.slot_data[1].tries_remaining = reg1.fields.tries_remaining; |
| abr_data.slot_data[1].successful_boot = reg1.fields.successful; |
| abr_data.crc32 = AbrHostToBigEndian( |
| AbrCrc32(&abr_data, sizeof(abr_data) - sizeof(uint32_t))); |
| |
| const AbrOps *ops = zircon_abr_ops(); |
| return ops->write_abr_metadata(NULL, (const uint8_t *)&abr_data, |
| sizeof(abr_data)) ? |
| 0 : |
| -1; |
| } |
| |
| int do_get_slot_and_sync_abr(cmd_tbl_t *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| if (get_abr_slot()) |
| return -1; |
| |
| if (sync_abr_reg_to_misc()) |
| return -1; |
| |
| if (invalidate_abr_sticky_register()) |
| return -1; |
| |
| return 0; |
| } |
| |
| U_BOOT_CMD(get_slot_and_sync_abr, 1, 0, do_get_slot_and_sync_abr, |
| "get_slot_and_sync_abr", |
| "\nThis command retrieves slot decision and flushes abr metadata\n" |
| "to storage\n"); |
| |
| int do_process_force_recovery_request(cmd_tbl_t *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| if (strncmp(g_tpl_slot, "_r", 2)) { |
| printf("Force recovery enabled, but tpl slot is %s. " |
| "Setting force recovery bit and reset...\n", |
| g_tpl_slot); |
| set_force_recovery_bit(true); |
| set_abr_sticky_reg_rebootmode(get_rebootmode_value()); |
| do_reset(NULL, 0, 0, NULL); |
| } |
| return 0; |
| } |
| |
| U_BOOT_CMD(process_force_recovery_request, 1, 0, |
| do_process_force_recovery_request, "process_force_recovery_request", |
| "\nProcess a force recovery request.\n"); |