1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2026-01-01 01:41:18 +01:00

Merge pull request #17943 from fjmolinas/pr_suit_vfs_storage

sys/suit/storage/vfs: initial import
This commit is contained in:
benpicco 2022-06-07 09:33:37 +02:00 committed by GitHub
commit 6019925c75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 575 additions and 69 deletions

View File

@ -59,6 +59,7 @@ DEFAULT_MODULE += test_utils_interactive_sync
ifeq ($(BOARD),native)
USE_ETHOS ?= 0
IFACE ?= tapbr0
# Configure two RAM regions with 2K each
CFLAGS += -DCONFIG_SUIT_STORAGE_RAM_REGIONS=2 -DCONFIG_SUIT_STORAGE_RAM_SIZE=2048
endif
@ -82,9 +83,9 @@ ifeq (1,$(USE_ETHOS))
# $ cd dist/tools/ethos; sudo ./setup_network.sh riot0 2001:db8::0/64
#
#... in another shell and keep it running.
export TAP ?= riot0
IFACE ?= riot0
TERMPROG = $(RIOTTOOLS)/ethos/ethos
TERMFLAGS = $(TAP) $(PORT)
TERMFLAGS = $(IFACE) $(PORT)
endif
# Ensure both slot bin files are always generated and linked to avoid compiling
@ -105,8 +106,16 @@ TESTRUNNER_RESET_AFTER_TERM ?= 1
# with ed25519 support.
TEST_ON_CI_BLACKLIST = all
# Add custom SUIT targets
include $(CURDIR)/Makefile.suit.custom
include $(RIOTBASE)/Makefile.include
# export IFACE for test
$(call target-export-variables,test-with-config test-with-config/check-config,IFACE)
# export BOARD
$(call target-export-variables,test-with-config test-with-config/check-config,BOARD)
# allow to use large blocks to utilize large MTUs (802.15.4g, Ethernet, WiFi)
LARGE_BLOCKS ?= 0
ifeq (1, $(LARGE_BLOCKS))

View File

@ -1,6 +1,14 @@
ifeq ($(BOARD),native)
USEMODULE += suit_storage_ram
USEMODULE += netdev_default
# Use VFS storage for native
USEMODULE += suit_storage_vfs
## Use VFS
USEMODULE += vfs
## Use default storage
USEMODULE += vfs_default
## Auto-format on mount
USEMODULE += vfs_auto_format
else
USEMODULE += suit_storage_flashwrite
endif

View File

@ -0,0 +1,31 @@
# Required variables defined in riotboot.inc.mk or Makefile.include
BINDIR_APP = $(CURDIR)/bin/$(BOARD)/$(APPLICATION)
$(BINDIR_APP): $(CLEAN)
$(Q)mkdir -p $(BINDIR_APP)
# Include to be able to use memoized
include $(RIOTBASE)/makefiles/utils/variables.mk
EPOCH = $(call memoized,EPOCH,$(shell date +%s))
APP_VER ?= $(EPOCH)
# Default addressing if following README.native.md
ifeq ($(BOARD),native)
SUIT_CLIENT ?= [2001:db8::2]
SUIT_COAP_SERVER ?= [2001:db8::1]
$(call target-export-variables,test-with-config,SUIT_COAP_SERVER)
endif
ifeq ($(BOARD),native)
# Set settings for publishing fake fw payloads to native
SUIT_NATIVE_PAYLOAD ?= "AABBCCDD"
SUIT_NATIVE_PAYLOAD_BIN ?= $(BINDIR_APP)/fw.$(APP_VER).bin
# Make sure it is built
BUILD_FILES += $(SUIT_NATIVE_PAYLOAD_BIN)
$(SUIT_NATIVE_PAYLOAD_BIN): $(BINDIR_APP)
$(Q)echo $(SUIT_NATIVE_PAYLOAD) > $@
SUIT_FW_STORAGE ?= /nvm0/SLOT0.TXT
SUIT_MANIFEST_PAYLOADS ?= $(SUIT_NATIVE_PAYLOAD_BIN)
SUIT_MANIFEST_SLOTFILES ?= $(SUIT_NATIVE_PAYLOAD_BIN):0:$(SUIT_FW_STORAGE)
endif

View File

