1 /*
2  * Copyright (c) 2017-2020 Nordic Semiconductor ASA
3  * Copyright (c) 2015 Runtime Inc
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "fcb_priv.h"
9 
10 #include <errno.h>
11 #include <limits.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/flash.h>
17 #include <zephyr/fs/fcb.h>
18 
fcb_get_align(const struct fcb * fcbp)19 uint8_t fcb_get_align(const struct fcb *fcbp)
20 {
21 	uint8_t align;
22 
23 	if (fcbp->fap == NULL) {
24 		return 0;
25 	}
26 
27 	align = flash_area_align(fcbp->fap);
28 
29 	return align;
30 }
31 
fcb_flash_read(const struct fcb * fcbp,const struct flash_sector * sector,off_t off,void * dst,size_t len)32 int fcb_flash_read(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
33 		   void *dst, size_t len)
34 {
35 	int rc;
36 
37 	if (off + len > sector->fs_size) {
38 		return -EINVAL;
39 	}
40 
41 	if (fcbp->fap == NULL) {
42 		return -EIO;
43 	}
44 
45 	rc = flash_area_read(fcbp->fap, sector->fs_off + off, dst, len);
46 
47 	if (rc != 0) {
48 		return -EIO;
49 	}
50 
51 	return 0;
52 }
53 
fcb_flash_write(const struct fcb * fcbp,const struct flash_sector * sector,off_t off,const void * src,size_t len)54 int fcb_flash_write(const struct fcb *fcbp, const struct flash_sector *sector, off_t off,
55 		    const void *src, size_t len)
56 {
57 	int rc;
58 
59 	if (off + len > sector->fs_size) {
60 		return -EINVAL;
61 	}
62 
63 	if (fcbp->fap == NULL) {
64 		return -EIO;
65 	}
66 
67 	rc = flash_area_write(fcbp->fap, sector->fs_off + off, src, len);
68 
69 	if (rc != 0) {
70 		return -EIO;
71 	}
72 
73 	return 0;
74 }
75 
fcb_erase_sector(const struct fcb * fcbp,const struct flash_sector * sector)76 int fcb_erase_sector(const struct fcb *fcbp, const struct flash_sector *sector)
77 {
78 	int rc;
79 
80 	if (fcbp->fap == NULL) {
81 		return -EIO;
82 	}
83 
84 	rc = flash_area_flatten(fcbp->fap, sector->fs_off, sector->fs_size);
85 	if (rc != 0) {
86 		return -EIO;
87 	}
88 
89 	return 0;
90 }
91 
fcb_init(int f_area_id,struct fcb * fcbp)92 int fcb_init(int f_area_id, struct fcb *fcbp)
93 {
94 	struct flash_sector *sector;
95 	int rc;
96 	int i;
97 	uint8_t align;
98 	int oldest = -1, newest = -1;
99 	struct flash_sector *oldest_sector = NULL, *newest_sector = NULL;
100 	struct fcb_disk_area fda;
101 	const struct flash_parameters *fparam;
102 
103 	if (!fcbp->f_sectors || fcbp->f_sector_cnt - fcbp->f_scratch_cnt < 1) {
104 		return -EINVAL;
105 	}
106 
107 	rc = flash_area_open(f_area_id, &fcbp->fap);
108 	if (rc != 0) {
109 		return -EINVAL;
110 	}
111 
112 	fparam = flash_get_parameters(fcbp->fap->fa_dev);
113 	fcbp->f_erase_value = fparam->erase_value;
114 
115 	align = fcb_get_align(fcbp);
116 	if (align == 0U) {
117 		return -EINVAL;
118 	}
119 
120 	/* Fill last used, first used */
121 	for (i = 0; i < fcbp->f_sector_cnt; i++) {
122 		sector = &fcbp->f_sectors[i];
123 		rc = fcb_sector_hdr_read(fcbp, sector, &fda);
124 		if (rc < 0) {
125 			return rc;
126 		}
127 		if (rc == 0) {
128 			continue;
129 		}
130 		if (oldest < 0) {
131 			oldest = newest = fda.fd_id;
132 			oldest_sector = newest_sector = sector;
133 			continue;
134 		}
135 		if (FCB_ID_GT(fda.fd_id, newest)) {
136 			newest = fda.fd_id;
137 			newest_sector = sector;
138 		} else if (FCB_ID_GT(oldest, fda.fd_id)) {
139 			oldest = fda.fd_id;
140 			oldest_sector = sector;
141 		}
142 	}
143 	if (oldest < 0) {
144 		/*
145 		 * No initialized areas.
146 		 */
147 		oldest_sector = newest_sector = &fcbp->f_sectors[0];
148 		rc = fcb_sector_hdr_init(fcbp, oldest_sector, 0);
149 		if (rc) {
150 			return rc;
151 		}
152 		newest = oldest = 0;
153 	}
154 	fcbp->f_align = align;
155 	fcbp->f_oldest = oldest_sector;
156 	fcbp->f_active.fe_sector = newest_sector;
157 	fcbp->f_active.fe_elem_off = fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area));
158 	fcbp->f_active_id = newest;
159 
160 	while (1) {
161 		rc = fcb_getnext_in_sector(fcbp, &fcbp->f_active);
162 		if (rc == -ENOTSUP) {
163 			rc = 0;
164 			break;
165 		}
166 		if (rc != 0) {
167 			break;
168 		}
169 	}
170 	k_mutex_init(&fcbp->f_mtx);
171 	return rc;
172 }
173 
fcb_free_sector_cnt(struct fcb * fcbp)174 int fcb_free_sector_cnt(struct fcb *fcbp)
175 {
176 	int i;
177 	struct flash_sector *fa;
178 
179 	fa = fcbp->f_active.fe_sector;
180 	for (i = 0; i < fcbp->f_sector_cnt; i++) {
181 		fa = fcb_getnext_sector(fcbp, fa);
182 		if (fa == fcbp->f_oldest) {
183 			break;
184 		}
185 	}
186 	return i;
187 }
188 
fcb_is_empty(struct fcb * fcbp)189 int fcb_is_empty(struct fcb *fcbp)
190 {
191 	return (fcbp->f_active.fe_sector == fcbp->f_oldest &&
192 		fcbp->f_active.fe_elem_off == fcb_len_in_flash(fcbp, sizeof(struct fcb_disk_area)));
193 }
194 
195 /**
196  * Length of an element is encoded in 1 or 2 bytes.
197  * 1 byte for lengths < 128 bytes, 2 bytes for < 16384.
198  *
199  * The storage of length has been originally designed to work with 0xff erasable
200  * flash devices and gives length 0xffff special meaning: that there is no value
201  * written; this is smart way to utilize value in non-written flash to figure
202  * out where data ends. Additionally it sets highest bit of first byte of
203  * the length to 1, to mark that there is second byte to be read.
204  * Above poses some problems when non-0xff erasable flash is used. To solve
205  * the problem all length values are xored with not of erase value for given
206  * flash:
207  *	len' = len ^ ~erase_value;
208  * To obtain original value, the logic is reversed:
209  *	len = len' ^ ~erase_value;
210  *
211  * In case of 0xff erased flash this does not modify data that is written to
212  * flash; in case of other flash devices, e.g. that erase to 0x00, it allows
213  * to correctly use the first bit of byte to figure out how many bytes are there
214  * and if there is any data at all or both bytes are equal to erase value.
215  */
fcb_put_len(const struct fcb * fcbp,uint8_t * buf,uint16_t len)216 int fcb_put_len(const struct fcb *fcbp, uint8_t *buf, uint16_t len)
217 {
218 	if (len < 0x80) {
219 		buf[0] = len ^ ~fcbp->f_erase_value;
220 		return 1;
221 	} else if (len <= FCB_MAX_LEN) {
222 		buf[0] = (len | 0x80) ^ ~fcbp->f_erase_value;
223 		buf[1] = (len >> 7) ^ ~fcbp->f_erase_value;
224 		return 2;
225 	} else {
226 		return -EINVAL;
227 	}
228 }
229 
fcb_get_len(const struct fcb * fcbp,uint8_t * buf,uint16_t * len)230 int fcb_get_len(const struct fcb *fcbp, uint8_t *buf, uint16_t *len)
231 {
232 	int rc;
233 	uint8_t buf0_xor;
234 	uint8_t buf1_xor;
235 
236 	buf0_xor = buf[0] ^ ~fcbp->f_erase_value;
237 	if (buf0_xor & 0x80) {
238 		if ((buf[0] == fcbp->f_erase_value) && (buf[1] == fcbp->f_erase_value)) {
239 			return -ENOTSUP;
240 		}
241 
242 		buf1_xor = buf[1] ^ ~fcbp->f_erase_value;
243 		*len = (uint16_t)((buf0_xor & 0x7f) | ((uint16_t)buf1_xor << 7));
244 		rc = 2;
245 	} else {
246 		*len = (uint16_t)(buf0_xor);
247 		rc = 1;
248 	}
249 	return rc;
250 }
251 
252 /**
253  * Initialize erased sector for use.
254  */
fcb_sector_hdr_init(struct fcb * fcbp,struct flash_sector * sector,uint16_t id)255 int fcb_sector_hdr_init(struct fcb *fcbp, struct flash_sector *sector, uint16_t id)
256 {
257 	struct fcb_disk_area fda;
258 	int rc;
259 
260 	fda.fd_magic = fcb_flash_magic(fcbp);
261 	fda.fd_ver = fcbp->f_version;
262 	fda._pad = fcbp->f_erase_value;
263 	fda.fd_id = id;
264 
265 	rc = fcb_flash_write(fcbp, sector, 0, &fda, sizeof(fda));
266 	if (rc != 0) {
267 		return -EIO;
268 	}
269 	return 0;
270 }
271 
272 /**
273  * Checks whether FCB sector contains data or not.
274  * Returns <0 in error.
275  * Returns 0 if sector is unused;
276  * Returns 1 if sector has data.
277  */
fcb_sector_hdr_read(struct fcb * fcbp,struct flash_sector * sector,struct fcb_disk_area * fdap)278 int fcb_sector_hdr_read(struct fcb *fcbp, struct flash_sector *sector, struct fcb_disk_area *fdap)
279 {
280 	struct fcb_disk_area fda;
281 	int rc;
282 
283 	if (!fdap) {
284 		fdap = &fda;
285 	}
286 	rc = fcb_flash_read(fcbp, sector, 0, fdap, sizeof(*fdap));
287 	if (rc) {
288 		return -EIO;
289 	}
290 	if (fdap->fd_magic == MK32(fcbp->f_erase_value)) {
291 		return 0;
292 	}
293 	if (fdap->fd_magic != fcb_flash_magic(fcbp)) {
294 		return -ENOMSG;
295 	}
296 	return 1;
297 }
298 
299 /**
300  * Finds the fcb entry that gives back upto n entries at the end.
301  * @param0 ptr to fcb
302  * @param1 n number of fcb entries the user wants to get
303  * @param2 ptr to the fcb_entry to be returned
304  * @return 0 on there are any fcbs aviable; -ENOENT otherwise
305  */
fcb_offset_last_n(struct fcb * fcbp,uint8_t entries,struct fcb_entry * last_n_entry)306 int fcb_offset_last_n(struct fcb *fcbp, uint8_t entries, struct fcb_entry *last_n_entry)
307 {
308 	struct fcb_entry loc;
309 	int i;
310 	int rc;
311 
312 	/* assure a minimum amount of entries */
313 	if (!entries) {
314 		entries = 1U;
315 	}
316 
317 	i = 0;
318 	(void)memset(&loc, 0, sizeof(loc));
319 	while (!fcb_getnext(fcbp, &loc)) {
320 		if (i == 0) {
321 			/* Start from the beginning of fcb entries */
322 			*last_n_entry = loc;
323 		}
324 		/* Update last_n_entry after n entries and keep updating */
325 		else if (i > (entries - 1)) {
326 			rc = fcb_getnext(fcbp, last_n_entry);
327 
328 			if (rc) {
329 				/* A fcb history must have been erased,
330 				 * wanted entry doesn't exist anymore.
331 				 */
332 				return -ENOENT;
333 			}
334 		}
335 		i++;
336 	}
337 
338 	return (i == 0) ? -ENOENT : 0;
339 }
340 
341 /**
342  * Clear fcb
343  * @param fcb
344  * @return 0 on success; non-zero on failure
345  */
fcb_clear(struct fcb * fcbp)346 int fcb_clear(struct fcb *fcbp)
347 {
348 	int rc;
349 
350 	rc = 0;
351 	while (!fcb_is_empty(fcbp)) {
352 		rc = fcb_rotate(fcbp);
353 		if (rc) {
354 			break;
355 		}
356 	}
357 	return rc;
358 }
359