1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8 #include <errno.h>
9 #include <limits.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <string.h>
13 /* FIXME: use k_off_t instead of off_t */
14 #include <sys/types.h>
15
16 #include <zephyr/drivers/flash.h>
17 #include <zephyr/internal/syscall_handler.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/sys/math_extras.h>
20
21 LOG_MODULE_REGISTER(flash, CONFIG_FLASH_LOG_LEVEL);
22
z_impl_flash_fill(const struct device * dev,uint8_t val,off_t offset,size_t size)23 int z_impl_flash_fill(const struct device *dev, uint8_t val, off_t offset,
24 size_t size)
25 {
26 uint8_t filler[CONFIG_FLASH_FILL_BUFFER_SIZE];
27 const struct flash_driver_api *api =
28 (const struct flash_driver_api *)dev->api;
29 const struct flash_parameters *fparams = api->get_parameters(dev);
30 int rc = 0;
31 size_t stored = 0;
32
33 if (sizeof(filler) < fparams->write_block_size) {
34 LOG_ERR("Size of CONFIG_FLASH_FILL_BUFFER_SIZE");
35 return -EINVAL;
36 }
37 /* The flash_write will, probably, check write alignment but this
38 * is too late, as we write datain chunks; data alignment may be
39 * broken by the size of the last chunk, that is why the check
40 * happens here too.
41 * Note that we have no way to check whether offset and size are
42 * are correct, as such info is only available at the level of
43 * a driver, so only basic check on offset.
44 */
45 if (offset < 0) {
46 LOG_ERR("Negative offset not allowed\n");
47 return -EINVAL;
48 }
49 if ((size | (size_t)offset) & (fparams->write_block_size - 1)) {
50 LOG_ERR("Incorrect size or offset alignment, expected %zx\n",
51 fparams->write_block_size);
52 return -EINVAL;
53 }
54
55 memset(filler, val, sizeof(filler));
56
57 while (stored < size) {
58 size_t chunk = MIN(sizeof(filler), size - stored);
59
60 rc = api->write(dev, offset + stored, filler, chunk);
61 if (rc < 0) {
62 LOG_DBG("Fill to dev %p failed at offset 0x%zx\n",
63 dev, (size_t)offset + stored);
64 break;
65 }
66 stored += chunk;
67 }
68 return rc;
69 }
70
z_impl_flash_flatten(const struct device * dev,off_t offset,size_t size)71 int z_impl_flash_flatten(const struct device *dev, off_t offset, size_t size)
72 {
73 const struct flash_driver_api *api =
74 (const struct flash_driver_api *)dev->api;
75 __maybe_unused const struct flash_parameters *params = api->get_parameters(dev);
76
77 #if defined(CONFIG_FLASH_HAS_EXPLICIT_ERASE)
78 if ((flash_params_get_erase_cap(params) & FLASH_ERASE_C_EXPLICIT) &&
79 api->erase != NULL) {
80 return api->erase(dev, offset, size);
81 }
82 #endif
83
84 #if defined(CONFIG_FLASH_HAS_NO_EXPLICIT_ERASE)
85 return flash_fill(dev, params->erase_value, offset, size);
86 #else
87 return -ENOSYS;
88 #endif
89 }
90
91 /* note: caller must first check for positivity (>=0) */
off_add_overflow(off_t offset,off_t size,off_t * result)92 static inline bool off_add_overflow(off_t offset, off_t size, off_t *result)
93 {
94 BUILD_ASSERT((sizeof(off_t) == sizeof(uint32_t)) || (sizeof(off_t) == sizeof(uint64_t)));
95
96 if (sizeof(off_t) == sizeof(uint32_t)) {
97 uint32_t end;
98
99 /* account for signedness of off_t due to lack of s32_add_overflow() */
100 if (u32_add_overflow((uint32_t)offset, (uint32_t)size, &end) || (end > INT32_MAX)) {
101 return true;
102 }
103 } else if (sizeof(off_t) == sizeof(uint64_t)) {
104 uint64_t end;
105
106 /* account for signedness of off_t due to lack of s64_add_overflow() */
107 if (u64_add_overflow((uint64_t)offset, (uint64_t)size, &end) || (end > INT64_MAX)) {
108 return true;
109 }
110 }
111
112 return false;
113 }
114
115 /* note: caller must first check for overflow */
flash_ranges_overlap(off_t a_start,off_t a_size,off_t b_start,off_t b_size)116 static inline bool flash_ranges_overlap(off_t a_start, off_t a_size, off_t b_start, off_t b_size)
117 {
118 off_t a_end = a_start + a_size;
119 off_t b_end = b_start + b_size;
120
121 return (a_start < b_end) && (a_end > b_start);
122 }
123
z_impl_flash_copy(const struct device * src_dev,off_t src_offset,const struct device * dst_dev,off_t dst_offset,off_t size,uint8_t * buf,size_t buf_size)124 int z_impl_flash_copy(const struct device *src_dev, off_t src_offset, const struct device *dst_dev,
125 off_t dst_offset, off_t size, uint8_t *buf, size_t buf_size)
126 {
127 int ret;
128 off_t end;
129 size_t write_size;
130
131 if ((src_offset < 0) || (dst_offset < 0) || (size < 0) || (buf == NULL) ||
132 (buf_size == 0) || off_add_overflow(src_offset, size, &end) ||
133 off_add_overflow(dst_offset, size, &end)) {
134 LOG_DBG("invalid argument");
135 return -EINVAL;
136 }
137
138 if (src_dev == dst_dev) {
139 if (src_offset == dst_offset) {
140 return 0;
141 }
142
143 if (flash_ranges_overlap(src_offset, size, dst_offset, size) != 0) {
144 return -EINVAL;
145 }
146 }
147
148 if (!device_is_ready(src_dev)) {
149 LOG_DBG("%s device not ready", "src");
150 return -ENODEV;
151 }
152
153 if (!device_is_ready(dst_dev)) {
154 LOG_DBG("%s device not ready", "dst");
155 return -ENODEV;
156 }
157
158 write_size = flash_get_write_block_size(dst_dev);
159 if ((buf_size < write_size) || ((buf_size % write_size) != 0)) {
160 LOG_DBG("buf size %zu is incompatible with write_size of %zu", buf_size,
161 write_size);
162 return -EINVAL;
163 }
164
165 for (uint32_t offs = 0, N = size, bytes_read = 0, bytes_left = N; offs < N;
166 offs += bytes_read, bytes_left -= bytes_read) {
167
168 if (bytes_left < write_size) {
169 const struct flash_driver_api *api =
170 (const struct flash_driver_api *)dst_dev->api;
171 const struct flash_parameters *params = api->get_parameters(dst_dev);
172
173 memset(buf, params->erase_value, write_size);
174 }
175 bytes_read = MIN(MAX(bytes_left, write_size), buf_size);
176 ret = flash_read(src_dev, src_offset + offs, buf, bytes_read);
177 if (ret < 0) {
178 LOG_DBG("%s() failed at offset %lx: %d", "flash_read",
179 (long)(src_offset + offs), ret);
180 return ret;
181 }
182
183 ret = flash_write(dst_dev, dst_offset + offs, buf, bytes_read);
184 if (ret < 0) {
185 LOG_DBG("%s() failed at offset %lx: %d", "flash_write",
186 (long)(src_offset + offs), ret);
187 return ret;
188 }
189 }
190
191 return 0;
192 }
193
194 #ifdef CONFIG_USERSPACE
z_vrfy_flash_copy(const struct device * src_dev,off_t src_offset,const struct device * dst_dev,off_t dst_offset,off_t size,uint8_t * buf,size_t buf_size)195 int z_vrfy_flash_copy(const struct device *src_dev, off_t src_offset, const struct device *dst_dev,
196 off_t dst_offset, off_t size, uint8_t *buf, size_t buf_size)
197 {
198 K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, buf_size));
199 return z_impl_flash_copy(src_dev, src_offset, dst_dev, dst_offset, size, buf, buf_size);
200 }
201 #include <zephyr/syscalls/flash_copy_mrsh.c>
202 #endif
203