blob: 8c4ccaaa674f50bfbf7de868bc138cbe5533943b [file] [log] [blame]
/*
* Copyright (c) 2023, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <limits.h>
#include <zephyr/kernel.h>
#include <zephyr/init.h>
#include <zephyr/devicetree.h>
#include <zephyr/retention/retention.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zephyr/retention/blinfo.h>
#include <bootutil/boot_status.h>
LOG_MODULE_REGISTER(blinfo_mcuboot, CONFIG_RETENTION_LOG_LEVEL);
static const struct device *bootloader_info_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_bootloader_info));
#if !defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_FUNCTION)
static
#endif
int blinfo_lookup(uint16_t key, char *val, int val_len_max)
{
struct shared_data_tlv_header header;
struct shared_data_tlv_entry tlv_entry = {0};
uintptr_t offset = SHARED_DATA_HEADER_SIZE;
int rc;
rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
if (rc != 0) {
return rc;
}
/* Iterate over the whole shared MCUboot data section and look for a TLV with
* the required tag.
*/
while (offset < header.tlv_tot_len) {
rc = retention_read(bootloader_info_dev, offset, (void *)&tlv_entry,
sizeof(tlv_entry));
if (rc != 0) {
return rc;
}
if (GET_MAJOR(tlv_entry.tlv_type) == TLV_MAJOR_BLINFO &&
GET_MINOR(tlv_entry.tlv_type) == key) {
/* Return an error if the provided buffer is too small to fit the
* value in it, bootloader values are small and concise and should
* be able to fit in a single buffer.
*/
if (tlv_entry.tlv_len > val_len_max) {
return -EOVERFLOW;
}
offset += SHARED_DATA_ENTRY_HEADER_SIZE;
rc = retention_read(bootloader_info_dev, offset, val,
tlv_entry.tlv_len);
if (rc != 0) {
return rc;
}
return tlv_entry.tlv_len;
}
offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
}
/* Return IO error as a valid key name was provided but the TLV was not found in
* the shared data section.
*/
return -EIO;
}
#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
static int blinfo_handle_get(const char *name, char *val, int val_len_max);
static int blinfo_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg);
static struct settings_handler blinfo_handler = {
.name = "blinfo",
.h_get = blinfo_handle_get,
.h_set = blinfo_handle_set,
};
static int blinfo_handle_get(const char *name, char *val, int val_len_max)
{
const char *next;
uint16_t index;
/* Allowed keys are mode, signature_type, recovery, running_slot, bootloader_version
* and max_application_size which cannot contain any additional entries
*/
if (settings_name_steq(name, "mode", &next) && !next) {
index = BLINFO_MODE;
} else if (settings_name_steq(name, "signature_type", &next) && !next) {
index = BLINFO_SIGNATURE_TYPE;
} else if (settings_name_steq(name, "recovery", &next) && !next) {
index = BLINFO_RECOVERY;
} else if (settings_name_steq(name, "running_slot", &next) && !next) {
index = BLINFO_RUNNING_SLOT;
} else if (settings_name_steq(name, "bootloader_version", &next) && !next) {
index = BLINFO_BOOTLOADER_VERSION;
} else if (settings_name_steq(name, "max_application_size", &next) && !next) {
index = BLINFO_MAX_APPLICATION_SIZE;
} else {
return -ENOENT;
}
return blinfo_lookup(index, val, val_len_max);
}
static int blinfo_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
{
return -ENOTSUP;
}
#endif
static int blinfo_init(void)
{
int rc;
rc = retention_is_valid(bootloader_info_dev);
if (rc == 1 || rc == -ENOTSUP) {
struct shared_data_tlv_header header;
rc = retention_read(bootloader_info_dev, 0, (void *)&header, sizeof(header));
if (rc == 0 && header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
/* Unknown data present */
LOG_ERR("MCUboot data load failed, expected magic value: 0x%x, got: 0x%x",
SHARED_DATA_TLV_INFO_MAGIC, header.tlv_magic);
rc = -EINVAL;
}
}
#if defined(CONFIG_RETENTION_BOOTLOADER_INFO_OUTPUT_SETTINGS)
if (rc == 0) {
settings_register(&blinfo_handler);
}
#endif
return rc;
}
SYS_INIT(blinfo_init, APPLICATION, CONFIG_RETENTION_BOOTLOADER_INFO_INIT_PRIORITY);