1 /* Copyright (c) 2024 BayLibre SAS
2  *
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <errno.h>
7 #include <string.h>
8 
9 #include "settings/settings_zms.h"
10 #include "settings_priv.h"
11 
12 #include <zephyr/settings/settings.h>
13 #include <zephyr/sys/hash_function.h>
14 #include <zephyr/storage/flash_map.h>
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
17 
18 #if DT_HAS_CHOSEN(zephyr_settings_partition)
19 #define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
20 #else
21 #define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
22 #endif
23 
24 struct settings_zms_read_fn_arg {
25 	struct zms_fs *fs;
26 	uint32_t id;
27 };
28 
29 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg);
30 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
31 			     size_t val_len);
32 static void *settings_zms_storage_get(struct settings_store *cs);
33 static int settings_zms_get_last_hash_ids(struct settings_zms *cf);
34 
35 static struct settings_store_itf settings_zms_itf = {.csi_load = settings_zms_load,
36 						     .csi_save = settings_zms_save,
37 						     .csi_storage_get = settings_zms_storage_get};
38 
settings_zms_read_fn(void * back_end,void * data,size_t len)39 static ssize_t settings_zms_read_fn(void *back_end, void *data, size_t len)
40 {
41 	struct settings_zms_read_fn_arg *rd_fn_arg;
42 
43 	rd_fn_arg = (struct settings_zms_read_fn_arg *)back_end;
44 
45 	return zms_read(rd_fn_arg->fs, rd_fn_arg->id, data, len);
46 }
47 
settings_zms_src(struct settings_zms * cf)48 static int settings_zms_src(struct settings_zms *cf)
49 {
50 	cf->cf_store.cs_itf = &settings_zms_itf;
51 	settings_src_register(&cf->cf_store);
52 
53 	return 0;
54 }
55 
settings_zms_dst(struct settings_zms * cf)56 static int settings_zms_dst(struct settings_zms *cf)
57 {
58 	cf->cf_store.cs_itf = &settings_zms_itf;
59 	settings_dst_register(&cf->cf_store);
60 
61 	return 0;
62 }
63 
settings_zms_unlink_ll_node(struct settings_zms * cf,uint32_t name_hash)64 static int settings_zms_unlink_ll_node(struct settings_zms *cf, uint32_t name_hash)
65 {
66 	int rc = 0;
67 	struct settings_hash_linked_list settings_element;
68 	struct settings_hash_linked_list settings_update_element;
69 
70 	/* let's update the linked list */
71 	rc = zms_read(&cf->cf_zms, name_hash | 1, &settings_element,
72 		      sizeof(struct settings_hash_linked_list));
73 	if (rc < 0) {
74 		return rc;
75 	}
76 	/* update the next element */
77 	if (settings_element.next_hash) {
78 		rc = zms_read(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
79 			      sizeof(struct settings_hash_linked_list));
80 		if (rc < 0) {
81 			return rc;
82 		}
83 		settings_update_element.previous_hash = settings_element.previous_hash;
84 		rc = zms_write(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
85 			       sizeof(struct settings_hash_linked_list));
86 		if (rc < 0) {
87 			return rc;
88 		}
89 		if (!settings_update_element.next_hash) {
90 			/* update second_to_last_hash_id */
91 			cf->second_to_last_hash_id = settings_element.previous_hash;
92 		}
93 	} else {
94 		/* we are deleting the last element of the linked list
95 		 * let's update the last_hash_id.
96 		 */
97 		cf->last_hash_id = settings_element.previous_hash;
98 	}
99 	/* update the previous element */
100 	if (settings_element.previous_hash) {
101 		rc = zms_read(&cf->cf_zms, settings_element.previous_hash, &settings_update_element,
102 			      sizeof(struct settings_hash_linked_list));
103 		if (rc < 0) {
104 			return rc;
105 		}
106 		if (!settings_element.next_hash) {
107 			/* we are deleting the last element of the linked list,
108 			 * let's update the second_to_last_hash_id
109 			 */
110 			cf->second_to_last_hash_id = settings_update_element.previous_hash;
111 		}
112 		settings_update_element.next_hash = settings_element.next_hash;
113 		rc = zms_write(&cf->cf_zms, settings_element.previous_hash,
114 			       &settings_update_element, sizeof(struct settings_hash_linked_list));
115 		if (rc < 0) {
116 			return rc;
117 		}
118 	}
119 
120 	return rc;
121 }
122 
settings_zms_delete(struct settings_zms * cf,uint32_t name_hash)123 static int settings_zms_delete(struct settings_zms *cf, uint32_t name_hash)
124 {
125 	int rc = 0;
126 
127 	rc = zms_delete(&cf->cf_zms, name_hash);
128 	if (rc >= 0) {
129 		rc = zms_delete(&cf->cf_zms, name_hash + ZMS_DATA_ID_OFFSET);
130 	}
131 	if (rc < 0) {
132 		return rc;
133 	}
134 
135 	rc = settings_zms_unlink_ll_node(cf, name_hash);
136 	if (rc < 0) {
137 		return rc;
138 	}
139 
140 	/* Now delete the current linked list element */
141 	rc = zms_delete(&cf->cf_zms, name_hash | 1);
142 	if (rc < 0) {
143 		return rc;
144 	}
145 
146 	return rc;
147 }
148 
settings_zms_load(struct settings_store * cs,const struct settings_load_arg * arg)149 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg)
150 {
151 	int ret = 0;
152 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
153 	struct settings_zms_read_fn_arg read_fn_arg;
154 	struct settings_hash_linked_list settings_element;
155 	char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
156 	ssize_t rc1;
157 	ssize_t rc2;
158 	uint32_t ll_hash_id;
159 
160 	ret = zms_read(&cf->cf_zms, ZMS_LL_HEAD_HASH_ID, &settings_element,
161 		       sizeof(struct settings_hash_linked_list));
162 	if (ret < 0) {
163 		return ret;
164 	}
165 	ll_hash_id = settings_element.next_hash;
166 
167 	while (ll_hash_id) {
168 
169 		/* In the ZMS backend, each setting item is stored in two ZMS
170 		 * entries one for the setting's name and one with the
171 		 * setting's value.
172 		 */
173 		rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id), &name,
174 			       sizeof(name) - 1);
175 		/* get the length of data and verify that it exists */
176 		rc2 = zms_get_data_length(&cf->cf_zms, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id) +
177 							       ZMS_DATA_ID_OFFSET);
178 
179 		if ((rc1 <= 0) || (rc2 <= 0)) {
180 			/* Settings item is not stored correctly in the ZMS.
181 			 * ZMS entry for its name or value is either missing
182 			 * or deleted. Clean dirty entries to make space for
183 			 * future settings item.
184 			 */
185 			ret = settings_zms_delete(cf, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id));
186 			if (ret < 0) {
187 				return ret;
188 			}
189 			continue;
190 		}
191 
192 		/* Found a name, this might not include a trailing \0 */
193 		name[rc1] = '\0';
194 		read_fn_arg.fs = &cf->cf_zms;
195 		read_fn_arg.id = ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id) + ZMS_DATA_ID_OFFSET;
196 
197 		ret = settings_call_set_handler(name, rc2, settings_zms_read_fn, &read_fn_arg,
198 						(void *)arg);
199 		if (ret) {
200 			break;
201 		}
202 
203 		/* update next ll_hash_id */
204 		ret = zms_read(&cf->cf_zms, ll_hash_id, &settings_element,
205 			       sizeof(struct settings_hash_linked_list));
206 		if (ret < 0) {
207 			return ret;
208 		}
209 		ll_hash_id = settings_element.next_hash;
210 	}
211 
212 	return ret;
213 }
214 
settings_zms_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)215 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
216 			     size_t val_len)
217 {
218 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
219 	struct settings_hash_linked_list settings_element;
220 	char rdname[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
221 	uint32_t name_hash;
222 	uint32_t collision_num = 0;
223 	bool delete;
224 	bool write_name;
225 	bool hash_collision;
226 	int rc = 0;
227 	int first_available_hash_index = -1;
228 
229 	if (!name) {
230 		return -EINVAL;
231 	}
232 
233 	/* Find out if we are doing a delete */
234 	delete = ((value == NULL) || (val_len == 0));
235 
236 	name_hash = sys_hash32(name, strlen(name)) & ZMS_HASH_MASK;
237 	/* MSB is always 1 */
238 	name_hash |= BIT(31);
239 
240 	/* Let's find out if there are hash collisions in the storage */
241 	write_name = true;
242 	hash_collision = true;
243 
244 	for (int i = 0; i <= cf->hash_collision_num; i++) {
245 		rc = zms_read(&cf->cf_zms, name_hash + i * LSB_GET(ZMS_COLLISIONS_MASK), &rdname,
246 			      sizeof(rdname));
247 		if (rc == -ENOENT) {
248 			if (first_available_hash_index < 0) {
249 				first_available_hash_index = i;
250 			}
251 			continue;
252 		} else if (rc < 0) {
253 			/* error while reading */
254 			return rc;
255 		}
256 		/* Settings entry exist, let's verify if this is the same
257 		 * name
258 		 */
259 		rdname[rc] = '\0';
260 		if (!strcmp(name, rdname)) {
261 			/* Hash exist and the names are equal, we should
262 			 * not write the names again.
263 			 */
264 			write_name = false;
265 			name_hash += i * LSB_GET(ZMS_COLLISIONS_MASK);
266 			goto no_hash_collision;
267 		}
268 		/* At this step a Hash collision exists and names are different.
269 		 * If we are in the middle of the loop, we should continue checking
270 		 * all other possible hash collisions.
271 		 * If we reach the end of the loop, either we should select the first
272 		 * free hash value otherwise we increment it to the next free value and
273 		 * update hash_collision_num
274 		 */
275 		collision_num++;
276 	}
277 
278 	if (collision_num <= cf->hash_collision_num) {
279 		/* At this step there is a free hash found */
280 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, first_available_hash_index);
281 		goto no_hash_collision;
282 	} else if (collision_num > cf->hash_collision_num) {
283 		/* We must create a new hash based on incremented collision_num */
284 		if (collision_num > ZMS_MAX_COLLISIONS) {
285 			/* At this step there is no more space to store hash values */
286 			LOG_ERR("Maximum hash collisions reached");
287 			return -ENOSPC;
288 		}
289 		cf->hash_collision_num = collision_num;
290 		name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, collision_num);
291 	}
292 
293 no_hash_collision:
294 	if (delete) {
295 		if (write_name) {
296 			/* hash doesn't exist, do not write anything here */
297 			return 0;
298 		}
299 
300 		rc = settings_zms_delete(cf, name_hash);
301 		return rc;
302 	}
303 
304 	/* write the value */
305 	rc = zms_write(&cf->cf_zms, name_hash + ZMS_DATA_ID_OFFSET, value, val_len);
306 	if (rc < 0) {
307 		return rc;
308 	}
309 
310 	/* write the name if required */
311 	if (write_name) {
312 		rc = zms_write(&cf->cf_zms, name_hash, name, strlen(name));
313 		if (rc < 0) {
314 			return rc;
315 		}
316 		/* write linked list structure element */
317 		settings_element.next_hash = 0;
318 		/* Verify first that the linked list last element is not broken.
319 		 * Settings subsystem uses ID that starts from ZMS_LL_HEAD_HASH_ID.
320 		 */
321 		if (cf->last_hash_id < ZMS_LL_HEAD_HASH_ID) {
322 			LOG_WRN("Linked list for hashes is broken, Trying to recover");
323 			rc = settings_zms_get_last_hash_ids(cf);
324 			if (rc < 0) {
325 				return rc;
326 			}
327 		}
328 		settings_element.previous_hash = cf->last_hash_id;
329 		rc = zms_write(&cf->cf_zms, name_hash | 1, &settings_element,
330 			       sizeof(struct settings_hash_linked_list));
331 		if (rc < 0) {
332 			return rc;
333 		}
334 		/* Now update the previous linked list element */
335 		settings_element.next_hash = name_hash | 1;
336 		settings_element.previous_hash = cf->second_to_last_hash_id;
337 		rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
338 			       sizeof(struct settings_hash_linked_list));
339 		if (rc < 0) {
340 			return rc;
341 		}
342 		cf->second_to_last_hash_id = cf->last_hash_id;
343 		cf->last_hash_id = name_hash | 1;
344 	}
345 
346 	return 0;
347 }
348 
settings_zms_get_last_hash_ids(struct settings_zms * cf)349 static int settings_zms_get_last_hash_ids(struct settings_zms *cf)
350 {
351 	struct settings_hash_linked_list settings_element;
352 	uint32_t ll_last_hash_id = ZMS_LL_HEAD_HASH_ID;
353 	int rc = 0;
354 
355 	cf->hash_collision_num = 0;
356 	do {
357 		rc = zms_read(&cf->cf_zms, ll_last_hash_id, &settings_element,
358 			      sizeof(settings_element));
359 		if (rc == -ENOENT) {
360 			/* header doesn't exist or linked list broken, reinitialize the header */
361 			const struct settings_hash_linked_list settings_element = {
362 				.previous_hash = 0, .next_hash = 0};
363 			rc = zms_write(&cf->cf_zms, ZMS_LL_HEAD_HASH_ID, &settings_element,
364 				       sizeof(struct settings_hash_linked_list));
365 			if (rc < 0) {
366 				return rc;
367 			}
368 			cf->last_hash_id = ZMS_LL_HEAD_HASH_ID;
369 			cf->second_to_last_hash_id = 0;
370 			return 0;
371 		} else if (rc < 0) {
372 			return rc;
373 		}
374 
375 		/* increment hash collision number if necessary */
376 		if (ZMS_COLLISION_NUM(ll_last_hash_id) > cf->hash_collision_num) {
377 			cf->hash_collision_num = ZMS_COLLISION_NUM(ll_last_hash_id);
378 		}
379 		cf->last_hash_id = ll_last_hash_id;
380 		cf->second_to_last_hash_id = settings_element.previous_hash;
381 		ll_last_hash_id = settings_element.next_hash;
382 	} while (settings_element.next_hash);
383 
384 	return 0;
385 }
386 
387 /* Initialize the zms backend. */
settings_zms_backend_init(struct settings_zms * cf)388 static int settings_zms_backend_init(struct settings_zms *cf)
389 {
390 	int rc;
391 
392 	cf->cf_zms.flash_device = cf->flash_dev;
393 	if (cf->cf_zms.flash_device == NULL) {
394 		return -ENODEV;
395 	}
396 
397 	rc = zms_mount(&cf->cf_zms);
398 	if (rc) {
399 		return rc;
400 	}
401 
402 	cf->hash_collision_num = 0;
403 
404 	rc = settings_zms_get_last_hash_ids(cf);
405 
406 	LOG_DBG("ZMS backend initialized");
407 	return rc;
408 }
409 
settings_backend_init(void)410 int settings_backend_init(void)
411 {
412 	static struct settings_zms default_settings_zms;
413 	int rc;
414 	uint32_t cnt = 0;
415 	size_t zms_sector_size;
416 	const struct flash_area *fa;
417 	struct flash_sector hw_flash_sector;
418 	uint32_t sector_cnt = 1;
419 
420 	rc = flash_area_open(SETTINGS_PARTITION, &fa);
421 	if (rc) {
422 		return rc;
423 	}
424 
425 	rc = flash_area_get_sectors(SETTINGS_PARTITION, &sector_cnt, &hw_flash_sector);
426 	if (rc != 0 && rc != -ENOMEM) {
427 		return rc;
428 	}
429 
430 	zms_sector_size = CONFIG_SETTINGS_ZMS_SECTOR_SIZE_MULT * hw_flash_sector.fs_size;
431 
432 	if (zms_sector_size > UINT32_MAX) {
433 		return -EDOM;
434 	}
435 
436 #if defined(CONFIG_SETTINGS_ZMS_CUSTOM_SECTOR_COUNT)
437 	size_t zms_size = 0;
438 
439 	while (cnt < CONFIG_SETTINGS_ZMS_SECTOR_COUNT) {
440 		zms_size += zms_sector_size;
441 		if (zms_size > fa->fa_size) {
442 			break;
443 		}
444 		cnt++;
445 	}
446 #else
447 	cnt = fa->fa_size / zms_sector_size;
448 #endif
449 	/* initialize the zms file system structure using the page_info */
450 	default_settings_zms.cf_zms.sector_size = zms_sector_size;
451 	default_settings_zms.cf_zms.sector_count = cnt;
452 	default_settings_zms.cf_zms.offset = fa->fa_off;
453 	default_settings_zms.flash_dev = fa->fa_dev;
454 
455 	rc = settings_zms_backend_init(&default_settings_zms);
456 	if (rc) {
457 		return rc;
458 	}
459 
460 	rc = settings_zms_src(&default_settings_zms);
461 
462 	if (rc) {
463 		return rc;
464 	}
465 
466 	rc = settings_zms_dst(&default_settings_zms);
467 
468 	return rc;
469 }
470 
settings_zms_storage_get(struct settings_store * cs)471 static void *settings_zms_storage_get(struct settings_store *cs)
472 {
473 	struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
474 
475 	return &cf->cf_zms;
476 }
477