blob: 1197af56f57863210258225e0b82872c55ce8225 [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
Sherry Zhang40478c22022-01-20 18:28:18 +080033/* Valid entries for data item width */
34static const uint32_t data_width_byte[] = {
35 sizeof(uint8_t),
36 sizeof(uint16_t),
37 sizeof(uint32_t),
38};
39
David Vinczecea8b592019-10-29 16:09:51 +010040/*
Sherry Zhang6e0f3242021-03-08 12:18:31 +080041 * Check the target address in the flash_area_xxx operation.
42 */
43static bool is_range_valid(const struct flash_area *area,
44 uint32_t off,
45 uint32_t len)
46{
47 uint32_t size;
48
49 if (!area) {
50 return false;
51 }
52
53 if (!boot_u32_safe_add(&size, off, len)) {
54 return false;
55 }
56
57 if (area->fa_size < size) {
58 return false;
59 }
60
61 return true;
62}
63
64/*
David Vinczecea8b592019-10-29 16:09:51 +010065 * `open` a flash area. The `area` in this case is not the individual
66 * sectors, but describes the particular flash area in question.
67 */
68int flash_area_open(uint8_t id, const struct flash_area **area)
69{
70 int i;
71
72 BOOT_LOG_DBG("area %d", id);
73
74 for (i = 0; i < flash_map_entry_num; i++) {
75 if (id == flash_map[i].fa_id) {
76 break;
77 }
78 }
79 if (i == flash_map_entry_num) {
80 return -1;
81 }
82
83 *area = &flash_map[i];
84 return 0;
85}
86
87void flash_area_close(const struct flash_area *area)
88{
89 /* Nothing to do. */
90}
91
Sherry Zhnag3fb26872021-08-05 16:55:27 +080092/*
93 * Read/write/erase. Offset is relative from beginning of flash area.
94 * `off` and `len` can be any alignment.
95 * Return 0 on success, other value on failure.
96 */
David Vinczecea8b592019-10-29 16:09:51 +010097int flash_area_read(const struct flash_area *area, uint32_t off, void *dst,
98 uint32_t len)
99{
Sherry Zhang49259162021-12-27 11:01:09 +0800100 uint32_t remaining_len, read_length;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800101 uint32_t aligned_off;
Sherry Zhang49259162021-12-27 11:01:09 +0800102 uint32_t item_number;
103
104 /* The maximum value of data_width is 4 bytes. */
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800105 uint8_t temp_buffer[sizeof(uint32_t)];
Sherry Zhang49259162021-12-27 11:01:09 +0800106 uint8_t data_width, i = 0, j;
Sherry Zhnag76727032021-09-09 11:49:49 +0800107 int ret = 0;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800108
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800109 ARM_FLASH_CAPABILITIES DriverCapabilities;
110
David Vinczecea8b592019-10-29 16:09:51 +0100111 BOOT_LOG_DBG("read area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800112
113 if (!is_range_valid(area, off, len)) {
114 return -1;
115 }
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800116 remaining_len = len;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800117
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800118 /* CMSIS ARM_FLASH_ReadData API requires the `addr` data type size aligned.
119 * Data type size is specified by the data_width in ARM_FLASH_CAPABILITIES.
120 */
121 DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
Sherry Zhang49259162021-12-27 11:01:09 +0800122 data_width = data_width_byte[DriverCapabilities.data_width];
123 aligned_off = FLOOR_ALIGN(off, data_width);
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800124
Sherry Zhang49259162021-12-27 11:01:09 +0800125 /* Read the first data_width long data if `off` is not aligned. */
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800126 if (aligned_off != off) {
127 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + aligned_off,
128 temp_buffer,
Sherry Zhang49259162021-12-27 11:01:09 +0800129 1);
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800130 if (ret < 0) {
131 return ret;
132 }
133
Sherry Zhang49259162021-12-27 11:01:09 +0800134 /* Record how many target data have been read. */
135 read_length = off - aligned_off + len >= data_width ?
136 data_width - (off - aligned_off) : len;
137
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800138 /* Copy the read data from off. */
Sherry Zhang49259162021-12-27 11:01:09 +0800139 for (i = 0; i < read_length; i++) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800140 ((uint8_t *)dst)[i] = temp_buffer[i + off - aligned_off];
141 }
Sherry Zhang49259162021-12-27 11:01:09 +0800142 remaining_len -= read_length;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800143 }
144
Sherry Zhang49259162021-12-27 11:01:09 +0800145 /* The `cnt` parameter in CMSIS ARM_FLASH_ReadData indicates number of data
146 * items to read.
147 */
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800148 if (remaining_len) {
Sherry Zhang49259162021-12-27 11:01:09 +0800149 item_number = remaining_len / data_width;
150 if (item_number) {
151 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + off + i,
152 (uint8_t *)dst + i,
153 item_number);
154 if (ret < 0) {
155 return ret;
156 }
157 remaining_len -= item_number * data_width;
158 }
159 }
160 if (remaining_len) {
161 ret = DRV_FLASH_AREA(area)->ReadData(
162 area->fa_off + off + i + item_number * data_width,
163 temp_buffer,
164 1);
165 if (ret < 0) {
166 return ret;
167 }
168 for (j = 0; j < remaining_len; j++) {
169 ((uint8_t *)dst)[i + item_number * data_width + j] = temp_buffer[j];
170 }
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800171 }
172
173 /* CMSIS ARM_FLASH_ReadData can return the number of data items read or
174 * Status Error Codes which are negative for failures.
175 */
176 if (ret < 0) {
177 return ret;
178 } else {
179 return 0;
180 }
David Vinczecea8b592019-10-29 16:09:51 +0100181}
182
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800183/* Writes `len` bytes of flash memory at `off` from the buffer at `src`.
184 * `off` and `len` can be any alignment.
185 */
David Vinczecea8b592019-10-29 16:09:51 +0100186int flash_area_write(const struct flash_area *area, uint32_t off,
187 const void *src, uint32_t len)
188{
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800189 uint8_t add_padding[FLASH_PROGRAM_UNIT];
TTornblom37b5e832021-09-28 13:41:58 +0200190#if (FLASH_PROGRAM_UNIT == 1)
191 uint8_t len_padding[FLASH_PROGRAM_UNIT]; /* zero sized arrayas are illegal C */
192#else
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800193 uint8_t len_padding[FLASH_PROGRAM_UNIT - 1];
TTornblom37b5e832021-09-28 13:41:58 +0200194#endif
Sherry Zhang49259162021-12-27 11:01:09 +0800195 ARM_FLASH_CAPABILITIES DriverCapabilities;
196 uint8_t data_width;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800197 /* The PROGRAM_UNIT aligned value of `off` */
198 uint32_t aligned_off;
199
200 /* The total write length. */
201 uint32_t aligned_len;
202 uint32_t i, k;
203
204 /* The index in src[] that has been programmed. */
205 uint32_t src_written_idx = 0;
206 uint32_t add_padding_size, len_padding_size;
207 uint32_t write_size;
Antonio de Angelis1ba9ff02021-12-02 15:49:44 +0000208 uint32_t last_unit_start_off;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800209 /*
210 * aligned_off off last_unit_start_off
211 * | | |
212 * | add_padding_size | | | len_padding_size |
213 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
214 * | | | |
215 * ---->--|---- PROGRAM UNIT ---|-- PROGRAM UNIT --|---- PROGRAM UNIT -----|
216 * | | | |
217 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
218 * |<-------- len --------->|
219 */
220
David Vinczecea8b592019-10-29 16:09:51 +0100221 BOOT_LOG_DBG("write area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800222
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800223 /* Align the target address. The area->fa_off should already be aligned. */
224 aligned_off = FLOOR_ALIGN(off, FLASH_PROGRAM_UNIT);
225 add_padding_size = off - aligned_off;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800226 if (!is_range_valid(area, off, len)) {
227 return -1;
228 }
229
Sherry Zhang49259162021-12-27 11:01:09 +0800230 DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
231 data_width = data_width_byte[DriverCapabilities.data_width];
232
233 if (FLASH_PROGRAM_UNIT)
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800234 /* Read the bytes from aligned_off to off. */
235 if (flash_area_read(area, aligned_off, add_padding, add_padding_size)) {
236 return -1;
237 }
238
239 /* Align the write size */
240 aligned_len = CEILING_ALIGN(len + add_padding_size, FLASH_PROGRAM_UNIT);
241 len_padding_size = aligned_len - len - add_padding_size;
242 if (!is_range_valid(area, aligned_off, aligned_len)) {
243 return -1;
244 }
245
246 /* Read the bytes from (off + len) to (off + aligned_len). */
247 if (flash_area_read(area, off + len, len_padding,
248 len_padding_size)) {
249 return -1;
250 }
251
252 /* Program the first FLASH_PROGRAM_UNIT. */
253 if (add_padding_size) {
254 /* Fill the first program unit bytes with data from src. */
255 for (i = add_padding_size, src_written_idx = 0;
256 i < FLASH_PROGRAM_UNIT && src_written_idx < len;
257 i++, src_written_idx++) {
258 add_padding[i] = ((uint8_t *)src)[src_written_idx];
259 }
260 if (src_written_idx == len) {
261 /* aligned_len equals to FLASH_PROGRAM_UNIT in this case.
262 * Fill the len_padding_size datas into add_padding.
263 */
264 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
265 i++, k++) {
266 add_padding[i] = len_padding[k];
267 }
268 if (k != len_padding_size) {
269 return -1;
270 }
271 }
272
273 /* Check the first program unit bytes are all filled. */
274 if (i != FLASH_PROGRAM_UNIT) {
275 return -1;
276 }
277 if (DRV_FLASH_AREA(area)->ProgramData(area->fa_off + aligned_off,
Sherry Zhang49259162021-12-27 11:01:09 +0800278 add_padding,
279 FLASH_PROGRAM_UNIT / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800280 return -1;
281 }
282 }
283
284 /* 'src_written_idx' indicates the number of the src data which has already
285 * been programed into flash. 'src_written_idx' equals to 'len' means that
286 * all the data in src has been programmed and aligned_len equals to
287 * FLASH_PROGRAM_UNIT. This case has been handled above.
288 * 'src_written_idx' less than 'len' means that not all the data in src has
289 * been programmed.
290 */
291 if (src_written_idx < len) {
292 /* Program from the first aligned bytes(src_written_idx) to the last
293 * aligned bytes in src.
294 */
295 write_size = FLOOR_ALIGN(len - src_written_idx, FLASH_PROGRAM_UNIT);
296 if (write_size > 0) {
297 if (DRV_FLASH_AREA(area)->ProgramData(
298 area->fa_off + off + src_written_idx,
299 src,
Sherry Zhang49259162021-12-27 11:01:09 +0800300 write_size / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800301 return -1;
302 }
303 src_written_idx += write_size;
304 }
305 last_unit_start_off = src_written_idx;
306
307 /* Program the last program unit data into flash. */
308 if (len_padding_size) {
309 /* Copy the last unaligned bytes in src to add_padding. */
310 for (i = 0; i < FLASH_PROGRAM_UNIT && src_written_idx < len;
311 i++, src_written_idx++) {
312 add_padding[i] = ((uint8_t *)src)[src_written_idx];
313 }
314
315 if (src_written_idx != len) {
316 return -1;
317 }
318 /* Copy the len_padding_size bytes in len_padding to add_padding. */
319 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
320 i++, k++) {
321 add_padding[i] = len_padding[k];
322 }
323 write_size = add_padding_size + last_unit_start_off +
324 FLASH_PROGRAM_UNIT;
325 if (i != FLASH_PROGRAM_UNIT || k != len_padding_size ||
326 aligned_len != write_size) {
327 return -1;
328 }
329 if (DRV_FLASH_AREA(area)->ProgramData(
330 area->fa_off + off + last_unit_start_off,
331 add_padding,
Sherry Zhang49259162021-12-27 11:01:09 +0800332 FLASH_PROGRAM_UNIT / data_width) < 0) {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800333 return -1;
334 }
335 }
336 }
337
338 return 0;
David Vinczecea8b592019-10-29 16:09:51 +0100339}
340
341int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)
342{
343 ARM_FLASH_INFO *flash_info;
344 uint32_t deleted_len = 0;
345 int32_t rc = 0;
346
347 BOOT_LOG_DBG("erase area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800348
349 if (!is_range_valid(area, off, len)) {
350 return -1;
351 }
352
Michel Jaouen26f6c022020-12-03 10:37:22 +0100353 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100354
355 if (flash_info->sector_info == NULL) {
356 /* Uniform sector layout */
357 while (deleted_len < len) {
Michel Jaouen26f6c022020-12-03 10:37:22 +0100358 rc = DRV_FLASH_AREA(area)->EraseSector(area->fa_off + off);
David Vinczecea8b592019-10-29 16:09:51 +0100359 if (rc != 0) {
360 break;
361 }
362 deleted_len += flash_info->sector_size;
363 off += flash_info->sector_size;
364 }
365 } else {
366 /* Inhomogeneous sector layout, explicitly defined
367 * Currently not supported.
368 */
369 }
370
371 return rc;
372}
373
374uint32_t flash_area_align(const struct flash_area *area)
375{
376 ARM_FLASH_INFO *flash_info;
377
Michel Jaouen26f6c022020-12-03 10:37:22 +0100378 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100379 return flash_info->program_unit;
380}