1 /*
2  * Copyright (c) 2017, 2020 Nordic Semiconductor ASA
3  * Copyright (c) 2017 Linaro Limited
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define LOG_MODULE_NAME STREAM_FLASH
9 #define LOG_LEVEL CONFIG_STREAM_FLASH_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_STREAM_FLASH_LOG_LEVEL);
12 
13 #include <zephyr/types.h>
14 #include <string.h>
15 #include <zephyr/drivers/flash.h>
16 
17 #include <zephyr/storage/stream_flash.h>
18 
19 #ifdef CONFIG_STREAM_FLASH_PROGRESS
20 #include <zephyr/settings/settings.h>
21 
settings_direct_loader(const char * key,size_t len,settings_read_cb read_cb,void * cb_arg,void * param)22 static int settings_direct_loader(const char *key, size_t len,
23 				  settings_read_cb read_cb, void *cb_arg,
24 				  void *param)
25 {
26 	struct stream_flash_ctx *ctx = (struct stream_flash_ctx *) param;
27 
28 	/* Handle the subtree if it is an exact key match. */
29 	if (settings_name_next(key, NULL) == 0) {
30 		size_t bytes_written = 0;
31 		ssize_t cb_len = read_cb(cb_arg, &bytes_written,
32 				      sizeof(bytes_written));
33 
34 		if (cb_len != sizeof(ctx->bytes_written)) {
35 			LOG_ERR("Unable to read bytes_written from storage");
36 			return cb_len;
37 		}
38 
39 		/* Check that loaded progress is not outdated. */
40 		if (bytes_written >= ctx->bytes_written) {
41 			ctx->bytes_written = bytes_written;
42 		} else {
43 			LOG_WRN("Loaded outdated bytes_written %zu < %zu",
44 				bytes_written, ctx->bytes_written);
45 			return 0;
46 		}
47 
48 #ifdef CONFIG_STREAM_FLASH_ERASE
49 		int rc;
50 		struct flash_pages_info page;
51 		off_t offset = (off_t) (ctx->offset + ctx->bytes_written) - 1;
52 
53 		/* Update the last erased page to avoid deleting already
54 		 * written data.
55 		 */
56 		if (ctx->bytes_written > 0) {
57 			rc = flash_get_page_info_by_offs(ctx->fdev, offset,
58 							 &page);
59 			if (rc != 0) {
60 				LOG_ERR("Error %d while getting page info", rc);
61 				return rc;
62 			}
63 			ctx->erased_up_to = page.start_offset + page.size - ctx->offset;
64 		} else {
65 			ctx->erased_up_to = 0;
66 		}
67 #endif /* CONFIG_STREAM_FLASH_ERASE */
68 	}
69 
70 	return 0;
71 }
72 
73 #endif /* CONFIG_STREAM_FLASH_PROGRESS */
74 
75 /* Will erase at most what is required to append given size, If already
76  * erased space can accommodate requested size, then no new page will
77  * be erased.
78  * Note that this function is supposed to fulfill hardware requirements
79  * for erase prior to write or allow faster writes when hardware supports
80  * erase as means to speed up writes, and will do nothing on devices that
81  * that not require erase before write.
82  */
stream_flash_erase_to_append(struct stream_flash_ctx * ctx,size_t size)83 static int stream_flash_erase_to_append(struct stream_flash_ctx *ctx, size_t size)
84 {
85 	int rc = 0;
86 #if defined(CONFIG_STREAM_FLASH_ERASE)
87 	struct flash_pages_info page;
88 #if defined(CONFIG_STREAM_FLASH_ERASE_ONLY_WHEN_SUPPORTED)
89 	const struct flash_parameters *fparams = flash_get_parameters(ctx->fdev);
90 #endif
91 
92 	/* ctx->erased_up_to points to first offset that has not yet been erased,
93 	 * relative to ctx->offset.
94 	 */
95 	if (ctx->bytes_written + size <= ctx->erased_up_to) {
96 		return 0;
97 	}
98 
99 	/* Trying to append beyond available range? */
100 	if (ctx->bytes_written + size > ctx->available) {
101 		return -ERANGE;
102 	}
103 
104 #if defined(CONFIG_STREAM_FLASH_ERASE_ONLY_WHEN_SUPPORTED)
105 	/* Stream flash does not rely on erase, it does it when device needs it */
106 	if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
107 		return 0;
108 	}
109 #endif
110 	/* Note that ctx->erased_up_to is offset relative to ctx->offset that
111 	 * points to first byte not yet erased.
112 	 */
113 	rc = flash_get_page_info_by_offs(ctx->fdev, ctx->offset + ctx->erased_up_to, &page);
114 	if (rc != 0) {
115 		LOG_ERR("Error %d while getting page info", rc);
116 		return rc;
117 	}
118 
119 	LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
120 
121 	rc = flash_erase(ctx->fdev, page.start_offset, page.size);
122 
123 	if (rc != 0) {
124 		LOG_ERR("Error %d while erasing page", rc);
125 	} else {
126 		ctx->erased_up_to += page.size;
127 	}
128 #endif
129 	return rc;
130 }
131 
132 #if defined(CONFIG_STREAM_FLASH_ERASE)
133 
stream_flash_erase_page(struct stream_flash_ctx * ctx,off_t off)134 int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off)
135 {
136 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
137 	int rc;
138 	struct flash_pages_info page;
139 
140 	if (off < ctx->offset || (off - ctx->offset) >= ctx->available) {
141 		LOG_ERR("Offset out of designated range");
142 		return -ERANGE;
143 	}
144 
145 	/* Do not allow pages that have already been erased */
146 	if ((off - ctx->offset) < ctx->erased_up_to) {
147 		return -EINVAL;
148 	}
149 
150 #if defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE)
151 	/* There are both types of devices */
152 	const struct flash_parameters *fparams = flash_get_parameters(ctx->fdev);
153 
154 	/* Stream flash does not rely on erase, it does it when device needs it */
155 	if (!(flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT)) {
156 		return 0;
157 	}
158 #endif
159 	rc = flash_get_page_info_by_offs(ctx->fdev, off, &page);
160 	if (rc != 0) {
161 		LOG_ERR("Error %d while getting page info", rc);
162 		return rc;
163 	}
164 
165 	if (ctx->erased_up_to >= page.start_offset + page.size) {
166 		return 0;
167 	}
168 
169 	LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
170 
171 	rc = flash_erase(ctx->fdev, page.start_offset, page.size);
172 
173 	if (rc != 0) {
174 		LOG_ERR("Error %d while erasing page", rc);
175 	} else {
176 		ctx->erased_up_to = page.start_offset + page.size;
177 	}
178 
179 	return rc;
180 #else
181 	return 0;
182 #endif
183 }
184 
185 #endif /* CONFIG_STREAM_FLASH_ERASE */
186 
flash_sync(struct stream_flash_ctx * ctx)187 static int flash_sync(struct stream_flash_ctx *ctx)
188 {
189 	int rc = 0;
190 	size_t write_addr = ctx->offset + ctx->bytes_written;
191 	size_t buf_bytes_aligned;
192 	size_t fill_length;
193 	uint8_t filler;
194 
195 
196 	if (ctx->buf_bytes == 0) {
197 		return 0;
198 	}
199 
200 	if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
201 
202 		rc = stream_flash_erase_to_append(ctx, ctx->buf_bytes);
203 		if (rc < 0) {
204 			LOG_ERR("stream_flash_forward_erase %d range=0x%08zx",
205 				rc, ctx->buf_bytes);
206 			return rc;
207 		}
208 	}
209 
210 	fill_length = ctx->write_block_size;
211 	if (ctx->buf_bytes % fill_length) {
212 		fill_length -= ctx->buf_bytes % fill_length;
213 		filler = ctx->erase_value;
214 
215 		memset(ctx->buf + ctx->buf_bytes, filler, fill_length);
216 	} else {
217 		fill_length = 0;
218 	}
219 
220 	buf_bytes_aligned = ctx->buf_bytes + fill_length;
221 	rc = flash_write(ctx->fdev, write_addr, ctx->buf, buf_bytes_aligned);
222 
223 	if (rc != 0) {
224 		LOG_ERR("flash_write error %d offset=0x%08zx", rc,
225 			write_addr);
226 		return rc;
227 	}
228 
229 #if defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
230 
231 	if (ctx->callback) {
232 		/* Invert to ensure that caller is able to discover a faulty
233 		 * flash_read() even if no error code is returned.
234 		 */
235 		for (int i = 0; i < ctx->buf_bytes; i++) {
236 			ctx->buf[i] = ~ctx->buf[i];
237 		}
238 
239 		rc = flash_read(ctx->fdev, write_addr, ctx->buf,
240 				ctx->buf_bytes);
241 		if (rc != 0) {
242 			LOG_ERR("flash read failed: %d", rc);
243 			return rc;
244 		}
245 
246 		rc = ctx->callback(ctx->buf, ctx->buf_bytes, write_addr);
247 		if (rc != 0) {
248 			LOG_ERR("callback failed: %d", rc);
249 			return rc;
250 		}
251 	}
252 
253 #endif
254 
255 	ctx->bytes_written += ctx->buf_bytes;
256 	ctx->buf_bytes = 0U;
257 
258 	return rc;
259 }
260 
stream_flash_buffered_write(struct stream_flash_ctx * ctx,const uint8_t * data,size_t len,bool flush)261 int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const uint8_t *data,
262 				size_t len, bool flush)
263 {
264 	int processed = 0;
265 	int rc = 0;
266 	int buf_empty_bytes;
267 
268 	if (!ctx) {
269 		return -EFAULT;
270 	}
271 
272 	if (ctx->bytes_written + ctx->buf_bytes + len > ctx->available) {
273 		return -ENOMEM;
274 	}
275 
276 	while ((len - processed) >=
277 	       (buf_empty_bytes = ctx->buf_len - ctx->buf_bytes)) {
278 		memcpy(ctx->buf + ctx->buf_bytes, data + processed,
279 		       buf_empty_bytes);
280 
281 		ctx->buf_bytes = ctx->buf_len;
282 		rc = flash_sync(ctx);
283 
284 		if (rc != 0) {
285 			return rc;
286 		}
287 
288 		processed += buf_empty_bytes;
289 	}
290 
291 	/* place rest of the data into ctx->buf */
292 	if (processed < len) {
293 		memcpy(ctx->buf + ctx->buf_bytes,
294 		       data + processed, len - processed);
295 		ctx->buf_bytes += len - processed;
296 	}
297 
298 	if (flush && ctx->buf_bytes > 0) {
299 		rc = flash_sync(ctx);
300 	}
301 
302 	return rc;
303 }
304 
stream_flash_bytes_written(const struct stream_flash_ctx * ctx)305 size_t stream_flash_bytes_written(const struct stream_flash_ctx *ctx)
306 {
307 	return ctx->bytes_written;
308 }
309 
310 #ifdef CONFIG_STREAM_FLASH_INSPECT
311 struct _inspect_flash {
312 	size_t buf_len;
313 	size_t total_size;
314 };
315 
find_flash_total_size(const struct flash_pages_info * info,void * data)316 static bool find_flash_total_size(const struct flash_pages_info *info,
317 				  void *data)
318 {
319 	struct _inspect_flash *ctx = (struct _inspect_flash *) data;
320 
321 	if (ctx->buf_len > info->size) {
322 		LOG_ERR("Buffer size is bigger than page");
323 		ctx->total_size = 0;
324 		return false;
325 	}
326 
327 	ctx->total_size += info->size;
328 
329 	return true;
330 }
331 
332 /* Internal function make sure *ctx is not NULL, no redundant check here */
inspect_device(const struct stream_flash_ctx * ctx)333 static inline int inspect_device(const struct stream_flash_ctx *ctx)
334 {
335 	struct _inspect_flash inspect_flash_ctx = {
336 		.buf_len = ctx->buf_len,
337 		.total_size = 0
338 	};
339 
340 	/* Calculate the total size of the flash device, and inspect pages
341 	 * while doing so.
342 	 */
343 	flash_page_foreach(ctx->fdev, find_flash_total_size, &inspect_flash_ctx);
344 
345 	if (inspect_flash_ctx.total_size == 0) {
346 		LOG_ERR("Device seems to have 0 size");
347 		return -EFAULT;
348 	} else if (inspect_flash_ctx.total_size < (ctx->offset + ctx->available)) {
349 		LOG_ERR("Requested range overflows device size");
350 		return -EFAULT;
351 	}
352 
353 	return 0;
354 }
355 #else
inspect_device(const struct stream_flash_ctx * ctx)356 static inline int inspect_device(const struct stream_flash_ctx *ctx)
357 {
358 	ARG_UNUSED(ctx);
359 	return 0;
360 }
361 #endif
362 
stream_flash_init(struct stream_flash_ctx * ctx,const struct device * fdev,uint8_t * buf,size_t buf_len,size_t offset,size_t size,stream_flash_callback_t cb)363 int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
364 		      uint8_t *buf, size_t buf_len, size_t offset, size_t size,
365 		      stream_flash_callback_t cb)
366 {
367 	const struct flash_parameters *params;
368 
369 	if (!ctx || !fdev || !buf) {
370 		return -EFAULT;
371 	}
372 
373 	params = flash_get_parameters(fdev);
374 
375 	if (buf_len % params->write_block_size) {
376 		LOG_ERR("Buffer size is not aligned to minimal write-block-size");
377 		return -EFAULT;
378 	}
379 
380 	if (offset % params->write_block_size) {
381 		LOG_ERR("Incorrect parameter");
382 		return -EFAULT;
383 	}
384 
385 	if (size == 0 || size % params->write_block_size) {
386 		LOG_ERR("Size is incorrect");
387 		return -EFAULT;
388 	}
389 
390 	ctx->fdev = fdev;
391 	ctx->buf = buf;
392 	ctx->buf_len = buf_len;
393 	ctx->bytes_written = 0;
394 	ctx->buf_bytes = 0U;
395 	ctx->offset = offset;
396 	ctx->available = size;
397 	ctx->write_block_size = params->write_block_size;
398 
399 #if !defined(CONFIG_STREAM_FLASH_POST_WRITE_CALLBACK)
400 	ARG_UNUSED(cb);
401 #else
402 	ctx->callback = cb;
403 #endif
404 
405 
406 #ifdef CONFIG_STREAM_FLASH_ERASE
407 	ctx->erased_up_to = 0;
408 #endif
409 	ctx->erase_value = params->erase_value;
410 
411 	/* Inspection is deliberately done once context has been filled in */
412 	if (IS_ENABLED(CONFIG_STREAM_FLASH_INSPECT)) {
413 		int ret  = inspect_device(ctx);
414 
415 		if (ret != 0) {
416 			/* No log here, the inspect_device already does logging */
417 			return ret;
418 		}
419 	}
420 
421 
422 	return 0;
423 }
424 
425 #ifdef CONFIG_STREAM_FLASH_PROGRESS
stream_flash_settings_init(void)426 static int stream_flash_settings_init(void)
427 {
428 	int rc = settings_subsys_init();
429 
430 	if (rc != 0) {
431 		LOG_ERR("Error %d initializing settings subsystem", rc);
432 	}
433 	return rc;
434 }
435 
stream_flash_progress_load(struct stream_flash_ctx * ctx,const char * settings_key)436 int stream_flash_progress_load(struct stream_flash_ctx *ctx,
437 			       const char *settings_key)
438 {
439 	if (!ctx || !settings_key) {
440 		return -EFAULT;
441 	}
442 
443 	int rc = stream_flash_settings_init();
444 
445 	if (rc == 0) {
446 		rc = settings_load_subtree_direct(settings_key, settings_direct_loader,
447 						  (void *)ctx);
448 	}
449 
450 	if (rc != 0) {
451 		LOG_ERR("Error %d while loading progress for \"%s\"",
452 			rc, settings_key);
453 	}
454 
455 	return rc;
456 }
457 
stream_flash_progress_save(const struct stream_flash_ctx * ctx,const char * settings_key)458 int stream_flash_progress_save(const struct stream_flash_ctx *ctx,
459 			       const char *settings_key)
460 {
461 	if (!ctx || !settings_key) {
462 		return -EFAULT;
463 	}
464 
465 	int rc = stream_flash_settings_init();
466 
467 	if (rc == 0) {
468 		rc = settings_save_one(settings_key, &ctx->bytes_written,
469 				       sizeof(ctx->bytes_written));
470 	}
471 
472 	if (rc != 0) {
473 		LOG_ERR("Error %d while storing progress for \"%s\"",
474 			rc, settings_key);
475 	}
476 
477 	return rc;
478 }
479 
stream_flash_progress_clear(const struct stream_flash_ctx * ctx,const char * settings_key)480 int stream_flash_progress_clear(const struct stream_flash_ctx *ctx,
481 				const char *settings_key)
482 {
483 	if (!ctx || !settings_key) {
484 		return -EFAULT;
485 	}
486 
487 	int rc = stream_flash_settings_init();
488 
489 	if (rc == 0) {
490 		rc = settings_delete(settings_key);
491 	}
492 
493 	if (rc != 0) {
494 		LOG_ERR("Error %d while deleting progress for \"%s\"",
495 			rc, settings_key);
496 	}
497 
498 	return rc;
499 }
500 
501 #endif  /* CONFIG_STREAM_FLASH_PROGRESS */
502