Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /* Copyright 2013-2018 IBM Corp. */
3 : :
4 : : #include <stdlib.h>
5 : : #include <unistd.h>
6 : : #include <stdio.h>
7 : : #include <stdbool.h>
8 : : #include <errno.h>
9 : : #include <string.h>
10 : : #include <inttypes.h>
11 : :
12 : : #include <libflash/libflash.h>
13 : : #include <libflash/errors.h>
14 : :
15 : : #include "blocklevel.h"
16 : : #include "ecc.h"
17 : :
18 : : #define PROT_REALLOC_NUM 25
19 : :
20 : : /* This function returns tristate values.
21 : : * 1 - The region is ECC protected
22 : : * 0 - The region is not ECC protected
23 : : * -1 - Partially protected
24 : : */
25 : 21 : static int ecc_protected(struct blocklevel_device *bl, uint64_t pos, uint64_t len, uint64_t *start)
26 : : {
27 : 21 : int i;
28 : :
29 : : /* Length of 0 is nonsensical so add 1 */
30 : 21 : if (len == 0)
31 : : len = 1;
32 : :
33 : 21 : for (i = 0; i < bl->ecc_prot.n_prot; i++) {
34 : : /* Fits entirely within the range */
35 : 0 : if (bl->ecc_prot.prot[i].start <= pos &&
36 : 0 : bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len >= pos + len) {
37 : 0 : if (start)
38 : 0 : *start = bl->ecc_prot.prot[i].start;
39 : 0 : return 1;
40 : : }
41 : :
42 : : /*
43 : : * Even if ranges are merged we can't currently guarantee two
44 : : * contiguous regions are sanely ECC protected so a partial fit
45 : : * is no good.
46 : : */
47 : 0 : if ((bl->ecc_prot.prot[i].start >= pos && bl->ecc_prot.prot[i].start < pos + len) ||
48 : 0 : (bl->ecc_prot.prot[i].start <= pos &&
49 : 0 : bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len > pos)) {
50 : 0 : if (start)
51 : 0 : *start = bl->ecc_prot.prot[i].start;
52 : 0 : return -1;
53 : : }
54 : : }
55 : : return 0;
56 : : }
57 : :
58 : 0 : static uint64_t with_ecc_pos(uint64_t ecc_start, uint64_t pos)
59 : : {
60 : 0 : return pos + ((pos - ecc_start) / (BYTES_PER_ECC));
61 : : }
62 : :
63 : 3369 : static int reacquire(struct blocklevel_device *bl)
64 : : {
65 : 3369 : if (!bl->keep_alive && bl->reacquire)
66 : 0 : return bl->reacquire(bl);
67 : : return 0;
68 : : }
69 : :
70 : 3369 : static int release(struct blocklevel_device *bl)
71 : : {
72 : 3369 : int rc = 0;
73 : 3369 : if (!bl->keep_alive && bl->release) {
74 : : /* This is the error return path a lot, preserve errno */
75 : 0 : int err = errno;
76 : 0 : rc = bl->release(bl);
77 : 0 : errno = err;
78 : : }
79 : 3369 : return rc;
80 : : }
81 : :
82 : 18 : int blocklevel_raw_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
83 : : {
84 : 18 : int rc;
85 : :
86 : 18 : FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
87 : 18 : if (!bl || !bl->read || !buf) {
88 : 0 : errno = EINVAL;
89 : 0 : return FLASH_ERR_PARM_ERROR;
90 : : }
91 : :
92 : 18 : rc = reacquire(bl);
93 : 18 : if (rc)
94 : : return rc;
95 : :
96 : 18 : rc = bl->read(bl, pos, buf, len);
97 : :
98 : 18 : release(bl);
99 : :
100 : 18 : return rc;
101 : : }
102 : :
103 : 18 : int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
104 : : {
105 : 18 : int rc, ecc_protection;
106 : 18 : struct ecc64 *buffer;
107 : 18 : uint64_t ecc_pos, ecc_start, ecc_diff, ecc_len;
108 : :
109 : 18 : FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
110 : 18 : if (!bl || !buf) {
111 : 0 : errno = EINVAL;
112 : 0 : return FLASH_ERR_PARM_ERROR;
113 : : }
114 : :
115 : 18 : ecc_protection = ecc_protected(bl, pos, len, &ecc_start);
116 : :
117 : 18 : FL_DBG("%s: 0x%" PRIx64 " for 0x%" PRIx64 " ecc=%s\n",
118 : : __func__, pos, len, ecc_protection ?
119 : : (ecc_protection == -1 ? "partial" : "yes") : "no");
120 : :
121 : 18 : if (!ecc_protection)
122 : 18 : return blocklevel_raw_read(bl, pos, buf, len);
123 : :
124 : : /*
125 : : * The region we're reading to has both ecc protection and not.
126 : : * Perhaps one day in the future blocklevel can cope with this.
127 : : */
128 : 0 : if (ecc_protection == -1) {
129 : 0 : FL_ERR("%s: Can't cope with partial ecc\n", __func__);
130 : 0 : errno = EINVAL;
131 : 0 : return FLASH_ERR_PARM_ERROR;
132 : : }
133 : :
134 : 0 : pos = with_ecc_pos(ecc_start, pos);
135 : :
136 : 0 : ecc_pos = ecc_buffer_align(ecc_start, pos);
137 : 0 : ecc_diff = pos - ecc_pos;
138 : 0 : ecc_len = ecc_buffer_size(len + ecc_diff);
139 : :
140 : 0 : FL_DBG("%s: adjusted_pos: 0x%" PRIx64 ", ecc_pos: 0x%" PRIx64
141 : : ", ecc_diff: 0x%" PRIx64 ", ecc_len: 0x%" PRIx64 "\n",
142 : : __func__, pos, ecc_pos, ecc_diff, ecc_len);
143 : 0 : buffer = malloc(ecc_len);
144 : 0 : if (!buffer) {
145 : 0 : errno = ENOMEM;
146 : 0 : rc = FLASH_ERR_MALLOC_FAILED;
147 : 0 : goto out;
148 : : }
149 : :
150 : 0 : rc = blocklevel_raw_read(bl, ecc_pos, buffer, ecc_len);
151 : 0 : if (rc)
152 : 0 : goto out;
153 : :
154 : : /*
155 : : * Could optimise and simply call memcpy_from_ecc() if ecc_diff
156 : : * == 0 but _unaligned checks and bascially does that for us
157 : : */
158 : 0 : if (memcpy_from_ecc_unaligned(buf, buffer, len, ecc_diff)) {
159 : 0 : errno = EBADF;
160 : 0 : rc = FLASH_ERR_ECC_INVALID;
161 : : }
162 : :
163 : 0 : out:
164 : 0 : free(buffer);
165 : 0 : return rc;
166 : : }
167 : :
168 : 3 : int blocklevel_raw_write(struct blocklevel_device *bl, uint64_t pos,
169 : : const void *buf, uint64_t len)
170 : : {
171 : 3 : int rc;
172 : :
173 : 3 : FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
174 : 3 : if (!bl || !bl->write || !buf) {
175 : 0 : errno = EINVAL;
176 : 0 : return FLASH_ERR_PARM_ERROR;
177 : : }
178 : :
179 : 3 : rc = reacquire(bl);
180 : 3 : if (rc)
181 : : return rc;
182 : :
183 : 3 : rc = bl->write(bl, pos, buf, len);
184 : :
185 : 3 : release(bl);
186 : :
187 : 3 : return rc;
188 : : }
189 : :
190 : 3 : int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf,
191 : : uint64_t len)
192 : : {
193 : 3 : int rc, ecc_protection;
194 : 3 : struct ecc64 *buffer;
195 : 3 : uint64_t ecc_len;
196 : 3 : uint64_t ecc_start, ecc_pos, ecc_diff;
197 : :
198 : 3 : FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len);
199 : 3 : if (!bl || !buf) {
200 : 0 : errno = EINVAL;
201 : 0 : return FLASH_ERR_PARM_ERROR;
202 : : }
203 : :
204 : 3 : ecc_protection = ecc_protected(bl, pos, len, &ecc_start);
205 : :
206 : 3 : FL_DBG("%s: 0x%" PRIx64 " for 0x%" PRIx64 " ecc=%s\n",
207 : : __func__, pos, len, ecc_protection ?
208 : : (ecc_protection == -1 ? "partial" : "yes") : "no");
209 : :
210 : 3 : if (!ecc_protection)
211 : 3 : return blocklevel_raw_write(bl, pos, buf, len);
212 : :
213 : : /*
214 : : * The region we're writing to has both ecc protection and not.
215 : : * Perhaps one day in the future blocklevel can cope with this.
216 : : */
217 : 0 : if (ecc_protection == -1) {
218 : 0 : FL_ERR("%s: Can't cope with partial ecc\n", __func__);
219 : 0 : errno = EINVAL;
220 : 0 : return FLASH_ERR_PARM_ERROR;
221 : : }
222 : :
223 : 0 : pos = with_ecc_pos(ecc_start, pos);
224 : :
225 : 0 : ecc_pos = ecc_buffer_align(ecc_start, pos);
226 : 0 : ecc_diff = pos - ecc_pos;
227 : 0 : ecc_len = ecc_buffer_size(len + ecc_diff);
228 : :
229 : 0 : FL_DBG("%s: adjusted_pos: 0x%" PRIx64 ", ecc_pos: 0x%" PRIx64
230 : : ", ecc_diff: 0x%" PRIx64 ", ecc_len: 0x%" PRIx64 "\n",
231 : : __func__, pos, ecc_pos, ecc_diff, ecc_len);
232 : :
233 : 0 : buffer = malloc(ecc_len);
234 : 0 : if (!buffer) {
235 : 0 : errno = ENOMEM;
236 : 0 : rc = FLASH_ERR_MALLOC_FAILED;
237 : 0 : goto out;
238 : : }
239 : :
240 : 0 : if (ecc_diff) {
241 : 0 : uint64_t start_chunk = ecc_diff;
242 : 0 : uint64_t end_chunk = BYTES_PER_ECC - ecc_diff;
243 : 0 : uint64_t end_len = ecc_len - end_chunk;
244 : :
245 : : /*
246 : : * Read the start bytes that memcpy_to_ecc_unaligned() will need
247 : : * to calculate the first ecc byte
248 : : */
249 : 0 : rc = blocklevel_raw_read(bl, ecc_pos, buffer, start_chunk);
250 : 0 : if (rc) {
251 : 0 : errno = EBADF;
252 : 0 : rc = FLASH_ERR_ECC_INVALID;
253 : 0 : goto out;
254 : : }
255 : :
256 : : /*
257 : : * Read the end bytes that memcpy_to_ecc_unaligned() will need
258 : : * to calculate the last ecc byte
259 : : */
260 : 0 : rc = blocklevel_raw_read(bl, ecc_pos + end_len, ((char *)buffer) + end_len,
261 : : end_chunk);
262 : 0 : if (rc) {
263 : 0 : errno = EBADF;
264 : 0 : rc = FLASH_ERR_ECC_INVALID;
265 : 0 : goto out;
266 : : }
267 : :
268 : 0 : if (memcpy_to_ecc_unaligned(buffer, buf, len, ecc_diff)) {
269 : 0 : errno = EBADF;
270 : 0 : rc = FLASH_ERR_ECC_INVALID;
271 : 0 : goto out;
272 : : }
273 : : } else {
274 : 0 : if (memcpy_to_ecc(buffer, buf, len)) {
275 : 0 : errno = EBADF;
276 : 0 : rc = FLASH_ERR_ECC_INVALID;
277 : 0 : goto out;
278 : : }
279 : : }
280 : 0 : rc = blocklevel_raw_write(bl, pos, buffer, ecc_len);
281 : :
282 : 0 : out:
283 : 0 : free(buffer);
284 : 0 : return rc;
285 : : }
286 : :
287 : 3328 : int blocklevel_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
288 : : {
289 : 3328 : int rc;
290 : 3328 : if (!bl || !bl->erase) {
291 : 0 : errno = EINVAL;
292 : 0 : return FLASH_ERR_PARM_ERROR;
293 : : }
294 : :
295 : 3328 : FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len);
296 : :
297 : : /* Programmer may be making a horrible mistake without knowing it */
298 : 3328 : if (pos & bl->erase_mask) {
299 : 0 : FL_ERR("blocklevel_erase: pos (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
300 : : pos, bl->erase_mask + 1);
301 : 0 : return FLASH_ERR_ERASE_BOUNDARY;
302 : : }
303 : :
304 : 3328 : if (len & bl->erase_mask) {
305 : 0 : FL_ERR("blocklevel_erase: len (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
306 : : len, bl->erase_mask + 1);
307 : 0 : return FLASH_ERR_ERASE_BOUNDARY;
308 : : }
309 : :
310 : 3328 : rc = reacquire(bl);
311 : 3328 : if (rc)
312 : : return rc;
313 : :
314 : 3328 : rc = bl->erase(bl, pos, len);
315 : :
316 : 3328 : release(bl);
317 : :
318 : 3328 : return rc;
319 : : }
320 : :
321 : 20 : int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint64_t *total_size,
322 : : uint32_t *erase_granule)
323 : : {
324 : 20 : int rc;
325 : :
326 : 20 : if (!bl || !bl->get_info) {
327 : 0 : errno = EINVAL;
328 : 0 : return FLASH_ERR_PARM_ERROR;
329 : : }
330 : :
331 : 20 : rc = reacquire(bl);
332 : 20 : if (rc)
333 : : return rc;
334 : :
335 : 20 : rc = bl->get_info(bl, name, total_size, erase_granule);
336 : :
337 : : /* Check the validity of what we are being told */
338 : 20 : if (erase_granule && *erase_granule != bl->erase_mask + 1)
339 : 0 : FL_ERR("blocklevel_get_info: WARNING: erase_granule (0x%08x) and erase_mask"
340 : : " (0x%08x) don't match\n", *erase_granule, bl->erase_mask + 1);
341 : :
342 : 20 : release(bl);
343 : :
344 : 20 : return rc;
345 : : }
346 : :
347 : : /*
348 : : * Compare flash and memory to determine if:
349 : : * a) Erase must happen before write
350 : : * b) Flash and memory are identical
351 : : * c) Flash can simply be written to
352 : : *
353 : : * returns -1 for a
354 : : * returns 0 for b
355 : : * returns 1 for c
356 : : */
357 : 0 : static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint64_t len)
358 : : {
359 : 0 : uint64_t i;
360 : 0 : int same = true;
361 : 0 : const uint8_t *f_buf, *m_buf;
362 : :
363 : 0 : f_buf = flash_buf;
364 : 0 : m_buf = mem_buf;
365 : :
366 : 0 : for (i = 0; i < len; i++) {
367 : 0 : if (m_buf[i] & ~f_buf[i])
368 : : return -1;
369 : 0 : if (same && (m_buf[i] != f_buf[i]))
370 : 0 : same = false;
371 : : }
372 : :
373 : 0 : return same ? 0 : 1;
374 : : }
375 : :
376 : 768 : int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
377 : : {
378 : 768 : uint64_t block_size;
379 : 768 : void *erase_buf;
380 : 768 : int rc;
381 : :
382 : 768 : if (!bl) {
383 : 0 : errno = EINVAL;
384 : 0 : return FLASH_ERR_PARM_ERROR;
385 : : }
386 : :
387 : 768 : FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len);
388 : :
389 : : /* Nothing smart needs to be done, pos and len are aligned */
390 : 768 : if ((pos & bl->erase_mask) == 0 && (len & bl->erase_mask) == 0) {
391 : 768 : FL_DBG("%s: Skipping smarts everything is aligned 0x%" PRIx64 " 0x%" PRIx64
392 : : "to 0x%08x\n", __func__, pos, len, bl->erase_mask);
393 : 768 : return blocklevel_erase(bl, pos, len);
394 : : }
395 : 0 : block_size = bl->erase_mask + 1;
396 : 0 : erase_buf = malloc(block_size);
397 : 0 : if (!erase_buf) {
398 : 0 : errno = ENOMEM;
399 : 0 : return FLASH_ERR_MALLOC_FAILED;
400 : : }
401 : :
402 : 0 : rc = reacquire(bl);
403 : 0 : if (rc) {
404 : 0 : free(erase_buf);
405 : 0 : return rc;
406 : : }
407 : :
408 : 0 : if (pos & bl->erase_mask) {
409 : : /*
410 : : * base_pos and base_len are the values in the first erase
411 : : * block that we need to preserve: the region up to pos.
412 : : */
413 : 0 : uint64_t base_pos = pos & ~(bl->erase_mask);
414 : 0 : uint64_t base_len = pos - base_pos;
415 : :
416 : 0 : FL_DBG("%s: preserving 0x%" PRIx64 "..0x%" PRIx64 "\n",
417 : : __func__, base_pos, base_pos + base_len);
418 : :
419 : : /*
420 : : * Read the entire block in case this is the ONLY block we're
421 : : * modifying, we may need the end chunk of it later
422 : : */
423 : 0 : rc = bl->read(bl, base_pos, erase_buf, block_size);
424 : 0 : if (rc)
425 : 0 : goto out;
426 : :
427 : 0 : rc = bl->erase(bl, base_pos, block_size);
428 : 0 : if (rc)
429 : 0 : goto out;
430 : :
431 : 0 : rc = bl->write(bl, base_pos, erase_buf, base_len);
432 : 0 : if (rc)
433 : 0 : goto out;
434 : :
435 : : /*
436 : : * The requested erase fits entirely into this erase block and
437 : : * so we need to write back the chunk at the end of the block
438 : : */
439 : 0 : if (base_pos + base_len + len < base_pos + block_size) {
440 : 0 : rc = bl->write(bl, pos + len, erase_buf + base_len + len,
441 : 0 : block_size - base_len - len);
442 : 0 : FL_DBG("%s: Early exit, everything was in one erase block\n",
443 : : __func__);
444 : 0 : goto out;
445 : : }
446 : :
447 : 0 : pos += block_size - base_len;
448 : 0 : len -= block_size - base_len;
449 : : }
450 : :
451 : : /* Now we should be aligned, best to double check */
452 : 0 : if (pos & bl->erase_mask) {
453 : 0 : FL_DBG("%s:pos 0x%" PRIx64 " isn't erase_mask 0x%08x aligned\n",
454 : : __func__, pos, bl->erase_mask);
455 : 0 : rc = FLASH_ERR_PARM_ERROR;
456 : 0 : goto out;
457 : : }
458 : :
459 : 0 : if (len & ~(bl->erase_mask)) {
460 : 0 : rc = bl->erase(bl, pos, len & ~(bl->erase_mask));
461 : 0 : if (rc)
462 : 0 : goto out;
463 : :
464 : 0 : pos += len & ~(bl->erase_mask);
465 : 0 : len -= len & ~(bl->erase_mask);
466 : : }
467 : :
468 : : /* Length should be less than a block now */
469 : 0 : if (len > block_size) {
470 : 0 : FL_DBG("%s: len 0x%" PRIx64 " is still exceeds block_size 0x%" PRIx64 "\n",
471 : : __func__, len, block_size);
472 : 0 : rc = FLASH_ERR_PARM_ERROR;
473 : 0 : goto out;
474 : : }
475 : :
476 : 0 : if (len & bl->erase_mask) {
477 : : /*
478 : : * top_pos is the first byte that must be preserved and
479 : : * top_len is the length from top_pos to the end of the erase
480 : : * block: the region that must be preserved
481 : : */
482 : 0 : uint64_t top_pos = pos + len;
483 : 0 : uint64_t top_len = block_size - len;
484 : :
485 : 0 : FL_DBG("%s: preserving 0x%" PRIx64 "..0x%" PRIx64 "\n",
486 : : __func__, top_pos, top_pos + top_len);
487 : :
488 : 0 : rc = bl->read(bl, top_pos, erase_buf, top_len);
489 : 0 : if (rc)
490 : 0 : goto out;
491 : :
492 : 0 : rc = bl->erase(bl, pos, block_size);
493 : 0 : if (rc)
494 : 0 : goto out;
495 : :
496 : 0 : rc = bl->write(bl, top_pos, erase_buf, top_len);
497 : 0 : if (rc)
498 : 0 : goto out;
499 : : }
500 : :
501 : 0 : out:
502 : 0 : free(erase_buf);
503 : 0 : release(bl);
504 : 0 : return rc;
505 : : }
506 : :
507 : 1 : int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
508 : : {
509 : 1 : void *ecc_buf = NULL;
510 : 1 : uint64_t ecc_start;
511 : 1 : int ecc_protection;
512 : :
513 : 1 : void *erase_buf = NULL;
514 : 1 : uint32_t erase_size;
515 : :
516 : 1 : const void *write_buf;
517 : 1 : uint64_t write_len;
518 : 1 : uint64_t write_pos;
519 : :
520 : 1 : int rc = 0;
521 : :
522 : 1 : if (!buf || !bl) {
523 : 0 : errno = EINVAL;
524 : 0 : return FLASH_ERR_PARM_ERROR;
525 : : }
526 : :
527 : 1 : FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len);
528 : :
529 : 1 : if (!(bl->flags & WRITE_NEED_ERASE)) {
530 : 1 : FL_DBG("%s: backend doesn't need erase\n", __func__);
531 : 1 : return blocklevel_write(bl, pos, buf, len);
532 : : }
533 : :
534 : 0 : rc = blocklevel_get_info(bl, NULL, NULL, &erase_size);
535 : 0 : if (rc)
536 : : return rc;
537 : :
538 : 0 : ecc_protection = ecc_protected(bl, pos, len, &ecc_start);
539 : 0 : if (ecc_protection == -1) {
540 : 0 : FL_ERR("%s: Can't cope with partial ecc\n", __func__);
541 : 0 : errno = EINVAL;
542 : 0 : return FLASH_ERR_PARM_ERROR;
543 : : }
544 : :
545 : 0 : if (ecc_protection) {
546 : 0 : uint64_t ecc_pos, ecc_align, ecc_diff, ecc_len;
547 : :
548 : 0 : FL_DBG("%s: region has ECC\n", __func__);
549 : :
550 : 0 : ecc_pos = with_ecc_pos(ecc_start, pos);
551 : 0 : ecc_align = ecc_buffer_align(ecc_start, ecc_pos);
552 : 0 : ecc_diff = ecc_pos - ecc_align;
553 : 0 : ecc_len = ecc_buffer_size(len + ecc_diff);
554 : :
555 : 0 : ecc_buf = malloc(ecc_len);
556 : 0 : if (!ecc_buf) {
557 : 0 : errno = ENOMEM;
558 : 0 : return FLASH_ERR_MALLOC_FAILED;
559 : : }
560 : :
561 : 0 : if (ecc_diff) {
562 : 0 : rc = blocklevel_read(bl, ecc_align, ecc_buf, ecc_diff);
563 : 0 : if (rc) {
564 : 0 : errno = EBADF;
565 : 0 : rc = FLASH_ERR_ECC_INVALID;
566 : 0 : goto out;
567 : : }
568 : : }
569 : :
570 : 0 : rc = memcpy_to_ecc_unaligned(ecc_buf, buf, len, ecc_diff);
571 : 0 : if (rc) {
572 : 0 : free(ecc_buf);
573 : 0 : errno = EBADF;
574 : 0 : return FLASH_ERR_ECC_INVALID;
575 : : }
576 : :
577 : : write_buf = ecc_buf;
578 : : write_len = ecc_len;
579 : : write_pos = ecc_pos;
580 : : } else {
581 : : write_buf = buf;
582 : : write_len = len;
583 : : write_pos = pos;
584 : : }
585 : :
586 : 0 : erase_buf = malloc(erase_size);
587 : 0 : if (!erase_buf) {
588 : 0 : errno = ENOMEM;
589 : 0 : rc = FLASH_ERR_MALLOC_FAILED;
590 : 0 : goto out_free;
591 : : }
592 : :
593 : 0 : rc = reacquire(bl);
594 : 0 : if (rc)
595 : 0 : goto out_free;
596 : :
597 : 0 : while (write_len > 0) {
598 : 0 : uint32_t erase_block = write_pos & ~(erase_size - 1);
599 : 0 : uint32_t block_offset = write_pos & (erase_size - 1);
600 : 0 : uint32_t chunk_size = erase_size > write_len ?
601 : 0 : write_len : erase_size;
602 : 0 : int cmp;
603 : :
604 : : /* Write crosses an erase boundary, shrink the write to the boundary */
605 : 0 : if (erase_size < block_offset + chunk_size) {
606 : 0 : chunk_size = erase_size - block_offset;
607 : : }
608 : :
609 : 0 : rc = bl->read(bl, erase_block, erase_buf, erase_size);
610 : 0 : if (rc)
611 : 0 : goto out;
612 : :
613 : 0 : cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf,
614 : : chunk_size);
615 : 0 : FL_DBG("%s: region 0x%08x..0x%08x ", __func__,
616 : : erase_block, erase_size);
617 : 0 : if (cmp != 0) {
618 : 0 : FL_DBG("needs ");
619 : 0 : if (cmp == -1) {
620 : 0 : FL_DBG("erase and ");
621 : 0 : bl->erase(bl, erase_block, erase_size);
622 : : }
623 : 0 : FL_DBG("write\n");
624 : 0 : memcpy(erase_buf + block_offset, write_buf, chunk_size);
625 : 0 : rc = bl->write(bl, erase_block, erase_buf, erase_size);
626 : 0 : if (rc)
627 : 0 : goto out;
628 : : } else {
629 : 0 : FL_DBG("clean\n");
630 : : }
631 : :
632 : 0 : write_len -= chunk_size;
633 : 0 : write_pos += chunk_size;
634 : 0 : write_buf += chunk_size;
635 : : }
636 : :
637 : 0 : out:
638 : 0 : release(bl);
639 : 0 : out_free:
640 : 0 : free(ecc_buf);
641 : 0 : free(erase_buf);
642 : 0 : return rc;
643 : : }
644 : :
645 : 0 : static bool insert_bl_prot_range(struct blocklevel_range *ranges, struct bl_prot_range range)
646 : : {
647 : 0 : int i;
648 : 0 : uint32_t pos, len;
649 : 0 : struct bl_prot_range *prot = ranges->prot;
650 : :
651 : 0 : pos = range.start;
652 : 0 : len = range.len;
653 : :
654 : 0 : if (len == 0)
655 : : return true;
656 : :
657 : : /* Check for overflow */
658 : 0 : if (pos + len < len)
659 : : return false;
660 : :
661 : 0 : for (i = 0; i < ranges->n_prot && len > 0; i++) {
662 : 0 : if (prot[i].start <= pos && prot[i].start + prot[i].len >= pos + len) {
663 : : len = 0;
664 : : break; /* Might as well, the next two conditions can't be true */
665 : : }
666 : :
667 : : /* Can easily extend this down just by adjusting start */
668 : 0 : if (pos <= prot[i].start && pos + len >= prot[i].start) {
669 : 0 : FL_DBG("%s: extending start down\n", __func__);
670 : 0 : prot[i].len += prot[i].start - pos;
671 : 0 : prot[i].start = pos;
672 : 0 : pos += prot[i].len;
673 : 0 : if (prot[i].len >= len)
674 : : len = 0;
675 : : else
676 : 0 : len -= prot[i].len;
677 : : }
678 : :
679 : : /*
680 : : * Jump over this range but the new range might be so big that
681 : : * theres a chunk after
682 : : */
683 : 0 : if (pos >= prot[i].start && pos < prot[i].start + prot[i].len) {
684 : 0 : FL_DBG("%s: fits within current range ", __func__);
685 : 0 : if (prot[i].start + prot[i].len - pos > len) {
686 : 0 : FL_DBG("but there is some extra at the end\n");
687 : 0 : len -= prot[i].start + prot[i].len - pos;
688 : 0 : pos = prot[i].start + prot[i].len;
689 : : } else {
690 : 0 : FL_DBG("\n");
691 : : len = 0;
692 : : }
693 : : }
694 : : /*
695 : : * This condition will be true if the range is smaller than
696 : : * the current range, therefore it should go here!
697 : : */
698 : 0 : if (pos < prot[i].start && pos + len <= prot[i].start)
699 : : break;
700 : : }
701 : :
702 : 0 : if (len) {
703 : 0 : int insert_pos = i;
704 : 0 : struct bl_prot_range *new_ranges = ranges->prot;
705 : :
706 : 0 : FL_DBG("%s: adding 0x%08x..0x%08x\n", __func__, pos, pos + len);
707 : :
708 : 0 : if (ranges->n_prot == ranges->total_prot) {
709 : 0 : new_ranges = realloc(ranges->prot,
710 : 0 : sizeof(range) * ((ranges->n_prot) + PROT_REALLOC_NUM));
711 : 0 : if (!new_ranges)
712 : : return false;
713 : 0 : ranges->total_prot += PROT_REALLOC_NUM;
714 : : }
715 : 0 : if (insert_pos != ranges->n_prot)
716 : 0 : for (i = ranges->n_prot; i > insert_pos; i--)
717 : 0 : memcpy(&new_ranges[i], &new_ranges[i - 1], sizeof(range));
718 : 0 : range.start = pos;
719 : 0 : range.len = len;
720 : 0 : memcpy(&new_ranges[insert_pos], &range, sizeof(range));
721 : 0 : ranges->prot = new_ranges;
722 : 0 : ranges->n_prot++;
723 : 0 : prot = new_ranges;
724 : : }
725 : :
726 : : return true;
727 : : }
728 : :
729 : 0 : int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len)
730 : : {
731 : : /*
732 : : * Could implement this at hardware level by having an accessor to the
733 : : * backend in struct blocklevel_device and as a result do nothing at
734 : : * this level (although probably not for ecc!)
735 : : */
736 : 0 : struct bl_prot_range range = { .start = start, .len = len };
737 : :
738 : 0 : if (len < BYTES_PER_ECC)
739 : : return -1;
740 : 0 : return !insert_bl_prot_range(&bl->ecc_prot, range);
741 : : }
|