| /* |
| * 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); |
| } |