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