Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App storage functionality simplification #865

Draft
wants to merge 2 commits into
base: API_LEVEL_22
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Makefile.standard_app
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#*******************************************************************************
# Ledger SDK
# (c) 2022 Ledger
# (c) 2022-2025 Ledger
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -53,6 +53,13 @@ endif
ifeq ($(ENABLE_APP_STORAGE), 1)
HAVE_APP_STORAGE = 1
DEFINES += HAVE_APP_STORAGE
ifeq ($(APP_STORAGE_SIZE),)
# Fall back to maximum page size for all the devices
APP_STORAGE_SIZE := 480 # 512 - 32 bytes for the system header
endif
DEFINES += APP_STORAGE_SIZE=$(APP_STORAGE_SIZE)
DEFINES += HAVE_APP_STORAGE_PROP_SETTINGS=$(ENABLE_APP_STORAGE_PROP_SETTINGS)
DEFINES += HAVE_APP_STORAGE_PROP_DATA=$(ENABLE_APP_STORAGE_PROP_DATA)
endif

#####################################################################
Expand Down
59 changes: 25 additions & 34 deletions include/app_storage.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*****************************************************************************
* (c) 2024 Ledger SAS.
* (c) 2024-2025 Ledger SAS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,53 +29,44 @@
///< bit to indicate in properties that the application contains data
#define APP_STORAGE_PROP_DATA (1 << 1)

///< Tag length
#define APP_STORAGE_TAG_LEN 4

///< Tag value
#define APP_STORAGE_TAG "NVRA"

///< Header structure version
#define APP_STORAGE_HEADER_STRUCT_VERSION 1

