1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21
22 #include "tinycbor/cbor.h"
23 #include "cborattr/cborattr.h"
24 #include "mgmt/mgmt.h"
25 #include "img_mgmt/img_mgmt.h"
26 #include "img_mgmt/image.h"
27 #include "img_mgmt_priv.h"
28 #include "img_mgmt/img_mgmt_impl.h"
29
30 /**
31 * Collects information about the specified image slot.
32 */
33 uint8_t
img_mgmt_state_flags(int query_slot)34 img_mgmt_state_flags(int query_slot)
35 {
36 uint8_t flags;
37 int swap_type;
38
39 assert(query_slot == 0 || query_slot == 1);
40
41 flags = 0;
42
43 /* Determine if this is is pending or confirmed (only applicable for
44 * unified images and loaders.
45 */
46 swap_type = img_mgmt_impl_swap_type();
47 switch (swap_type) {
48 case IMG_MGMT_SWAP_TYPE_NONE:
49 if (query_slot == IMG_MGMT_BOOT_CURR_SLOT) {
50 flags |= IMG_MGMT_STATE_F_CONFIRMED;
51 flags |= IMG_MGMT_STATE_F_ACTIVE;
52 }
53 break;
54
55 case IMG_MGMT_SWAP_TYPE_TEST:
56 if (query_slot == IMG_MGMT_BOOT_CURR_SLOT) {
57 flags |= IMG_MGMT_STATE_F_CONFIRMED;
58 } else {
59 flags |= IMG_MGMT_STATE_F_PENDING;
60 }
61 break;
62
63 case IMG_MGMT_SWAP_TYPE_PERM:
64 if (query_slot == IMG_MGMT_BOOT_CURR_SLOT) {
65 flags |= IMG_MGMT_STATE_F_CONFIRMED;
66 } else {
67 flags |= IMG_MGMT_STATE_F_PENDING | IMG_MGMT_STATE_F_PERMANENT;
68 }
69 break;
70
71 case IMG_MGMT_SWAP_TYPE_REVERT:
72 if (query_slot == IMG_MGMT_BOOT_CURR_SLOT) {
73 flags |= IMG_MGMT_STATE_F_ACTIVE;
74 } else {
75 flags |= IMG_MGMT_STATE_F_CONFIRMED;
76 }
77 break;
78 }
79
80 /* Slot 0 is always active. */
81 /* XXX: The slot 0 assumption only holds when running from flash. */
82 if (query_slot == IMG_MGMT_BOOT_CURR_SLOT) {
83 flags |= IMG_MGMT_STATE_F_ACTIVE;
84 }
85
86 return flags;
87 }
88
89 /**
90 * Indicates whether any image slot is pending (i.e., whether a test swap will
91 * happen on the next reboot.
92 */
93 int
img_mgmt_state_any_pending(void)94 img_mgmt_state_any_pending(void)
95 {
96 return img_mgmt_state_flags(0) & IMG_MGMT_STATE_F_PENDING ||
97 img_mgmt_state_flags(1) & IMG_MGMT_STATE_F_PENDING;
98 }
99
100 /**
101 * Indicates whether the specified slot has any flags. If no flags are set,
102 * the slot can be freely erased.
103 */
104 int
img_mgmt_slot_in_use(int slot)105 img_mgmt_slot_in_use(int slot)
106 {
107 uint8_t state_flags;
108
109 state_flags = img_mgmt_state_flags(slot);
110 return state_flags & IMG_MGMT_STATE_F_ACTIVE ||
111 state_flags & IMG_MGMT_STATE_F_CONFIRMED ||
112 state_flags & IMG_MGMT_STATE_F_PENDING;
113 }
114
115 /**
116 * Sets the pending flag for the specified image slot. That is, the system
117 * will swap to the specified image on the next reboot. If the permanent
118 * argument is specified, the system doesn't require a confirm after the swap
119 * occurs.
120 */
121 int
img_mgmt_state_set_pending(int slot,int permanent)122 img_mgmt_state_set_pending(int slot, int permanent)
123 {
124 uint8_t hash[IMAGE_HASH_LEN];
125 uint8_t state_flags;
126 const uint8_t *hashp;
127 int rc;
128
129 state_flags = img_mgmt_state_flags(slot);
130
131 /* Unconfirmed slots are always runable. A confirmed slot can only be
132 * run if it is a loader in a split image setup.
133 */
134 if (state_flags & IMG_MGMT_STATE_F_CONFIRMED && slot != 0) {
135 rc = MGMT_ERR_EBADSTATE;
136 goto done;
137 }
138
139 rc = img_mgmt_impl_write_pending(slot, permanent);
140 if (rc != 0) {
141 rc = MGMT_ERR_EUNKNOWN;
142 }
143
144 done:
145 /* Log the image hash if we know it. */
146 if (img_mgmt_read_info(slot, NULL, hash, NULL)) {
147 hashp = NULL;
148 } else {
149 hashp = hash;
150 }
151
152 if (permanent) {
153 (void) img_mgmt_impl_log_confirm(rc, hashp);
154 } else {
155 (void) img_mgmt_impl_log_pending(rc, hashp);
156 }
157
158 return rc;
159 }
160
161 /**
162 * Confirms the current image state. Prevents a fallback from occurring on the
163 * next reboot if the active image is currently being tested.
164 */
165 int
img_mgmt_state_confirm(void)166 img_mgmt_state_confirm(void)
167 {
168 int rc;
169
170 /* Confirm disallowed if a test is pending. */
171 if (img_mgmt_state_any_pending()) {
172 rc = MGMT_ERR_EBADSTATE;
173 goto err;
174 }
175
176 rc = img_mgmt_impl_write_confirmed();
177 if (rc != 0) {
178 rc = MGMT_ERR_EUNKNOWN;
179 }
180
181 img_mgmt_dfu_confirmed();
182 err:
183 return img_mgmt_impl_log_confirm(rc, NULL);
184 }
185
186 /**
187 * Command handler: image state read
188 */
189 int
img_mgmt_state_read(struct mgmt_ctxt * ctxt)190 img_mgmt_state_read(struct mgmt_ctxt *ctxt)
191 {
192 char vers_str[IMG_MGMT_VER_MAX_STR_LEN];
193 uint8_t hash[IMAGE_HASH_LEN]; /* SHA256 hash */
194 struct image_version ver;
195 CborEncoder images;
196 CborEncoder image;
197 CborError err;
198 uint32_t flags;
199 uint8_t state_flags;
200 int rc;
201 int i;
202
203 err = 0;
204 err |= cbor_encode_text_stringz(&ctxt->encoder, "images");
205
206 err |= cbor_encoder_create_array(&ctxt->encoder, &images,
207 CborIndefiniteLength);
208 for (i = 0; i < 2; i++) {
209 rc = img_mgmt_read_info(i, &ver, hash, &flags);
210 if (rc != 0) {
211 continue;
212 }
213
214 state_flags = img_mgmt_state_flags(i);
215
216 err |= cbor_encoder_create_map(&images, &image,
217 CborIndefiniteLength);
218 err |= cbor_encode_text_stringz(&image, "slot");
219 err |= cbor_encode_int(&image, i);
220
221 err |= cbor_encode_text_stringz(&image, "version");
222 img_mgmt_ver_str(&ver, vers_str);
223 err |= cbor_encode_text_stringz(&image, vers_str);
224
225 err |= cbor_encode_text_stringz(&image, "hash");
226 err |= cbor_encode_byte_string(&image, hash, IMAGE_HASH_LEN);
227
228 err |= cbor_encode_text_stringz(&image, "bootable");
229 err |= cbor_encode_boolean(&image, !(flags & IMAGE_F_NON_BOOTABLE));
230
231 err |= cbor_encode_text_stringz(&image, "pending");
232 err |= cbor_encode_boolean(&image,
233 state_flags & IMG_MGMT_STATE_F_PENDING);
234
235 err |= cbor_encode_text_stringz(&image, "confirmed");
236 err |= cbor_encode_boolean(&image,
237 state_flags & IMG_MGMT_STATE_F_CONFIRMED);
238
239 err |= cbor_encode_text_stringz(&image, "active");
240 err |= cbor_encode_boolean(&image,
241 state_flags & IMG_MGMT_STATE_F_ACTIVE);
242
243 err |= cbor_encode_text_stringz(&image, "permanent");
244 err |= cbor_encode_boolean(&image,
245 state_flags & IMG_MGMT_STATE_F_PERMANENT);
246
247 err |= cbor_encoder_close_container(&images, &image);
248 }
249
250 err |= cbor_encoder_close_container(&ctxt->encoder, &images);
251
252 err |= cbor_encode_text_stringz(&ctxt->encoder, "splitStatus");
253 err |= cbor_encode_int(&ctxt->encoder, 0);
254
255 if (err != 0) {
256 return MGMT_ERR_ENOMEM;
257 }
258
259 return 0;
260 }
261
262 /**
263 * Command handler: image state write
264 */
265 int
img_mgmt_state_write(struct mgmt_ctxt * ctxt)266 img_mgmt_state_write(struct mgmt_ctxt *ctxt)
267 {
268 /*
269 * We add 1 to the 32-byte hash buffer as _cbor_value_copy_string() adds
270 * a null character at the end of the buffer.
271 */
272 uint8_t hash[IMAGE_HASH_LEN + 1];
273 size_t hash_len;
274 bool confirm;
275 int slot;
276 int rc;
277
278 const struct cbor_attr_t write_attr[] = {
279 [0] = {
280 .attribute = "hash",
281 .type = CborAttrByteStringType,
282 .addr.bytestring.data = hash,
283 .addr.bytestring.len = &hash_len,
284 .len = sizeof(hash),
285 },
286 [1] = {
287 .attribute = "confirm",
288 .type = CborAttrBooleanType,
289 .addr.boolean = &confirm,
290 .dflt.boolean = false,
291 },
292 [2] = { 0 },
293 };
294
295 hash_len = 0;
296 rc = cbor_read_object(&ctxt->it, write_attr);
297 if (rc != 0) {
298 return MGMT_ERR_EINVAL;
299 }
300
301 /* Determine which slot is being operated on. */
302 if (hash_len == 0) {
303 if (confirm) {
304 slot = IMG_MGMT_BOOT_CURR_SLOT;
305 } else {
306 /* A 'test' without a hash is invalid. */
307 return MGMT_ERR_EINVAL;
308 }
309 } else {
310 slot = img_mgmt_find_by_hash(hash, NULL);
311 if (slot < 0) {
312 return MGMT_ERR_EINVAL;
313 }
314 }
315
316 if (slot == IMG_MGMT_BOOT_CURR_SLOT && confirm) {
317 /* Confirm current setup. */
318 rc = img_mgmt_state_confirm();
319 } else {
320 rc = img_mgmt_state_set_pending(slot, confirm);
321 }
322 if (rc != 0) {
323 return rc;
324 }
325
326 /* Send the current image state in the response. */
327 rc = img_mgmt_state_read(ctxt);
328 if (rc != 0) {
329 return rc;
330 }
331
332 return 0;
333 }
334