1 /*
2 * Copyright (c) 2017 Nordic Semiconductor ASA
3 * Copyright (c) 2016-2017 Linaro Limited
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <stddef.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <zephyr/drivers/flash.h>
12 #include <zephyr/storage/flash_map.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/init.h>
15 #include <zephyr/logging/log.h>
16
17 #include <zephyr/sys/__assert.h>
18 #include <zephyr/sys/byteorder.h>
19
20 #include "bootutil/bootutil_public.h"
21 #include <zephyr/dfu/mcuboot.h>
22
23 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
24 #include <bootutil/boot_status.h>
25 #include <zephyr/retention/blinfo.h>
26 #endif
27
28 #include "mcuboot_priv.h"
29
30 LOG_MODULE_REGISTER(mcuboot_dfu, LOG_LEVEL_DBG);
31
32 /*
33 * Helpers for image headers and trailers, as defined by mcuboot.
34 */
35
36 /*
37 * Strict defines: the definitions in the following block contain
38 * values which are MCUboot implementation requirements.
39 */
40
41 /* Header: */
42 #define BOOT_HEADER_MAGIC_V1 0x96f3b83d
43 #define BOOT_HEADER_SIZE_V1 32
44
45 enum IMAGE_INDEXES {
46 IMAGE_INDEX_INVALID = -1,
47 IMAGE_INDEX_0,
48 IMAGE_INDEX_1,
49 IMAGE_INDEX_2
50 };
51
52 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
53 /* For RAM LOAD mode, the active image must be fetched from the bootloader */
54 #define ACTIVE_SLOT_FLASH_AREA_ID boot_fetch_active_slot()
55 #define INVALID_SLOT_ID 255
56 #else
57 /* Get active partition. zephyr,code-partition chosen node must be defined */
58 #define ACTIVE_SLOT_FLASH_AREA_ID DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_code_partition))
59 #endif
60
61 /*
62 * Raw (on-flash) representation of the v1 image header.
63 */
64 struct mcuboot_v1_raw_header {
65 uint32_t header_magic;
66 uint32_t image_load_address;
67 uint16_t header_size;
68 uint16_t pad;
69 uint32_t image_size;
70 uint32_t image_flags;
71 struct {
72 uint8_t major;
73 uint8_t minor;
74 uint16_t revision;
75 uint32_t build_num;
76 } version;
77 uint32_t pad2;
78 } __packed;
79
80 /*
81 * End of strict defines
82 */
83
84 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD)
boot_fetch_active_slot(void)85 uint8_t boot_fetch_active_slot(void)
86 {
87 int rc;
88 uint8_t slot;
89
90 rc = blinfo_lookup(BLINFO_RUNNING_SLOT, &slot, sizeof(slot));
91
92 if (rc <= 0) {
93 LOG_ERR("Failed to fetch active slot: %d", rc);
94
95 return INVALID_SLOT_ID;
96 }
97
98 LOG_DBG("Active slot: %d", slot);
99
100 return slot;
101 }
102 #else /* CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD */
boot_fetch_active_slot(void)103 uint8_t boot_fetch_active_slot(void)
104 {
105 return ACTIVE_SLOT_FLASH_AREA_ID;
106 }
107 #endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_RAM_LOAD */
108
109 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
boot_get_image_start_offset(uint8_t area_id)110 size_t boot_get_image_start_offset(uint8_t area_id)
111 {
112 size_t off = 0;
113 int image = IMAGE_INDEX_INVALID;
114
115 if (area_id == FIXED_PARTITION_ID(slot1_partition)) {
116 image = IMAGE_INDEX_0;
117 #if FIXED_PARTITION_EXISTS(slot3_partition)
118 } else if (area_id == FIXED_PARTITION_ID(slot3_partition)) {
119 image = IMAGE_INDEX_1;
120 #endif
121 #if FIXED_PARTITION_EXISTS(slot5_partition)
122 } else if (area_id == FIXED_PARTITION_ID(slot5_partition)) {
123 image = IMAGE_INDEX_2;
124 #endif
125 }
126
127 if (image != IMAGE_INDEX_INVALID) {
128 /* Need to check status from primary slot to get correct offset for secondary
129 * slot image header
130 */
131 const struct flash_area *fa;
132 uint32_t num_sectors = SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN;
133 struct flash_sector sector_data;
134 int rc;
135
136 rc = flash_area_open(area_id, &fa);
137 if (rc) {
138 LOG_ERR("Flash open area %u failed: %d", area_id, rc);
139 goto done;
140 }
141
142 if (mcuboot_swap_type_multi(image) != BOOT_SWAP_TYPE_REVERT) {
143 /* For swap using offset mode, the image starts in the second sector of
144 * the upgrade slot, so apply the offset when this is needed, do this by
145 * getting information on first sector only, this is expected to return an
146 * error as there are more slots, so allow the not enough memory error
147 */
148 rc = flash_area_get_sectors(area_id, &num_sectors, §or_data);
149 if ((rc != 0 && rc != -ENOMEM) ||
150 num_sectors != SWAP_USING_OFFSET_SECTOR_UPDATE_BEGIN) {
151 LOG_ERR("Failed to get sector details: %d", rc);
152 } else {
153 off = sector_data.fs_size;
154 }
155 }
156
157 flash_area_close(fa);
158 }
159
160 done:
161 LOG_DBG("Start offset for area %u: 0x%x", area_id, off);
162 return off;
163 }
164 #endif /* CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET */
165
boot_read_v1_header(uint8_t area_id,struct mcuboot_v1_raw_header * v1_raw)166 static int boot_read_v1_header(uint8_t area_id,
167 struct mcuboot_v1_raw_header *v1_raw)
168 {
169 const struct flash_area *fa;
170 int rc;
171 size_t off = boot_get_image_start_offset(area_id);
172
173 rc = flash_area_open(area_id, &fa);
174 if (rc) {
175 return rc;
176 }
177
178 /*
179 * Read and validty-check the raw header.
180 */
181 rc = flash_area_read(fa, off, v1_raw, sizeof(*v1_raw));
182 flash_area_close(fa);
183 if (rc) {
184 return rc;
185 }
186
187 v1_raw->header_magic = sys_le32_to_cpu(v1_raw->header_magic);
188 v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
189
190 /*
191 * Validity checks.
192 *
193 * Larger values in header_size than BOOT_HEADER_SIZE_V1 are
194 * possible, e.g. if Zephyr was linked with
195 * CONFIG_ROM_START_OFFSET > BOOT_HEADER_SIZE_V1.
196 */
197 if ((v1_raw->header_magic != BOOT_HEADER_MAGIC_V1) ||
198 (v1_raw->header_size < BOOT_HEADER_SIZE_V1)) {
199 return -EIO;
200 }
201
202 v1_raw->image_load_address =
203 sys_le32_to_cpu(v1_raw->image_load_address);
204 v1_raw->header_size = sys_le16_to_cpu(v1_raw->header_size);
205 v1_raw->image_size = sys_le32_to_cpu(v1_raw->image_size);
206 v1_raw->image_flags = sys_le32_to_cpu(v1_raw->image_flags);
207 v1_raw->version.revision =
208 sys_le16_to_cpu(v1_raw->version.revision);
209 v1_raw->version.build_num =
210 sys_le32_to_cpu(v1_raw->version.build_num);
211
212 return 0;
213 }
214
boot_read_bank_header(uint8_t area_id,struct mcuboot_img_header * header,size_t header_size)215 int boot_read_bank_header(uint8_t area_id,
216 struct mcuboot_img_header *header,
217 size_t header_size)
218 {
219 int rc;
220 struct mcuboot_v1_raw_header v1_raw;
221 struct mcuboot_img_sem_ver *sem_ver;
222 size_t v1_min_size = (sizeof(uint32_t) +
223 sizeof(struct mcuboot_img_header_v1));
224
225 /*
226 * Only version 1 image headers are supported.
227 */
228 if (header_size < v1_min_size) {
229 return -ENOMEM;
230 }
231 rc = boot_read_v1_header(area_id, &v1_raw);
232 if (rc) {
233 return rc;
234 }
235
236 /*
237 * Copy just the fields we care about into the return parameter.
238 *
239 * - header_magic: skip (only used to check format)
240 * - image_load_address: skip (only matters for PIC code)
241 * - header_size: skip (only used to check format)
242 * - image_size: include
243 * - image_flags: skip (all unsupported or not relevant)
244 * - version: include
245 */
246 header->mcuboot_version = 1U;
247 header->h.v1.image_size = v1_raw.image_size;
248 sem_ver = &header->h.v1.sem_ver;
249 sem_ver->major = v1_raw.version.major;
250 sem_ver->minor = v1_raw.version.minor;
251 sem_ver->revision = v1_raw.version.revision;
252 sem_ver->build_num = v1_raw.version.build_num;
253 return 0;
254 }
255
mcuboot_swap_type_multi(int image_index)256 int mcuboot_swap_type_multi(int image_index)
257 {
258 return boot_swap_type_multi(image_index);
259 }
260
mcuboot_swap_type(void)261 int mcuboot_swap_type(void)
262 {
263 #ifdef FLASH_AREA_IMAGE_SECONDARY
264 return boot_swap_type();
265 #else
266 return BOOT_SWAP_TYPE_NONE;
267 #endif
268
269 }
270
boot_request_upgrade(int permanent)271 int boot_request_upgrade(int permanent)
272 {
273 #ifdef FLASH_AREA_IMAGE_SECONDARY
274 int rc;
275
276 rc = boot_set_pending(permanent);
277 if (rc) {
278 return -EFAULT;
279 }
280 #endif /* FLASH_AREA_IMAGE_SECONDARY */
281 return 0;
282 }
283
boot_request_upgrade_multi(int image_index,int permanent)284 int boot_request_upgrade_multi(int image_index, int permanent)
285 {
286 int rc;
287
288 rc = boot_set_pending_multi(image_index, permanent);
289 if (rc) {
290 return -EFAULT;
291 }
292 return 0;
293 }
294
boot_is_img_confirmed(void)295 bool boot_is_img_confirmed(void)
296 {
297 struct boot_swap_state state;
298 const struct flash_area *fa;
299 int rc;
300
301 rc = flash_area_open(ACTIVE_SLOT_FLASH_AREA_ID, &fa);
302 if (rc) {
303 return false;
304 }
305
306 rc = boot_read_swap_state(fa, &state);
307 if (rc != 0) {
308 return false;
309 }
310
311 if (state.magic == BOOT_MAGIC_UNSET) {
312 /* This is initial/preprogramed image.
313 * Such image can neither be reverted nor physically confirmed.
314 * Treat this image as confirmed which ensures consistency
315 * with `boot_write_img_confirmed...()` procedures.
316 */
317 return true;
318 }
319
320 return state.image_ok == BOOT_FLAG_SET;
321 }
322
boot_write_img_confirmed(void)323 int boot_write_img_confirmed(void)
324 {
325 const struct flash_area *fa;
326 int rc = 0;
327
328 if (flash_area_open(ACTIVE_SLOT_FLASH_AREA_ID, &fa) != 0) {
329 return -EIO;
330 }
331
332 rc = boot_set_next(fa, true, true);
333
334 flash_area_close(fa);
335
336 return rc;
337 }
338
boot_write_img_confirmed_multi(int image_index)339 int boot_write_img_confirmed_multi(int image_index)
340 {
341 int rc;
342
343 rc = boot_set_confirmed_multi(image_index);
344 if (rc) {
345 return -EIO;
346 }
347
348 return 0;
349 }
350
boot_erase_img_bank(uint8_t area_id)351 int boot_erase_img_bank(uint8_t area_id)
352 {
353 const struct flash_area *fa;
354 int rc;
355
356 rc = flash_area_open(area_id, &fa);
357 if (rc) {
358 return rc;
359 }
360
361 rc = flash_area_flatten(fa, 0, fa->fa_size);
362
363 flash_area_close(fa);
364
365 return rc;
366 }
367
boot_get_trailer_status_offset(size_t area_size)368 ssize_t boot_get_trailer_status_offset(size_t area_size)
369 {
370 return (ssize_t)area_size - BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 2;
371 }
372
boot_get_area_trailer_status_offset(uint8_t area_id)373 ssize_t boot_get_area_trailer_status_offset(uint8_t area_id)
374 {
375 int rc;
376 const struct flash_area *fa;
377 ssize_t offset;
378
379 rc = flash_area_open(area_id, &fa);
380 if (rc) {
381 return rc;
382 }
383
384 offset = boot_get_trailer_status_offset(fa->fa_size);
385
386 flash_area_close(fa);
387
388 if (offset < 0) {
389 return -EFAULT;
390 }
391
392 return offset;
393 }
394