blob: 6c3ecf534df3eb4b3c5e068837a5e2da40736d47 [file] [log] [blame]
David Vinczecea8b592019-10-29 16:09:51 +01001/*
Sherry Zhang49259162021-12-27 11:01:09 +08002 * Copyright (c) 2019-2022, Arm Limited. All rights reserved.
David Vinczecea8b592019-10-29 16:09:51 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include <stdbool.h>
9#include "target.h"
David Vinczecea8b592019-10-29 16:09:51 +010010#include "flash_map/flash_map.h"
David Vincze225c58f2019-12-09 17:32:48 +010011#include "flash_map_backend/flash_map_backend.h"
Sherry Zhang6e0f3242021-03-08 12:18:31 +080012#include "bootutil_priv.h"
David Vinczecea8b592019-10-29 16:09:51 +010013#include "bootutil/bootutil_log.h"
14#include "Driver_Flash.h"
15
Sherry Zhnag3fb26872021-08-05 16:55:27 +080016#define FLASH_PROGRAM_UNIT TFM_HAL_FLASH_PROGRAM_UNIT
17
18/**
19 * Return the greatest value not greater than `value` that is aligned to
20 * `alignment`.
21 */
22#define FLOOR_ALIGN(value, alignment) ((value) & ~((alignment) - 1))
23
24/**
25 * Return the least value not less than `value` that is aligned to `alignment`.
26 */
27#define CEILING_ALIGN(value, alignment) \
28 (((value) + ((alignment) - 1)) & ~((alignment) - 1))
29
Mark Horvath8576e382021-03-12 10:24:55 +010030extern const struct flash_area flash_map[];
31extern const int flash_map_entry_num;
David Vinczecea8b592019-10-29 16:09:51 +010032
33/*
Sherry Zhang6e0f3242021-03-08 12:18:31 +080034 * Check the target address in the flash_area_xxx operation.
35 */
36static bool is_range_valid(const struct flash_area *area,
37 uint32_t off,
38 uint32_t len)
39{
40 uint32_t size;
41
42 if (!area) {
43 return false;
44 }
45
46 if (!boot_u32_safe_add(&size, off, len)) {
47 return false;
48 }
49
50 if (area->fa_size < size) {
51 return false;
52 }
53
54 return true;
55}
56
57/*
David Vinczecea8b592019-10-29 16:09:51 +010058 * `open` a flash area. The `area` in this case is not the individual
59 * sectors, but describes the particular flash area in question.
60 */
61int flash_area_open(uint8_t id, const struct flash_area **area)
62{
63 int i;
64
65 BOOT_LOG_DBG("area %d", id);
66
67 for (i = 0; i < flash_map_entry_num; i++) {
68 if (id == flash_map[i].fa_id) {
69 break;
70 }
71 }
72 if (i == flash_map_entry_num) {
73 return -1;
74 }
75
76 *area = &flash_map[i];
77 return 0;
78}
79
80void flash_area_close(const struct flash_area *area)
81{
82 /* Nothing to do. */
83}
84
Sherry Zhnag3fb26872021-08-05 16:55:27 +080085/*
86 * Read/write/erase. Offset is relative from beginning of flash area.
87 * `off` and `len` can be any alignment.
88 * Return 0 on success, other value on failure.
89 */
David Vinczecea8b592019-10-29 16:09:51 +010090int flash_area_read(const struct flash_area *area, uint32_t off, void *dst,
91 uint32_t len)
92{
Sherry Zhang49259162021-12-27 11:01:09 +080093 uint32_t remaining_len, read_length;
Sherry Zhnag3fb26872021-08-05 16:55:27 +080094 uint32_t aligned_off;
Sherry Zhang49259162021-12-27 11:01:09 +080095 uint32_t item_number;
96
97 /* The maximum value of data_width is 4 bytes. */
Sherry Zhnag3fb26872021-08-05 16:55:27 +080098 uint8_t temp_buffer[sizeof(uint32_t)];
Sherry Zhang49259162021-12-27 11:01:09 +080099 uint8_t data_width, i = 0, j;
Sherry Zhnag76727032021-09-09 11:49:49 +0800100 int ret = 0;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800101
102 /* Valid entries for data item width */
Antonio de Angelis1ba9ff02021-12-02 15:49:44 +0000103 const uint32_t data_width_byte[] = {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800104 sizeof(uint8_t),
105 sizeof(uint16_t),
106 sizeof(uint32_t),
107 };
108 ARM_FLASH_CAPABILITIES DriverCapabilities;
109
David Vinczecea8b592019-10-29 16:09:51 +0100110 BOOT_LOG_DBG("read area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800111
112 if (!is_range_valid(area, off, len)) {
113 return -1;
114 }
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800115 remaining_len = len;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800116
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800117 /* CMSIS ARM_FLASH_ReadData API requires the `addr` data type size aligned.
118 * Data type size is specified by the data_width in ARM_FLASH_CAPABILITIES.
119 */
120 DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
Sherry Zhang49259162021-12-27 11:01:09 +0800121 data_width = data_width_byte[DriverCapabilities.data_width];
122 aligned_off = FLOOR_ALIGN(off, data_width);
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800123
Sherry Zhang49259162021-12-27 11:01:09 +0800124 /* Read the first data_width long data if `off` is not aligned. */
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800125 if (aligned_off != off) {
126 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + aligned_off,
127 temp_buffer,
Sherry Zhang49259162021-12-27 11:01:09 +0800128 1);
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800129 if (ret < 0) {
130 return ret;
131 }
132
Sherry Zhang49259162021-12-27 11:01:09 +0800133 /* Record how many target data have been read. */
134 read_length = off - aligned_off + len >= data_width ?
135 data_width - (off - aligned_off) : len;
136
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800137 /* Copy the read data from off. */
Sherry Zhang49259162021-12-27 11:01:09 +0800138 for (i = 0; i < read_length; i++) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800139 ((uint8_t *)dst)[i] = temp_buffer[i + off - aligned_off];
140 }
Sherry Zhang49259162021-12-27 11:01:09 +0800141 remaining_len -= read_length;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800142 }
143
Sherry Zhang49259162021-12-27 11:01:09 +0800144 /* The `cnt` parameter in CMSIS ARM_FLASH_ReadData indicates number of data
145 * items to read.
146 */
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800147 if (remaining_len) {
Sherry Zhang49259162021-12-27 11:01:09 +0800148 item_number = remaining_len / data_width;
149 if (item_number) {
150 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + off + i,
151 (uint8_t *)dst + i,
152 item_number);
153 if (ret < 0) {
154 return ret;
155 }
156 remaining_len -= item_number * data_width;
157 }
158 }
159 if (remaining_len) {
160 ret = DRV_FLASH_AREA(area)->ReadData(
161 area->fa_off + off + i + item_number * data_width,
162 temp_buffer,
163 1);
164 if (ret < 0) {
165 return ret;
166 }
167 for (j = 0; j < remaining_len; j++) {
168 ((uint8_t *)dst)[i + item_number * data_width + j] = temp_buffer[j];
169 }
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800170 }
171
172 /* CMSIS ARM_FLASH_ReadData can return the number of data items read or
173 * Status Error Codes which are negative for failures.
174 */
175 if (ret < 0) {
176 return ret;
177 } else {
178 return 0;
179 }
David Vinczecea8b592019-10-29 16:09:51 +0100180}
181
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800182/* Writes `len` bytes of flash memory at `off` from the buffer at `src`.
183 * `off` and `len` can be any alignment.
184 */
David Vinczecea8b592019-10-29 16:09:51 +0100185int flash_area_write(const struct flash_area *area, uint32_t off,
186 const void *src, uint32_t len)
187{
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800188 uint8_t add_padding[FLASH_PROGRAM_UNIT];
TTornblom37b5e832021-09-28 13:41:58 +0200189#if (FLASH_PROGRAM_UNIT == 1)
190 uint8_t len_padding[FLASH_PROGRAM_UNIT]; /* zero sized arrayas are illegal C */
191#else
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800192 uint8_t len_padding[FLASH_PROGRAM_UNIT - 1];
TTornblom37b5e832021-09-28 13:41:58 +0200193#endif
Sherry Zhang49259162021-12-27 11:01:09 +0800194 /* Valid entries for data item width */
195 uint32_t data_width_byte[] = {
196 sizeof(uint8_t),
197 sizeof(uint16_t),
198 sizeof(uint32_t),
199 };
200 ARM_FLASH_CAPABILITIES DriverCapabilities;
201 uint8_t data_width;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800202 /* The PROGRAM_UNIT aligned value of `off` */
203 uint32_t aligned_off;
204
205 /* The total write length. */
206 uint32_t aligned_len;
207 uint32_t i, k;
208
209 /* The index in src[] that has been programmed. */
210 uint32_t src_written_idx = 0;
211 uint32_t add_padding_size, len_padding_size;
212 uint32_t write_size;
Antonio de Angelis1ba9ff02021-12-02 15:49:44 +0000213 uint32_t last_unit_start_off;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800214 /*
215 * aligned_off off last_unit_start_off
216 * | | |
217 * | add_padding_size | | | len_padding_size |
218 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
219 * | | | |
220 * ---->--|---- PROGRAM UNIT ---|-- PROGRAM UNIT --|---- PROGRAM UNIT -----|
221 * | | | |
222 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
223 * |<-------- len --------->|
224 */
225
David Vinczecea8b592019-10-29 16:09:51 +0100226 BOOT_LOG_DBG("write area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800227
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800228 /* Align the target address. The area->fa_off should already be aligned. */
229 aligned_off = FLOOR_ALIGN(off, FLASH_PROGRAM_UNIT);
230 add_padding_size = off - aligned_off;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800231 if (!is_range_valid(area, off, len)) {
232 return -1;
233 }
234
Sherry Zhang49259162021-12-27 11:01:09 +0800235 DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
236 data_width = data_width_byte[DriverCapabilities.data_width];
237
238 if (FLASH_PROGRAM_UNIT)
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800239 /* Read the bytes from aligned_off to off. */
240 if (flash_area_read(area, aligned_off, add_padding, add_padding_size)) {
241 return -1;
242 }
243
244 /* Align the write size */
245 aligned_len = CEILING_ALIGN(len + add_padding_size, FLASH_PROGRAM_UNIT);
246 len_padding_size = aligned_len - len - add_padding_size;
247 if (!is_range_valid(area, aligned_off, aligned_len)) {
248 return -1;
249 }
250
251 /* Read the bytes from (off + len) to (off + aligned_len). */
252 if (flash_area_read(area, off + len, len_padding,
253 len_padding_size)) {
254 return -1;
255 }
256
257 /* Program the first FLASH_PROGRAM_UNIT. */
258 if (add_padding_size) {
259 /* Fill the first program unit bytes with data from src. */
260 for (i = add_padding_size, src_written_idx = 0;
261 i < FLASH_PROGRAM_UNIT && src_written_idx < len;
262 i++, src_written_idx++) {
263 add_padding[i] = ((uint8_t *)src)[src_written_idx];
264 }
265 if (src_written_idx == len) {
266 /* aligned_len equals to FLASH_PROGRAM_UNIT in this case.
267 * Fill the len_padding_size datas into add_padding.
268 */
269 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
270 i++, k++) {
271 add_padding[i] = len_padding[k];
272 }
273 if (k != len_padding_size) {
274 return -1;
275 }
276 }
277
278 /* Check the first program unit bytes are all filled. */
279 if (i != FLASH_PROGRAM_UNIT) {
280 return -1;
281 }
282 if (DRV_FLASH_AREA(area)->ProgramData(area->fa_off + aligned_off,
Sherry Zhang49259162021-12-27 11:01:09 +0800283 add_padding,
284 FLASH_PROGRAM_UNIT / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800285 return -1;
286 }
287 }
288
289 /* 'src_written_idx' indicates the number of the src data which has already
290 * been programed into flash. 'src_written_idx' equals to 'len' means that
291 * all the data in src has been programmed and aligned_len equals to
292 * FLASH_PROGRAM_UNIT. This case has been handled above.
293 * 'src_written_idx' less than 'len' means that not all the data in src has
294 * been programmed.
295 */
296 if (src_written_idx < len) {
297 /* Program from the first aligned bytes(src_written_idx) to the last
298 * aligned bytes in src.
299 */
300 write_size = FLOOR_ALIGN(len - src_written_idx, FLASH_PROGRAM_UNIT);
301 if (write_size > 0) {
302 if (DRV_FLASH_AREA(area)->ProgramData(
303 area->fa_off + off + src_written_idx,
304 src,
Sherry Zhang49259162021-12-27 11:01:09 +0800305 write_size / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800306 return -1;
307 }
308 src_written_idx += write_size;
309 }
310 last_unit_start_off = src_written_idx;
311
312 /* Program the last program unit data into flash. */
313 if (len_padding_size) {
314 /* Copy the last unaligned bytes in src to add_padding. */
315 for (i = 0; i < FLASH_PROGRAM_UNIT && src_written_idx < len;
316 i++, src_written_idx++) {
317 add_padding[i] = ((uint8_t *)src)[src_written_idx];
318 }
319
320 if (src_written_idx != len) {
321 return -1;
322 }
323 /* Copy the len_padding_size bytes in len_padding to add_padding. */
324 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
325 i++, k++) {
326 add_padding[i] = len_padding[k];
327 }
328 write_size = add_padding_size + last_unit_start_off +
329 FLASH_PROGRAM_UNIT;
330 if (i != FLASH_PROGRAM_UNIT || k != len_padding_size ||
331 aligned_len != write_size) {
332 return -1;
333 }
334 if (DRV_FLASH_AREA(area)->ProgramData(
335 area->fa_off + off + last_unit_start_off,
336 add_padding,
Sherry Zhang49259162021-12-27 11:01:09 +0800337 FLASH_PROGRAM_UNIT / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800338 return -1;
339 }
340 }
341 }
342
343 return 0;
David Vinczecea8b592019-10-29 16:09:51 +0100344}
345
346int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)
347{
348 ARM_FLASH_INFO *flash_info;
349 uint32_t deleted_len = 0;
350 int32_t rc = 0;
351
352 BOOT_LOG_DBG("erase area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800353
354 if (!is_range_valid(area, off, len)) {
355 return -1;
356 }
357
Michel Jaouen26f6c022020-12-03 10:37:22 +0100358 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100359
360 if (flash_info->sector_info == NULL) {
361 /* Uniform sector layout */
362 while (deleted_len < len) {
Michel Jaouen26f6c022020-12-03 10:37:22 +0100363 rc = DRV_FLASH_AREA(area)->EraseSector(area->fa_off + off);
David Vinczecea8b592019-10-29 16:09:51 +0100364 if (rc != 0) {
365 break;
366 }
367 deleted_len += flash_info->sector_size;
368 off += flash_info->sector_size;
369 }
370 } else {
371 /* Inhomogeneous sector layout, explicitly defined
372 * Currently not supported.
373 */
374 }
375
376 return rc;
377}
378
379uint32_t flash_area_align(const struct flash_area *area)
380{
381 ARM_FLASH_INFO *flash_info;
382
Michel Jaouen26f6c022020-12-03 10:37:22 +0100383 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100384 return flash_info->program_unit;
385}