1 /*
2  * Copyright (c) 2022-2024 Libre Solar Technologies GmbH
3  * Copyright (c) 2022-2024 tado GmbH
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "frag_flash.h"
9 
10 #include <FragDecoder.h>
11 
12 #include <zephyr/dfu/mcuboot.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/storage/flash_map.h>
16 
17 LOG_MODULE_REGISTER(lorawan_frag_flash, CONFIG_LORAWAN_SERVICES_LOG_LEVEL);
18 
19 #define TARGET_IMAGE_AREA FIXED_PARTITION_ID(slot1_partition)
20 
21 struct frag_cache_entry {
22 	uint32_t addr;
23 	uint8_t data[FRAG_MAX_SIZE];
24 };
25 
26 static struct frag_cache_entry frag_cache[FRAG_MAX_REDUNDANCY];
27 static uint32_t frag_size;
28 static int cached_frags;
29 static bool use_cache;
30 
31 static const struct flash_area *fa;
32 
frag_flash_init(uint32_t fragment_size)33 int frag_flash_init(uint32_t fragment_size)
34 {
35 	int err;
36 
37 	if (fragment_size > FRAG_MAX_SIZE) {
38 		return -ENOSPC;
39 	}
40 
41 	frag_size = fragment_size;
42 	cached_frags = 0;
43 	use_cache = false;
44 
45 	err = flash_area_open(TARGET_IMAGE_AREA, &fa);
46 	if (err) {
47 		return err;
48 	}
49 
50 	LOG_DBG("Starting to erase flash area");
51 
52 	err = flash_area_erase(fa, 0, fa->fa_size);
53 
54 	LOG_DBG("Finished erasing flash area");
55 
56 	return err;
57 }
58 
frag_flash_write(uint32_t addr,uint8_t * data,uint32_t size)59 int8_t frag_flash_write(uint32_t addr, uint8_t *data, uint32_t size)
60 {
61 	int8_t err = 0;
62 
63 	if (!use_cache) {
64 		LOG_DBG("Writing %u bytes to addr 0x%X", size, addr);
65 
66 		err = flash_area_write(fa, addr, data, size);
67 	} else {
68 		LOG_DBG("Caching %u bytes for addr 0x%X", size, addr);
69 
70 		if (size != frag_size) {
71 			LOG_ERR("Invalid fragment size %d", size);
72 			return -EINVAL;
73 		}
74 
75 		/* overwrite fragment in cache if existing */
76 		for (int i = 0; i < cached_frags; i++) {
77 			if (frag_cache[i].addr == addr) {
78 				memcpy(frag_cache[i].data, data, size);
79 				return 0;
80 			}
81 		}
82 
83 		/* otherwise create new cache entry */
84 		if (cached_frags < ARRAY_SIZE(frag_cache)) {
85 			frag_cache[cached_frags].addr = addr;
86 			memcpy(frag_cache[cached_frags].data, data, size);
87 			cached_frags++;
88 		} else {
89 			LOG_ERR("Fragment cache too small");
90 			err = -ENOSPC;
91 		}
92 	}
93 
94 	return err == 0 ? 0 : -1;
95 }
96 
frag_flash_read(uint32_t addr,uint8_t * data,uint32_t size)97 int8_t frag_flash_read(uint32_t addr, uint8_t *data, uint32_t size)
98 {
99 	for (int i = 0; i < cached_frags; i++) {
100 		if (frag_cache[i].addr == addr) {
101 			memcpy(data, frag_cache[i].data, size);
102 			return 0;
103 		}
104 	}
105 
106 	return flash_area_read(fa, addr, data, size) == 0 ? 0 : -1;
107 }
108 
frag_flash_use_cache(void)109 void frag_flash_use_cache(void)
110 {
111 	use_cache = true;
112 }
113 
frag_flash_finish(void)114 void frag_flash_finish(void)
115 {
116 	int err;
117 
118 	for (int i = 0; i < cached_frags; i++) {
119 		LOG_DBG("Writing %u bytes to addr 0x%x", frag_size, frag_cache[i].addr);
120 		flash_area_write(fa, frag_cache[i].addr, frag_cache[i].data, frag_size);
121 	}
122 
123 	flash_area_close(fa);
124 
125 	err = boot_request_upgrade(BOOT_UPGRADE_TEST);
126 	if (err) {
127 		LOG_ERR("Failed to request upgrade (err %d)", err);
128 	}
129 }
130