@ -48,7 +48,7 @@ $ dist/tools/suit/suit-manifest-generator/bin/suit-tool sign -k keys/default.pem
5. Pull the manifest from the native instance:
```
> suit coap://[2001:db8::1]/suit_manifest.signed
> suit fetch coap://[2001:db8::1]/suit_manifest.signed
```
6. Verify the content of the storage location
@ -172,14 +172,18 @@ the payloads.
lsstorage
RAM slot 0: ".ram.0"
RAM slot 1: ".ram.1"
VFS 0: "/nvm0/SLOT0.txt"
VFS 1: "/nvm0/SLOT1.txt"
```
As shown above, two storage locations are available, `.ram.0` and `.ram.1`.
While two slots are available, in this example only the content of the `.ram.0`
slot will be updated.
As shown above, four storage locations are available, RAM based storage: `.ram.0` and `.ram.1`
as well as VFS based storage: `/nvm0/SLOT0.TXT` and `/nvm0/SLOT1.TXT`.
While multiple slots are available, in this example only the content of the `.ram.0`
slot will be updated, but the procedure is the same for any other slot, just replace
`.ram.0
- The `storage_content` command can be used to display a hex dump command of one
of the storage locations. It requires a location string, an offset and a
of the RAM storage locations. It requires a location string, an offset and a
number of bytes to print:
```console
@ -188,6 +192,13 @@ slot will be updated.
```
As the storage location is empty on boot, nothing is printed.
For VFS based storage the `vfs` command can be used instead:
```console
> vfs r /nvm0/SLOT0.txt
Error opening file "/nvm0/SLOT0.txt": -ENOENT
```
But as the file does not initially exist, nothing is printed.
### Generating the payload and manifest
[generating-the-payload-and-manifest]: #generating-the-payload-and-manifest
@ -208,6 +219,12 @@ acts as a template for the real SUIT manifest. Within RIOT, the script
$ dist/tools/suit/gen_manifest.py --urlroot coap://[2001:db8::1]/ --seqnr 1 -o suit.tmp coaproot/payload.bin:0:ram:0
```
or for vfs storage:
```console
$ dist/tools/suit/gen_manifest.py --urlroot coap://[2001:db8::1]/ --seqnr 1 -o suit.tmp coaproot/payload.bin:0:/nvm0/SLOT0.txt
```
This generates a suit manifest template with the sequence number set to `1`, a
payload that should be stored at slot offset zero in slot `.ram.0`. The url for
the payload starts with `coap://[fe80::4049:bfff:fe60:db09]/`. Make sure to
@ -294,7 +311,7 @@ command sequences in the manifest and download the payload when instructed to.
The URL for the manifest can be supplied to the instance via the command line.
```console
> suit coap://[2001:db8::1]/suit_manifest.signed
> suit fetch coap://[2001:db8::1]/suit_manifest.signed
```
The payload is the full URL to the signed manifest. The native instance should
@ -338,6 +355,14 @@ same payload as suggested above was used, it should look like this:
41414242434344440A
```
The process can be done multiple times with both slot `.ram.0` and `.ram.1` and
Or for vfs storage:
```
> vfs r /nvm0/SLOT0.txt
vfs r /nvm0/SLOT0.txt
00000000: 4141 4242 4343 4444 0a AABBCCDD.
-- EOF --
```
The process can be done multiple times for any of the slots and
different payloads. Keep in mind that the sequence number is a strict
monotonically number and must be increased after every update.

View File

@ -33,6 +33,11 @@
#include "suit/storage.h"
#include "suit/storage/ram.h"
#ifdef BOARD_NATIVE
#include "suit/storage/vfs.h"
#include "xfa.h"
#include "vfs_default.h"
#endif
#ifdef MODULE_PERIPH_GPIO
#include "periph/gpio.h"
@ -48,6 +53,15 @@ static msg_t _nanocoap_server_msg_queue[NANOCOAP_SERVER_QUEUE_SIZE];
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
/* add handled storages */
#if IS_USED(MODULE_SUIT_STORAGE_VFS)
XFA_USE(char*, suit_storage_files_reg);
#ifdef BOARD_NATIVE
XFA(suit_storage_files_reg, 0) char* _slot0 = VFS_DEFAULT_DATA "/SLOT0.txt";
XFA(suit_storage_files_reg, 1) char* _slot1 = VFS_DEFAULT_DATA "/SLOT1.txt";
#endif
#endif
static void *_nanocoap_server_thread(void *arg)
{
(void)arg;
@ -162,6 +176,12 @@ static int cmd_lsstorage(int argc, char **argv)
if (IS_ACTIVE(MODULE_SUIT_STORAGE_FLASHWRITE)) {
puts("Flashwrite slot 0: \"\"\n");
}
#if IS_USED(MODULE_SUIT_STORAGE_VFS)
for (unsigned i = 0; i < XFA_LEN(char **, suit_storage_files_reg); i++) {
const char *filepath = (const char *)suit_storage_files_reg[i];
printf("VFS %u: \"%s\"\n", i, filepath);
}
#endif
return 0;
}
@ -189,7 +209,8 @@ int main(void)
cmd_print_current_slot(0, NULL);
cmd_print_riotboot_hdr(0, NULL);
#endif
/* initialize suit storage */
suit_storage_init_all();
/* start suit coap updater thread */
suit_coap_run();

View File

@ -11,21 +11,39 @@ import subprocess
import sys
import tempfile
import time
import re
import random
from ipaddress import (
IPv6Address,
IPv6Network,
)
from testrunner import run
from testrunner import utils
# Default test over loopback interface
COAP_HOST = "[fd00:dead:beef::1]"
COAP_HOST = os.getenv("SUIT_COAP_SERVER", "[fd00:dead:beef::1]")
BOARD = os.getenv("BOARD", "samr21-xpro")
UPDATING_TIMEOUT = 10
MANIFEST_TIMEOUT = 15
USE_ETHOS = int(os.getenv("USE_ETHOS", "1"))
TAP = os.getenv("TAP", "riot0")
IFACE = os.getenv("IFACE", "tapbr0")
TMPDIR = tempfile.TemporaryDirectory()
def get_iface_addr(iface):
out = subprocess.check_output(["ip", "a", "s", "dev", iface]).decode()
p = re.compile(
r"inet6\s+(?P<global>[0-9a-fA-F:]+:[A-Fa-f:0-9]+/\d+)\s+" r"scope\s+global"
)
for line in out.splitlines():
m = p.search(line)
if m is not None:
return m.group("global")
return None
def start_aiocoap_fileserver():
aiocoap_process = subprocess.Popen(
"exec aiocoap-fileserver %s" % TMPDIR.name, shell=True
@ -51,7 +69,7 @@ def notify(coap_server, client_url, version=None):
assert not subprocess.call(cmd)
def publish(server_dir, server_url, app_ver, keys='default', latest_name=None):
def publish(server_dir, server_url, app_ver, keys="default", latest_name=None):
cmd = [
"make",
"suit/publish",
@ -68,14 +86,15 @@ def publish(server_dir, server_url, app_ver, keys='default', latest_name=None):
def wait_for_update(child):
return child.expect([r"Fetching firmware \|[█ ]+\|\s+\d+\%",
"Finalizing payload store"],
timeout=UPDATING_TIMEOUT)
return child.expect(
[r"Fetching firmware \|[█ ]+\|\s+\d+\%", "Finalizing payload store"],
timeout=UPDATING_TIMEOUT,
)
def get_ipv6_addr(child):
child.expect_exact('>')
child.sendline('ifconfig')
child.expect_exact(">")
child.sendline("ifconfig")
if USE_ETHOS == 0:
# Get device global address
child.expect(
@ -87,10 +106,9 @@ def get_ipv6_addr(child):
# Get device local address
child.expect_exact("Link type: wired")
child.expect(
r"inet6 addr: (?P<lladdr>[0-9a-fA-F:]+:[A-Fa-f:0-9]+)"
" scope: link VAL"
r"inet6 addr: (?P<lladdr>[0-9a-fA-F:]+:[A-Fa-f:0-9]+)" " scope: link VAL"
)
addr = "{}%{}".format(child.match.group("lladdr").lower(), TAP)
addr = "{}%{}".format(child.match.group("lladdr").lower(), IFACE)
return addr
@ -117,7 +135,20 @@ def get_reachable_addr(child):
# Give some time for the network interface to be configured
time.sleep(1)
# Get address
client_addr = get_ipv6_addr(child)
if BOARD == "native":
iface_addr = get_iface_addr(IFACE)
network = IPv6Network(iface_addr, strict=False)
client_addr = iface_addr
while iface_addr == client_addr:
client_addr = IPv6Address(
random.randrange(
int(network.network_address) + 1, int(network.broadcast_address) - 1
)
)
child.sendline(f"ifconfig 5 add {client_addr}/{format(network.prefixlen)}")
client_addr = format(client_addr)
else:
client_addr = get_ipv6_addr(child)
# Verify address is reachable
ping6(client_addr)
return "[{}]".format(client_addr)
@ -127,7 +158,7 @@ def seq_no(child):
utils.test_utils_interactive_sync_shell(child, 5, 1)
# get version of currently running image
# "seq_no: 0x00000000"
child.sendline('suit seq_no')
child.sendline("suit seq_no")
child.expect(r"seq_no: (?P<seq_no>0x[0-9a-fA-F:]+)\r\n")
app_ver = int(child.match.group("seq_no"), 16)
return app_ver
@ -136,7 +167,7 @@ def seq_no(child):
def running_slot(child):
utils.test_utils_interactive_sync_shell(child, 5, 1)
child.sendline('current-slot')
child.sendline("current-slot")
child.expect(r"Running from slot (\d+)\r\n")
slot = int(child.match.group(1))
return slot
@ -151,7 +182,7 @@ def _test_invalid_version(child, client, app_ver):
def _test_invalid_signature(child, client, app_ver):
publish(TMPDIR.name, COAP_HOST, app_ver + 1, 'invalid_keys')
publish(TMPDIR.name, COAP_HOST, app_ver + 1, "invalid_keys")
notify(COAP_HOST, client, app_ver + 1)
child.expect_exact("suit_coap: trigger received")
child.expect_exact("suit: verifying manifest signature")
@ -166,25 +197,26 @@ def _test_successful_update(child, client, app_ver):
child.expect_exact("suit_coap: trigger received")
child.expect_exact("suit: verifying manifest signature")
child.expect(
r"riotboot_flashwrite: initializing update to target slot (\d+)\r\n",
r"SUIT policy check OK.\r\n",
timeout=MANIFEST_TIMEOUT,
)
target_slot = int(child.match.group(1))
# Wait for update to complete
while wait_for_update(child) == 0:
pass
# Check successful install
child.expect_exact("Install correct payload")
child.expect_exact("Install correct payload")
# Wait for reboot
child.expect_exact("suit_coap: rebooting...")
# Verify running slot
current_slot = running_slot(child)
assert target_slot == current_slot, "BOOTED FROM SAME SLOT"
# Verify client is reachable and get address
client = get_reachable_addr(child)
# Wait for reboot on non-native BOARDs
if BOARD != "native":
child.expect_exact("suit_coap: rebooting...")
# Verify client is reachable and get address
client = get_reachable_addr(child)
assert seq_no(child) == version
def _test_suit_command_is_there(child):
child.sendline('suit')
child.sendline("suit")
child.expect_exact("Usage: suit fetch <manifest url>")

View File

@ -0,0 +1,36 @@
#! /bin/sh
#
# check_config.sh
# Copyright (C) 2021 Martine Lenders <mail@martine-lenders.eu>
# Copyright (C) 2022 Inria
#
# Distributed under terms of the MIT license.
#
ip link show dev "${IFACE}" > /dev/null
RESULT=$?
if [ $RESULT -eq 0 ]; then
if [ "${BOARD}" = "native" ]; then
IFACE_IPV6_ADDR=$(ip -6 addr show dev "${IFACE}"| grep inet6 | \
awk -F '[ \t]+|/' '{print $3}' | grep -v ^::1 | \
grep -v ^fe80)
if [ -n "${IFACE_IPV6_ADDR}" ]; then
exit 0
fi
else
exit 0
fi
fi
if [ "${BOARD}" = "native" ]; then
echo "You may be able to create \"${IFACE}\" by using e.g." \
"\`${RIOTTOOLS#${RIOTBASE}/}/tapsetup/tapsetup\`."
echo "You can add a routable IPV6 address by using e.g." \
"sudo ip address add 2001:db8::1/64 dev ${IFACE}"
else
echo "You may setup \"${IFACE}\" by using e.g." \
"\`${RIOTTOOLS#${RIOTBASE}/}/ethos/setup_network.sh ${IFACE} 2001:db8::/64\`"
fi
exit 1

View File

@ -870,6 +870,11 @@ ifneq (,$(filter suit_storage_flashwrite, $(USEMODULE)))
USEMODULE += riotboot_flashwrite_verify_sha256
endif
ifneq (,$(filter suit_storage_vfs,$(USEMODULE)))
USEMODULE += vfs
USEMODULE += mtd
endif
ifneq (,$(filter suit_%,$(USEMODULE)))
USEMODULE += suit
endif

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2022 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_vfs riotboot vfs storage backend
* @ingroup sys_suit_storage
* @brief SUIT riotboot firmware storage backend
*
* VFS storage can service different files mounted on the filesystem. Serviceable
* FILES must be registered:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* #include "suit/storage/vfs.h"
* #include "xfa.h"
*
* XFA_USE(char*, suit_storage_files_reg);
* XFA(suit_storage_files_reg, 0) char* _firmware_0 = VFS_DEFAULT_DATA "/FW0.TXT";
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Once registered its content may be securely updated via SUIT by specifying the
* "install-id" as the filepath.
*
* @{
*
* @brief riotboot vfs storage backend functions for SUIT manifests
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef SUIT_STORAGE_VFS_H
#define SUIT_STORAGE_VFS_H
#include "suit.h"
#include "../../sys/include/vfs.h"
#include "vfs_default.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Storage location string separators
*/
#ifndef CONFIG_SUIT_STORAGE_MOUNT_POINT
#define CONFIG_SUIT_STORAGE_MOUNT_POINT VFS_DEFAULT_DATA
#endif
/**
* @brief Storage location string separators
*/
#ifndef CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION
#define CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION (CONFIG_SUIT_STORAGE_MOUNT_POINT "/SEQNO.txt")
#endif
/**
* @brief riotboot vfs SUIT storage context
*/
typedef struct {
suit_storage_t storage; /**< parent struct */
const char **files; /**< storage file array */
uint8_t active_region; /**< Active file idx to write to */
} suit_storage_vfs_t;
#ifdef __cplusplus
}
#endif
#endif /* SUIT_STORAGE_VFS_H */
/** @} */

View File

@ -22,6 +22,7 @@
#include "kernel_defines.h"
#include "log.h"
#include "xfa.h"
#include "suit.h"
#include "suit/storage.h"
@ -29,6 +30,8 @@
#include "riotboot/flashwrite.h"
#include "riotboot/slot.h"
XFA_USE(suit_storage_t, suit_storage_reg);
static inline suit_storage_flashwrite_t *_get_fw(suit_storage_t *storage)
{
return container_of(storage, suit_storage_flashwrite_t, storage);
@ -234,8 +237,10 @@ static const suit_storage_driver_t suit_storage_flashwrite_driver = {
.separator = '\0',
};
suit_storage_flashwrite_t suit_storage_flashwrite = {
static suit_storage_flashwrite_t suit_storage_flashwrite = {
.storage = {
.driver = &suit_storage_flashwrite_driver,
},
};
XFA(suit_storage_reg, 0) suit_storage_t* suit_storage_flashwrite_ptr = &suit_storage_flashwrite.storage;

View File

@ -24,11 +24,14 @@
#include "fmt.h"
#include "kernel_defines.h"
#include "log.h"
#include "xfa.h"
#include "suit.h"
#include "suit/storage.h"
#include "suit/storage/ram.h"
XFA_USE(suit_storage_t, suit_storage_reg);
static inline suit_storage_ram_t *_get_ram(suit_storage_t *storage)
{
return container_of(storage, suit_storage_ram_t, storage);
@ -227,3 +230,5 @@ suit_storage_ram_t suit_storage_ram = {
.driver = &suit_storage_ram_driver,
},
};
XFA(suit_storage_reg, 0) suit_storage_t* suit_storage_ram_ptr = &suit_storage_ram.storage;

View File

@ -25,32 +25,15 @@
#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
#include "xfa.h"
#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);
XFA_INIT(suit_storage_t*, suit_storage_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];
for (size_t i = 0; i < XFA_LEN(suit_storage_t*, suit_storage_reg); i++) {
if (suit_storage_has_location(suit_storage_reg[i], id)) {
return suit_storage_reg[i];
}
}
return NULL;
@ -58,22 +41,22 @@ suit_storage_t *suit_storage_find_by_id(const char *id)
void suit_storage_init_all(void)
{
for (size_t i = 0; i < reg_size; i++) {
suit_storage_init(reg[i]);
for (size_t i = 0; i < XFA_LEN(suit_storage_t*, suit_storage_reg); i++) {
suit_storage_init(suit_storage_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++) {
for (size_t i = 0; i < XFA_LEN(suit_storage_t*, suit_storage_reg); i++) {
char name[CONFIG_SUIT_COMPONENT_MAX_NAME_LEN];
if (suit_component_name_to_string(manifest, component,
reg[i]->driver->separator,
suit_storage_reg[i]->driver->separator,
name, sizeof(name)) == SUIT_OK) {
if (suit_storage_has_location(reg[i], name)) {
return reg[i];
if (suit_storage_has_location(suit_storage_reg[i], name)) {
return suit_storage_reg[i];
}
}
}
@ -85,9 +68,9 @@ 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++) {
for (size_t i = 0; i < XFA_LEN(suit_storage_t*, suit_storage_reg); i++) {
uint32_t seq_no = 0;
if (suit_storage_get_seq_no(reg[i], &seq_no) == SUIT_OK) {
if (suit_storage_get_seq_no(suit_storage_reg[i], &seq_no) == SUIT_OK) {
res = SUIT_OK;
if (seq_no > max_seq) {
max_seq = seq_no;
@ -100,8 +83,8 @@ int suit_storage_get_highest_seq_no(uint32_t *seq_no)
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);
for (size_t i = 0; i < XFA_LEN(suit_storage_t*, suit_storage_reg); i++) {
suit_storage_set_seq_no(suit_storage_reg[i], seq_no);
}
return 0;
}

274
sys/suit/storage/vfs.c Normal file
View File

@ -0,0 +1,274 @@
/*
* Copyright (C) 2022 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 vfs storage module implementation
*
*
* @}
*/
#include <string.h>
#include <inttypes.h>
#include <fcntl.h>
#include "board.h"
#include "xfa.h"
#include "fmt.h"
#include "kernel_defines.h"
#include "log.h"
#include "vfs.h"
#include "suit.h"
#include "suit/storage.h"
#include "suit/storage/vfs.h"
XFA_USE(suit_storage_t, suit_storage_reg);
XFA_INIT(char *, suit_storage_files_reg);
static inline suit_storage_vfs_t *_get_vfs(suit_storage_t *storage)
{
return container_of(storage, suit_storage_vfs_t, storage);
}
static inline const suit_storage_vfs_t *_get_vfs_const(const suit_storage_t *storage)
{
return container_of(storage, suit_storage_vfs_t, storage);
}
static inline const char *_get_active_file(suit_storage_vfs_t *vfs)
{
return vfs->files[vfs->active_region];
}
static int _vfs_update_seq_no(uint32_t seq_no)
{
char buf[16];
uint32_t sequence_no = 0;
int res = SUIT_ERR_SEQUENCE_NUMBER;
int fd = vfs_open(CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION, O_RDWR | O_CREAT, 0);
if (fd < 0) {
LOG_INFO("ERROR: failed to open %s\n", CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION);
return res;
}
if (vfs_read(fd, buf, strlen("4294967295")) > 0) {
sequence_no = strtoul(buf, NULL, 0);
}
if (sequence_no < seq_no) {
ssize_t len = fmt_u32_dec(buf, seq_no);
vfs_lseek(fd, 0, SEEK_SET);
if (vfs_write(fd, buf, len) == len) {
res = SUIT_OK;
LOG_DEBUG("Stored sequence number: %" PRIu32 "\n", seq_no);
}
else {
LOG_INFO("ERROR: failed to write seq_no %" PRIu32 " to %s\n", seq_no,
CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION);
}
}
vfs_close(fd);
return res;
}
static int _vfs_init(suit_storage_t *storage)
{
suit_storage_vfs_t *vfs = _get_vfs(storage);
vfs->files = (const char **)suit_storage_files_reg;
return SUIT_OK;
}
static int _vfs_start(suit_storage_t *storage, const suit_manifest_t *manifest,
size_t len)
{
(void)manifest;
(void)len;
(void)storage;
return SUIT_OK;
}
static int _vfs_write(suit_storage_t *storage, const suit_manifest_t *manifest,
const uint8_t *buf, size_t offset, size_t len)
{
(void)manifest;
suit_storage_vfs_t *vfs = _get_vfs(storage);
const char *filepath = _get_active_file(vfs);
int fd = vfs_open(filepath, O_RDWR | O_CREAT, 0);
/* seek to the given offset */
int rc = vfs_lseek(fd, offset, SEEK_SET);
if (rc < 0) {
return rc;
}
else if ((size_t)rc != offset) {
return SUIT_ERR_STORAGE_EXCEEDED;
}
/* write all bytes to the file */
rc = vfs_write(fd, buf, len);
if (rc < 0) {
return rc;
}
else if ((size_t)rc != len) {
return SUIT_ERR_STORAGE_EXCEEDED;
}
vfs_close(fd);
return SUIT_OK;
}
static int _vfs_finish(suit_storage_t *storage, const suit_manifest_t *manifest)
{
(void)manifest;
(void)storage;
return SUIT_OK;
}
static int _vfs_install(suit_storage_t *storage, const suit_manifest_t *manifest)
{
(void)storage;
return _vfs_update_seq_no(manifest->seq_number);
}
static int _vfs_erase(suit_storage_t *storage)
{
suit_storage_vfs_t *vfs = _get_vfs(storage);
const char *filepath = _get_active_file(vfs);
vfs_unlink(filepath);
return SUIT_OK;
}
static int _vfs_read(suit_storage_t *storage, uint8_t *buf, size_t offset,
size_t len)
{
suit_storage_vfs_t *vfs = _get_vfs(storage);
const char *filepath = _get_active_file(vfs);
int fd = vfs_open(filepath, O_RDWR | O_CREAT, 0);
/* seek to the given offset */
int rc = vfs_lseek(fd, offset, SEEK_SET);
if (rc < 0) {
return rc;
}
else if ((size_t)rc != offset) {
return SUIT_ERR_STORAGE;
}
/* read from file into the buffer */
rc = vfs_read(fd, buf, len);
if (rc < 0) {
return rc;
}
else if ((size_t)rc != len) {
return SUIT_ERR_STORAGE;
}
vfs_close(fd);
return SUIT_OK;
}
static bool _get_region_by_string(const char *location, uint32_t *val)
{
for (size_t i = 0; i < XFA_LEN(char **, suit_storage_files_reg); i++) {
const char *filepath = (const char *)suit_storage_files_reg[i];
if (strncmp(filepath, location, strlen(filepath)) == 0) {
*val = i;
return true;
}
}
return false;
}
static int _vfs_set_active_location(suit_storage_t *storage,
const char *location)
{
suit_storage_vfs_t *vfs = _get_vfs(storage);
uint32_t region = 0;
if (!_get_region_by_string(location, &region)) {
return -1;
}
vfs->active_region = region;
return 0;
}
static bool _vfs_has_location(const suit_storage_t *storage, const char *location)
{
(void)storage;
uint32_t region = 0;
return _get_region_by_string(location, &region);
}
static int _vfs_get_seq_no(const suit_storage_t *storage, uint32_t *seq_no)
{
(void)storage;
char buf[16];
int fd = vfs_open(CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION, O_RDWR | O_CREAT, 0);
if (fd < 0) {
LOG_INFO("ERROR: failed to open %s\n", CONFIG_SUIT_STORAGE_SEQ_NO_LOCATION);
return SUIT_ERR_SEQUENCE_NUMBER;
}
if (vfs_read(fd, buf, strlen("4294967295")) > 0) {
*seq_no = strtoul(buf, NULL, 0);
}
LOG_INFO("Retrieved sequence number: %" PRIu32 "\n", *seq_no);
vfs_close(fd);
return SUIT_OK;
}
static int _vfs_set_seq_no(suit_storage_t *storage, uint32_t seq_no)
{
(void)storage;
return _vfs_update_seq_no(seq_no);
}
const suit_storage_driver_t suit_storage_vfs_driver = {
.init = _vfs_init,
.start = _vfs_start,
.write = _vfs_write,
.finish = _vfs_finish,
.read = _vfs_read,
.install = _vfs_install,
.erase = _vfs_erase,
.set_active_location = _vfs_set_active_location,
.has_location = _vfs_has_location,
.get_seq_no = _vfs_get_seq_no,
.set_seq_no = _vfs_set_seq_no,
.separator = '\0',
};
suit_storage_vfs_t suit_storage_vfs = {
.storage = {
.driver = &suit_storage_vfs_driver,
},
};
XFA(suit_storage_reg, 0) suit_storage_t *suit_storage_vfs_ptr = &suit_storage_vfs.storage;