1 /*
2  * Copyright (c) 2025 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <string.h>
10 
11 #include <zephyr/logging/log.h>
12 #include <zephyr/retention/retention.h>
13 #include <zephyr/settings/settings.h>
14 #include "settings_priv.h"
15 
16 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
17 
18 #if !DT_HAS_CHOSEN(zephyr_settings_partition)
19 #error "Missing zephyr,settings-partition chosen node"
20 #elif !DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_settings_partition), zephyr_retention)
21 #error "zephyr,settings-partition must be a zephyr,retention node"
22 #endif
23 
24 /*
25  * Retention storage stores each setting in the following format:
26  *   uint16_t length_name
27  *   uint16_t length_value
28  *   uint8_t name[...]
29  *   uint8_t value[...]
30  *
31  * Each setting is placed sequentially into the retention memory area, it is assumed that the
32  * checksum feature is used to ensure data validity upon loading settings from the retained
33  * memory though this is optional.
34  *
35  * Upon saving settings, the whole retention area is cleared first, then settings are written
36  * one-by-one, it is only supported to save/load all settings in one go.
37  */
38 
39 /** Retention settings context object */
40 struct settings_retention {
41 	/** Settings storage */
42 	struct settings_store cf_store;
43 
44 	/** Retention device */
45 	const struct device *cf_retention;
46 
47 	/** Last write position when setting was saved */
48 	uint32_t last_write_pos;
49 };
50 
51 /** Length of name and value object, used when reading/saving settings */
52 struct settings_retention_lengths {
53 	/** Length of name */
54 	uint16_t length_name;
55 
56 	/** Length of value */
57 	uint16_t length_value;
58 
59 	/* Name and value byte arrays follow past this point... */
60 };
61 
62 BUILD_ASSERT(sizeof(struct settings_retention_lengths) == sizeof(uint16_t) + sizeof(uint16_t));
63 
64 /** Used with read callback */
65 struct settings_retention_read_arg {
66 	/** Retention device */
67 	const struct device *cf_retention;
68 
69 	/** Offset to read from */
70 	uint32_t offset;
71 };
72 
73 static int settings_retention_load(struct settings_store *cs, const struct settings_load_arg *arg);
74 static int settings_retention_save(struct settings_store *cs, const char *name, const char *value,
75 				   size_t val_len);
76 static void *settings_retention_storage_get(struct settings_store *cs);
77 static int settings_retention_save_start(struct settings_store *cs);
78 
79 static const struct device *storage_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_settings_partition));
80 
81 static const struct settings_store_itf settings_retention_itf = {
82 	.csi_load = settings_retention_load,
83 	.csi_save = settings_retention_save,
84 	.csi_storage_get = settings_retention_storage_get,
85 	.csi_save_start = settings_retention_save_start,
86 };
87 
settings_retention_src(struct settings_retention * cf)88 static int settings_retention_src(struct settings_retention *cf)
89 {
90 	if (!retention_is_valid(cf->cf_retention)) {
91 		return -EIO;
92 	}
93 
94 	cf->cf_store.cs_itf = &settings_retention_itf;
95 	settings_src_register(&cf->cf_store);
96 
97 	return 0;
98 }
99 
settings_retention_dst(struct settings_retention * cf)100 static int settings_retention_dst(struct settings_retention *cf)
101 {
102 	cf->cf_store.cs_itf = &settings_retention_itf;
103 	settings_dst_register(&cf->cf_store);
104 
105 	return 0;
106 }
107 
settings_retention_read_value(void * cb_arg,void * data,size_t len)108 static int settings_retention_read_value(void *cb_arg, void *data, size_t len)
109 {
110 	int rc;
111 	struct settings_retention_read_arg *ctx = cb_arg;
112 
113 	rc = retention_read(ctx->cf_retention, ctx->offset, data, len);
114 
115 	if (rc != 0) {
116 		return rc;
117 	}
118 
119 	return len;
120 }
121 
settings_retention_load(struct settings_store * cs,const struct settings_load_arg * arg)122 static int settings_retention_load(struct settings_store *cs, const struct settings_load_arg *arg)
123 {
124 	int rc;
125 	uint32_t pos = 0;
126 	struct settings_retention *cf = CONTAINER_OF(cs, struct settings_retention, cf_store);
127 	uint32_t max_pos = retention_size(cf->cf_retention);
128 	struct settings_retention_read_arg read_arg = {
129 		.cf_retention = cf->cf_retention,
130 	};
131 
132 	while (pos < max_pos) {
133 		struct settings_retention_lengths lengths;
134 		char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
135 
136 		if ((pos + sizeof(lengths)) >= max_pos) {
137 			return -EIO;
138 		}
139 
140 		/* Read lengths and check validity */
141 		rc = retention_read(cf->cf_retention, pos, (uint8_t *)&lengths, sizeof(lengths));
142 
143 		if (rc != 0) {
144 			return rc;
145 		}
146 
147 		if ((lengths.length_name == 0 && lengths.length_value == 0) ||
148 		    (lengths.length_name == USHRT_MAX && lengths.length_value == USHRT_MAX)) {
149 			/* Empty data, finished loading */
150 			LOG_DBG("Finished loading retentions settings, size: 0x%x", pos);
151 			break;
152 		} else if (lengths.length_name > SETTINGS_MAX_NAME_LEN) {
153 			LOG_ERR("Invalid name length: %d, max supported: %d",
154 				lengths.length_name, SETTINGS_MAX_NAME_LEN);
155 			return -EIO;
156 		} else if (lengths.length_value > SETTINGS_MAX_VAL_LEN) {
157 			LOG_ERR("Invalid value length: %d, max supported: %d",
158 				lengths.length_name, SETTINGS_MAX_VAL_LEN);
159 			return -EIO;
160 		} else if ((lengths.length_name + lengths.length_value + pos) > max_pos) {
161 			LOG_ERR("Data length goes beyond retention area: 0x%x, max size: 0x%x",
162 				(lengths.length_name + lengths.length_value + pos), max_pos);
163 			return -EIO;
164 		}
165 
166 		/* Read values */
167 		pos += sizeof(lengths);
168 		rc = retention_read(cf->cf_retention, pos, name, lengths.length_name);
169 
170 		if (rc != 0) {
171 			return rc;
172 		}
173 
174 		name[lengths.length_name] = '\0';
175 		pos += lengths.length_name;
176 		read_arg.offset = pos;
177 
178 		rc = settings_call_set_handler(name, lengths.length_value,
179 					       &settings_retention_read_value, &read_arg, arg);
180 
181 		if (rc != 0) {
182 			return rc;
183 		}
184 
185 		pos += lengths.length_value;
186 	}
187 
188 	return 0;
189 }
190 
settings_retention_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)191 static int settings_retention_save(struct settings_store *cs, const char *name, const char *value,
192 				   size_t val_len)
193 {
194 	struct settings_retention *cf = CONTAINER_OF(cs, struct settings_retention, cf_store);
195 	struct settings_retention_lengths lengths;
196 	uint32_t off = cf->last_write_pos;
197 	int rc = -EINVAL;
198 
199 	if (name == NULL || (value == NULL && val_len > 0)) {
200 		return -EINVAL;
201 	}
202 
203 	lengths.length_name = (uint16_t)strlen(name);
204 	lengths.length_value = (uint16_t)val_len;
205 
206 	if (lengths.length_name == 0) {
207 		return -EINVAL;
208 	} else if ((cf->last_write_pos + sizeof(lengths) + lengths.length_name + val_len) >
209 		   retention_size(cf->cf_retention)) {
210 		return -E2BIG;
211 	}
212 
213 	/* Write data before writing length header to ensure that if something happens before one
214 	 * is written then the data is not wrongly seen as valid upon reading, as would be the
215 	 * case if it was partially written
216 	 */
217 	off += sizeof(lengths);
218 	rc = retention_write(cf->cf_retention, off, name, lengths.length_name);
219 
220 	if (rc != 0) {
221 		return rc;
222 	}
223 
224 	off += lengths.length_name;
225 	rc = retention_write(cf->cf_retention, off, value, val_len);
226 
227 	if (rc != 0) {
228 		goto tidy;
229 	}
230 
231 	rc = retention_write(cf->cf_retention, cf->last_write_pos, (uint8_t *)&lengths,
232 			     sizeof(lengths));
233 
234 	if (rc != 0) {
235 		goto tidy;
236 	}
237 
238 	off += val_len;
239 	cf->last_write_pos = off;
240 
241 tidy:
242 	if (rc != 0) {
243 		/* Attempt to clear data header that was partially written */
244 		uint8_t empty_data[sizeof(lengths)] = { 0x00 };
245 		uint8_t l = sizeof(lengths) + lengths.length_name + val_len;
246 		uint8_t i = 0;
247 
248 		while (i < l) {
249 			uint8_t write_len = (i + sizeof(empty_data)) > l ? (l - i) :
250 					    sizeof(empty_data);
251 
252 			rc = retention_write(cf->cf_retention, (cf->last_write_pos + i),
253 					     empty_data, write_len);
254 
255 			if (rc != 0) {
256 				break;
257 			}
258 
259 			i += write_len;
260 		}
261 	}
262 
263 	return rc;
264 }
265 
settings_retention_save_start(struct settings_store * cs)266 static int settings_retention_save_start(struct settings_store *cs)
267 {
268 	struct settings_retention *cf = CONTAINER_OF(cs, struct settings_retention, cf_store);
269 
270 	cf->last_write_pos = 0;
271 
272 	return retention_clear(cf->cf_retention);
273 }
274 
settings_backend_init(void)275 int settings_backend_init(void)
276 {
277 	int rc;
278 	static struct settings_retention config_init_settings_retention;
279 
280 	if (!device_is_ready(storage_dev)) {
281 		return -ENOENT;
282 	}
283 
284 	config_init_settings_retention.cf_retention = storage_dev;
285 	config_init_settings_retention.last_write_pos = 0;
286 
287 	rc = settings_retention_src(&config_init_settings_retention);
288 
289 	if (rc != 0 && rc != -EIO) {
290 		return rc;
291 	}
292 
293 	rc = settings_retention_dst(&config_init_settings_retention);
294 
295 	return rc;
296 }
297 
settings_retention_storage_get(struct settings_store * cs)298 static void *settings_retention_storage_get(struct settings_store *cs)
299 {
300 	struct settings_retention *cf = CONTAINER_OF(cs, struct settings_retention, cf_store);
301 
302 	return &cf->cf_retention;
303 }
304