1 /*
2 * Copyright (c) 2019 Laczen
3 * Copyright (c) 2019 Nordic Semiconductor ASA
4 * Copyright (c) 2025 Analog Devices, Inc.
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <config_tfm.h>
10 #include <zephyr/settings/settings.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13
14 /* TF-M config file containing ITS_MAX_ASSET_SIZE */
15 #include <config_base.h>
16
17 #include "settings_tfm_psa_priv.h"
18
19 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
20
21 K_MUTEX_DEFINE(worker_mutex);
22 static struct k_work_delayable worker;
23
24 static struct setting_entry entries[CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES];
25 static int entries_count;
26
27 static int settings_psa_load(struct settings_store *cs, const struct settings_load_arg *arg);
28 static int settings_psa_save(struct settings_store *cs, const char *name, const char *value,
29 size_t val_len);
30
31 static const struct settings_store_itf settings_psa_itf = {
32 .csi_load = settings_psa_load,
33 .csi_save = settings_psa_save,
34 };
35
36 static struct settings_store default_settings_psa = {.cs_itf = &settings_psa_itf};
37
38 #if defined(CONFIG_SETTINGS_TFM_PSA_BACKEND_PS)
39 #include <psa/protected_storage.h>
40 #include <zephyr/psa/ps_ids.h>
41
42 #define SETTINGS_PSA_MAX_ASSET_SIZE PS_MAX_ASSET_SIZE
43 #define SETTINGS_PSA_UID_RANGE_BEGIN ZEPHYR_PSA_SETTINGS_TFM_PS_UID_RANGE_BEGIN
44 #define SETTINGS_PSA_UID_RANGE_SIZE ZEPHYR_PSA_SETTINGS_TFM_PS_UID_RANGE_SIZE
45 #define SETTINGS_PSA_SET psa_ps_set
46 #define SETTINGS_PSA_GET psa_ps_get
47
48 #elif defined(CONFIG_SETTINGS_TFM_PSA_BACKEND_ITS)
49 #include <psa/internal_trusted_storage.h>
50 #include <zephyr/psa/its_ids.h>
51
52 #define SETTINGS_PSA_MAX_ASSET_SIZE ITS_MAX_ASSET_SIZE
53 #define SETTINGS_PSA_UID_RANGE_BEGIN ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_BEGIN
54 #define SETTINGS_PSA_UID_RANGE_SIZE ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_SIZE
55 #define SETTINGS_PSA_SET psa_its_set
56 #define SETTINGS_PSA_GET psa_its_get
57
58 #else
59 #error "No PSA backend selected"
60 #endif /* CONFIG_SETTINGS_TFM_PSA_BACKEND */
61
62 /* Ensure Key configured max size does not exceed reserved Key range */
63 BUILD_ASSERT(sizeof(entries) / SETTINGS_PSA_MAX_ASSET_SIZE <=
64 SETTINGS_PSA_UID_RANGE_SIZE,
65 "entries array exceeds reserved PSA storage UID range");
66
store_entries(void)67 static int store_entries(void)
68 {
69 psa_status_t status;
70 psa_storage_uid_t uid = SETTINGS_PSA_UID_RANGE_BEGIN;
71 size_t write_size = SETTINGS_PSA_MAX_ASSET_SIZE;
72 size_t remaining = sizeof(entries);
73 const uint8_t *data_ptr = (const uint8_t *)&entries;
74
75 /*
76 * Each storage UID is treated like a sector. Data is written to each KV pair until
77 * that data field is full, before incrementing the UID. This is done to minimize the
78 * number of allocated UIDs and to allocate bytes in the most efficient way.
79 */
80 while (remaining > 0) {
81 if (remaining < SETTINGS_PSA_MAX_ASSET_SIZE) {
82 write_size = remaining;
83 }
84
85 status = SETTINGS_PSA_SET(uid, write_size, data_ptr, PSA_STORAGE_FLAG_NONE);
86 if (status) {
87 LOG_ERR("Error storing %d bytes of metadata! Bytes Remaining: %d, status: "
88 "%d",
89 write_size, remaining, status);
90 return status;
91 }
92
93 data_ptr += write_size;
94 remaining -= write_size;
95 uid++;
96 }
97
98 LOG_DBG("PSA entries stored successfully - bytes_saved: %d num_entries: %d max_uid: %lld",
99 sizeof(entries), entries_count, uid);
100
101 return 0;
102 }
103
load_entries(void)104 static int load_entries(void)
105 {
106 psa_status_t status;
107 size_t bytes_read;
108 psa_storage_uid_t uid = SETTINGS_PSA_UID_RANGE_BEGIN;
109 size_t read_size = SETTINGS_PSA_MAX_ASSET_SIZE;
110 size_t remaining = sizeof(entries);
111 uint8_t *data_ptr = (uint8_t *)&entries;
112 int i;
113
114 /*
115 * Each storage UID is treated like a sector. Data is written to each KV pair until
116 * that data field is full, before incrementing the UID. This is done to minimize the
117 * number of allocated UIDs and to allocate bytes in the most efficient way.
118 */
119 while (remaining > 0) {
120 if (remaining < SETTINGS_PSA_MAX_ASSET_SIZE) {
121 read_size = remaining;
122 }
123
124 status = SETTINGS_PSA_GET(uid, 0, read_size, data_ptr, &bytes_read);
125 if (status) {
126 return status;
127 }
128
129 data_ptr += bytes_read;
130 remaining -= bytes_read;
131 uid++;
132 }
133
134 for (i = 0; i < CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES; i++) {
135 if (strnlen(entries[i].name, SETTINGS_MAX_NAME_LEN) != 0) {
136 entries_count++;
137 }
138 }
139
140 LOG_DBG("PSA storage entries restored successfully - bytes_loaded: %d, num_entries: %d",
141 sizeof(entries), entries_count);
142
143 return 0;
144 }
145
146 /* void *back_end is the index of the entry in metadata entries struct */
settings_psa_read_fn(void * back_end,void * data,size_t len)147 static ssize_t settings_psa_read_fn(void *back_end, void *data, size_t len)
148 {
149 int index = *(int *)back_end;
150
151 LOG_DBG("reading index: %d", index);
152
153 if (index < 0 || index >= CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES) {
154 LOG_ERR("Invalid index %d in metadata", index);
155 return 0;
156 }
157
158 memcpy(data, entries[index].value, len);
159
160 /*
161 * Callback expects return value of the number of bytes read
162 */
163 return entries[index].val_len;
164 }
165
settings_psa_load(struct settings_store * cs,const struct settings_load_arg * arg)166 static int settings_psa_load(struct settings_store *cs, const struct settings_load_arg *arg)
167 {
168 int i;
169 int ret;
170
171 for (i = 0; i < entries_count; i++) {
172 if (strnlen(entries[i].name, SETTINGS_MAX_NAME_LEN) != 0) {
173 /*
174 * Pass the key to the settings handler with it's index as an argument,
175 * to be read during callback function later.
176 */
177 ret = settings_call_set_handler(entries[i].name, entries[i].val_len,
178 settings_psa_read_fn, (void *)&i,
179 arg);
180 if (ret) {
181 return ret;
182 }
183 }
184 }
185
186 return 0;
187 }
188
settings_psa_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)189 static int settings_psa_save(struct settings_store *cs, const char *name, const char *value,
190 size_t val_len)
191 {
192 int index;
193 bool delete;
194
195 if (entries_count >= CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES) {
196 LOG_ERR("%s: Max settings reached: %d", __func__,
197 CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES);
198 return -ENOMEM;
199 }
200
201 if (val_len > SETTINGS_MAX_VAL_LEN) {
202 LOG_ERR("%s: Invalid settings size - val_len: %d", __func__, val_len);
203 return -EINVAL;
204 }
205
206 /* Find out if we are doing a delete */
207 delete = ((value == NULL) || (val_len == 0));
208
209 /* Lock mutex before manipulating settings array */
210 k_mutex_lock(&worker_mutex, K_FOREVER);
211
212 /*
213 * Search metadata to see if entry already exists. Array is compacted, so first blank entry
214 * signals end of settings.
215 */
216 for (index = 0; index < CONFIG_SETTINGS_TFM_PSA_NUM_ENTRIES; index++) {
217 if (strncmp(entries[index].name, name, SETTINGS_MAX_NAME_LEN) == 0) {
218 break;
219 } else if (entries[index].val_len == 0) {
220 /* Setting already deleted */
221 if (delete) {
222 LOG_DBG("%s: %s Already deleted!", __func__, name);
223 k_mutex_unlock(&worker_mutex);
224 return 0;
225 }
226
227 /* New setting being entered */
228 entries_count++;
229 break;
230 }
231 }
232
233 LOG_DBG("writing index %d: name %s, val_len %d", index, name, val_len);
234
235 if (delete) {
236 /* Clear metadata */
237 memset(entries[index].name, 0, SETTINGS_MAX_NAME_LEN);
238 memset(entries[index].value, 0, SETTINGS_MAX_VAL_LEN);
239 entries[index].val_len = 0;
240
241 /* If setting not at end of array, shift entries */
242 if (index < entries_count - 1) {
243 memcpy(&entries[index], &entries[index + 1],
244 (entries_count - index - 1) * sizeof(struct setting_entry));
245 /* Remove duplicate entry at end of array */
246 memset(&entries[entries_count - 1], 0, sizeof(struct setting_entry));
247 }
248
249 entries_count--;
250 } else {
251 /* Update metadata */
252 strncpy(entries[index].name, name, SETTINGS_MAX_NAME_LEN);
253 memcpy(entries[index].value, value, val_len);
254 entries[index].val_len = val_len;
255 }
256
257 k_mutex_unlock(&worker_mutex);
258 k_work_schedule(&worker, K_MSEC(CONFIG_SETTINGS_TFM_PSA_LAZY_PERSIST_DELAY_MS));
259
260 return 0;
261 }
262
worker_persist_entries_struct_fn(struct k_work * work)263 void worker_persist_entries_struct_fn(struct k_work *work)
264 {
265 k_mutex_lock(&worker_mutex, K_FOREVER);
266 store_entries();
267 k_mutex_unlock(&worker_mutex);
268 }
269
settings_backend_init(void)270 int settings_backend_init(void)
271 {
272 /* Load settings from storage */
273 psa_status_t status = load_entries();
274
275 if (status != PSA_SUCCESS) {
276 if (status != PSA_ERROR_DOES_NOT_EXIST) {
277 LOG_ERR("Error %s metadata: status %d", "loading", status);
278 return -EIO;
279 }
280
281 /* If resource does not exist, we need to allocate it */
282 status = store_entries();
283 if (status != PSA_SUCCESS) {
284 LOG_ERR("Error %s metadata: status %d", "storing", status);
285 return -EIO;
286 }
287 }
288
289 settings_dst_register(&default_settings_psa);
290 settings_src_register(&default_settings_psa);
291
292 k_work_init_delayable(&worker, worker_persist_entries_struct_fn);
293
294 return 0;
295 }
296