blob: 112750dfbe1891813326ca69071898e68cbd8856 [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;
97 int ret;
98
99 /* Valid entries for data item width */
100 uint32_t data_width_byte[] = {
101 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];
161 uint8_t len_padding[FLASH_PROGRAM_UNIT - 1];
162
163 /* The PROGRAM_UNIT aligned value of `off` */
164 uint32_t aligned_off;
165
166 /* The total write length. */
167 uint32_t aligned_len;
168 uint32_t i, k;
169
170 /* The index in src[] that has been programmed. */
171 uint32_t src_written_idx = 0;
172 uint32_t add_padding_size, len_padding_size;
173 uint32_t write_size;
174 uint32_t last_unit_start_off = 0;
175 /*
176 * aligned_off off last_unit_start_off
177 * | | |
178 * | add_padding_size | | | len_padding_size |
179 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
180 * | | | |
181 * ---->--|---- PROGRAM UNIT ---|-- PROGRAM UNIT --|---- PROGRAM UNIT -----|
182 * | | | |
183 * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
184 * |<-------- len --------->|
185 */
186
David Vinczecea8b592019-10-29 16:09:51 +0100187 BOOT_LOG_DBG("write area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800188
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800189 /* Align the target address. The area->fa_off should already be aligned. */
190 aligned_off = FLOOR_ALIGN(off, FLASH_PROGRAM_UNIT);
191 add_padding_size = off - aligned_off;
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800192 if (!is_range_valid(area, off, len)) {
193 return -1;
194 }
195
Sherry Zhnag3fb26872021-08-05 16:55:27 +0800196 /* Read the bytes from aligned_off to off. */
197 if (flash_area_read(area, aligned_off, add_padding, add_padding_size)) {
198 return -1;
199 }
200
201 /* Align the write size */
202 aligned_len = CEILING_ALIGN(len + add_padding_size, FLASH_PROGRAM_UNIT);
203 len_padding_size = aligned_len - len - add_padding_size;
204 if (!is_range_valid(area, aligned_off, aligned_len)) {
205 return -1;
206 }
207
208 /* Read the bytes from (off + len) to (off + aligned_len). */
209 if (flash_area_read(area, off + len, len_padding,
210 len_padding_size)) {
211 return -1;
212 }
213
214 /* Program the first FLASH_PROGRAM_UNIT. */
215 if (add_padding_size) {
216 /* Fill the first program unit bytes with data from src. */
217 for (i = add_padding_size, src_written_idx = 0;
218 i < FLASH_PROGRAM_UNIT && src_written_idx < len;
219 i++, src_written_idx++) {
220 add_padding[i] = ((uint8_t *)src)[src_written_idx];
221 }
222 if (src_written_idx == len) {
223 /* aligned_len equals to FLASH_PROGRAM_UNIT in this case.
224 * Fill the len_padding_size datas into add_padding.
225 */
226 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
227 i++, k++) {
228 add_padding[i] = len_padding[k];
229 }
230 if (k != len_padding_size) {
231 return -1;
232 }
233 }
234
235 /* Check the first program unit bytes are all filled. */
236 if (i != FLASH_PROGRAM_UNIT) {
237 return -1;
238 }
239 if (DRV_FLASH_AREA(area)->ProgramData(area->fa_off + aligned_off,
240 add_padding,
241 FLASH_PROGRAM_UNIT)) {
242 return -1;
243 }
244 }
245
246 /* 'src_written_idx' indicates the number of the src data which has already
247 * been programed into flash. 'src_written_idx' equals to 'len' means that
248 * all the data in src has been programmed and aligned_len equals to
249 * FLASH_PROGRAM_UNIT. This case has been handled above.
250 * 'src_written_idx' less than 'len' means that not all the data in src has
251 * been programmed.
252 */
253 if (src_written_idx < len) {
254 /* Program from the first aligned bytes(src_written_idx) to the last
255 * aligned bytes in src.
256 */
257 write_size = FLOOR_ALIGN(len - src_written_idx, FLASH_PROGRAM_UNIT);
258 if (write_size > 0) {
259 if (DRV_FLASH_AREA(area)->ProgramData(
260 area->fa_off + off + src_written_idx,
261 src,
262 write_size)) {
263 return -1;
264 }
265 src_written_idx += write_size;
266 }
267 last_unit_start_off = src_written_idx;
268
269 /* Program the last program unit data into flash. */
270 if (len_padding_size) {
271 /* Copy the last unaligned bytes in src to add_padding. */
272 for (i = 0; i < FLASH_PROGRAM_UNIT && src_written_idx < len;
273 i++, src_written_idx++) {
274 add_padding[i] = ((uint8_t *)src)[src_written_idx];
275 }
276
277 if (src_written_idx != len) {
278 return -1;
279 }
280 /* Copy the len_padding_size bytes in len_padding to add_padding. */
281 for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
282 i++, k++) {
283 add_padding[i] = len_padding[k];
284 }
285 write_size = add_padding_size + last_unit_start_off +
286 FLASH_PROGRAM_UNIT;
287 if (i != FLASH_PROGRAM_UNIT || k != len_padding_size ||
288 aligned_len != write_size) {
289 return -1;
290 }
291 if (DRV_FLASH_AREA(area)->ProgramData(
292 area->fa_off + off + last_unit_start_off,
293 add_padding,
294 FLASH_PROGRAM_UNIT)) {
295 return -1;
296 }
297 }
298 }
299
300 return 0;
David Vinczecea8b592019-10-29 16:09:51 +0100301}
302
303int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)
304{
305 ARM_FLASH_INFO *flash_info;
306 uint32_t deleted_len = 0;
307 int32_t rc = 0;
308
309 BOOT_LOG_DBG("erase area=%d, off=%#x, len=%#x", area->fa_id, off, len);
Sherry Zhang6e0f3242021-03-08 12:18:31 +0800310
311 if (!is_range_valid(area, off, len)) {
312 return -1;
313 }
314
Michel Jaouen26f6c022020-12-03 10:37:22 +0100315 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100316
317 if (flash_info->sector_info == NULL) {
318 /* Uniform sector layout */
319 while (deleted_len < len) {
Michel Jaouen26f6c022020-12-03 10:37:22 +0100320 rc = DRV_FLASH_AREA(area)->EraseSector(area->fa_off + off);
David Vinczecea8b592019-10-29 16:09:51 +0100321 if (rc != 0) {
322 break;
323 }
324 deleted_len += flash_info->sector_size;
325 off += flash_info->sector_size;
326 }
327 } else {
328 /* Inhomogeneous sector layout, explicitly defined
329 * Currently not supported.
330 */
331 }
332
333 return rc;
334}
335
336uint32_t flash_area_align(const struct flash_area *area)
337{
338 ARM_FLASH_INFO *flash_info;
339
Michel Jaouen26f6c022020-12-03 10:37:22 +0100340 flash_info = DRV_FLASH_AREA(area)->GetInfo();
David Vinczecea8b592019-10-29 16:09:51 +0100341 return flash_info->program_unit;
342}