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 <psa/internal_trusted_storage.h>
10 #include <zephyr/settings/settings.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/psa/its_ids.h>
14
15 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
16
17 K_MUTEX_DEFINE(worker_mutex);
18 static struct k_work_delayable worker;
19
20 struct setting_entry {
21 char name[SETTINGS_MAX_NAME_LEN];
22 char value[SETTINGS_MAX_VAL_LEN];
23 size_t val_len;
24 };
25
26 static struct setting_entry entries[CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES];
27 static int entries_count;
28
29 static int settings_its_load(struct settings_store *cs, const struct settings_load_arg *arg);
30 static int settings_its_save(struct settings_store *cs, const char *name, const char *value,
31 size_t val_len);
32
33 static const struct settings_store_itf settings_its_itf = {
34 .csi_load = settings_its_load,
35 .csi_save = settings_its_save,
36 };
37
38 static struct settings_store default_settings_its = {.cs_itf = &settings_its_itf};
39
40 /* Ensure Key configured max size does not exceed reserved Key range */
41 BUILD_ASSERT(sizeof(entries) / CONFIG_TFM_ITS_MAX_ASSET_SIZE <=
42 ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_SIZE,
43 "entries array exceeds reserved ITS UID range");
44
store_entries(void)45 static int store_entries(void)
46 {
47 psa_status_t status;
48 psa_storage_uid_t uid = ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_BEGIN;
49 size_t remaining = sizeof(entries);
50 size_t chunk_size = CONFIG_TFM_ITS_MAX_ASSET_SIZE;
51 const uint8_t *data_ptr = (const uint8_t *)&entries;
52
53 /*
54 * Each ITS UID is treated like a sector. Data is written to each ITS node until
55 * that node is full, before incrementing the UID. This is done to minimize the
56 * number of allocated ITS nodes and to avoid wasting allocated bytes.
57 */
58 while (remaining > 0) {
59 size_t write_size = (remaining > chunk_size) ? chunk_size : remaining;
60
61 status = psa_its_set(uid, write_size, data_ptr, PSA_STORAGE_FLAG_NONE);
62 if (status) {
63 LOG_ERR("Error storing %d bytes of metadata! Bytes Remaining: %d, status: "
64 "%d",
65 write_size, remaining, status);
66 return status;
67 }
68
69 data_ptr += write_size;
70 remaining -= write_size;
71 uid++;
72 }
73
74 LOG_DBG("ITS entries stored successfully - bytes_saved: %d num_entries: %d max_uid: %lld",
75 sizeof(entries), entries_count, uid);
76
77 return 0;
78 }
79
load_entries(void)80 static int load_entries(void)
81 {
82 psa_status_t status;
83 size_t bytes_read;
84 psa_storage_uid_t uid = ZEPHYR_PSA_SETTINGS_TFM_ITS_UID_RANGE_BEGIN;
85 size_t remaining = sizeof(entries);
86 size_t chunk_size = CONFIG_TFM_ITS_MAX_ASSET_SIZE;
87 uint8_t *data_ptr = (uint8_t *)&entries;
88
89 /*
90 * Each ITS UID is treated like a sector. Data is written to each ITS node until
91 * that node is full, before incrementing the UID. This is done to minimize the
92 * number of allocated ITS nodes and to avoid wasting allocated bytes.
93 */
94 while (remaining > 0) {
95 size_t to_read = (remaining > chunk_size) ? chunk_size : remaining;
96
97 status = psa_its_get(uid, 0, to_read, data_ptr, &bytes_read);
98 if (status) {
99 return status;
100 }
101
102 data_ptr += bytes_read;
103 remaining -= bytes_read;
104 uid++;
105 }
106
107 for (int i = 0; i < CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES; i++) {
108 if (strnlen(entries[i].name, SETTINGS_MAX_NAME_LEN) != 0) {
109 entries_count++;
110 }
111 }
112
113 LOG_DBG("ITS entries restored successfully - bytes_loaded: %d, num_entries: %d",
114 sizeof(entries), entries_count);
115
116 return 0;
117 }
118
119 /* void *back_end is the index of the entry in metadata entries struct */
settings_its_read_fn(void * back_end,void * data,size_t len)120 static ssize_t settings_its_read_fn(void *back_end, void *data, size_t len)
121 {
122 int index = *(int *)back_end;
123
124 LOG_DBG("ITS Read - index: %d", index);
125
126 if (index < 0 || index >= CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES) {
127 LOG_ERR("Invalid index %d in ITS metadata", index);
128 return 0;
129 }
130
131 memcpy(data, entries[index].value, len);
132
133 /*
134 * Callback expects return value of the number of bytes read
135 */
136 return entries[index].val_len;
137 }
138
settings_its_load(struct settings_store * cs,const struct settings_load_arg * arg)139 static int settings_its_load(struct settings_store *cs, const struct settings_load_arg *arg)
140 {
141 int ret;
142
143 for (int i = 0; i < entries_count; i++) {
144 if (strnlen(entries[i].name, SETTINGS_MAX_NAME_LEN) != 0) {
145 /*
146 * Pass the key to the settings handler with it's index as an argument,
147 * to be read during callback function later.
148 */
149 ret = settings_call_set_handler(entries[i].name, entries[i].val_len,
150 settings_its_read_fn, (void *)&i,
151 (void *)arg);
152 if (ret) {
153 return ret;
154 }
155 }
156 }
157
158 return 0;
159 }
160
settings_its_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)161 static int settings_its_save(struct settings_store *cs, const char *name, const char *value,
162 size_t val_len)
163 {
164 if (entries_count >= CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES) {
165 LOG_ERR("%s: Max settings reached: %d", __func__,
166 CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES);
167 return -ENOMEM;
168 }
169
170 if (val_len > SETTINGS_MAX_VAL_LEN) {
171 LOG_ERR("%s: Invalid settings size - val_len: %d", __func__, val_len);
172 return -EINVAL;
173 }
174
175 int index;
176 bool delete;
177
178 /* Find out if we are doing a delete */
179 delete = ((value == NULL) || (val_len == 0));
180
181 /* Lock mutex before manipulating settings array */
182 k_mutex_lock(&worker_mutex, K_FOREVER);
183
184 /*
185 * Search metadata to see if entry already exists. Array is compacted, so first blank entry
186 * signals end of settings.
187 */
188 for (index = 0; index < CONFIG_SETTINGS_TFM_ITS_NUM_ENTRIES; index++) {
189 if (strncmp(entries[index].name, name, SETTINGS_MAX_NAME_LEN) == 0) {
190 break;
191 } else if (entries[index].val_len == 0) {
192
193 /* Setting already deleted */
194 if (delete) {
195 LOG_DBG("%s: %s Already deleted!", __func__, name);
196 k_mutex_unlock(&worker_mutex);
197 return 0;
198 }
199
200 /* New setting being entered */
201 entries_count++;
202 break;
203 }
204 }
205
206 LOG_DBG("ITS Save - index %d: name %s, val_len %d", index, name, val_len);
207
208 if (delete) {
209 /* Clear metadata */
210 memset(entries[index].name, 0, SETTINGS_MAX_NAME_LEN);
211 memset(entries[index].value, 0, SETTINGS_MAX_VAL_LEN);
212 entries[index].val_len = 0;
213
214 /* If setting not at end of array, shift entries */
215 if (index < entries_count - 1) {
216 memcpy(&entries[index], &entries[index + 1],
217 (entries_count - index - 1) * sizeof(struct setting_entry));
218 /* Remove duplicate entry at end of array */
219 memset(&entries[entries_count - 1], 0, sizeof(struct setting_entry));
220 }
221
222 entries_count--;
223 } else {
224 /* Update metadata */
225 strncpy(entries[index].name, name, SETTINGS_MAX_NAME_LEN);
226 memcpy(entries[index].value, value, val_len);
227 entries[index].val_len = val_len;
228 }
229
230 k_mutex_unlock(&worker_mutex);
231 k_work_schedule(&worker, K_MSEC(CONFIG_SETTINGS_TFM_ITS_LAZY_PERSIST_DELAY_MS));
232
233 return 0;
234 }
235
worker_persist_entries_struct_fn(struct k_work * work)236 void worker_persist_entries_struct_fn(struct k_work *work)
237 {
238 k_mutex_lock(&worker_mutex, K_FOREVER);
239 store_entries();
240 k_mutex_unlock(&worker_mutex);
241 }
242
settings_backend_init(void)243 int settings_backend_init(void)
244 {
245 psa_status_t status;
246
247 /* Load ITS metadata */
248 status = load_entries();
249
250 /* If resource DNE, we need to allocate it */
251 if (status == PSA_ERROR_DOES_NOT_EXIST) {
252 status = store_entries();
253 if (status) {
254 LOG_ERR("Error storing metadata in %s: (status %d)", __func__, status);
255 return -EIO;
256 }
257 } else if (status) {
258 LOG_ERR("Error loading metadata in %s: (status %d)", __func__, status);
259 return -EIO;
260 }
261
262 settings_dst_register(&default_settings_its);
263 settings_src_register(&default_settings_its);
264
265 k_work_init_delayable(&worker, worker_persist_entries_struct_fn);
266
267 return 0;
268 }
269