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