blob: a2899a96c47c0442aa8a3b4ba8dcff17418c1933 [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
29#include <assert.h>
30#include "crc32c.h"
31#include <string.h>
32#include "swap_status.h"
33
34#ifdef MCUBOOT_SWAP_USING_STATUS
35
36#define IMAGE_0_STATUS_OFFS 0
37#define IMAGE_0_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
38
39#define IMAGE_1_STATUS_OFFS (IMAGE_0_STATUS_OFFS + IMAGE_0_STATUS_SIZE)
40#define IMAGE_1_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
41
42#define SCRATCH_STATUS_OFFS (IMAGE_1_STATUS_OFFS + BOOT_SWAP_STATUS_SIZE)
43#ifdef MCUBOOT_SWAP_USING_SCRATCH
44#define SCRATCH_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
45#else
46#define SCRATCH_STATUS_SIZE 0
47#endif
48
49#if (MCUBOOT_IMAGE_NUMBER == 2)
50#define IMAGE_2_STATUS_OFFS (SCRATCH_STATUS_OFFS + SCRATCH_STATUS_SIZE)
51#define IMAGE_2_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
52
53#define IMAGE_3_STATUS_OFFS (IMAGE_2_STATUS_OFFS + IMAGE_2_STATUS_SIZE)
54#define IMAGE_3_STATUS_SIZE (BOOT_SWAP_STATUS_SIZE)
55#endif
56
57const uint32_t stat_part_magic[] = {
58 BOOT_SWAP_STATUS_MAGIC
59};
60
61uint32_t calc_rec_idx(uint32_t value)
62{
63 uint32_t rec_idx;
64
65 rec_idx = value/BOOT_SWAP_STATUS_PAYLD_SZ;
66
67 return rec_idx;
68}
69
70uint32_t calc_record_offs(uint32_t offs)
71{
72 uint32_t rec_offs;
73
74 rec_offs = BOOT_SWAP_STATUS_ROW_SZ*calc_rec_idx(offs);
75
76 return rec_offs;
77}
78
79uint32_t calc_record_crc(const uint8_t *data, uint32_t length)
80{
81 uint32_t crc;
82
83 crc = crc32c_checksum(data, length);
84
85 return crc;
86}
87
88int32_t swap_status_init_offset(uint32_t area_id)
89{
90 int32_t offset = -1;
91 /* calculate an offset caused by area type: primary_x/secondary_x */
92 switch (area_id) {
93 case FLASH_AREA_IMAGE_0:
94 offset = (int)IMAGE_0_STATUS_OFFS;
95 break;
96 case FLASH_AREA_IMAGE_1:
97 offset = (int)IMAGE_1_STATUS_OFFS;
98 break;
99#ifdef MCUBOOT_SWAP_USING_SCRATCH
100 case FLASH_AREA_IMAGE_SCRATCH:
101 offset = (int)SCRATCH_STATUS_OFFS;
102 break;
103#endif
104#if (MCUBOOT_IMAGE_NUMBER == 2)
105 case FLASH_AREA_IMAGE_2:
106 offset = (int)IMAGE_2_STATUS_OFFS;
107 break;
108 case FLASH_AREA_IMAGE_3:
109 offset = (int)IMAGE_3_STATUS_OFFS;
110 break;
111#endif
112 default:
113 offset = -1;
114 break;
115 }
116 return offset;
117}
118
119int swap_status_read_record(uint32_t rec_offset, uint8_t *data, uint32_t *copy_counter)
120{ /* returns BOOT_SWAP_STATUS_PAYLD_SZ of data */
121 int rc = -1;
122
123 uint32_t fin_offset;
124 uint32_t data_offset = 0;
125 uint32_t counter, crc, magic;
126 uint32_t crc_fail = 0;
127 uint32_t magic_fail = 0;
128 uint32_t max_cnt = 0;
129
130 int32_t max_idx = 0;
131
132 uint8_t buff[BOOT_SWAP_STATUS_ROW_SZ];
133
134 const struct flash_area *fap_stat = NULL;
135
136 rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
137 if (rc != 0) {
138 return BOOT_EFLASH;
139 }
140
141 /* loop over copies/duplicates */
142 for(uint32_t i = 0; i<BOOT_SWAP_STATUS_MULT; i++) {
143 /* calculate final duplicate offset */
144 fin_offset = rec_offset + i*BOOT_SWAP_STATUS_D_SIZE;
145
146 rc = flash_area_read(fap_stat, fin_offset, buff, sizeof(buff));
147 if (rc != 0) {
148 return BOOT_EFLASH;
149 }
150 /* read magic value to know if area was pre-erased */
151 magic = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
152 BOOT_SWAP_STATUS_MGCREC_SZ -\
153 BOOT_SWAP_STATUS_CNT_SZ-\
154 BOOT_SWAP_STATUS_CRC_SZ]);
155 if (magic == BOOT_SWAP_STATUS_MAGIC) { /* read CRC */
156 crc = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
157 BOOT_SWAP_STATUS_CRC_SZ]);
158 /* check record data integrity first */
159 if (crc == calc_record_crc(buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ)) {
160 /* look for counter */
161 counter = *((uint32_t *)&buff[BOOT_SWAP_STATUS_ROW_SZ -\
162 BOOT_SWAP_STATUS_CNT_SZ - \
163 BOOT_SWAP_STATUS_CRC_SZ]);
164 /* find out counter max */
165 if (counter >= max_cnt) {
166 max_cnt = counter;
167 max_idx = (int32_t)i;
168 data_offset = fin_offset;
169 }
170 }
171 /* if crc != calculated() */
172 else {
173 crc_fail++;
174 }
175 }
176 else {
177 magic_fail++;
178 }
179 }
180 /* no magic found - status area is pre-erased, start from scratch */
181 if (magic_fail == BOOT_SWAP_STATUS_MULT) {
182 /* emulate last index was received, so next will start from beginning */
183 max_idx = (int32_t)(BOOT_SWAP_STATUS_MULT-1U);
184 *copy_counter = 0;
185 /* return all erased values */
186 (void)memset(data, (int32_t)flash_area_erased_val(fap_stat), BOOT_SWAP_STATUS_PAYLD_SZ);
187 }
188 else {
189 /* no valid CRC found - status pre-read failure */
190 if (crc_fail == BOOT_SWAP_STATUS_MULT) {
191 max_idx = -1;
192 }
193 else {
194 *copy_counter = max_cnt;
195 /* read payload data */
196 rc = flash_area_read(fap_stat, data_offset, data, BOOT_SWAP_STATUS_PAYLD_SZ);
197 if (rc != 0) {
198 return BOOT_EFLASH;
199 }
200 }
201 }
202 flash_area_close(fap_stat);
203
204 /* return back duplicate index */
205 return max_idx;
206}
207
208static int swap_status_write_record(uint32_t rec_offset, uint32_t copy_num, uint32_t copy_counter, const uint8_t *data)
209{ /* it receives explicitly BOOT_SWAP_STATUS_PAYLD_SZ of data */
210 int rc = -1;
211
212 uint32_t fin_offset;
213 /* increment counter field */
214 uint32_t next_counter = copy_counter + 1U;
215 uint32_t next_crc;
216
217 uint8_t buff[BOOT_SWAP_STATUS_ROW_SZ];
218
219 const struct flash_area *fap_stat = NULL;
220
221 rc = flash_area_open(FLASH_AREA_IMAGE_SWAP_STATUS, &fap_stat);
222 if (rc != 0) {
223 return BOOT_EFLASH;
224 }
225
226 /* copy data into buffer */
227 (void)memcpy(buff, data, BOOT_SWAP_STATUS_PAYLD_SZ);
228 /* append next counter to whole record row */
229 (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CNT_SZ-BOOT_SWAP_STATUS_CRC_SZ], \
230 &next_counter, \
231 BOOT_SWAP_STATUS_CNT_SZ);
232 /* append record magic */
233 (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-\
234 BOOT_SWAP_STATUS_MGCREC_SZ-\
235 BOOT_SWAP_STATUS_CNT_SZ-\
236 BOOT_SWAP_STATUS_CRC_SZ], \
237 stat_part_magic, \
238 BOOT_SWAP_STATUS_MGCREC_SZ);
239
240 /* calculate CRC field*/
241 next_crc = calc_record_crc(buff, BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ);
242
243 /* append new CRC to whole record row */
244 (void)memcpy(&buff[BOOT_SWAP_STATUS_ROW_SZ-BOOT_SWAP_STATUS_CRC_SZ], \
245 &next_crc, \
246 BOOT_SWAP_STATUS_CRC_SZ);
247
248 /* we already know what copy number was last and correct */
249 /* increment duplicate index */
250 /* calculate final duplicate offset */
251 if (copy_num == (BOOT_SWAP_STATUS_MULT - 1U)) {
252 copy_num = 0;
253 }
254 else {
255 copy_num++;
256 }
257 fin_offset = rec_offset + copy_num*BOOT_SWAP_STATUS_D_SIZE;
258
259 /* write prepared record into flash */
260 rc = flash_area_write(fap_stat, fin_offset, buff, sizeof(buff));
261
262 flash_area_close(fap_stat);
263
264 return rc;
265}
266
267/**
268 * Updates len bytes of status partition with values from *data-pointer.
269 *
270 * @param targ_area_id Target area id for which status is being written.
271 * Not a status-partition area id.
272 * @param offset Status byte offset inside status table. Should not include CRC and CNT.
273 * @param data Pointer to data status table to needs to be updated with.
274 * @param len Number of bytes to be written
275 *
276 * @return 0 on success; nonzero on failure.
277 */
278int swap_status_update(uint32_t targ_area_id, uint32_t offs, const void *data, uint32_t len)
279{
280 int rc = -1;
281
282 int32_t init_offs;
283 int32_t length = (int32_t)len;
284 int32_t copy_num;
285
286 uint32_t rec_offs;
287 uint32_t copy_sz;
288 uint32_t copy_counter;
289 uint32_t data_idx = 0;
290 uint32_t buff_idx = offs%BOOT_SWAP_STATUS_PAYLD_SZ;
291
292 uint8_t buff[BOOT_SWAP_STATUS_PAYLD_SZ];
293
294 /* check if end of data is still inside writable area */
295 assert ((int)((offs + len) <= BOOT_SWAP_STATUS_D_SIZE_RAW));
296
297 /* pre-calculate sub-area offset */
298 init_offs = swap_status_init_offset(targ_area_id);
299 assert ((int)(init_offs >= 0));
300
301 /* will start from it
302 * this will be write-aligned */
303 rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
304
305 /* go over all records to be updated */
306 while (length > 0) {
307 /* preserve record */
308 copy_num = swap_status_read_record(rec_offs, buff, &copy_counter);
309 /* it returns copy number */
310 if (copy_num < 0)
311 { /* something went wrong while read, exit */
312 rc = -1;
313 break;
314 }
315 /* update record data */
316 if (length > (int)BOOT_SWAP_STATUS_PAYLD_SZ) {
317 copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
318 }
319 else {
320 copy_sz = (uint32_t)length;
321 }
322 (void)memcpy((void *)&buff[buff_idx], &((uint8_t *)data)[data_idx], copy_sz);
323 buff_idx = 0;
324
325 /* write record back */
326 rc = swap_status_write_record(rec_offs, (uint32_t)copy_num, copy_counter, buff);
327 assert ((int)(rc == 0));
328
329 /* proceed to next record */
330 length -= (int32_t)BOOT_SWAP_STATUS_PAYLD_SZ;
331 rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
332 data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
333 }
334 return rc;
335}
336
337/**
338 * Reads len bytes of status partition with values from *data-pointer.
339 *
340 * @param targ_area_id Target area id for which status is being read.
341 * Not a status-partition area id.
342 * @param offset Status byte offset inside status table. Should not include CRC and CNT.
343 * @param data Pointer to data where status table values will be written.
344 * @param len Number of bytes to be read from status table.
345 *
346 * @return 0 on success; nonzero on failure.
347 */
348int swap_status_retrieve(uint32_t target_area_id, uint32_t offs, void *data, uint32_t len)
349{
350 int rc = 0;
351
352 int32_t init_offs;
353 int32_t length = (int32_t)len;
354 int32_t copy_num;
355
356 uint32_t rec_offs;
357 uint32_t copy_sz;
358 uint32_t copy_counter;
359 uint32_t data_idx = 0;
360 uint32_t buff_idx = offs % BOOT_SWAP_STATUS_PAYLD_SZ;
361
362 uint8_t buff[BOOT_SWAP_STATUS_PAYLD_SZ];
363
364 /* check if end of data is still inside writable area */
365 // TODO: update for multi image
366 assert ((int)((offs + len) <= BOOT_SWAP_STATUS_D_SIZE_RAW));
367
368 /* pre-calculate sub-area offset */
369 init_offs = swap_status_init_offset(target_area_id);
370 assert ((int)(init_offs >= 0));
371
372 /* will start from it
373 * this will be write-aligned */
374 rec_offs = (uint32_t)init_offs + calc_record_offs(offs);
375
376 /* go over all records to be updated */
377 while (length > 0) {
378 /* preserve record */
379 copy_num = swap_status_read_record(rec_offs, buff, &copy_counter);
380 /* it returns copy number */
381 if (copy_num < 0) {
382 /* something went wrong while read, exit */
383 rc = -1;
384 break;
385 }
386 /* update record data */
387 if (length > (int)BOOT_SWAP_STATUS_PAYLD_SZ) {
388 copy_sz = BOOT_SWAP_STATUS_PAYLD_SZ - buff_idx;
389 }
390 else {
391 copy_sz = (uint32_t)length;
392 }
393 (void)memcpy(&((uint8_t *)data)[data_idx], &buff[buff_idx], copy_sz);
394 buff_idx = 0;
395
396 /* proceed to next record */
397 length -= (int32_t)BOOT_SWAP_STATUS_PAYLD_SZ;
398 rec_offs += BOOT_SWAP_STATUS_ROW_SZ;
399 data_idx += BOOT_SWAP_STATUS_PAYLD_SZ;
400 }
401 return rc;
402}
403
404#endif /* MCUBOOT_SWAP_USING_STATUS */