diff --git a/include/zephyr/bindesc.h b/include/zephyr/bindesc.h index fa7cbd5a25d..aed5edf1c95 100644 --- a/include/zephyr/bindesc.h +++ b/include/zephyr/bindesc.h @@ -23,6 +23,8 @@ extern "C" { #define BINDESC_TYPE_STR 0x1 #define BINDESC_TYPE_BYTES 0x2 #define BINDESC_TYPE_DESCRIPTORS_END 0xf +/* sizeof ignores the data as it's a flexible array */ +#define BINDESC_ENTRY_HEADER_SIZE (sizeof(struct bindesc_entry)) /** * @brief Binary Descriptor Definition @@ -131,11 +133,18 @@ extern "C" { */ #define BINDESC_TAG(type, id) ((BINDESC_TYPE_##type & 0xf) << 12 | (id & 0x0fff)) +/** + * @brief Utility macro to get the type of a bindesc tag + * + * @param tag Tag to get the type of + */ +#define BINDESC_GET_TAG_TYPE(tag) ((tag >> 12) & 0xf) + /** * @endcond */ -#if !defined(_LINKER) +#if !defined(_LINKER) || defined(__DOXYGEN__) #include @@ -278,6 +287,10 @@ extern "C" { */ #define BINDESC_GET_SIZE(name) BINDESC_NAME(name).len +/** + * @} + */ + /* * An entry of the binary descriptor header. Each descriptor is * described by one of these entries. @@ -301,6 +314,176 @@ BUILD_ASSERT(offsetof(struct bindesc_entry, tag) == 0, "Incorrect memory layout" BUILD_ASSERT(offsetof(struct bindesc_entry, len) == 2, "Incorrect memory layout"); BUILD_ASSERT(offsetof(struct bindesc_entry, data) == 4, "Incorrect memory layout"); +struct bindesc_handle { + const uint8_t *address; + enum { + BINDESC_HANDLE_TYPE_RAM, + BINDESC_HANDLE_TYPE_MEMORY_MAPPED_FLASH, + BINDESC_HANDLE_TYPE_FLASH, + } type; + size_t size_limit; +#if IS_ENABLED(CONFIG_BINDESC_READ_FLASH) + const struct device *flash_device; + uint8_t buffer[sizeof(struct bindesc_entry) + + CONFIG_BINDESC_READ_FLASH_MAX_DATA_SIZE] __aligned(BINDESC_ALIGNMENT); +#endif /* IS_ENABLED(CONFIG_BINDESC_READ_FLASH) */ +}; + +/** + * @brief Reading Binary Descriptors of other images. + * @defgroup bindesc_read Bindesc Read + * @ingroup os_services + * @{ + */ + +/** + * @brief Callback type to be called on descriptors found during a walk + * + * @param entry Current descriptor + * @param user_data The user_data given to @ref bindesc_foreach + * + * @return Any non zero value will halt the walk + */ +typedef int (*bindesc_callback_t)(const struct bindesc_entry *entry, void *user_data); + +/** + * @brief Open an image's binary descriptors for reading, from a memory mapped flash + * + * @details + * Initializes a bindesc handle for subsequent calls to bindesc API. + * Memory mapped flash is any flash that can be directly accessed by the CPU, + * without needing to use the flash API for copying the data to RAM. + * + * @param handle Bindesc handle to be given to subsequent calls + * @param offset The offset from the beginning of the flash that the bindesc magic can be found at + * + * @retval 0 On success + * @retval -ENOENT If no bindesc magic was found at the given offset + */ +int bindesc_open_memory_mapped_flash(struct bindesc_handle *handle, size_t offset); + +/** + * @brief Open an image's binary descriptors for reading, from RAM + * + * @details + * Initializes a bindesc handle for subsequent calls to bindesc API. + * It's assumed that the whole bindesc context was copied to RAM prior to calling + * this function, either by the user or by a bootloader. + * + * @note The given address must be aligned to BINDESC_ALIGNMENT + * + * @param handle Bindesc handle to be given to subsequent calls + * @param address The address that the bindesc magic can be found at + * @param max_size Maximum size of the given buffer + * + * @retval 0 On success + * @retval -ENOENT If no bindesc magic was found at the given address + * @retval -EINVAL If the given address is not aligned + */ +int bindesc_open_ram(struct bindesc_handle *handle, const uint8_t *address, size_t max_size); + +/** + * @brief Open an image's binary descriptors for reading, from flash + * + * @details + * Initializes a bindesc handle for subsequent calls to bindesc API. + * As opposed to reading bindesc from RAM or memory mapped flash, this + * backend requires reading the data from flash to an internal buffer + * using the flash API + * + * @param handle Bindesc handle to be given to subsequent calls + * @param offset The offset from the beginning of the flash that the bindesc magic can be found at + * @param flash_device Flash device to read descriptors from + * + * @retval 0 On success + * @retval -ENOENT If no bindesc magic was found at the given offset + */ +int bindesc_open_flash(struct bindesc_handle *handle, size_t offset, + const struct device *flash_device); + +/** + * @brief Walk the binary descriptors and run a user defined callback on each of them + * + * @note + * If the callback returns a non zero value, the walk stops. + * + * @param handle An initialized bindesc handle + * @param callback A user defined callback to be called on each descriptor + * @param user_data User defined data to be given to the callback + * + * @return If the walk was finished prematurely by the callback, + * return the callback's retval, zero otherwise + */ +int bindesc_foreach(struct bindesc_handle *handle, bindesc_callback_t callback, void *user_data); + +/** + * @brief Find a specific descriptor of type string + * + * @warning + * When using the flash backend, result will be invalidated by the next call to any bindesc API. + * Use the value immediately or copy it elsewhere. + * + * @param handle An initialized bindesc handle + * @param id ID to search for + * @param result Pointer to the found string + * + * @retval 0 If the descriptor was found + * @retval -ENOENT If the descriptor was not found + */ +int bindesc_find_str(struct bindesc_handle *handle, uint16_t id, const char **result); + +/** + * @brief Find a specific descriptor of type uint + * + * @warning + * When using the flash backend, result will be invalidated by the next call to any bindesc API. + * Use the value immediately or copy it elsewhere. + * + * @param handle An initialized bindesc handle + * @param id ID to search for + * @param result Pointer to the found uint + * + * @retval 0 If the descriptor was found + * @retval -ENOENT If the descriptor was not found + */ +int bindesc_find_uint(struct bindesc_handle *handle, uint16_t id, const uint32_t **result); + +/** + * @brief Find a specific descriptor of type bytes + * + * @warning + * When using the flash backend, result will be invalidated by the next call to any bindesc API. + * Use the value immediately or copy it elsewhere. + * + * @param handle An initialized bindesc handle + * @param id ID to search for + * @param result Pointer to the found bytes + * @param result_size Size of the found bytes + * + * @retval 0 If the descriptor was found + * @retval -ENOENT If the descriptor was not found + */ +int bindesc_find_bytes(struct bindesc_handle *handle, uint16_t id, const uint8_t **result, + size_t *result_size); + +/** + * @brief Get the size of an image's binary descriptors + * + * @details + * Walks the binary descriptor structure to caluculate the total size of the structure + * in bytes. This is useful, for instance, if the whole structure is to be copied to RAM. + * + * @param handle An initialized bindesc handle + * @param result Pointer to write result to + * + * @return 0 On success, negative errno otherwise + */ +int bindesc_get_size(struct bindesc_handle *handle, size_t *result); + +/** + * @} + */ + #if defined(CONFIG_BINDESC_KERNEL_VERSION_STRING) extern const struct bindesc_entry BINDESC_NAME(kernel_version_string); #endif /* defined(CONFIG_BINDESC_KERNEL_VERSION_STRING) */ @@ -411,10 +594,6 @@ extern const struct bindesc_entry BINDESC_NAME(cxx_compiler_version); #endif /* !defined(_LINKER) */ -/** - * @} - */ - #ifdef __cplusplus } #endif diff --git a/subsys/bindesc/CMakeLists.txt b/subsys/bindesc/CMakeLists.txt index c597eddd3de..b9a5a312fbc 100644 --- a/subsys/bindesc/CMakeLists.txt +++ b/subsys/bindesc/CMakeLists.txt @@ -66,3 +66,5 @@ if(CONFIG_BINDESC_DEFINE_HOST_INFO) gen_str_definition(CXX_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID}) gen_str_definition(CXX_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) endif() + +zephyr_library_sources_ifdef(CONFIG_BINDESC_READ bindesc_read.c) diff --git a/subsys/bindesc/Kconfig b/subsys/bindesc/Kconfig index 0ab1460771a..50de5286d6b 100644 --- a/subsys/bindesc/Kconfig +++ b/subsys/bindesc/Kconfig @@ -31,4 +31,46 @@ config BINDESC_DEFINE_MAX_DATA_SIZE endif # BINDESC_DEFINE +config BINDESC_READ + bool "Binary Descriptors Read" + help + Enable the app to read the binary descriptors of another image + +if BINDESC_READ + +module = BINDESC_READ +module-str = Binary Descriptor read +source "subsys/logging/Kconfig.template.log_config" + +config BINDESC_READ_RAM + bool "Bindesc read from RAM" + help + Enable reading and parsing binary descriptors from RAM. + +config BINDESC_READ_MEMORY_MAPPED_FLASH + bool "Bindesc read from memory mapped flash" + help + Enable reading and parsing binary descriptors from memory mapped flash. + +config BINDESC_READ_FLASH + bool "Bindesc read from flash" + help + Enable reading and parsing binary descriptors from non memory mapped flash + (e.g. external flash). + +if BINDESC_READ_FLASH + +config BINDESC_READ_FLASH_MAX_DATA_SIZE + int "Bindesc read flash max data size" + range 4 $(UINT16_MAX) + default 128 + help + The maximum expected size of the descriptors' data. This should be set to + the value set to BINDESC_DEFINE_MAX_DATA_SIZE by the read image. + Any descriptor that exceeds this size will be ignored. + +endif # BINDESC_READ_FLASH + +endif # BINDESC_READ + endif # BINDESC diff --git a/subsys/bindesc/bindesc_read.c b/subsys/bindesc/bindesc_read.c new file mode 100644 index 00000000000..41056396277 --- /dev/null +++ b/subsys/bindesc/bindesc_read.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2023 Yonatan Schachter + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +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); +}