/**
* @brief Structure defining the header of application storage header
*
*/
typedef struct app_storage_header_s {
char tag[4]; ///< ['N','V','R','A'] array, when properly initialized
typedef struct __attribute__((packed)) app_storage_header_s {
char tag[APP_STORAGE_TAG_LEN]; ///< ['N','V','R','A'] array, when properly initialized
uint32_t size; ///< size in bytes of the data (app_storage_data_t structure)
uint16_t struct_version; ///< version of the structure of data (to be set once at first
///< application start-up)
uint16_t struct_version; ///< version of this structure (for OS)
uint16_t properties; ///< used as a bitfield to set properties, like: contains settings,
///< contains sensitive data
uint32_t data_version; ///< version of the content of data (to be updated every time data are
///< updated)
} app_storage_header_t;

/**
* @brief Structure defining the application storage
*
*/
typedef struct app_storage_s {
app_storage_header_t header; ///< header describing the data
#ifndef HAVE_BOLOS
app_storage_data_t data; ///< application data, app_storage_data_t must be defined in
///< "app_storage_data.h" file
#endif // HAVE_BOLOS
} app_storage_t;

#ifndef HAVE_BOLOS
/**
* @brief This variable must be defined in Application code, and never used directly,
* except by @ref N_nvram
*
*/
extern const app_storage_t N_app_storage_real;

/**
* @brief To be used by function accessing application storage data (not N_storage_real)
*
*/
#define N_app_storage (*(volatile app_storage_t *) PIC(&N_app_storage_real))

void app_storage_init(uint32_t data_version);
/* Getters for system information */
uint32_t app_storage_get_size(void);
uint16_t app_storage_get_struct_version(void);
uint16_t app_storage_get_properties(void);
uint32_t app_storage_get_data_version(void);
bool app_storage_is_initalized(void);
void app_storage_set_data_version(uint32_t data_version);

#endif // HAVE_BOLOS
/* Reads app storage data */
int32_t app_storage_pread(void *buf, uint32_t nbyte, uint32_t offset);

/* Writes app storage data */
int32_t app_storage_pwrite(const void *buf, uint32_t nbyte, uint32_t offset);

/* Setters */
void app_storage_set_data_version(uint32_t data_version);
void app_storage_increment_data_version(void);

#endif // #ifndef HAVE_BOLOS
135 changes: 92 additions & 43 deletions lib_standard_app/app_storage.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*****************************************************************************
* (c) 2024 Ledger SAS.
* (c) 2024-2025 Ledger SAS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,87 +20,136 @@
#include "os_nvm.h"
#include "os_pic.h"

// TODO: Create new section for the linker script
/*app_storage app_storage_real __attribute__((section(".bss.app_storage")));*/
const app_storage_t N_app_storage_real;
/* The storage consists of the system and the app parts */
typedef struct __attribute__((packed)) app_storage_s {
app_storage_header_t header;
uint8_t data[APP_STORAGE_SIZE];
} app_storage_t;

const app_storage_t app_storage_real __attribute__((section(".storage_section")));
#define as (*(volatile app_storage_t *) PIC(&app_storage_real))

/**
* @brief checks if the app storage struct is initialized
*/
static bool app_storage_is_initalized(void)
{
if (memcmp((const void *) &as.header.tag, APP_STORAGE_TAG, APP_STORAGE_TAG_LEN)) {
return false;
}
return true;
}

/**
* @brief init header of application storage structure :
* - set "NVRA" tag
* - set size
* - set struct and data versions
* - set properties
* @param data_version Version of the data
* @brief initializes the header of application storage structure:
* - checks if it already initialized, if not:
* - sets "NVRA" tag
* - sets initial size (0)
* - sets struct and data versions (1)
* - sets properties (from Makefile)
*/
void app_storage_init(uint32_t data_version)
void app_storage_init(void)
{
if (app_storage_is_initalized()) {
return;
}

app_storage_header_t header = {0};

memcpy(header.tag, (void *) "NVRA", 4);
// APP_STORAGE_DATA_STRUCT_VERSION and APP_STORAGE_PROPERTIES must be defined in
// app_storage_data.h
header.struct_version = APP_STORAGE_DATA_STRUCT_VERSION;
header.data_version = data_version;
header.properties = APP_STORAGE_PROPERTIES;
// TODO: Doing this lead to have app storage bigger than needed
header.size = sizeof(app_storage_data_t);
nvm_write((void *) &N_app_storage.header, &header, sizeof(header));
memcpy(&header.tag, (void *) APP_STORAGE_TAG, APP_STORAGE_TAG_LEN);
header.struct_version = APP_STORAGE_HEADER_STRUCT_VERSION;
header.data_version = 1;
header.properties = ((HAVE_APP_STORAGE_PROP_SETTINGS << 0) | (HAVE_APP_STORAGE_PROP_DATA << 1));
header.size = 0;
nvm_write((void *) &as.header, &header, sizeof(header));
}

/**
* @brief get the size of app data
* @brief returns the size of app data
*/
uint32_t app_storage_get_size(void)
{
return N_app_storage.header.size;
return as.header.size;
}

/**
* @brief get the version of app data structure
* @brief returns the version of app data
*/
uint16_t app_storage_get_struct_version(void)
uint32_t app_storage_get_data_version(void)
{
return N_app_storage.header.struct_version;
return as.header.data_version;
}

/**
* @brief get the version of app data
* @brief returns the properties of app data
*/
uint32_t app_storage_get_data_version(void)
uint16_t app_storage_get_properties(void)
{
return N_app_storage.header.data_version;
return as.header.properties;
}

/**
* @brief get the properties of app data
* @brief increments by 1 the data_version field
*/
uint16_t app_storage_get_properties(void)
void app_storage_increment_data_version(void)
{
return N_app_storage.header.properties;
uint32_t data_version = as.header.data_version;
data_version++;
nvm_write((void *) &as.header.data_version, (void *) &data_version, sizeof(data_version));
}

/**
* @brief ensure app storage struct is initialized
* @brief writes application data to the storage
*/
bool app_storage_is_initalized(void)
void app_storage_set_data_version(uint32_t data_version)
{
if (memcmp((void *) N_app_storage.header.tag, "NVRA", 4)) {
return false;
nvm_write((void *) &as.header.data_version, (void *) &data_version, sizeof(data_version));
}

/**
* @brief writes application storage data with length and offset
*/
int32_t app_storage_pwrite(const void *buf, uint32_t nbyte, uint32_t offset)
{
/* Input parameters verification */
if (buf == NULL) {
return -1;
}
if (N_app_storage.header.size == 0) {
return false;

uint32_t size = offset + nbyte;
if (size >= APP_STORAGE_SIZE) {
return -1;
}
return true;

/* Updating data */
nvm_write((void *) &as.data[offset], (void *) buf, nbyte);

/* Updating size if it increased */
if (as.header.size < size) {
nvm_write((void *) &as.header.size, (void *) &size, sizeof(size));
}
return nbyte;
}

/**
* @brief set data version of app data
* @brief reads application data from the storage
*/
void app_storage_set_data_version(uint32_t data_version)
int32_t app_storage_pread(void *buf, uint32_t nbyte, uint32_t offset)
{
nvm_write((void *) &N_app_storage.header.data_version,
(void *) &data_version,
sizeof(N_app_storage.header.data_version));
/* Input parameters verification */
if (buf == NULL) {
return -1;
}

uint32_t size = offset + nbyte;
if (size >= as.header.size) {
return -1;
}

/* Reading data */
memcpy((void *) buf, (void *) &as.data[offset], nbyte);

return nbyte;
}

#endif // HAVE_APP_STORAGE
7 changes: 7 additions & 0 deletions lib_standard_app/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ WEAK void __attribute__((noreturn)) app_exit(void)
os_sched_exit(-1);
}

extern void app_storage_init(void);

WEAK void common_app_init(void)
{
UX_INIT();
Expand All @@ -55,6 +57,11 @@ WEAK void common_app_init(void)
BLE_power(0, NULL);
BLE_power(1, NULL);
#endif // HAVE_BLE

#ifdef HAVE_APP_STORAGE
/* Implicit app storage initialization */
app_storage_init();
#endif // #ifdef HAVE_APP_STORAGE
}

WEAK void standalone_app_main(void)
Expand Down
5 changes: 4 additions & 1 deletion target/flex/script.ld
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Ledger - Secure firmware
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Ledger
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2025 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -69,6 +69,9 @@ SECTIONS

_nvram_data = .;

/* App storage */
*(.storage_section)

/* NVM data (ex-filesystem) */
*(.bss.N_* .rodata.N_*)

Expand Down
5 changes: 4 additions & 1 deletion target/nanos2/script.ld
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Ledger - Secure firmware
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Ledger
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2025 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -68,6 +68,9 @@ SECTIONS

_nvram_data = .;

/* App storage */
*(.storage_section)

/* NVM data (ex-filesystem) */
*(.bss.N_* .rodata.N_*)

Expand Down
5 changes: 4 additions & 1 deletion target/nanox/script.ld
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Ledger - Secure firmware
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Ledger
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2025 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -70,6 +70,9 @@ SECTIONS

_nvram_data = .;

/* App storage */
*(.storage_section)

/* NVM data (ex-filesystem) */
*(.bss.N_* .rodata.N_*)

Expand Down
6 changes: 5 additions & 1 deletion target/stax/script.ld
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*******************************************************************************
* Ledger - Secure firmware
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Ledger
* (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2025 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -69,7 +69,11 @@ SECTIONS

_nvram_data = .;

/* App storage */
*(.storage_section)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jarevalo-ledger >> to add preprocessor checking for HAVE_APP_STORAGE.

Copy link
Collaborator Author

@iartemov-ledger iartemov-ledger Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not run processor against the .ld files currently.
If no variable is declared in storage_section it will not occupy anything.


/* NVM data (ex-filesystem) */
/* TODO: should we align the start of N_ variables on page size ? */
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current version supports 2 approaches:

  • NVM accessible through app_storage API, backupable and restorable
  • NVM accessible through N_ variable (legacy approach), NOT backupable and restorable

I do not see anything bad to support the both it gives the possibility to user to backuping/restoring some data. And it will work for apps that have not yet migrated to the new API (we'll need tests for all this).

*(.bss.N_* .rodata.N_*)

. = ALIGN(PAGE_SIZE);
Expand Down
Loading