diff --git a/Makefile.dep b/Makefile.dep index 5dece5690e..45f5eaefcc 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1049,6 +1049,10 @@ ifneq (,$(filter suit_transport_coap, $(USEMODULE))) USEMODULE += nanocoap endif +ifneq (,$(filter suit_storage_%, $(USEMODULE))) + USEMODULE += suit_storage +endif + ifneq (,$(filter suit_%,$(USEMODULE))) USEMODULE += suit endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d3e9d84756..3dcb7e0e53 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -119,6 +119,7 @@ PSEUDOMODULES += stdio_uart_rx PSEUDOMODULES += stm32_eth PSEUDOMODULES += stm32_eth_link_up PSEUDOMODULES += suit_transport_% +PSEUDOMODULES += suit_storage_% PSEUDOMODULES += wakaama_objects_% PSEUDOMODULES += wifi_enterprise PSEUDOMODULES += xtimer_on_ztimer diff --git a/sys/include/suit.h b/sys/include/suit.h index 2f217671bb..300ac34fe3 100644 --- a/sys/include/suit.h +++ b/sys/include/suit.h @@ -56,6 +56,13 @@ extern "C" { #define CONFIG_SUIT_COMPONENT_MAX (1U) #endif +/** + * @brief Maximum name of component, includes separator + */ +#ifndef CONFIG_SUIT_COMPONENT_MAX_NAME_LEN +#define CONFIG_SUIT_COMPONENT_MAX_NAME_LEN (32U) +#endif + /** * @brief Current SUIT serialization format version * @@ -100,6 +107,10 @@ typedef enum { SUIT_ERR_SIGNATURE = -6, /**< Unable to verify signature */ SUIT_ERR_DIGEST_MISMATCH = -7, /**< Digest mismatch with COSE and SUIT */ SUIT_ERR_POLICY_FORBIDDEN = -8, /**< Denied because of policy mismatch */ + SUIT_ERR_NO_MEM = -9, /**< Out of memory condition */ + SUIT_ERR_STORAGE = -50, /**< Backend returned an error */ + SUIT_ERR_STORAGE_EXCEEDED = -51, /**< Backend out of space */ + SUIT_ERR_STORAGE_UNAVAILABLE = -52, /**< Backend location not available */ } suit_error_t; /** @@ -189,12 +200,21 @@ typedef struct { #define SUIT_COMPONENT_STATE_FINALIZED (1 << 3) /**< Component successfully installed */ /** @} */ +/** + * @brief Forward declaration for storage struct + * + * Breaks a dependency chain + */ +typedef struct suit_storage suit_storage_t; + + /** * @brief SUIT component struct as decoded from the manifest * * The parameters are references to CBOR-encoded information in the manifest. */ typedef struct { + suit_storage_t *storage_backend; /**< Storage backend used */ uint16_t state; /**< Component status flags */ suit_param_ref_t identifier; /**< Component identifier */ suit_param_ref_t param_vendor_id; /**< Vendor ID */ @@ -296,6 +316,18 @@ static inline bool suit_component_check_flag(suit_component_t *component, return (component->state & flag); } +/** + * @brief Convert a component name to a string + * + * Each component part is prefixed with @p separator + * + * @return SUIT_OK if succesful + * @return negative error code on error + */ +int suit_component_name_to_string(const suit_manifest_t *manifest, + const suit_component_t *component, + char separator, char *buf, size_t buf_len); + /** * @brief Helper function for writing bytes on flash a specified offset * diff --git a/sys/include/suit/storage.h b/sys/include/suit/storage.h new file mode 100644 index 0000000000..b7efbc8138 --- /dev/null +++ b/sys/include/suit/storage.h @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * 2020 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @defgroup sys_suit_storage SUIT secure firmware OTA upgrade storage + * infrastructure + * @ingroup sys_suit + * @brief SUIT firmware storage backends + * + * @{ + * + * @brief Storage backend functions for SUIT manifests + * @author Koen Zandberg + * + * The interface defined here specifies a generic API for SUIT OTA storage + * backends. + * + * The driver allows for creating multiple backends, each possibly servicing + * multiple locations. An example of this is a VFS storage backend. This backend + * could service multiple file locations on a filesystem. + * + * A SUIT component ID is formatted as an array of bytestrings. To make it easy + * to match and use a string, the location is supplied as string, each component + * separated by a separator provided in the driver. If no separator (`\0`) is + * set, the components are concatenated without separator. The @ref + * suit_storage_driver_t::set_active_location must be called before starting + * operations on the backend. + * + * A write sequence by the caller must start with @ref + * suit_storage_driver_t::start. The total length of the image is supplied to + * allow the backend to check if the payload fits in the available space. The + * payload data can be supplied piecewise with multiple calls to @ref + * suit_storage_driver_t::write. The caller is free to specify the offset, but + * the backend may enforce strict monotonicity on the offset and may enforce the + * gapless writes. After all bytes are supplied, the @ref + * suit_storage_driver_t::finish function must be called to signal the end of + * the write stage. + * + * Only when the @ref suit_storage_driver_t::install is called, the payload must + * be marked as valid. The mechanism for this can be backend specific. However + * in the case of a firmware image, it must not be bootable before this function + * is called. Similarly, a file payload must not be available at its provided + * path until after this function is called. The reason behind this is that the + * payload often must first be stored on the device before the image_match is + * called by the manifest. + * + * The other option is that the @ref suit_storage_driver_t::erase is called. In + * this case the not-yet-installed payload must be erased again as its contents + * might not be what is expected by the digest in the manifest. The payload must + * then be removed to prevent the possibility of storing malicious code on the + * device. + * + * A form of read access must be implemented to provide a way to read back the + * data and check the digest of the payload. @ref suit_storage_driver_t::read + * must be implemented, providing piecewise reading of the data. @ref + * suit_storage_driver_t::read_ptr is optional to implement, it can provide + * direct read access on memory-mapped storage. + * + * As the storage backend provides a mechanism to store persistent data, + * functions are added to set and retrieve the manifest sequence number. While + * not strictly required to implement, a firmware without a mechanism to + * retrieve and store sequence numbers will always fail to update. + * + * The @ref suit_storage_driver_t::match_offset function allows the manifest + * handler to check the component-offset condition against a storage backend. + * + * The usual call sequence by a manifest handler is: + * + * 1. @ref suit_storage_driver_t::init as on-boot initialization. + * 2. @ref suit_storage_driver_t::get_seq_no to determine if the manifest is + * not replayed. + * 3. @ref suit_storage_driver_t::has_location to determine if the backend + * handles the payload in the manifest. + * 4. @ref suit_storage_driver_t::set_active_location to set the active + * location for the payload. + * 5. @ref suit_storage_driver_t::start to start a payload write sequence. + * 6. At least one @ref suit_storage_driver_t::write calls to write the payload + * data. + * 7. @ref suit_storage_driver_t::finish to mark the end of the payload write. + * 8. @ref suit_storage_driver_t::read or @ref suit_storage_driver_t::read_ptr + * to read back the written payload. This to verify the digest of the + * payload with what is provided in the manifest. + * 9. @ref suit_storage_driver_t::install if the digest matches with what is + * expected and the payload can be installed or marked as valid, or: + * 10. @ref suit_storage_driver_t::erase if the digest does not match with what + * is expected and must be erased. + * 11. ref suit_storage_driver_t::set_seq_no to update the sequence number + * stored in the backend. + * + * @warning This API is by design not thread safe + */ + +#ifndef SUIT_STORAGE_H +#define SUIT_STORAGE_H + +#include "suit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forward declaration for storage struct + */ +typedef struct suit_storage suit_storage_t; + +/** + * @brief SUIT storage backend driver struct + */ +typedef struct suit_storage_driver { + + /** + * @brief One-time initialization function. Called at boot. + * + * @param[in] storage Storage context + */ + int (*init)(suit_storage_t *storage); + + /** + * @brief Start a new payload write sequence + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * @param[in] len Total size of the payload in bytes + * + * @returns @ref SUIT_OK on successfully starting the write + * @returns @ref suit_error_t on error + */ + int (*start)(suit_storage_t *storage, const suit_manifest_t *manifest, + size_t len); + + /** + * @brief Write a new chunk of the payload to the storage backend + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * @param[in] buf Buffer to read the payload chunk from + * @param[in] offset Offset to write at + * @param[in] len Length of the payload chunk + * + * @returns @ref SUIT_OK on successfully writing the chunk + * @returns @ref suit_error_t on error + */ + int (*write)(suit_storage_t *storage, const suit_manifest_t *manifest, const + uint8_t *buf, size_t offset, size_t len); + + /** + * @brief Signal that the payload write stage done to the storage backend + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * + * @returns @ref SUIT_OK on successfully finalizing the write + * @returns @ref suit_error_t on error + */ + int (*finish)(suit_storage_t *storage, const suit_manifest_t *manifest); + + /** + * @brief Read a chunk of previously written data back. + * + * @param[in] storage Storage context + * @param[out] buf Buffer to write the read data in + * @param[in] offset Offset to read from + * @param[in] len Number of bytes to read + * + * @returns @ref SUIT_OK on successfully reading the chunk + * @returns @ref suit_error_t on error + */ + int (*read)(suit_storage_t *storage, uint8_t *buf, size_t offset, + size_t len); + + /** + * @brief retrieve a direct read pointer for this storage backend + * + * @note Optional to implement + * + * @param[in] storage Storage context + * @param[out] buf Pointer to the location data + * @param[out] len Full length of the location data + * + * @returns @ref SUIT_OK on successfully providing the region + * @returns @ref suit_error_t on error + */ + int (*read_ptr)(suit_storage_t *storage, + const uint8_t **buf, size_t *len); + + /** + * @brief Install the payload or mark the payload as valid + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * + * @returns @ref SUIT_OK on successfully installing the payload + * @returns @ref suit_error_t on error + */ + int (*install)(suit_storage_t *storage, const suit_manifest_t *manifest); + + /** + * @brief Erase the previously loaded payload + * + * @param[in] storage Storage context + * + * @returns @ref SUIT_OK on successfully erasing the data + * @returns @ref suit_error_t on error + */ + int (*erase)(suit_storage_t *storage); + + /** + * @brief Check if this storage backend services a location + * + * @param[in] storage Storage context + * @param[in] location Location to check + * + * @returns True if this storage driver must be used for the + * supplied location + */ + bool (*has_location)(const suit_storage_t *storage, const char *location); + + /** + * @brief Checks if the supplied offset is true or false for the current + * location + * + * @note Optional to implement, should not be implemented if the backend + * doesn't support the image_offset + * + * @param[in] storage Storage context + * @param[in] offset Offset to check + * + * @returns True if the location matches the offset, + * @returns False otherwise + */ + bool (*match_offset)(const suit_storage_t *storage, size_t offset); + + /** + * @brief Set the active location of the storage handler + * + * A storage backend can handle multiple locations, e.g. a VFS backend + * targeting multiple files on a filesystem, setting the location selects + * the target location for writes or reads. + * + * @note Must be idempotent + * + * @param[in] storage Storage backend context + * @param[in] location The location supplied as string with components + * separated by the + * @ref suit_storage_driver_t::separator + * + * @returns SUIT_OK on success + * @returns SUIT_ERR_STORAGE_UNAVAILABLE if the location is not + * available. + */ + int (*set_active_location)(suit_storage_t *storage, const char *location); + + /** + * @brief Retrieve the sequence number from the storage backend + * + * @note The sequence number must be global to the storage context, it must + * not depend on the location + * + * @param[in] storage Storage context + * @param[out] seq_no Retrieved sequence number + * + * @returns SUIT_OK on success + * @returns @ref suit_error_t if the sequence number can't be retrieved + */ + int (*get_seq_no)(const suit_storage_t *storage, uint32_t *seq_no); + + /** + * @brief Set a new sequence number in the storage backend. + * + * @param[in] storage Storage context + * @param[in] seq_no Sequence number to store + * + * @returns SUIT_OK on success + * @returns @ref suit_error_t if the sequence number can't be stored. + */ + int (*set_seq_no)(suit_storage_t *storage, uint32_t seq_no); + + /** + * @brief Component ID separator used by this storage driver. + */ + char separator; +} suit_storage_driver_t; + +/** + * @brief Generic storage backend state. + */ +struct suit_storage { + const suit_storage_driver_t *driver; /**< Storage driver functions */ +}; + +/** + * @brief retrieve a storage backend based on the location ID string + * + * @param[in] id ID string to match + * + * @returns The first storage driver that handles this ID + * @returns NULL if none of the storage drivers is able to handle this ID. + */ +suit_storage_t *suit_storage_find_by_id(const char *id); + +/** + * @brief retrieve a storage backend based on the suit component + * + * @param[in] manifest SUIT manifest context + * @param[in] component Component to find a storage backend for + * + * @returns The first storage driver that handles this component + * @returns NULL if none of the storage drivers is able to handle this + * component. + */ +suit_storage_t *suit_storage_find_by_component(const suit_manifest_t *manifest, + const suit_component_t *component); + +/** + * @brief initialize all storage backends + */ +void suit_storage_init_all(void); + +/** + * @brief Get the highest sequence number among available backends + * + * @param[out] seq_no Retrieved sequence number + * + * @returns suit_ok if at least one sequence number is retrieved + * @returns @ref suit_error_t on error + */ +int suit_storage_get_highest_seq_no(uint32_t *seq_no); + +/** + * @brief Set the new sequence number on all available backends + * + * @param[in] seq_no Sequence number to store + * + * @returns @ref SUIT_OK on successfully storing the sequence number on at + * least one backend + * @returns @ref suit_error_t on error + */ +int suit_storage_set_seq_no_all(uint32_t seq_no); + +/** + * @name Storage driver helper functions + * + * For easy access to the @ref suit_storage_driver_t functions. + * @{ + */ + +/** + * @brief get the separator for a storage backend + * + * @param[in] storage Storage context + * + * @returns The separator char + */ +static inline char suit_storage_get_separator(const suit_storage_t *storage) +{ + return storage->driver->separator; +} + +/** + * @brief Check if the storage backend implements the @ref + * suit_storage_driver_t::read_ptr function + * + * @param[in] storage Storage context + * + * @returns True if the function is implemented, + * @returns False otherwise + */ +static inline bool suit_storage_has_readptr(const suit_storage_t *storage) +{ + return (storage->driver->read_ptr); +} + +/** + * @brief Check if the storage backend implements the @ref + * suit_storage_driver_t::match_offset function + * + * @param[in] storage Storage context + * + * @returns True if the function is implemented, + * @returns False otherwise + */ +static inline bool suit_storage_has_offset(const suit_storage_t *storage) +{ + return (storage->driver->match_offset); +} + +/** + * @brief One-time initialization function. Called at boot. + * + * @param[in] storage Storage context + */ +static inline int suit_storage_init(suit_storage_t *storage) +{ + return storage->driver->init(storage); +} + +/** + * @brief Start a new payload write sequence + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * @param[in] len Total size of the payload in bytes + * + * @returns @ref SUIT_OK on successfully starting the write + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_start(suit_storage_t *storage, + const suit_manifest_t *manifest, + size_t len) +{ + return storage->driver->start(storage, manifest, len); +} + +/** + * @brief Write a new chunk of the payload to the storage backend + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * @param[in] buf Buffer to read the payload chunk from + * @param[in] offset Offset to write at + * @param[in] len Length of the payload chunk + * + * @returns @ref SUIT_OK on successfully writing the chunk + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_write(suit_storage_t *storage, + const suit_manifest_t *manifest, + const uint8_t *buf, size_t offset, + size_t len) +{ + return storage->driver->write(storage, manifest, buf, offset, len); +} + +/** + * @brief Signal that the payload write stage done to the storage backend + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * + * @returns @ref SUIT_OK on successfully finalizing the write + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_finish(suit_storage_t *storage, + const suit_manifest_t *manifest) +{ + return storage->driver->finish(storage, manifest); +} + +/** + * @brief Read a chunk of previously written data back. + * + * @param[in] storage Storage context + * @param[out] buf Buffer to write the read data in + * @param[in] offset Offset to read from + * @param[in] len Number of bytes to read + * + * @returns @ref SUIT_OK on successfully reading the chunk + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_read(suit_storage_t *storage, uint8_t *buf, + size_t offset, size_t len) +{ + return storage->driver->read(storage, buf, offset, len); +} + +/** + * @brief retrieve a direct read pointer for this storage backend + * + * @note Optional to implement + * + * @param[in] storage Storage context + * @param[out] buf Pointer to the location data + * @param[out] len Full length of the location data + * + * @returns @ref SUIT_OK on successfully providing the region + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_read_ptr(suit_storage_t *storage, const uint8_t + **buf, size_t *len) +{ + return storage->driver->read_ptr(storage, buf, len); +} + +/** + * @brief Install the payload or mark the payload as valid + * + * @param[in] storage Storage context + * @param[in] manifest The suit manifest context + * + * @returns @ref SUIT_OK on successfully installing the payload + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_install(suit_storage_t *storage, + const suit_manifest_t *manifest) +{ + return storage->driver->install(storage, manifest); +} + +/** + * @brief Erase the previously loaded payload + * + * @param[in] storage Storage context + * + * @returns @ref SUIT_OK on successfully erasing the data + * @returns @ref suit_error_t on error + */ +static inline int suit_storage_erase(suit_storage_t *storage) +{ + return storage->driver->erase(storage); +} + +/** + * @brief Check if this storage backend services a location + * + * @param[in] storage Storage context + * @param[in] location Location to check + * + * @returns True if this storage driver must be used for the + * supplied location + */ +static inline bool suit_storage_has_location(suit_storage_t *storage, + const char *location) +{ + return storage->driver->has_location(storage, location); +} + +/** + * @brief Checks if the supplied offset is true or false for the current + * location + * + * @note Optional to implement, should not be implemented if the backend + * doesn't support the image_offset + * + * @param[in] storage Storage context + * @param[in] offset Offset to check + * + * @returns True if the location matches the offset, + * @returns False otherwise + */ +static inline int suit_storage_match_offset(const suit_storage_t *storage, + size_t offset) +{ + return storage->driver->match_offset(storage, offset); +} + +/** + * @brief Set the active location of the storage handler + * + * A storage backend can handle multiple locations, e.g. a VFS backend + * targeting multiple files on a filesystem, setting the location selects + * the target location for writes or reads. + * + * @note Must be idempotent + * + * @param[in] storage Storage backend context + * @param[in] location The location supplied as string with components + * separated by the + * @ref suit_storage_driver_t::separator + * + * @returns SUIT_OK on success + * @returns SUIT_ERR_STORAGE_UNAVAILABLE if the location is not + * available. + */ +static inline int suit_storage_set_active_location(suit_storage_t *storage, + const char *location) +{ + return storage->driver->set_active_location(storage, location); +} + +/** + * @brief Retrieve the sequence number from the storage backend + * + * @note The sequence number must be global to the storage context, it must + * not depend on the location + * + * @param[in] storage Storage context + * @param[out] seq_no Retrieved sequence number + * + * @returns SUIT_OK on success + * @returns @ref suit_error_t if the sequence number can't be retrieved + */ +static inline int suit_storage_get_seq_no(const suit_storage_t *storage, + uint32_t *seq_no) +{ + return storage->driver->get_seq_no(storage, seq_no); +} + +/** + * @brief Set a new sequence number in the storage backend. + * + * @param[in] storage Storage context + * @param[in] seq_no Sequence number to store + * + * @returns SUIT_OK on success + * @returns @ref suit_error_t if the sequence number can't be stored. + */ +static inline int suit_storage_set_seq_no(suit_storage_t *storage, + uint32_t seq_no) +{ + return storage->driver->set_seq_no(storage, seq_no); +} +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SUIT_STORAGE_H */ +/** @} */ diff --git a/sys/suit/Makefile b/sys/suit/Makefile index c763337223..b630980f31 100644 --- a/sys/suit/Makefile +++ b/sys/suit/Makefile @@ -2,4 +2,8 @@ ifneq (,$(filter suit_transport_%,$(USEMODULE))) DIRS += transport endif +ifneq (,$(filter suit_storage_%,$(USEMODULE))) + DIRS += storage +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/suit/handlers.c b/sys/suit/handlers.c index 3bb68bba65..7f224ae205 100644 --- a/sys/suit/handlers.c +++ b/sys/suit/handlers.c @@ -55,6 +55,48 @@ void suit_param_cbor_to_ref(const suit_manifest_t *manifest, ref->offset = val->cur - manifest->buf; } +int suit_component_name_to_string(const suit_manifest_t *manifest, + const suit_component_t *component, + char separator, char *buf, size_t buf_len) +{ + assert(buf_len); + nanocbor_value_t comp_id, arr; + suit_param_ref_to_cbor(manifest, &component->identifier, &comp_id); + + if (nanocbor_enter_array(&comp_id, &arr) < 0) { + return SUIT_ERR_INVALID_MANIFEST; + } + + size_t pos = 0; + while (!nanocbor_at_end(&arr)) { + const uint8_t *bstr; + size_t bstr_len; + + if (separator) { + buf[pos++] = separator; + } + + if (nanocbor_get_bstr(&arr, &bstr, &bstr_len) < 0) { + return SUIT_ERR_INVALID_MANIFEST; + } + + if ((buf_len - pos - 1) < bstr_len) { + /* No space */ + return SUIT_ERR_NO_MEM; + } + + memcpy(&buf[pos], bstr, bstr_len); + pos += bstr_len; + } + + buf[pos] = '\0'; + + LOG_INFO("Formatted component name: %s\n", buf); + + return SUIT_OK; + +} + int suit_handle_manifest_structure(suit_manifest_t *manifest, nanocbor_value_t *it, const suit_manifest_handler_t *handlers, diff --git a/sys/suit/storage.c b/sys/suit/storage.c new file mode 100644 index 0000000000..a4be3f3c52 --- /dev/null +++ b/sys/suit/storage.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * 2020 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_suit_storage + * @{ + * + * @file + * @brief SUIT storage backend helpers + * + * @author Koen Zandberg + * + * @} + */ + +#include +#include "kernel_defines.h" + +#include "suit.h" +#include "suit/storage.h" + +#ifdef MODULE_SUIT_STORAGE_FLASHWRITE +#include "suit/storage/flashwrite.h" +extern suit_storage_flashwrite_t suit_storage_flashwrite; +#endif + +#ifdef MODULE_SUIT_STORAGE_RAM +#include "suit/storage/ram.h" +extern suit_storage_ram_t suit_storage_ram; +#endif + +static suit_storage_t *reg[] = { +#ifdef MODULE_SUIT_STORAGE_FLASHWRITE + &suit_storage_flashwrite.storage, +#endif +#ifdef MODULE_SUIT_STORAGE_RAM + &suit_storage_ram.storage, +#endif +}; + +static const size_t reg_size = ARRAY_SIZE(reg); + +suit_storage_t *suit_storage_find_by_id(const char *id) +{ + for (size_t i = 0; i < reg_size; i++) { + if (suit_storage_has_location(reg[i], id)) { + return reg[i]; + } + } + return NULL; +} + +void suit_storage_init_all(void) +{ + for (size_t i = 0; i < reg_size; i++) { + suit_storage_init(reg[i]); + } +} + +suit_storage_t *suit_storage_find_by_component(const suit_manifest_t *manifest, + const suit_component_t *component) +{ + for (size_t i = 0; i < reg_size; i++) { + char name[CONFIG_SUIT_COMPONENT_MAX_NAME_LEN]; + if (suit_component_name_to_string(manifest, component, + reg[i]->driver->separator, + name, sizeof(name)) == SUIT_OK) { + + if (suit_storage_has_location(reg[i], name)) { + return reg[i]; + } + } + } + return NULL; +} + +int suit_storage_get_highest_seq_no(uint32_t *seq_no) +{ + uint32_t max_seq = 0; + int res = SUIT_ERR_STORAGE; + + for (size_t i = 0; i < reg_size; i++) { + uint32_t seq_no = 0; + if (suit_storage_get_seq_no(reg[i], &seq_no) == SUIT_OK) { + res = SUIT_OK; + if (seq_no > max_seq) { + max_seq = seq_no; + } + } + } + *seq_no = max_seq; + return res; +} + +int suit_storage_set_seq_no_all(uint32_t seq_no) +{ + for (size_t i = 0; i < reg_size; i++) { + suit_storage_set_seq_no(reg[i], seq_no); + } + return 0; +} diff --git a/sys/suit/storage/Makefile b/sys/suit/storage/Makefile new file mode 100644 index 0000000000..6494b1ad4a --- /dev/null +++ b/sys/suit/storage/Makefile @@ -0,0 +1,5 @@ +MODULE := suit_storage +SUBMODULES := 1 +BASE_MODULE := suit_storage + +include $(RIOTBASE)/Makefile.base