1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

suit: Introduce generic storage backend

This commit introduces a common storage backend for SUIT manifest
payloads. Different backends can be compiled into a single firmware.
Degending on the component name in the SUIT manifest, a storage backend
is selected by the parser.
This commit is contained in:
Koen Zandberg 2020-09-28 22:59:24 +02:00
parent ea007ada05
commit 7742750abd
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
8 changed files with 810 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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
*

615
sys/include/suit/storage.h Normal file
View File

@ -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 <koen@bergzand.net>
*
* 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 */
/** @} */

View File

@ -2,4 +2,8 @@ ifneq (,$(filter suit_transport_%,$(USEMODULE)))
DIRS += transport
endif
ifneq (,$(filter suit_storage_%,$(USEMODULE)))
DIRS += storage
endif
include $(RIOTBASE)/Makefile.base

View File

@ -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,

107
sys/suit/storage.c Normal file
View File

@ -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 <koen@bergzand.net>
*
* @}
*/
#include <string.h>
#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;
}

View File

@ -0,0 +1,5 @@
MODULE := suit_storage
SUBMODULES := 1
BASE_MODULE := suit_storage
include $(RIOTBASE)/Makefile.base