1 /*
2  * Copyright (c) 2017, 2020 Nordic Semiconductor ASA
3  * Copyright (c) 2017 Linaro Limited
4  * Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 #include <zephyr/types.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <zephyr/dfu/flash_img.h>
13 #include <zephyr/storage/flash_map.h>
14 #include <zephyr/storage/stream_flash.h>
15 
16 #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY
17 #include <bootutil/bootutil_public.h>
18 #include <zephyr/dfu/mcuboot.h>
19 #endif
20 
21 #include <zephyr/devicetree.h>
22 #ifdef CONFIG_TRUSTED_EXECUTION_NONSECURE
23 	#define UPLOAD_FLASH_AREA_LABEL slot1_ns_partition
24 #else
25 #if FIXED_PARTITION_EXISTS(slot1_partition)
26 	#define UPLOAD_FLASH_AREA_LABEL slot1_partition
27 #else
28 	#define UPLOAD_FLASH_AREA_LABEL slot0_partition
29 #endif
30 #endif
31 
32 /* FIXED_PARTITION_ID() values used below are auto-generated by DT */
33 #define UPLOAD_FLASH_AREA_ID FIXED_PARTITION_ID(UPLOAD_FLASH_AREA_LABEL)
34 #define UPLOAD_FLASH_AREA_CONTROLLER \
35 	DT_GPARENT(DT_NODELABEL(UPLOAD_FLASH_AREA_LABEL))
36 
37 #if DT_NODE_HAS_PROP(UPLOAD_FLASH_AREA_CONTROLLER, write_block_size)
38 #define FLASH_WRITE_BLOCK_SIZE \
39 	DT_PROP(UPLOAD_FLASH_AREA_CONTROLLER, write_block_size)
40 
41 BUILD_ASSERT((CONFIG_IMG_BLOCK_BUF_SIZE % FLASH_WRITE_BLOCK_SIZE == 0),
42 	     "CONFIG_IMG_BLOCK_BUF_SIZE is not a multiple of "
43 	     "FLASH_WRITE_BLOCK_SIZE");
44 #endif
45 
scramble_mcuboot_trailer(struct flash_img_context * ctx)46 static int scramble_mcuboot_trailer(struct flash_img_context *ctx)
47 {
48 	int rc = 0;
49 
50 #ifdef CONFIG_IMG_ERASE_PROGRESSIVELY
51 	if (stream_flash_bytes_written(&ctx->stream) == 0) {
52 		off_t toff = boot_get_trailer_status_offset(ctx->flash_area->fa_size);
53 		off_t offset;
54 		size_t size;
55 		const struct flash_parameters *fparams =
56 			flash_get_parameters(flash_area_get_device(ctx->flash_area));
57 #ifdef CONFIG_STREAM_FLASH_ERASE
58 		/* for erasable devices prgressive-erase works only along with
59 		 * CONFIG_STREAM_FLASH_ERASE option.
60 		 */
61 		if (flash_params_get_erase_cap(fparams) & FLASH_ERASE_C_EXPLICIT) {
62 			/* On devices with explicit erase we are aligning to page
63 			 * layout.
64 			 */
65 			struct flash_pages_info info;
66 
67 			rc = flash_get_page_info_by_offs(flash_area_get_device(ctx->flash_area),
68 							 toff, &info);
69 			if (rc != 0) {
70 				return rc;
71 			}
72 			offset = info.start_offset;
73 			size = info.size;
74 
75 		} else
76 #endif
77 		{
78 			/* On devices with no erase, we are aligning to write block
79 			 * size.
80 			 */
81 			offset = (toff + fparams->write_block_size - 1) &
82 				 ~(fparams->write_block_size - 1);
83 			/* No alignment correction needed here, offset is corrected already
84 			 * and, size should be aligned.
85 			 */
86 			size = ctx->flash_area->fa_size - offset;
87 		}
88 
89 		rc = flash_area_flatten(ctx->flash_area, offset, size);
90 	}
91 #endif
92 
93 	return rc;
94 }
95 
96 
flash_img_buffered_write(struct flash_img_context * ctx,const uint8_t * data,size_t len,bool flush)97 int flash_img_buffered_write(struct flash_img_context *ctx, const uint8_t *data,
98 			     size_t len, bool flush)
99 {
100 	int rc;
101 
102 	/* If there is a need to erase the trailer, that should happen before any
103 	 * write is done to partition.
104 	 */
105 	rc = scramble_mcuboot_trailer(ctx);
106 	if (rc != 0) {
107 		return rc;
108 	}
109 
110 
111 	/* if CONFIG_IMG_ERASE_PROGRESSIVELY is enabled the enabled CONFIG_STREAM_FLASH_ERASE
112 	 * ensures that stream_flash erases flash progresively.
113 	 */
114 	rc = stream_flash_buffered_write(&ctx->stream, data, len, flush);
115 	if (!flush) {
116 		return rc;
117 	}
118 
119 	flash_area_close(ctx->flash_area);
120 	ctx->flash_area = NULL;
121 
122 	return rc;
123 }
124 
flash_img_bytes_written(struct flash_img_context * ctx)125 size_t flash_img_bytes_written(struct flash_img_context *ctx)
126 {
127 	return stream_flash_bytes_written(&ctx->stream);
128 }
129 
flash_img_init_id(struct flash_img_context * ctx,uint8_t area_id)130 int flash_img_init_id(struct flash_img_context *ctx, uint8_t area_id)
131 {
132 	int rc;
133 	const struct device *flash_dev;
134 
135 	rc = flash_area_open(area_id,
136 			       (const struct flash_area **)&(ctx->flash_area));
137 	if (rc) {
138 		return rc;
139 	}
140 
141 	flash_dev = flash_area_get_device(ctx->flash_area);
142 
143 	return stream_flash_init(&ctx->stream, flash_dev, ctx->buf,
144 			CONFIG_IMG_BLOCK_BUF_SIZE, ctx->flash_area->fa_off,
145 			ctx->flash_area->fa_size, NULL);
146 }
147 
flash_img_init(struct flash_img_context * ctx)148 int flash_img_init(struct flash_img_context *ctx)
149 {
150 	return flash_img_init_id(ctx, UPLOAD_FLASH_AREA_ID);
151 }
152 
153 #if defined(CONFIG_IMG_ENABLE_IMAGE_CHECK)
flash_img_check(struct flash_img_context * ctx,const struct flash_img_check * fic,uint8_t area_id)154 int flash_img_check(struct flash_img_context *ctx,
155 		    const struct flash_img_check *fic,
156 		    uint8_t area_id)
157 {
158 	struct flash_area_check fac;
159 	int rc;
160 
161 	if (!ctx || !fic) {
162 		return -EINVAL;
163 	}
164 
165 	rc = flash_area_open(area_id,
166 			     (const struct flash_area **)&(ctx->flash_area));
167 	if (rc) {
168 		return rc;
169 	}
170 
171 	fac.match = fic->match;
172 	fac.clen = fic->clen;
173 	fac.off = 0;
174 	fac.rbuf = ctx->buf;
175 	fac.rblen = sizeof(ctx->buf);
176 
177 	rc = flash_area_check_int_sha256(ctx->flash_area, &fac);
178 
179 	flash_area_close(ctx->flash_area);
180 	ctx->flash_area = NULL;
181 
182 	return rc;
183 }
184 #endif
185