From a14cd9c5c04bf9ef154581b63e205c7040eac271 Mon Sep 17 00:00:00 2001 From: Anthony Tarbinian Date: Wed, 27 Nov 2024 23:15:44 -0800 Subject: [PATCH] isolated nonvolatile storage capsule with test --- .../isolated_nonvolatile_storage/Makefile | 11 +++ .../isolated_nonvolatile_storage/README.md | 83 +++++++++++++++++++ .../tests/isolated_nonvolatile_storage/main.c | 81 ++++++++++++++++++ .../storage/isolated_nonvolatile_storage.c | 80 ++++++++++++++++++ .../storage/isolated_nonvolatile_storage.h | 25 ++++++ .../storage/isolated_nonvolatile_storage.c | 65 +++++++++++++++ .../storage/isolated_nonvolatile_storage.h | 42 ++++++++++ .../isolated_nonvolatile_storage_syscalls.c | 46 ++++++++++ .../isolated_nonvolatile_storage_syscalls.h | 31 +++++++ 9 files changed, 464 insertions(+) create mode 100644 examples/tests/isolated_nonvolatile_storage/Makefile create mode 100644 examples/tests/isolated_nonvolatile_storage/README.md create mode 100644 examples/tests/isolated_nonvolatile_storage/main.c create mode 100644 libtock-sync/storage/isolated_nonvolatile_storage.c create mode 100644 libtock-sync/storage/isolated_nonvolatile_storage.h create mode 100644 libtock/storage/isolated_nonvolatile_storage.c create mode 100644 libtock/storage/isolated_nonvolatile_storage.h create mode 100644 libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.c create mode 100644 libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.h diff --git a/examples/tests/isolated_nonvolatile_storage/Makefile b/examples/tests/isolated_nonvolatile_storage/Makefile new file mode 100644 index 000000000..54d6a7969 --- /dev/null +++ b/examples/tests/isolated_nonvolatile_storage/Makefile @@ -0,0 +1,11 @@ +# Makefile for user application + +# Specify this directory relative to the current application. +TOCK_USERLAND_BASE_DIR = ../../.. + +# Which files to compile. +C_SRCS := $(wildcard *.c) + +# Include userland master makefile. Contains rules and flags for actually +# building the application. +include $(TOCK_USERLAND_BASE_DIR)/AppMakefile.mk diff --git a/examples/tests/isolated_nonvolatile_storage/README.md b/examples/tests/isolated_nonvolatile_storage/README.md new file mode 100644 index 000000000..486b08a75 --- /dev/null +++ b/examples/tests/isolated_nonvolatile_storage/README.md @@ -0,0 +1,83 @@ +Nonvolatile Storage Test App +============================ + +This app writes to flash storage and reads it back to test that flash storage +is working. It requires that a +`capsules::nonvolatile_storage_driver::NonvolatileStorage` interface be provided +to userland. + + + +Example Hail Setup +------------------ + +One way to provide the Driver interface with Hail looks like: + +```diff +--- a/boards/hail/src/main.rs ++++ b/boards/hail/src/main.rs +@@ -74,6 +74,7 @@ struct Hail { + ipc: kernel::ipc::IPC, + crc: &'static capsules::crc::Crc<'static, sam4l::crccu::Crccu<'static>>, + dac: &'static capsules::dac::Dac<'static>, ++ nv: &'static capsules::nonvolatile_storage_driver::NonvolatileStorage<'static>, + } + + /// Mapping of integer syscalls to objects that implement syscalls. +@@ -104,6 +105,8 @@ impl Platform for Hail { + capsules::dac::DRIVER_NUM => f(Some(self.dac)), + + kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)), ++ ++ 27 => f(Some(self.nv)), + _ => f(None), + } + } +@@ -443,6 +446,38 @@ pub unsafe fn reset_handler() { + capsules::dac::Dac::new(&mut sam4l::dac::DAC) + ); + ++ // Flash ++ let mux_flash = static_init!( ++ capsules::virtual_flash::MuxFlash<'static, sam4l::flashcalw::FLASHCALW>, ++ capsules::virtual_flash::MuxFlash::new(&sam4l::flashcalw::FLASH_CONTROLLER)); ++ hil::flash::HasClient::set_client(&sam4l::flashcalw::FLASH_CONTROLLER, mux_flash); ++ ++ // Nonvolatile Storage ++ let virtual_flash_nv = static_init!( ++ capsules::virtual_flash::FlashUser<'static, sam4l::flashcalw::FLASHCALW>, ++ capsules::virtual_flash::FlashUser::new(mux_flash)); ++ pub static mut NV_PAGEBUFFER: sam4l::flashcalw::Sam4lPage = sam4l::flashcalw::Sam4lPage::new(); ++ ++ let nv_nv_to_page = static_init!( ++ capsules::nonvolatile_to_pages::NonvolatileToPages<'static, ++ capsules::virtual_flash::FlashUser<'static, sam4l::flashcalw::FLASHCALW>>, ++ capsules::nonvolatile_to_pages::NonvolatileToPages::new( ++ virtual_flash_nv, ++ &mut NV_PAGEBUFFER)); ++ hil::flash::HasClient::set_client(virtual_flash_nv, nv_nv_to_page); ++ ++ pub static mut NV_BUFFER: [u8; 512] = [0; 512]; ++ let nv = static_init!( ++ capsules::nonvolatile_storage_driver::NonvolatileStorage<'static>, ++ capsules::nonvolatile_storage_driver::NonvolatileStorage::new( ++ nv_nv_to_page, kernel::Grant::create(), ++ 0x60000, // Start address for userspace accessible region ++ 0x20000, // Length of userspace accessible region ++ 0, // Start address of kernel accessible region ++ 0, // Length of kernel accessible region ++ &mut NV_BUFFER)); ++ hil::nonvolatile_storage::NonvolatileStorage::set_client(nv_nv_to_page, nv); ++ + let hail = Hail { + console: console, + gpio: gpio, +@@ -460,6 +495,7 @@ pub unsafe fn reset_handler() { + ipc: kernel::ipc::IPC::new(), + crc: crc, + dac: dac, ++ nv: nv, + }; + + // Need to reset the nRF on boot +``` \ No newline at end of file diff --git a/examples/tests/isolated_nonvolatile_storage/main.c b/examples/tests/isolated_nonvolatile_storage/main.c new file mode 100644 index 000000000..2cd2654a6 --- /dev/null +++ b/examples/tests/isolated_nonvolatile_storage/main.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include + +static int test_all(void); +static int test(uint8_t* readbuf, uint8_t* writebuf, size_t size, size_t offset, size_t len); + +int main(void) { + printf("[TEST] Isolated Nonvolatile Storage\n"); + + int r = test_all(); + if (r == 0) { + printf("All tests succeeded\n"); + } else { + printf("Failed with code %d\n", r); + } + + return r; +} + +static int test_all(void) { + int num_bytes; + libtocksync_isolated_nonvolatile_storage_get_number_bytes((uint32_t*) &num_bytes); + printf("Have %i bytes of nonvolatile storage\n", num_bytes); + + int r; + uint8_t readbuf[512]; + uint8_t writebuf[512]; + + if ((r = test(readbuf, writebuf, 256, 0, 14)) != 0) return r; + if ((r = test(readbuf, writebuf, 256, 20, 14)) != 0) return r; + if ((r = test(readbuf, writebuf, 512, 0, 512)) != 0) return r; + + printf("Write to end of region (offset %d)\n", num_bytes - 512); + if ((r = test(readbuf, writebuf, 512, num_bytes - 512, 500)) != 0) return r; + + printf("Write beyond end region, should fail (offset %d)\n", num_bytes); + if ((r = test(readbuf, writebuf, 512, num_bytes, 501)) == 0) return -1; + + printf("Write starts beyond end region, should fail (offset %d)\n", num_bytes + 1); + if ((r = test(readbuf, writebuf, 512, num_bytes + 1, 1)) == 0) return -1; + + printf("Write starts before start region, should fail (offset %d)\n", -1); + if ((r = test(readbuf, writebuf, 512, -1, 1)) == 0) return -1; + + return 0; +} + +static int test(uint8_t* readbuf, uint8_t* writebuf, size_t size, size_t offset, size_t len) { + int ret; + int length_written, length_read; + + printf("\tTest with size %d ...\n", size); + + for (size_t i = 0; i < len; i++) { + writebuf[i] = i; + } + + ret = libtocksync_isolated_nonvolatile_storage_write(offset, len, writebuf, size, &length_written); + if (ret != RETURNCODE_SUCCESS) { + printf("\tERROR calling write. returncode: %d\n", ret); + return ret; + } + + ret = libtocksync_isolated_nonvolatile_storage_read(offset, len, readbuf, size, &length_read); + if (ret != RETURNCODE_SUCCESS) { + printf("\tERROR calling read. returncode: %d\n", ret); + return ret; + } + + for (size_t i = 0; i < len; i++) { + if (readbuf[i] != writebuf[i]) { + printf("\tInconsistency between data written and read at index %u\n", i); + return -1; + } + } + + return 0; +} diff --git a/libtock-sync/storage/isolated_nonvolatile_storage.c b/libtock-sync/storage/isolated_nonvolatile_storage.c new file mode 100644 index 000000000..bfb37adc8 --- /dev/null +++ b/libtock-sync/storage/isolated_nonvolatile_storage.c @@ -0,0 +1,80 @@ +#include "isolated_nonvolatile_storage.h" +#include + +struct nv_data { + bool fired; + returncode_t ret; + int length; + int storage_size; +}; + +static struct nv_data result = {.fired = false}; + +static void get_number_bytes_cb(returncode_t ret, int number_bytes) { + result.fired = true; + result.ret = ret; + result.storage_size = number_bytes; +} + +static void write_cb(returncode_t ret, int length) { + result.fired = true; + result.ret = ret; + result.length = length; +} + +static void read_cb(returncode_t ret, int length) { + result.fired = true; + result.ret = ret; + result.length = length; +} + +returncode_t libtocksync_isolated_nonvolatile_storage_get_number_bytes(uint32_t* number_bytes) { + returncode_t ret; + result.fired = false; + + ret = libtock_isolated_nonvolatile_storage_get_number_bytes(get_number_bytes_cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + yield_for(&result.fired); + if (result.ret != RETURNCODE_SUCCESS) return result.ret; + + *number_bytes = result.storage_size; + return RETURNCODE_SUCCESS; +} + +returncode_t libtocksync_isolated_nonvolatile_storage_write(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, int* length_written) { + returncode_t ret; + result.fired = false; + + ret = libtock_isolated_nonvolatile_storage_write(offset, length, buffer, buffer_length, write_cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + yield_for(&result.fired); + if (result.ret != RETURNCODE_SUCCESS) return result.ret; + + ret = libtock_isolated_nonvolatile_storage_set_allow_readonly_write_buffer(NULL, 0); + if (result.ret != RETURNCODE_SUCCESS) return result.ret; + + *length_written = result.length; + return RETURNCODE_SUCCESS; +} + +returncode_t libtocksync_isolated_nonvolatile_storage_read(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, int* length_read) { + returncode_t ret; + result.fired = false; + + ret = libtock_isolated_nonvolatile_storage_read(offset, length, buffer, buffer_length, read_cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + yield_for(&result.fired); + if (result.ret != RETURNCODE_SUCCESS) return result.ret; + + ret = libtock_isolated_nonvolatile_storage_set_allow_readwrite_read_buffer(NULL, 0); + if (result.ret != RETURNCODE_SUCCESS) return result.ret; + + *length_read = result.length; + return RETURNCODE_SUCCESS; +} + diff --git a/libtock-sync/storage/isolated_nonvolatile_storage.h b/libtock-sync/storage/isolated_nonvolatile_storage.h new file mode 100644 index 000000000..d211ee33a --- /dev/null +++ b/libtock-sync/storage/isolated_nonvolatile_storage.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Get number of bytes available to this app in nonvolatile storage +returncode_t libtocksync_isolated_nonvolatile_storage_get_number_bytes(uint32_t* number_bytes); + +// Write `length` bytes from `buffer` to the nonvolatile storage starting at +// `offset`. +returncode_t libtocksync_isolated_nonvolatile_storage_write(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, int* length_written); + +// Read `length` bytes into `buffer` from the nonvolatile storage starting at +// `offset`. +returncode_t libtocksync_isolated_nonvolatile_storage_read(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, int* length_read); + +#ifdef __cplusplus +} +#endif diff --git a/libtock/storage/isolated_nonvolatile_storage.c b/libtock/storage/isolated_nonvolatile_storage.c new file mode 100644 index 000000000..a9f5e53bd --- /dev/null +++ b/libtock/storage/isolated_nonvolatile_storage.c @@ -0,0 +1,65 @@ +#include "isolated_nonvolatile_storage.h" +#include "syscalls/isolated_nonvolatile_storage_syscalls.h" + +static void get_number_bytes_done(int ret, + int number_bytes, + __attribute__ ((unused)) int arg3, + void* opaque) { + libtock_isolated_nonvolatile_storage_callback_get_number_bytes cb = (libtock_isolated_nonvolatile_storage_callback_get_number_bytes) opaque; + cb(tock_status_to_returncode(ret), number_bytes); +} + +static void write_done(int ret, + int length, + __attribute__ ((unused)) int arg2, + void* opaque) { + libtock_isolated_nonvolatile_storage_callback_write cb = (libtock_isolated_nonvolatile_storage_callback_write) opaque; + cb(tock_status_to_returncode(ret), length); +} + +static void read_done(int ret, + int length, + __attribute__ ((unused)) int arg2, + void* opaque) { + libtock_isolated_nonvolatile_storage_callback_read cb = (libtock_isolated_nonvolatile_storage_callback_read) opaque; + cb(tock_status_to_returncode(ret), length); +} + +returncode_t libtock_isolated_nonvolatile_storage_get_number_bytes(libtock_isolated_nonvolatile_storage_callback_get_number_bytes cb) { + returncode_t ret; + + ret = libtock_isolated_nonvolatile_storage_set_upcall_get_number_bytes_done(get_number_bytes_done, cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + ret = libtock_isolated_nonvolatile_storage_command_get_number_bytes(); + return ret; +} + +returncode_t libtock_isolated_nonvolatile_storage_write(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, libtock_isolated_nonvolatile_storage_callback_write cb) { + returncode_t ret; + + ret = libtock_isolated_nonvolatile_storage_set_upcall_write_done(write_done, cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + ret = libtock_isolated_nonvolatile_storage_set_allow_readonly_write_buffer(buffer, buffer_length); + if (ret != RETURNCODE_SUCCESS) return ret; + + ret = libtock_isolated_nonvolatile_storage_command_write(offset, length); + return ret; +} + +returncode_t libtock_isolated_nonvolatile_storage_read(uint32_t offset, uint32_t length, uint8_t* buffer, uint32_t buffer_length, + libtock_isolated_nonvolatile_storage_callback_read cb) { + returncode_t ret; + + ret = libtock_isolated_nonvolatile_storage_set_upcall_read_done(read_done, cb); + if (ret != RETURNCODE_SUCCESS) return ret; + + ret = libtock_isolated_nonvolatile_storage_set_allow_readwrite_read_buffer(buffer, buffer_length); + if (ret != RETURNCODE_SUCCESS) return ret; + + ret = libtock_isolated_nonvolatile_storage_command_read(offset, length); + return ret; +} + diff --git a/libtock/storage/isolated_nonvolatile_storage.h b/libtock/storage/isolated_nonvolatile_storage.h new file mode 100644 index 000000000..c1a628324 --- /dev/null +++ b/libtock/storage/isolated_nonvolatile_storage.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../tock.h" +#include "syscalls/isolated_nonvolatile_storage_syscalls.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Function signature for nonvolatile storage get_number_bytes callbacks. +// +// - `arg1` (`returncode_t`): Status of get_number_bytes. +// - `arg2` (`int`): Number of bytes available in storage. +typedef void (*libtock_isolated_nonvolatile_storage_callback_get_number_bytes)(returncode_t, int); + +// Function signature for nonvolatile storage write callbacks. +// +// - `arg1` (`returncode_t`): Status of write. +// - `arg2` (`int`): Length written. +typedef void (*libtock_isolated_nonvolatile_storage_callback_write)(returncode_t, int); + +// Function signature for nonvolatile storage read callbacks. +// +// - `arg1` (`returncode_t`): Status of read. +// - `arg2` (`int`): Length ead. +typedef void (*libtock_isolated_nonvolatile_storage_callback_read)(returncode_t, int); + +// Get the number of bytes available for storage. +returncode_t libtock_isolated_nonvolatile_storage_get_number_bytes(libtock_isolated_nonvolatile_storage_callback_get_number_bytes cb); + +// Write `length` bytes from `buffer` to the storage starting at `offset`. +returncode_t libtock_isolated_nonvolatile_storage_write(uint32_t offset, uint32_t length, uint8_t* buffer, + uint32_t buffer_length, libtock_isolated_nonvolatile_storage_callback_write cb); + +// Read `length` bytes into `buffer` from the storage starting at `offset`. +returncode_t libtock_isolated_nonvolatile_storage_read(uint32_t offset, uint32_t length, uint8_t* buffer, uint32_t buffer_length, + libtock_isolated_nonvolatile_storage_callback_read cb); + +#ifdef __cplusplus +} +#endif + diff --git a/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.c b/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.c new file mode 100644 index 000000000..1d1a25570 --- /dev/null +++ b/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.c @@ -0,0 +1,46 @@ +#include "isolated_nonvolatile_storage_syscalls.h" + +bool libtock_isolated_nonvolatile_storage_exists(void) { + return driver_exists(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE); +} + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_get_number_bytes_done(subscribe_upcall cb, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 0, cb, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_read_done(subscribe_upcall cb, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 1, cb, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_write_done(subscribe_upcall cb, void* opaque) { + subscribe_return_t sval = subscribe(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 2, cb, opaque); + return tock_subscribe_return_to_returncode(sval); +} + +returncode_t libtock_isolated_nonvolatile_storage_set_allow_readwrite_read_buffer(uint8_t* buffer, uint32_t len) { + allow_rw_return_t aval = allow_readwrite(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 0, (void*) buffer, len); + return tock_allow_rw_return_to_returncode(aval); +} + +returncode_t libtock_isolated_nonvolatile_storage_set_allow_readonly_write_buffer(uint8_t* buffer, uint32_t len) { + allow_ro_return_t aval = allow_readonly(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 0, (void*) buffer, len); + return tock_allow_ro_return_to_returncode(aval); +} + +returncode_t libtock_isolated_nonvolatile_storage_command_get_number_bytes(void) { + syscall_return_t cval = command(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 1, 0, 0); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_isolated_nonvolatile_storage_command_read(uint32_t offset, uint32_t length) { + syscall_return_t cval = command(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 2, offset, length); + return tock_command_return_novalue_to_returncode(cval); +} + +returncode_t libtock_isolated_nonvolatile_storage_command_write(uint32_t offset, uint32_t length) { + syscall_return_t cval = command(DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE, 3, offset, length); + return tock_command_return_novalue_to_returncode(cval); +} + diff --git a/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.h b/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.h new file mode 100644 index 000000000..0bca1887c --- /dev/null +++ b/libtock/storage/syscalls/isolated_nonvolatile_storage_syscalls.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../../tock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DRIVER_NUM_ISOLATED_NONVOLATILE_STORAGE 0x50004 + +bool libtock_isolated_nonvolatile_storage_exists(void); + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_get_number_bytes_done(subscribe_upcall cb, void* opaque); + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_read_done(subscribe_upcall cb, void* opaque); + +returncode_t libtock_isolated_nonvolatile_storage_set_upcall_write_done(subscribe_upcall cb, void* opaque); + +returncode_t libtock_isolated_nonvolatile_storage_set_allow_readwrite_read_buffer(uint8_t* buffer, uint32_t len); + +returncode_t libtock_isolated_nonvolatile_storage_set_allow_readonly_write_buffer(uint8_t* buffer, uint32_t len); + +returncode_t libtock_isolated_nonvolatile_storage_command_get_number_bytes(void); + +returncode_t libtock_isolated_nonvolatile_storage_command_read(uint32_t offset, uint32_t length); + +returncode_t libtock_isolated_nonvolatile_storage_command_write(uint32_t offset, uint32_t length); + +#ifdef __cplusplus +} +#endif