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, §or_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