blob: 366606c3a3da500f66fdfeee8c6425e11899e2e3 [file] [log] [blame]
David Vinczecea8b592019-10-29 16:09:51 +01001/*
Sherry Zhang6e0f3242021-03-08 12:18:31 +08002 * Copyright (c) 2019-2021, 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 Zhnag3fb26872021-08-05 16:55:27 +080093 uint32_t remaining_len;
94 uint32_t aligned_off;
95 uint8_t temp_buffer[sizeof(uint32_t)];
96 uint8_t align_unit, i = 0;
Sherry Zhnag76727032021-09-09 11:49:49 +080097 int ret = 0;
Sherry Zhnag3fb26872021-08-05 16:55:27 +080098
99 /* Valid entries for data item width */
Antonio de Angelis1ba9ff02021-12-02 15:49:44 +0000100 const uint32_t data_width_byte[] = {
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800101 sizeof(uint8_t),
102 sizeof(uint16_t),
103 sizeof(uint32_t),
104 };
105 ARM_FLASH_CAPABILITIES DriverCapabilities;
106
David Vinczecea8b592019-10-29 16:09:51 +0100107 BOOT_LOG_DBG("read area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800108
109 if (!is_range_valid(area, off, len)) {
110 return -1;
111 }
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800112 remaining_len = len;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800113
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800114 /* CMSIS ARM_FLASH_ReadData API requires the `addr` data type size aligned.
115 * Data type size is specified by the data_width in ARM_FLASH_CAPABILITIES.
116 */
117 DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
118 align_unit = data_width_byte[DriverCapabilities.data_width];
119 aligned_off = FLOOR_ALIGN(off, align_unit);
120
121 /* Read the first align_unit long data if `off` is not aligned. */
122 if (aligned_off != off) {
123 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + aligned_off,
124 temp_buffer,
125 align_unit);
126 if (ret < 0) {
127 return ret;
128 }
129
130 /* Copy the read data from off. */
131 for (i = 0; i + off - aligned_off < align_unit; i++) {
132 ((uint8_t *)dst)[i] = temp_buffer[i + off - aligned_off];
133 }
134 remaining_len -= align_unit - (off - aligned_off);
135 }
136
137 /* CMSIS ARM_FLASH_ReadData does not require the alignment of `cnt`.*/
138 if (remaining_len) {
139 ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + off + i,
140 (uint8_t *)dst + i,
141 remaining_len);
142 }
143
144 /* CMSIS ARM_FLASH_ReadData can return the number of data items read or
145 * Status Error Codes which are negative for failures.
146 */
147 if (ret < 0) {
148 return ret;
149 } else {
150 return 0;
151 }
David Vinczecea8b592019-10-29 16:09:51 +0100152}
153
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800154/* Writes `len` bytes of flash memory at `off` from the buffer at `src`.
155 * `off` and `len` can be any alignment.
156 */
David Vinczecea8b592019-10-29 16:09:51 +0100157int flash_area_write(const struct flash_area *area, uint32_t off,
158 const void *src, uint32_t len)
159{
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800160 uint8_t add_padding[FLASH_PROGRAM_UNIT];
TTornblom37b5e832021-09-28 13:41:58 +0200161#if (FLASH_PROGRAM_UNIT == 1)
162 uint8_t len_padding[FLASH_PROGRAM_UNIT]; /* zero sized arrayas are illegal C */
163#else
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800164 uint8_t len_padding[FLASH_PROGRAM_UNIT - 1];
TTornblom37b5e832021-09-28 13:41:58 +0200165#endif
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800166
167 /* The PROGRAM_UNIT aligned value of `off` */
168 uint32_t aligned_off;
169
170 /* The total write length. */
171 uint32_t aligned_len;
172 uint32_t i, k;
173
174 /* The index in src[] that has been programmed. */
175 uint32_t src_written_idx = 0;
176 uint32_t add_padding_size, len_padding_size;
177 uint32_t write_size;
Antonio de Angelis1ba9ff02021-12-02 15:49:44 +0000178 uint32_t last_unit_start_off;
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800179 /*
180 * aligned_off off last_unit_start_off
181 * | | |
182 * | add_padding_size | | | len_padding_size |
183 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
184 * | | | |
185 * ---->--|---- PROGRAM UNIT ---|-- PROGRAM UNIT --|---- PROGRAM UNIT -----|
186 * | | | |
187 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
188 * |<-------- len --------->|
189 */
190
David Vinczecea8b592019-10-29 16:09:51 +0100191 BOOT_LOG_DBG("write area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800192
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800193 /* Align the target address. The area->fa_off should already be aligned. */
194 aligned_off = FLOOR_ALIGN(off, FLASH_PROGRAM_UNIT);
195 add_padding_size = off - aligned_off;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800196 if (!is_range_valid(area, off, len)) {
197 return -1;
198 }
199
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800200 /* Read the bytes from aligned_off to off. */
201 if (flash_area_read(area, aligned_off, add_padding, add_padding_size)) {
202 return -1;
203 }
204
205 /* Align the write size */
206 aligned_len = CEILING_ALIGN(len + add_padding_size, FLASH_PROGRAM_UNIT);
207 len_padding_size = aligned_len - len - add_padding_size;
208 if (!is_range_valid(area, aligned_off, aligned_len)) {
209 return -1;
210 }
211
212 /* Read the bytes from (off + len) to (off + aligned_len). */
213 if (flash_area_read(area, off + len, len_padding,
214 len_padding_size)) {
215 return -1;
216 }
217
218 /* Program the first FLASH_PROGRAM_UNIT. */
219 if (add_padding_size) {
220 /* Fill the first program unit bytes with data from src. */
221 for (i = add_padding_size, src_written_idx = 0;
222 i < FLASH_PROGRAM_UNIT && src_written_idx < len;
223 i++, src_written_idx++) {
224 add_padding[i] = ((uint8_t *)src)[src_written_idx];
225 }
226 if (src_written_idx == len) {
227 /* aligned_len equals to FLASH_PROGRAM_UNIT in this case.
228 * Fill the len_padding_size datas into add_padding.
229 */
230 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
231 i++, k++) {
232 add_padding[i] = len_padding[k];
233 }
234 if (k != len_padding_size) {
235 return -1;
236 }
237 }
238
239 /* Check the first program unit bytes are all filled. */
240 if (i != FLASH_PROGRAM_UNIT) {
241 return -1;
242 }
243 if (DRV_FLASH_AREA(area)->ProgramData(area->fa_off + aligned_off,
244 add_padding,
245 FLASH_PROGRAM_UNIT)) {
246 return -1;
247 }
248 }
249
250 /* 'src_written_idx' indicates the number of the src data which has already
251 * been programed into flash. 'src_written_idx' equals to 'len' means that
252 * all the data in src has been programmed and aligned_len equals to
253 * FLASH_PROGRAM_UNIT. This case has been handled above.
254 * 'src_written_idx' less than 'len' means that not all the data in src has
255 * been programmed.
256 */
257 if (src_written_idx < len) {
258 /* Program from the first aligned bytes(src_written_idx) to the last
259 * aligned bytes in src.
260 */
261 write_size = FLOOR_ALIGN(len - src_written_idx, FLASH_PROGRAM_UNIT);
262 if (write_size > 0) {
263 if (DRV_FLASH_AREA(area)->ProgramData(
264 area->fa_off + off + src_written_idx,
265 src,
266 write_size)) {
267 return -1;
268 }
269 src_written_idx += write_size;
270 }
271 last_unit_start_off = src_written_idx;
272
273 /* Program the last program unit data into flash. */
274 if (len_padding_size) {
275 /* Copy the last unaligned bytes in src to add_padding. */
276 for (i = 0; i < FLASH_PROGRAM_UNIT && src_written_idx < len;
277 i++, src_written_idx++) {
278 add_padding[i] = ((uint8_t *)src)[src_written_idx];
279 }
280
281 if (src_written_idx != len) {
282 return -1;
283 }
284 /* Copy the len_padding_size bytes in len_padding to add_padding. */
285 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
286 i++, k++) {
287 add_padding[i] = len_padding[k];
288 }
289 write_size = add_padding_size + last_unit_start_off +
290 FLASH_PROGRAM_UNIT;
291 if (i != FLASH_PROGRAM_UNIT || k != len_padding_size ||
292 aligned_len != write_size) {
293 return -1;
294 }
295 if (DRV_FLASH_AREA(area)->ProgramData(
296 area->fa_off + off + last_unit_start_off,
297 add_padding,
298 FLASH_PROGRAM_UNIT)) {
299 return -1;
300 }
301 }
302 }
303
304 return 0;
David Vinczecea8b592019-10-29 16:09:51 +0100305}
306
307int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)
308{
309 ARM_FLASH_INFO *flash_info;
310 uint32_t deleted_len = 0;
311 int32_t rc = 0;
312
313 BOOT_LOG_DBG("erase area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800314
315 if (!is_range_valid(area, off, len)) {
316 return -1;
317 }
318
Michel Jaouen26f6c022020-12-03 10:37:22 +0100319 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100320
321 if (flash_info->sector_info == NULL) {
322 /* Uniform sector layout */
323 while (deleted_len < len) {
Michel Jaouen26f6c022020-12-03 10:37:22 +0100324 rc = DRV_FLASH_AREA(area)->EraseSector(area->fa_off + off);
David Vinczecea8b592019-10-29 16:09:51 +0100325 if (rc != 0) {
326 break;
327 }
328 deleted_len += flash_info->sector_size;
329 off += flash_info->sector_size;
330 }
331 } else {
332 /* Inhomogeneous sector layout, explicitly defined
333 * Currently not supported.
334 */
335 }
336
337 return rc;
338}
339
340uint32_t flash_area_align(const struct flash_area *area)
341{
342 ARM_FLASH_INFO *flash_info;
343
Michel Jaouen26f6c022020-12-03 10:37:22 +0100344 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100345 return flash_info->program_unit;
346}