blob: 9fb74203540c9804706b4eb2bb8b3c4526de2f3a [file] [log] [blame]
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +02001/*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2017-2019 Linaro LTD
5 * Copyright (c) 2016-2019 JUUL Labs
6 * Copyright (c) 2019-2020 Arm Limited
7 * Copyright (c) 2020 Cypress Semiconductors
8 *
9 * Original license:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one
12 * or more contributor license agreements. See the NOTICE file
13 * distributed with this work for additional information
14 * regarding copyright ownership. The ASF licenses this file
15 * to you under the Apache License, Version 2.0 (the
16 * "License"); you may not use this file except in compliance
17 * with the License. You may obtain a copy of the License at
18 *
19 * http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing,
22 * software distributed under the License is distributed on an
23 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24 * KIND, either express or implied. See the License for the
25 * specific language governing permissions and limitations
26 * under the License.
27 */
28
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020029#include "crc32c.h"
30#include <string.h>
Roman Okhrimenko977b3752022-03-31 14:40:48 +030031#include <stdlib.h>
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020032#include "swap_status.h"
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030033#include "sysflash/sysflash.h"
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020034
35#ifdef MCUBOOT_SWAP_USING_STATUS
36
Roman Okhrimenko977b3752022-03-31 14:40:48 +030037static uint8_t record_buff[BOOT_SWAP_STATUS_ROW_SZ];
38static uint8_t status_buff[BOOT_SWAP_STATUS_PAYLD_SZ];
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +020039static const struct flash_area *last_fap_stat;
40static uint32_t last_fin_offset;
41
42static void status_buff_cache_inv(void)
43{
44 last_fap_stat = NULL;
45}
46
47static void status_buff_cache_upd(const struct flash_area* fap, uint32_t offset)
48{
49 last_fap_stat = fap;
50 last_fin_offset = offset;
51}
52
53static bool status_buff_cache_valid(const struct flash_area* fap, uint32_t offset)
54{
55 return (last_fap_stat == fap) && (last_fin_offset == offset);
56}
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020057
58const uint32_t stat_part_magic[] = {
59 BOOT_SWAP_STATUS_MAGIC
60};
61
Roman Okhrimenko977b3752022-03-31 14:40:48 +030062static inline uint32_t calc_rec_idx(uint32_t value)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020063{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030064 return value / BOOT_SWAP_STATUS_PAYLD_SZ;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020065}
66
Roman Okhrimenko977b3752022-03-31 14:40:48 +030067static inline uint32_t calc_record_offs(uint32_t offs)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020068{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030069 return BOOT_SWAP_STATUS_ROW_SZ * calc_rec_idx(offs);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020070}
71
Roman Okhrimenko977b3752022-03-31 14:40:48 +030072static inline uint32_t calc_record_crc(const uint8_t *data, uint32_t length)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020073{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030074 return crc32c_checksum(data, length);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020075}
76
Roman Okhrimenko977b3752022-03-31 14:40:48 +030077static inline uint32_t pack_bytes_u32(const uint8_t *data)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020078{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030079 uint32_t result = 0U;
80
81 result = ((uint32_t)data[3U] << 24U) | ((uint32_t)data[2U] << 16U) |
82 ((uint32_t)data[1U] << 8U) | (uint32_t)data[0U];
83
84 return result;
85}
86
87int32_t swap_status_init_offset(uint8_t area_id)
88{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030089 int32_t result = -1;
90 int32_t offset = 0;
91 uint32_t i;
92
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030093#ifdef MCUBOOT_SWAP_USING_SCRATCH
94 #define ADD_ARRAY_MEMBER_FOR_SCRATCH (1U)
95#else
96 #define ADD_ARRAY_MEMBER_FOR_SCRATCH (0U)
97#endif /* MCUBOOT_SWAP_USING_SCRATCH */
98
99 /* we always have at least 2 images in BOOT and UPGRADE slots */
100#define ARR_SIZE (SLOTS_FOR_IMAGE + ADD_ARRAY_MEMBER_FOR_SCRATCH + ((uint8_t)BOOT_IMAGE_NUMBER - 1U) * SLOTS_FOR_IMAGE)
101
102 uint8_t slots_ids[ARR_SIZE];
103
104 slots_ids[0] = FLASH_AREA_IMAGE_PRIMARY(0U);
105 slots_ids[1] = FLASH_AREA_IMAGE_SECONDARY(0U);
106
107#ifdef MCUBOOT_SWAP_USING_SCRATCH
108 /* The third position of SCRATCH is saved as it was before */
109 slots_ids[2] = FLASH_AREA_IMAGE_SCRATCH;
110#endif /* MCUBOOT_SWAP_USING_SCRATCH */
111
112#if (BOOT_IMAGE_NUMBER > 1)
113
114 uint8_t primary_slots_per_id = SLOTS_FOR_IMAGE + ADD_ARRAY_MEMBER_FOR_SCRATCH;
115 uint8_t secondary_slots_per_id = primary_slots_per_id + 1U;
116
117 for (i = 1U; i < (uint32_t) BOOT_IMAGE_NUMBER; ++i)
118 {
119 slots_ids[primary_slots_per_id] = FLASH_AREA_IMAGE_PRIMARY( i );
120 slots_ids[secondary_slots_per_id] = FLASH_AREA_IMAGE_SECONDARY( i );
121 primary_slots_per_id += 2U;
122 secondary_slots_per_id += 2U;
123 }
124
125#endif /* BOOT_IMAGE_NUMBER > 1 */
126
127 for (i = 0U; i < ARR_SIZE; i++) {
128 if (slots_ids[i] == area_id) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300129 result = offset;
130 break;
131 }
132 offset += BOOT_SWAP_STATUS_SIZE;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200133 }
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300134
135 return result;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200136}
137
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300138static int swap_status_read_record(uint32_t rec_offset, uint8_t *data, uint32_t *copy_counter, uint32_t *max_idx)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200139{ /* returns BOOT_SWAP_STATUS_PAYLD_SZ of data */
140 int rc = -1;
141
142 uint32_t fin_offset;
143 uint32_t data_offset = 0;
144 uint32_t counter, crc, magic;
145 uint32_t crc_fail = 0;
146 uint32_t magic_fail = 0;
147 uint32_t max_cnt = 0;
148
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200149 const struct flash_area *fap_stat = NULL;
150
151 rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
152 if (rc != 0) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300153 return -1;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200154 }
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300155 else {
156 /* loop over copies/duplicates */
157 for (uint32_t i = 0; i < BOOT_SWAP_STATUS_MULT; i++) {
158 /* calculate final duplicate offset */
159 fin_offset = rec_offset + i * BOOT_SWAP_STATUS_D_SIZE;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200160
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300161 rc = flash_area_read(fap_stat, fin_offset, record_buff, sizeof(record_buff));
162 if (rc != 0) {
163 return -1;
164 }
165 else {
166 /* read magic value to know if area was pre-erased */
167 magic = pack_bytes_u32(&record_buff[BOOT_SWAP_STATUS_PAYLD_SZ]);
168 if (magic == BOOT_SWAP_STATUS_MAGIC) { /* read CRC */
169 crc = pack_bytes_u32(&record_buff[BOOT_SWAP_STATUS_ROW_SZ -
170 BOOT_SWAP_STATUS_CRC_SZ]);
171 /* check record data integrity first */
172 if (crc == calc_record_crc(record_buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ)) {
173 /* look for counter */
174 counter = pack_bytes_u32(&record_buff[BOOT_SWAP_STATUS_ROW_SZ -
175 BOOT_SWAP_STATUS_CNT_SZ -
176 BOOT_SWAP_STATUS_CRC_SZ]);
177 /* find out counter max */
178 if (counter >= max_cnt) {
179 max_cnt = counter;
180 *max_idx = i;
181 data_offset = fin_offset;
182 }
183 }
184 /* if crc != calculated() */
185 else {
186 crc_fail++;
187 }
188 }
189 else {
190 magic_fail++;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200191 }
192 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200193 }
194 }
195 /* no magic found - status area is pre-erased, start from scratch */
196 if (magic_fail == BOOT_SWAP_STATUS_MULT) {
197 /* emulate last index was received, so next will start from beginning */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300198 *max_idx = BOOT_SWAP_STATUS_MULT - 1U;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200199 *copy_counter = 0;
200 /* return all erased values */
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200201 if (status_buff_cache_valid(fap_stat, fin_offset) == false) {
202 (void)memset(data, (int32_t)flash_area_erased_val(fap_stat), BOOT_SWAP_STATUS_PAYLD_SZ);
203 status_buff_cache_upd(fap_stat, fin_offset);
204 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200205 }
206 else {
207 /* no valid CRC found - status pre-read failure */
208 if (crc_fail == BOOT_SWAP_STATUS_MULT) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300209 rc = -1;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200210 }
211 else {
212 *copy_counter = max_cnt;
213 /* read payload data */
214 rc = flash_area_read(fap_stat, data_offset, data, BOOT_SWAP_STATUS_PAYLD_SZ);
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200215 status_buff_cache_inv();
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300216 if (rc != 0) {
217 rc = -1;
218 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200219 }
220 }
221 flash_area_close(fap_stat);
222
223 /* return back duplicate index */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300224 return rc;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200225}
226
227static int swap_status_write_record(uint32_t rec_offset, uint32_t copy_num, uint32_t copy_counter, const uint8_t *data)
228{ /* it receives explicitly BOOT_SWAP_STATUS_PAYLD_SZ of data */
229 int rc = -1;
230
231 uint32_t fin_offset;
232 /* increment counter field */
233 uint32_t next_counter = copy_counter + 1U;
234 uint32_t next_crc;
235
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200236 const struct flash_area *fap_stat = NULL;
237
238 rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
239 if (rc != 0) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300240 return -1;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200241 }
242
243 /* copy data into buffer */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300244 (void)memcpy(record_buff, data, BOOT_SWAP_STATUS_PAYLD_SZ);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200245 /* append next counter to whole record row */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300246 (void)memcpy(&record_buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CNT_SZ-BOOT_SWAP_STATUS_CRC_SZ],
247 (uint8_t *)&next_counter,
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200248 BOOT_SWAP_STATUS_CNT_SZ);
249 /* append record magic */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300250 (void)memcpy(&record_buff[BOOT_SWAP_STATUS_PAYLD_SZ], (const uint8_t *)stat_part_magic, BOOT_SWAP_STATUS_MGCREC_SZ);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200251
252 /* calculate CRC field*/
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300253 next_crc = calc_record_crc(record_buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200254
255 /* append new CRC to whole record row */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300256 (void)memcpy(&record_buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ],
257 (uint8_t *)&next_crc,
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200258 BOOT_SWAP_STATUS_CRC_SZ);
259
260 /* we already know what copy number was last and correct */
261 /* increment duplicate index */
262 /* calculate final duplicate offset */
263 if (copy_num == (BOOT_SWAP_STATUS_MULT - 1U)) {
264 copy_num = 0;
265 }
266 else {
267 copy_num++;
268 }
269 fin_offset = rec_offset + copy_num*BOOT_SWAP_STATUS_D_SIZE;
270
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300271 /* erase obsolete status record before write */
272 rc = flash_area_erase(fap_stat, fin_offset, sizeof(record_buff));
273 if (rc != 0) {
274 return -1;
275 }
276
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200277 /* write prepared record into flash */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300278 rc = flash_area_write(fap_stat, fin_offset, record_buff, sizeof(record_buff));
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200279
280 flash_area_close(fap_stat);
281
282 return rc;
283}
284
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300285static int boot_magic_decode(uint8_t *magic)
286{
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300287 if (memcmp((const void *)magic, (const void *)&boot_img_magic.val, BOOT_MAGIC_SZ) == 0) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300288 return BOOT_MAGIC_GOOD;
289 }
290 return BOOT_MAGIC_BAD;
291}
292
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200293/**
294 * Updates len bytes of status partition with values from *data-pointer.
295 *
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300296 * @param target_area_id Target area id for which status is being written.
297 * Not a status-partition area id.
298 * @param offset Status byte offset inside status table. Should not include CRC and CNT.
299 * @param data Pointer to data status table to needs to be updated with.
300 * @param len Number of bytes to be written
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200301 *
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300302 * @return 0 on success; -1 on failure.
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200303 */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300304int swap_status_update(uint8_t target_area_id, uint32_t offs, const void *data, uint32_t len)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200305{
306 int rc = -1;
307
308 int32_t init_offs;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300309 uint32_t copy_num = 0;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200310
311 uint32_t rec_offs;
312 uint32_t copy_sz;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300313 uint32_t copy_counter = 0;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200314 uint32_t data_idx = 0;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300315 uint32_t buff_idx = offs % BOOT_SWAP_STATUS_PAYLD_SZ;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200316
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300317 if ((UINT32_MAX - offs) < len) {
318 return -1;
319 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200320
321 /* check if end of data is still inside writable area */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300322 if ((offs + len) > BOOT_SWAP_STATUS_D_SIZE_RAW) {
323 return -1;
324 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200325
326 /* pre-calculate sub-area offset */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300327 init_offs = swap_status_init_offset(target_area_id);
328 if (init_offs < 0) {
329 return -1;
330 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200331
332 /* will start from it
333 * this will be write-aligned */
334 rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
335
336 /* go over all records to be updated */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300337 while (len != 0U) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200338 /* preserve record */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300339 rc = swap_status_read_record(rec_offs, status_buff, &copy_counter, &copy_num);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200340 /* it returns copy number */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300341 if (rc < 0)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200342 { /* something went wrong while read, exit */
343 rc = -1;
344 break;
345 }
346 /* update record data */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300347 if (len > BOOT_SWAP_STATUS_PAYLD_SZ) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200348 copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
349 }
350 else {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300351 copy_sz = len;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200352 }
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300353
354 (void)memcpy(status_buff + buff_idx, (const uint8_t *)data + data_idx, copy_sz);
Roman Okhrimenko883cb5b2024-03-28 17:22:33 +0200355 status_buff_cache_inv();
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200356 buff_idx = 0;
357
358 /* write record back */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300359 rc = swap_status_write_record(rec_offs, copy_num, copy_counter, status_buff);
360 if (rc != 0) {
361 break;
362 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200363
364 /* proceed to next record */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300365 if (len < BOOT_SWAP_STATUS_PAYLD_SZ) {
366 len = 0;
367 }
368 else {
369 len -= BOOT_SWAP_STATUS_PAYLD_SZ;
370 rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
371 data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
372 }
373
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200374 }
375 return rc;
376}
377
378/**
379 * Reads len bytes of status partition with values from *data-pointer.
380 *
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300381 * @param target_area_id Target area id for which status is being read.
382 * Not a status-partition area id.
383 * @param offset Status byte offset inside status table. Should not include CRC and CNT.
384 * @param data Pointer to data where status table values will be written.
385 * @param len Number of bytes to be read from status table.
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200386 *
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300387 * @return 0 on success; -1 on failure.
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200388 */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300389int swap_status_retrieve(uint8_t target_area_id, uint32_t offs, void *data, uint32_t len)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200390{
391 int rc = 0;
392
393 int32_t init_offs;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300394 uint32_t copy_num = 0;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200395
396 uint32_t rec_offs;
397 uint32_t copy_sz;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300398 uint32_t copy_counter = 0;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200399 uint32_t data_idx = 0;
400 uint32_t buff_idx = offs % BOOT_SWAP_STATUS_PAYLD_SZ;
401
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300402 if ((UINT32_MAX - offs) < len) {
403 return -1;
404 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200405
406 /* check if end of data is still inside writable area */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300407 if ((offs + len) > BOOT_SWAP_STATUS_D_SIZE_RAW) {
408 return -1;
409 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200410
411 /* pre-calculate sub-area offset */
412 init_offs = swap_status_init_offset(target_area_id);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300413 if (init_offs < 0) {
414 return -1;
415 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200416
417 /* will start from it
418 * this will be write-aligned */
419 rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
420
421 /* go over all records to be updated */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300422 while (len != 0U) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200423 /* preserve record */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300424 rc = swap_status_read_record(rec_offs, status_buff, &copy_counter, &copy_num);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200425 /* it returns copy number */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300426 if (rc < 0) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200427 /* something went wrong while read, exit */
428 rc = -1;
429 break;
430 }
431 /* update record data */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300432 if (len > BOOT_SWAP_STATUS_PAYLD_SZ) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200433 copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
434 }
435 else {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300436 copy_sz = len;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200437 }
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300438
439 (void)memcpy((uint8_t *)data + data_idx, status_buff + buff_idx, copy_sz);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200440 buff_idx = 0;
441
442 /* proceed to next record */
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300443 if (len < BOOT_SWAP_STATUS_PAYLD_SZ) {
444 len = 0;
445 }
446 else {
447 len -= BOOT_SWAP_STATUS_PAYLD_SZ;
448 rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
449 data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
450 }
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200451 }
452 return rc;
453}
454
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300455/**
456 * Copy trailer from status partition to primary image and set copy_done flag.
457 * This function calls only once, before set copy_done flag in status trailer
458 *
459 * @param fap Target area id for which status is being read.
460 *
461 * @return 0 on success; -1 on failure.
462 */
463int swap_status_to_image_trailer(const struct flash_area *fap)
464{
465 uint8_t erased_val;
466 uint8_t stat_erased_val;
467 const uint8_t *copy_src;
468 uint32_t cur_trailer_pos;
469 uint32_t primary_trailer_sz;
470 uint32_t primary_trailer_buf_sz;
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300471 uint32_t align = MEMORY_ALIGN;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300472 int rc = 0;
473 const struct flash_area *fap_stat = NULL;
474 uint8_t primary_trailer_buf[MAX_TRAILER_BUF_SIZE];
475
476 /* get the erased flash byte for status partition */
477 rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
478 if (rc != 0) {
479 return rc;
480 }
481 stat_erased_val = flash_area_erased_val(fap_stat);
482 flash_area_close(fap_stat);
483
484 /* get status partition trailer size and copy it to buffer */
485 const uint32_t status_trailer_buf_sz = BOOT_SWAP_STATUS_SWAPSZ_SZ + BOOT_SWAP_STATUS_SWAPINF_SZ +
486 BOOT_SWAP_STATUS_COPY_DONE_SZ + BOOT_SWAP_STATUS_IMG_OK_SZ + BOOT_SWAP_STATUS_MAGIC_SZ;
487 uint8_t status_trailer_buf[status_trailer_buf_sz];
488 (void)memset(&status_trailer_buf, 0, status_trailer_buf_sz);
489 rc = swap_status_retrieve(fap->fa_id, boot_swap_size_off(fap), (uint8_t *)status_trailer_buf, status_trailer_buf_sz);
490 if (rc != 0) {
491 return rc;
492 }
493
494 /* check trailer magic in status partition */
495 if (boot_magic_decode(&status_trailer_buf[status_trailer_buf_sz - BOOT_SWAP_STATUS_MAGIC_SZ]) != BOOT_MAGIC_GOOD) {
496 return -1;
497 }
498
499 /* get primary slot trailer size without status data fields */
500 primary_trailer_sz = boot_trailer_sz(0);
501
502 /* align image trailer buffer size to minimal write size */
503#if !defined(__BOOTSIM__)
504 align = flash_area_align(fap);
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300505 /* TODO: this code needs to be refined. It is introduced to overcome
506 * situation when MCUBootApp lives in internal memory, but user app
507 * is executed from different type memory - external in XIP mode in
508 * this case. This situation now arise on PSOC6 when XIP execution is
509 * used, bay may be applicable to other devices, where solution is
510 * distributed between memories with different write/erase sizes.
511 */
512#ifdef CY_BOOT_USE_EXTERNAL_FLASH
513 if (align > MEMORY_ALIGN) {
514 align = MEMORY_ALIGN;
515 }
516#endif
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300517#else
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300518 align = MEMORY_ALIGN;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300519#endif
520
521 if ((align > MAX_TRAILER_BUF_SIZE) || (align == 0U)) {
522 return -1;
523 }
524
525 primary_trailer_buf_sz = align;
526 while (primary_trailer_buf_sz < primary_trailer_sz) {
527 primary_trailer_buf_sz += align;
528 }
529 if (primary_trailer_buf_sz > MAX_TRAILER_BUF_SIZE) {
530 return -1;
531 }
532
533 /* erase primary slot trailer */
534 rc= flash_area_erase(fap, fap->fa_size - primary_trailer_buf_sz, primary_trailer_buf_sz);
535 if (rc != 0) {
536 return rc;
537 }
538
539 /* erase trailer area */
540 erased_val = flash_area_erased_val(fap); /* stat_erased_val and erased_val may differ! */
541 (void)memset(&primary_trailer_buf[primary_trailer_buf_sz-primary_trailer_sz], (int) erased_val, primary_trailer_sz);
542
543 /* copy and align flags and data from status_trailer_buf to primary_trailer_buf
544 Status part trailer --> Pimary image trailer */
545
546 /* copy trailer magic */
547 cur_trailer_pos = primary_trailer_buf_sz - BOOT_SWAP_STATUS_MAGIC_SZ;
548 copy_src = &status_trailer_buf[status_trailer_buf_sz - BOOT_SWAP_STATUS_MAGIC_SZ];
549 if (stat_erased_val != erased_val &&
550 bootutil_buffer_is_filled(copy_src, stat_erased_val, BOOT_SWAP_STATUS_MAGIC_SZ)) {
551
552 (void)memset(&primary_trailer_buf[cur_trailer_pos], (int)erased_val, BOOT_SWAP_STATUS_MAGIC_SZ);
553 }
554 else {
555 (void)memcpy(&primary_trailer_buf[cur_trailer_pos], copy_src, BOOT_SWAP_STATUS_MAGIC_SZ);
556 }
557
558 /* copy image_ok flag */
559 cur_trailer_pos -= BOOT_MAX_ALIGN;
560 copy_src = &status_trailer_buf[BOOT_SWAP_STATUS_SWAPSZ_SZ + BOOT_SWAP_STATUS_SWAPINF_SZ + BOOT_SWAP_STATUS_COPY_DONE_SZ];
561 if (stat_erased_val != erased_val && stat_erased_val == *copy_src) {
562 copy_src = &erased_val;
563 }
564 primary_trailer_buf[cur_trailer_pos] = *copy_src;
565
566 /* set copy_done flag */
567 cur_trailer_pos -= BOOT_MAX_ALIGN;
568 primary_trailer_buf[cur_trailer_pos] = BOOT_FLAG_SET;
569
570 /* copy swap_type flag */
571 cur_trailer_pos -= BOOT_MAX_ALIGN;
572 copy_src = &status_trailer_buf[BOOT_SWAP_STATUS_SWAPSZ_SZ];
573 if (stat_erased_val != erased_val && stat_erased_val == *copy_src) {
574 copy_src = &erased_val;
575 }
576 primary_trailer_buf[cur_trailer_pos] = *copy_src;
577
578 /* copy swap_size field */
579 cur_trailer_pos -= BOOT_MAX_ALIGN;
580 if (stat_erased_val != erased_val &&
581 bootutil_buffer_is_filled(status_trailer_buf, stat_erased_val, BOOT_SWAP_STATUS_SWAPSZ_SZ)) {
582
583 (void)memset(&primary_trailer_buf[cur_trailer_pos], (int)erased_val, BOOT_SWAP_STATUS_SWAPSZ_SZ);
584 }
585 else {
586 (void)memcpy(&primary_trailer_buf[cur_trailer_pos], status_trailer_buf, BOOT_SWAP_STATUS_SWAPSZ_SZ);
587 }
588
589 /* write primary image trailer with copy_done flag set */
590 rc = flash_area_write(fap, fap->fa_size - primary_trailer_buf_sz, primary_trailer_buf, primary_trailer_buf_sz);
591
592 return rc;
593}
594
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200595#endif /* MCUBOOT_SWAP_USING_STATUS */