blob: be4e558713f151bb07dec5030e0f909e98386f10 [file] [log] [blame]
/*
* Copyright (c) 2020 Grinn
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bootutil/bootutil_public.h"
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/init.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include "mcuboot_priv.h"
#ifdef CONFIG_RETENTION_BOOT_MODE
#include <zephyr/retention/bootmode.h>
#ifdef CONFIG_REBOOT
#include <zephyr/sys/reboot.h>
#endif
#endif
struct area_desc {
const char *name;
unsigned int id;
};
static const struct area_desc areas[] = {
{"primary", FLASH_AREA_IMAGE_PRIMARY},
#ifdef FLASH_AREA_IMAGE_SECONDARY
{"secondary", FLASH_AREA_IMAGE_SECONDARY},
#endif
};
static const char *swap_state_magic_str(uint8_t magic)
{
switch (magic) {
case BOOT_MAGIC_GOOD:
return "good";
case BOOT_MAGIC_BAD:
return "bad";
case BOOT_MAGIC_UNSET:
return "unset";
case BOOT_MAGIC_ANY:
return "any";
case BOOT_MAGIC_NOTGOOD:
return "notgood";
}
return "unknown";
}
static const char *swap_type_str(uint8_t type)
{
switch (type) {
case BOOT_SWAP_TYPE_NONE:
return "none";
case BOOT_SWAP_TYPE_TEST:
return "test";
case BOOT_SWAP_TYPE_PERM:
return "perm";
case BOOT_SWAP_TYPE_REVERT:
return "revert";
case BOOT_SWAP_TYPE_FAIL:
return "fail";
}
return "unknown";
}
static const char *swap_state_flag_str(uint8_t flag)
{
switch (flag) {
case BOOT_FLAG_SET:
return "set";
case BOOT_FLAG_BAD:
return "bad";
case BOOT_FLAG_UNSET:
return "unset";
case BOOT_FLAG_ANY:
return "any";
}
return "unknown";
}
static int cmd_mcuboot_erase(const struct shell *sh, size_t argc,
char **argv)
{
unsigned int id;
int err;
id = strtoul(argv[1], NULL, 0);
/* Check if this is the parent (MCUboot) or own slot and if so, deny the request */
#if FIXED_PARTITION_EXISTS(boot_partition)
if (id == FIXED_PARTITION_ID(boot_partition)) {
shell_error(sh, "Cannot erase boot partition");
return -EACCES;
}
#endif
#if DT_FIXED_PARTITION_EXISTS(DT_CHOSEN(zephyr_code_partition))
if (id == DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_code_partition))) {
shell_error(sh, "Cannot erase active partitions");
return -EACCES;
}
#endif
err = boot_erase_img_bank(id);
if (err) {
shell_error(sh, "failed to erase bank %u", id);
return err;
}
return 0;
}
static int cmd_mcuboot_confirm(const struct shell *sh, size_t argc,
char **argv)
{
int err;
err = boot_write_img_confirmed();
if (err) {
shell_error(sh, "failed to confirm: %d", err);
}
return err;
}
static int cmd_mcuboot_request_upgrade(const struct shell *sh, size_t argc,
char **argv)
{
int permanent = 0;
int err;
if (argc > 1) {
if (!strcmp(argv[1], "permanent")) {
permanent = 1;
} else {
shell_warn(sh, "invalid argument!");
return -EINVAL;
}
}
err = boot_request_upgrade(permanent);
if (err) {
shell_error(sh, "failed to request upgrade: %d", err);
}
return err;
}
#ifdef CONFIG_RETENTION_BOOT_MODE
static int cmd_mcuboot_serial_recovery(const struct shell *sh, size_t argc,
char **argv)
{
int rc;
rc = bootmode_set(BOOT_MODE_TYPE_BOOTLOADER);
if (rc) {
shell_error(sh, "Failed to set serial recovery mode: %d", rc);
return rc;
}
#ifdef CONFIG_REBOOT
sys_reboot(SYS_REBOOT_COLD);
#else
shell_error(sh, "mcuboot serial recovery mode set, please reboot your device");
#endif
return rc;
}
#endif
static int cmd_mcuboot_info_area(const struct shell *sh,
const struct area_desc *area)
{
struct mcuboot_img_header hdr;
struct boot_swap_state swap_state;
int err;
err = boot_read_bank_header(area->id, &hdr, sizeof(hdr));
if (err) {
shell_error(sh, "failed to read %s area (%u) %s: %d",
area->name, area->id, "header", err);
return err;
}
shell_print(sh, "%s area (%u):", area->name, area->id);
shell_print(sh, " version: %u.%u.%u+%u",
(unsigned int) hdr.h.v1.sem_ver.major,
(unsigned int) hdr.h.v1.sem_ver.minor,
(unsigned int) hdr.h.v1.sem_ver.revision,
(unsigned int) hdr.h.v1.sem_ver.build_num);
shell_print(sh, " image size: %u",
(unsigned int) hdr.h.v1.image_size);
err = boot_read_swap_state_by_id(area->id, &swap_state);
if (err) {
shell_error(sh, "failed to read %s area (%u) %s: %d",
area->name, area->id, "swap state", err);
return err;
}
shell_print(sh, " magic: %s",
swap_state_magic_str(swap_state.magic));
if (IS_ENABLED(CONFIG_MCUBOOT_TRAILER_SWAP_TYPE)) {
shell_print(sh, " swap type: %s",
swap_type_str(swap_state.swap_type));
}
shell_print(sh, " copy done: %s",
swap_state_flag_str(swap_state.copy_done));
shell_print(sh, " image ok: %s",
swap_state_flag_str(swap_state.image_ok));
return 0;
}
static int cmd_mcuboot_info(const struct shell *sh, size_t argc,
char **argv)
{
int i;
shell_print(sh, "swap type: %s", swap_type_str(mcuboot_swap_type()));
shell_print(sh, "confirmed: %d", boot_is_img_confirmed());
for (i = 0; i < ARRAY_SIZE(areas); i++) {
shell_print(sh, "");
cmd_mcuboot_info_area(sh, &areas[i]);
}
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(mcuboot_cmds,
SHELL_CMD_ARG(confirm, NULL, "confirm", cmd_mcuboot_confirm, 1, 0),
SHELL_CMD_ARG(erase, NULL, "erase <area_id>", cmd_mcuboot_erase, 2, 0),
SHELL_CMD_ARG(request_upgrade, NULL, "request_upgrade [permanent]",
cmd_mcuboot_request_upgrade, 1, 1),
#ifdef CONFIG_RETENTION_BOOT_MODE
SHELL_CMD_ARG(serial_recovery, NULL, "serial_recovery", cmd_mcuboot_serial_recovery, 1, 0),
#endif
SHELL_SUBCMD_SET_END /* Array terminated. */
);
SHELL_CMD_REGISTER(mcuboot, &mcuboot_cmds, "MCUboot commands",
cmd_mcuboot_info);