blob: 97c212a7cf3e195a517199c52b0b78516baa137d [file] [log] [blame]
Dominik Ermel8101c0c2020-05-19 13:01:16 +00001/*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2020 Nordic Semiconductor ASA
Tamas Banee6615d2020-09-30 07:58:48 +01005 * Copyright (c) 2020 Arm Limited
Dominik Ermel8101c0c2020-05-19 13:01:16 +00006 */
7
8#include <assert.h>
9#include "bootutil/image.h"
10#include "bootutil_priv.h"
11#include "bootutil/bootutil_log.h"
Tamas Banee6615d2020-09-30 07:58:48 +010012#include "bootutil/fault_injection_hardening.h"
Dominik Ermel8101c0c2020-05-19 13:01:16 +000013
14#include "mcuboot_config/mcuboot_config.h"
15
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020016BOOT_LOG_MODULE_DECLARE(mcuboot);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000017
18/* Variables passed outside of unit via poiters. */
19static const struct flash_area *_fa_p;
20static struct image_header _hdr = { 0 };
21
Wouter Cappelle953a7612021-05-03 16:53:05 +020022#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
Dominik Ermel8101c0c2020-05-19 13:01:16 +000023/**
24 * Validate hash of a primary boot image.
25 *
26 * @param[in] fa_p flash area pointer
27 * @param[in] hdr boot image header pointer
28 *
Tamas Banee6615d2020-09-30 07:58:48 +010029 * @return FIH_SUCCESS on success, error code otherwise
Dominik Ermel8101c0c2020-05-19 13:01:16 +000030 */
Michael Grand5047f032022-11-24 16:49:56 +010031fih_ret
Dominik Ermel8101c0c2020-05-19 13:01:16 +000032boot_image_validate(const struct flash_area *fa_p,
33 struct image_header *hdr)
34{
35 static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
Michael Grand5047f032022-11-24 16:49:56 +010036 FIH_DECLARE(fih_rc, FIH_FAILURE);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000037
Dominik Ermeld8db0252020-10-07 11:22:45 +000038 /* NOTE: The first argument to boot_image_validate, for enc_state pointer,
39 * is allowed to be NULL only because the single image loader compiles
40 * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses
41 * the pointer from compilation.
Dominik Ermel8101c0c2020-05-19 13:01:16 +000042 */
43 /* Validate hash */
Wouter Cappelle76792152022-01-19 17:28:55 +010044 if (IS_ENCRYPTED(hdr))
Wouter Cappelle953a7612021-05-03 16:53:05 +020045 {
46 /* Clear the encrypted flag we didn't supply a key
47 * This flag could be set if there was a decryption in place
48 * was performed. We will try to validate the image, and if still
49 * encrypted the validation will fail, and go in panic mode
50 */
Wouter Cappelle76792152022-01-19 17:28:55 +010051 hdr->ih_flags &= ~(ENCRYPTIONFLAGS);
Wouter Cappelle953a7612021-05-03 16:53:05 +020052 }
Tamas Banee6615d2020-09-30 07:58:48 +010053 FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf,
54 BOOT_TMPBUF_SZ, NULL, 0, NULL);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000055
Tamas Banee6615d2020-09-30 07:58:48 +010056 FIH_RET(fih_rc);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000057}
Wouter Cappelle953a7612021-05-03 16:53:05 +020058#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/
Dominik Ermel8101c0c2020-05-19 13:01:16 +000059
60
Michael Grand5047f032022-11-24 16:49:56 +010061inline static fih_ret
Wouter Cappellebb7a39d2021-05-03 16:44:44 +020062boot_image_validate_once(const struct flash_area *fa_p,
63 struct image_header *hdr)
64{
65 static struct boot_swap_state state;
66 int rc;
Michael Grand5047f032022-11-24 16:49:56 +010067 FIH_DECLARE(fih_rc, FIH_FAILURE);
Wouter Cappellebb7a39d2021-05-03 16:44:44 +020068
69 memset(&state, 0, sizeof(struct boot_swap_state));
70 rc = boot_read_swap_state(fa_p, &state);
71 if (rc != 0)
72 FIH_RET(FIH_FAILURE);
73 if (state.magic != BOOT_MAGIC_GOOD
74 || state.image_ok != BOOT_FLAG_SET) {
75 /* At least validate the image once */
76 FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr);
Michael Grand5047f032022-11-24 16:49:56 +010077 if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
Wouter Cappellebb7a39d2021-05-03 16:44:44 +020078 FIH_RET(FIH_FAILURE);
79 }
80 if (state.magic != BOOT_MAGIC_GOOD) {
81 rc = boot_write_magic(fa_p);
82 if (rc != 0)
83 FIH_RET(FIH_FAILURE);
84 }
85 rc = boot_write_image_ok(fa_p);
86 if (rc != 0)
87 FIH_RET(FIH_FAILURE);
88 }
89 FIH_RET(FIH_SUCCESS);
90}
91
Dominik Ermel8101c0c2020-05-19 13:01:16 +000092/**
93 * Attempts to load image header from flash; verifies flash header fields.
94 *
95 * @param[in] fa_p flash area pointer
96 * @param[out] hdr buffer for image header
97 *
98 * @return 0 on success, error code otherwise
99 */
100static int
101boot_image_load_header(const struct flash_area *fa_p,
102 struct image_header *hdr)
103{
104 uint32_t size;
105 int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr);
106
107 if (rc != 0) {
108 rc = BOOT_EFLASH;
109 BOOT_LOG_ERR("Failed reading image header");
110 return BOOT_EFLASH;
111 }
112
113 if (hdr->ih_magic != IMAGE_MAGIC) {
114 BOOT_LOG_ERR("Bad image magic 0x%lx", (unsigned long)hdr->ih_magic);
115
116 return BOOT_EBADIMAGE;
117 }
118
119 if (hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
120 BOOT_LOG_ERR("Image not bootable");
121
122 return BOOT_EBADIMAGE;
123 }
124
125 if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) ||
Dominik Ermel036d5212021-07-01 11:07:41 +0000126 size >= flash_area_get_size(fa_p)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000127 return BOOT_EBADIMAGE;
128 }
129
130 return 0;
131}
132
Wouter Cappelle953a7612021-05-03 16:53:05 +0200133#ifdef MCUBOOT_ENC_IMAGES
134
135/**
136 * Validate hash of a primary boot image doing on the fly decryption as well
137 *
138 * @param[in] fa_p flash area pointer
139 * @param[in] hdr boot image header pointer
140 *
141 * @return FIH_SUCCESS on success, error code otherwise
142 */
Michael Grand5047f032022-11-24 16:49:56 +0100143inline static fih_ret
Wouter Cappelle953a7612021-05-03 16:53:05 +0200144boot_image_validate_encrypted(const struct flash_area *fa_p,
145 struct image_header *hdr)
146{
147 static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
Michael Grand5047f032022-11-24 16:49:56 +0100148 FIH_DECLARE(fih_rc, FIH_FAILURE);
Wouter Cappelle953a7612021-05-03 16:53:05 +0200149
150 struct boot_loader_state boot_data;
151 struct boot_loader_state *state = &boot_data;
152 struct boot_status _bs;
153 struct boot_status *bs = &_bs;
154 uint8_t image_index;
155 int rc;
156
157 memset(&boot_data, 0, sizeof(struct boot_loader_state));
158 image_index = BOOT_CURR_IMG(state);
159 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
160 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
161 if (rc < 0) {
162 FIH_RET(fih_rc);
163 }
164 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
165 FIH_RET(fih_rc);
166 }
167 }
168 FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index,
169 hdr, fa_p, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL);
170
171 FIH_RET(fih_rc);
172}
173
174/*
175 * Compute the total size of the given image. Includes the size of
176 * the TLVs.
177 */
178static int
179read_image_size(const struct flash_area *fa_p,
180 struct image_header *hdr,
181 uint32_t *size)
182{
183 struct image_tlv_info info;
184 uint32_t off;
185 uint32_t protect_tlv_size;
186 int rc;
187
188 off = BOOT_TLV_OFF(hdr);
189
190 if (flash_area_read(fa_p, off, &info, sizeof(info))) {
191 rc = BOOT_EFLASH;
192 goto done;
193 }
194
195 protect_tlv_size = hdr->ih_protect_tlv_size;
196 if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
197 if (protect_tlv_size != info.it_tlv_tot) {
198 rc = BOOT_EBADIMAGE;
199 goto done;
200 }
201
202 if (flash_area_read(fa_p, off + info.it_tlv_tot, &info, sizeof(info))) {
203 rc = BOOT_EFLASH;
204 goto done;
205 }
206 } else if (protect_tlv_size != 0) {
207 rc = BOOT_EBADIMAGE;
208 goto done;
209 }
210
211 if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
212 rc = BOOT_EBADIMAGE;
213 goto done;
214 }
215
216 *size = off + protect_tlv_size + info.it_tlv_tot;
217 rc = 0;
218
219done:
220 return rc;
221}
222
223
Andrzej Puzdrowski2b822272022-02-21 13:16:19 +0100224/* Get the SOC's flash erase block size from the DTS, fallback to 1024. */
225#define SOC_FLASH_ERASE_BLK_SZ \
226 DT_PROP_OR(DT_CHOSEN(zephyr_flash), erase_block_size,1024)
227
Wouter Cappelle953a7612021-05-03 16:53:05 +0200228/**
Andrzej Puzdrowski2b822272022-02-21 13:16:19 +0100229 * reads, decrypts in RAM & write back the decrypted image in the same region
230 * This function is NOT power failsafe since the image is decrypted in the RAM
231 * buffer.
Wouter Cappelle953a7612021-05-03 16:53:05 +0200232 *
233 * @param flash_area The ID of the source flash area.
234 * @param off_src The offset within the flash area to
235 * copy from.
236 * @param sz The number of bytes to copy. should match erase sector
237 *
238 * @return 0 on success; nonzero on failure.
239 */
240int
241decrypt_region_inplace(struct boot_loader_state *state,
242 const struct flash_area *fap,
243 struct image_header *hdr,
244 uint32_t off, uint32_t sz)
245{
246 uint32_t bytes_copied;
247 int chunk_sz;
248 int rc;
249 uint32_t tlv_off;
250 size_t blk_off;
251 uint16_t idx;
252 uint32_t blk_sz;
253 uint8_t image_index;
254
Andrzej Puzdrowski2b822272022-02-21 13:16:19 +0100255 static uint8_t buf[SOC_FLASH_ERASE_BLK_SZ] __attribute__((aligned));
Wouter Cappelle953a7612021-05-03 16:53:05 +0200256 assert(sz <= sizeof buf);
257
258 bytes_copied = 0;
259 while (bytes_copied < sz) {
260 if (sz - bytes_copied > sizeof buf) {
261 chunk_sz = sizeof buf;
262 } else {
263 chunk_sz = sz - bytes_copied;
264 }
265
266 rc = flash_area_read(fap, off + bytes_copied, buf, chunk_sz);
267 if (rc != 0) {
268 return BOOT_EFLASH;
269 }
270
271 image_index = BOOT_CURR_IMG(state);
272 if (IS_ENCRYPTED(hdr)) {
273 blk_sz = chunk_sz;
274 idx = 0;
275 if (off + bytes_copied < hdr->ih_hdr_size) {
276 /* do not decrypt header */
277 if (hdr->ih_hdr_size > (off + bytes_copied + chunk_sz)) {
278 /* all bytes in header, skip decryption */
279 blk_sz = 0;
280 }
281 else {
282 blk_sz = off + bytes_copied + chunk_sz - hdr->ih_hdr_size;
283 }
284
285 blk_off = 0;
286 idx = hdr->ih_hdr_size;
287 } else {
288 blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
289 }
290 tlv_off = BOOT_TLV_OFF(hdr);
291 if (off + bytes_copied + chunk_sz > tlv_off) {
292 /* do not decrypt TLVs */
293 if (off + bytes_copied >= tlv_off) {
294 blk_sz = 0;
295 } else {
296 blk_sz = tlv_off - (off + bytes_copied);
297 }
298 }
299 boot_encrypt(BOOT_CURR_ENC(state), image_index, fap,
300 (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz,
301 blk_off, &buf[idx]);
302 }
303 rc = flash_area_erase(fap, off + bytes_copied, chunk_sz);
304 if (rc != 0) {
305 return BOOT_EFLASH;
306 }
307 rc = flash_area_write(fap, off + bytes_copied, buf, chunk_sz);
308 if (rc != 0) {
309 return BOOT_EFLASH;
310 }
311
312 bytes_copied += chunk_sz;
313
314 MCUBOOT_WATCHDOG_FEED();
315 }
316
317 return 0;
318}
319
320/**
321 * Check if a image was encrypted into the first slot, and decrypt it
322 * in place. this operation is not power failsafe.
323 *
324 * The operation is done by checking the last flash sector, and using it as a
325 * temporarely scratch partition. The
326 *
327 * @param[in] fa_p flash area pointer
328 * @param[in] hdr boot image header pointer
329 *
330 * @return FIH_SUCCESS on success, error code otherwise
331 */
Michael Grand5047f032022-11-24 16:49:56 +0100332inline static fih_ret
Wouter Cappelle953a7612021-05-03 16:53:05 +0200333decrypt_image_inplace(const struct flash_area *fa_p,
334 struct image_header *hdr)
335{
Michael Grand5047f032022-11-24 16:49:56 +0100336 FIH_DECLARE(fih_rc, FIH_FAILURE);
Wouter Cappelle953a7612021-05-03 16:53:05 +0200337 int rc;
338 struct boot_loader_state boot_data;
339 struct boot_loader_state *state = &boot_data;
340 struct boot_status _bs;
341 struct boot_status *bs = &_bs;
342 size_t size;
343 size_t sect_size;
344 size_t sect_count;
345 size_t sect;
346 uint8_t image_index;
347 struct flash_sector sector;
348
349 memset(&boot_data, 0, sizeof(struct boot_loader_state));
350 memset(&_bs, 0, sizeof(struct boot_status));
351
352 /* Get size from last sector to know page/sector erase size */
Dominik Ermel68dcc0e2023-01-05 13:38:23 +0000353 rc = flash_area_get_sector(fap, boot_status_off(fa_p), &sector);
Wouter Cappelle953a7612021-05-03 16:53:05 +0200354
355
356 image_index = BOOT_CURR_IMG(state);
357
358 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
359#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly
360 /* First check if the encrypted image is a good image before decrypting */
361 FIH_CALL(boot_image_validate_encrypted,fih_rc,_fa_p,&_hdr);
Michael Grand5047f032022-11-24 16:49:56 +0100362 if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
Wouter Cappelle953a7612021-05-03 16:53:05 +0200363 FIH_RET(fih_rc);
364 }
365#endif
366 memset(&boot_data, 0, sizeof(struct boot_loader_state));
367 /* Load the encryption keys into cache */
368 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
369 if (rc < 0) {
370 FIH_RET(fih_rc);
371 }
372 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
373 FIH_RET(fih_rc);
374 }
375 }
376 else
377 {
378 /* Expected encrypted image! */
379 FIH_RET(fih_rc);
380 }
381
382 uint32_t src_size = 0;
383 rc = read_image_size(fa_p,hdr, &src_size);
384 if (rc != 0) {
385 FIH_RET(fih_rc);
386 }
387
388 sect_size = sector.fs_size;
389 sect_count = fa_p->fa_size / sect_size;
390 for (sect = 0, size = 0; size < src_size && sect < sect_count; sect++) {
391 rc = decrypt_region_inplace(state, fa_p,hdr, size, sect_size);
392 if (rc != 0) {
393 FIH_RET(fih_rc);
394 }
395 size += sect_size;
396 }
397
398 fih_rc = FIH_SUCCESS;
399 FIH_RET(fih_rc);
400}
401
402int
403boot_handle_enc_fw()
404{
405 int rc = -1;
Michael Grand5047f032022-11-24 16:49:56 +0100406 FIH_DECLARE(fih_rc, FIH_FAILURE);
Wouter Cappelle953a7612021-05-03 16:53:05 +0200407
408 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
409 assert(rc == 0);
410
411 rc = boot_image_load_header(_fa_p, &_hdr);
412 if (rc != 0) {
413 goto out;
414 }
415
416 if (IS_ENCRYPTED(&_hdr)) {
417 //encrypted, we need to decrypt in place
418 FIH_CALL(decrypt_image_inplace,fih_rc,_fa_p,&_hdr);
Michael Grand5047f032022-11-24 16:49:56 +0100419 if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
Wouter Cappelle953a7612021-05-03 16:53:05 +0200420 rc = -1;
421 goto out;
422 }
423 }
424 else
425 {
426 rc = 0;
427 }
428
429out:
430 flash_area_close(_fa_p);
431 return rc;
432}
433#endif
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000434
435/**
436 * Gather information on image and prepare for booting.
437 *
438 * @parami[out] rsp Parameters for booting image, on success
439 *
Tamas Banee6615d2020-09-30 07:58:48 +0100440 * @return FIH_SUCCESS on success; nonzero on failure.
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000441 */
Michael Grand5047f032022-11-24 16:49:56 +0100442fih_ret
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000443boot_go(struct boot_rsp *rsp)
444{
445 int rc = -1;
Michael Grand5047f032022-11-24 16:49:56 +0100446 FIH_DECLARE(fih_rc, FIH_FAILURE);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000447
448 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
449 assert(rc == 0);
450
451 rc = boot_image_load_header(_fa_p, &_hdr);
452 if (rc != 0)
453 goto out;
454
455#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
Tamas Banee6615d2020-09-30 07:58:48 +0100456 FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
Michael Grand5047f032022-11-24 16:49:56 +0100457 if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000458 goto out;
459 }
Wouter Cappellebb7a39d2021-05-03 16:44:44 +0200460#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
461 FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
Michael Grand5047f032022-11-24 16:49:56 +0100462 if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
Wouter Cappellebb7a39d2021-05-03 16:44:44 +0200463 goto out;
464 }
Tamas Banee6615d2020-09-30 07:58:48 +0100465#else
466 fih_rc = FIH_SUCCESS;
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000467#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
468
Dominik Ermel036d5212021-07-01 11:07:41 +0000469 rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p);
470 rsp->br_image_off = flash_area_get_off(_fa_p);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000471 rsp->br_hdr = &_hdr;
472
473out:
474 flash_area_close(_fa_p);
Tamas Banee6615d2020-09-30 07:58:48 +0100475
476 FIH_RET(fih_rc);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000477}