zephyr/subsys/bindesc/bindesc_read.c
Yonatan Schachter 00800d4818 bindesc: Add support for reading binary descriptors
This commit adds support for reading and parsing binary descriptors.
It can be used for reading the descriptors of another image, or for
iterating over one's own descriptors.

Signed-off-by: Yonatan Schachter <yonatan.schachter@gmail.com>
2024-10-11 13:20:49 -04:00

233 lines
6 KiB
C

/*
* Copyright (c) 2023 Yonatan Schachter
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/bindesc.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/flash.h>
LOG_MODULE_REGISTER(bindesc_read, CONFIG_BINDESC_READ_LOG_LEVEL);
struct find_user_data {
const void *result;
size_t size;
uint16_t tag;
};
/**
* A callback used by the bindesc_find_* functions.
*/
static int find_callback(const struct bindesc_entry *entry, void *user_data)
{
struct find_user_data *data = (struct find_user_data *)user_data;
if (data->tag == entry->tag) {
data->result = (const void *)&(entry->data);
data->size = entry->len;
return 1;
}
return 0;
}
/**
* A callback used by the bindesc_get_size function.
*/
static int get_size_callback(const struct bindesc_entry *entry, void *user_data)
{
size_t *result = (size_t *)user_data;
*result += WB_UP(BINDESC_ENTRY_HEADER_SIZE + entry->len);
return 0;
}
/**
* This helper function is used to abstract the different methods of reading
* data from the binary descriptors.
* For RAM and memory mapped flash, the implementation is very simple, as both
* are memory mapped and can simply return a pointer to the data.
* Flash is more complex because it needs to read the data from flash, and do
* error checking.
*/
static inline int get_entry(struct bindesc_handle *handle, const uint8_t *address,
const struct bindesc_entry **entry)
{
int retval = 0;
int flash_retval;
/* Check if reading from flash is enabled, if not, this if/else will be optimized out */
if (IS_ENABLED(CONFIG_BINDESC_READ_FLASH) && handle->type == BINDESC_HANDLE_TYPE_FLASH) {
flash_retval = flash_read(handle->flash_device, (size_t)address,
handle->buffer, BINDESC_ENTRY_HEADER_SIZE);
if (flash_retval) {
LOG_ERR("Flash read error: %d", flash_retval);
return -EIO;
}
/* Make sure buffer is large enough for the data */
if (((const struct bindesc_entry *)handle->buffer)->len + BINDESC_ENTRY_HEADER_SIZE
> sizeof(handle->buffer)) {
LOG_WRN("Descriptor too large to copy, skipping");
retval = -ENOMEM;
} else {
flash_retval = flash_read(handle->flash_device,
(size_t)address + BINDESC_ENTRY_HEADER_SIZE,
handle->buffer + BINDESC_ENTRY_HEADER_SIZE,
((const struct bindesc_entry *)handle->buffer)->len);
if (flash_retval) {
LOG_ERR("Flash read error: %d", flash_retval);
return -EIO;
}
}
*entry = (const struct bindesc_entry *)handle->buffer;
} else {
*entry = (const struct bindesc_entry *)address;
}
return retval;
}
#if IS_ENABLED(CONFIG_BINDESC_READ_MEMORY_MAPPED_FLASH)
int bindesc_open_memory_mapped_flash(struct bindesc_handle *handle, size_t offset)
{
uint8_t *address = (uint8_t *)CONFIG_FLASH_BASE_ADDRESS + offset;
if (*(uint64_t *)address != BINDESC_MAGIC) {
LOG_ERR("Magic not found in given address");
return -ENOENT;
}
handle->address = address;
handle->type = BINDESC_HANDLE_TYPE_MEMORY_MAPPED_FLASH;
handle->size_limit = UINT16_MAX;
return 0;
}
#endif /* IS_ENABLED(CONFIG_BINDESC_READ_RAM) */
#if IS_ENABLED(CONFIG_BINDESC_READ_RAM)
int bindesc_open_ram(struct bindesc_handle *handle, const uint8_t *address, size_t max_size)
{
if (!IS_ALIGNED(address, BINDESC_ALIGNMENT)) {
LOG_ERR("Given address is not aligned");
return -EINVAL;
}
if (*(uint64_t *)address != BINDESC_MAGIC) {
LOG_ERR("Magic not found in given address");
return -ENONET;
}
handle->address = address;
handle->type = BINDESC_HANDLE_TYPE_RAM;
handle->size_limit = max_size;
return 0;
}
#endif /* IS_ENABLED(CONFIG_BINDESC_READ_RAM) */
#if IS_ENABLED(CONFIG_BINDESC_READ_FLASH)
int bindesc_open_flash(struct bindesc_handle *handle, size_t offset,
const struct device *flash_device)
{
int retval;
retval = flash_read(flash_device, offset, handle->buffer, sizeof(BINDESC_MAGIC));
if (retval) {
LOG_ERR("Flash read error: %d", retval);
return -EIO;
}
if (*(uint64_t *)handle->buffer != BINDESC_MAGIC) {
LOG_ERR("Magic not found in given address");
return -ENOENT;
}
handle->address = (uint8_t *)offset;
handle->type = BINDESC_HANDLE_TYPE_FLASH;
handle->flash_device = flash_device;
handle->size_limit = UINT16_MAX;
return 0;
}
#endif /* IS_ENABLED(CONFIG_BINDESC_READ_FLASH) */
int bindesc_foreach(struct bindesc_handle *handle, bindesc_callback_t callback, void *user_data)
{
const struct bindesc_entry *entry;
const uint8_t *address = handle->address;
int retval;
address += sizeof(BINDESC_MAGIC);
do {
retval = get_entry(handle, address, &entry);
if (retval == -EIO) {
return -EIO;
}
address += WB_UP(BINDESC_ENTRY_HEADER_SIZE + entry->len);
if (retval) {
continue;
}
retval = callback(entry, user_data);
if (retval) {
return retval;
}
} while ((entry->tag != BINDESC_TAG_DESCRIPTORS_END) &&
((address - handle->address) <= handle->size_limit));
return 0;
}
int bindesc_find_str(struct bindesc_handle *handle, uint16_t id, const char **result)
{
struct find_user_data data = {
.tag = BINDESC_TAG(STR, id),
};
if (!bindesc_foreach(handle, find_callback, &data)) {
LOG_WRN("The requested descriptor was not found");
return -ENOENT;
}
*result = (char *)data.result;
return 0;
}
int bindesc_find_uint(struct bindesc_handle *handle, uint16_t id, const uint32_t **result)
{
struct find_user_data data = {
.tag = BINDESC_TAG(UINT, id),
};
if (!bindesc_foreach(handle, find_callback, &data)) {
LOG_WRN("The requested descriptor was not found");
return -ENOENT;
}
*result = (const uint32_t *)data.result;
return 0;
}
int bindesc_find_bytes(struct bindesc_handle *handle, uint16_t id, const uint8_t **result,
size_t *result_size)
{
struct find_user_data data = {
.tag = BINDESC_TAG(BYTES, id),
};
if (!bindesc_foreach(handle, find_callback, &data)) {
LOG_WRN("The requested descriptor was not found");
return -ENOENT;
}
*result = (const uint8_t *)data.result;
*result_size = data.size;
return 0;
}
int bindesc_get_size(struct bindesc_handle *handle, size_t *result)
{
*result = sizeof(BINDESC_MAGIC);
return bindesc_foreach(handle, get_size_callback, result);
}