1 /*
2  * Copyright (c) 2018-2021 Arm Limited
3  * Copyright (c) 2020 Linaro Limited
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <limits.h>
21 #include <stdbool.h>
22 #include <string.h>
23 
24 #include "mcuboot_config/mcuboot_config.h"
25 
26 #if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING)
27 #include "bootutil/boot_record.h"
28 #include "bootutil/boot_status.h"
29 #include "bootutil_priv.h"
30 #include "bootutil/image.h"
31 #include "flash_map_backend/flash_map_backend.h"
32 
33 /* Error codes for using the shared memory area. */
34 #define SHARED_MEMORY_OK            (0)
35 #define SHARED_MEMORY_OVERFLOW      (1)
36 #define SHARED_MEMORY_OVERWRITE     (2)
37 #define SHARED_MEMORY_GEN_ERROR     (3)
38 
39 /**
40  * @var shared_memory_init_done
41  *
42  * @brief Indicates whether shared memory area was already initialized.
43  *
44  */
45 static bool shared_memory_init_done;
46 
47 /* See in boot_record.h */
48 int
boot_add_data_to_shared_area(uint8_t major_type,uint16_t minor_type,size_t size,const uint8_t * data)49 boot_add_data_to_shared_area(uint8_t        major_type,
50                              uint16_t       minor_type,
51                              size_t         size,
52                              const uint8_t *data)
53 {
54     struct shared_data_tlv_entry tlv_entry = {0};
55     struct shared_boot_data *boot_data;
56     uint16_t boot_data_size;
57     uintptr_t tlv_end, offset;
58 
59     if (data == NULL) {
60         return SHARED_MEMORY_GEN_ERROR;
61     }
62 
63     boot_data = (struct shared_boot_data *)MCUBOOT_SHARED_DATA_BASE;
64 
65     /* Check whether first time to call this function. If it is, then check
66      * whether the shared data area needs to be initialised.
67      */
68     if (!shared_memory_init_done) {
69         if ((boot_data->header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) ||
70             (boot_data->header.tlv_tot_len > MCUBOOT_SHARED_DATA_SIZE)) {
71             memset((void *)MCUBOOT_SHARED_DATA_BASE, 0,
72                            MCUBOOT_SHARED_DATA_SIZE);
73             boot_data->header.tlv_magic   = SHARED_DATA_TLV_INFO_MAGIC;
74             boot_data->header.tlv_tot_len = SHARED_DATA_HEADER_SIZE;
75         }
76 
77         shared_memory_init_done = true;
78     }
79 
80     /* Check whether TLV entry is already added.
81      * Get the boundaries of TLV section
82      */
83     tlv_end = MCUBOOT_SHARED_DATA_BASE + boot_data->header.tlv_tot_len;
84     offset  = MCUBOOT_SHARED_DATA_BASE + SHARED_DATA_HEADER_SIZE;
85 
86     /* Iterates over the TLV section looks for the same entry if found then
87      * returns with error: SHARED_MEMORY_OVERWRITE
88      */
89     while (offset < tlv_end) {
90         /* Create local copy to avoid unaligned access */
91         memcpy(&tlv_entry, (const void *)offset, SHARED_DATA_ENTRY_HEADER_SIZE);
92         if (GET_MAJOR(tlv_entry.tlv_type) == major_type &&
93             GET_MINOR(tlv_entry.tlv_type) == minor_type) {
94             return SHARED_MEMORY_OVERWRITE;
95         }
96 
97         offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
98     }
99 
100     /* Add TLV entry */
101     tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
102     tlv_entry.tlv_len  = size;
103 
104     if (!boot_u16_safe_add(&boot_data_size, boot_data->header.tlv_tot_len,
105                            SHARED_DATA_ENTRY_SIZE(size))) {
106         return SHARED_MEMORY_GEN_ERROR;
107     }
108 
109     /* Verify overflow of shared area */
110     if (boot_data_size > MCUBOOT_SHARED_DATA_SIZE) {
111         return SHARED_MEMORY_OVERFLOW;
112     }
113 
114     offset = tlv_end;
115     memcpy((void *)offset, &tlv_entry, SHARED_DATA_ENTRY_HEADER_SIZE);
116 
117     offset += SHARED_DATA_ENTRY_HEADER_SIZE;
118     memcpy((void *)offset, data, size);
119 
120     boot_data->header.tlv_tot_len += SHARED_DATA_ENTRY_SIZE(size);
121 
122     return SHARED_MEMORY_OK;
123 }
124 #endif /* MCUBOOT_MEASURED_BOOT OR MCUBOOT_DATA_SHARING */
125 
126 #ifdef MCUBOOT_MEASURED_BOOT
127 /* See in boot_record.h */
128 int
boot_save_boot_status(uint8_t sw_module,const struct image_header * hdr,const struct flash_area * fap)129 boot_save_boot_status(uint8_t sw_module,
130                       const struct image_header *hdr,
131                       const struct flash_area *fap)
132 {
133 
134     struct image_tlv_iter it;
135     uint32_t offset;
136     uint16_t len;
137     uint16_t type;
138     uint16_t ias_minor;
139     size_t record_len = 0;
140     uint8_t image_hash[32]; /* SHA256 - 32 Bytes */
141     uint8_t buf[MAX_BOOT_RECORD_SZ];
142     bool boot_record_found = false;
143     bool hash_found = false;
144     int rc;
145 
146     /* Manifest data is concatenated to the end of the image.
147      * It is encoded in TLV format.
148      */
149 
150     rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
151     if (rc) {
152         return -1;
153     }
154 
155     /* Traverse through the TLV area to find the boot record
156      * and image hash TLVs.
157      */
158     while (true) {
159         rc = bootutil_tlv_iter_next(&it, &offset, &len, &type);
160         if (rc < 0) {
161             return -1;
162         } else if (rc > 0) {
163             break;
164         }
165 
166         if (type == IMAGE_TLV_BOOT_RECORD) {
167             if (len > sizeof(buf)) {
168                 return -1;
169             }
170             rc = flash_area_read(fap, offset, buf, len);
171             if (rc) {
172                 return -1;
173             }
174 
175             record_len = len;
176             boot_record_found = true;
177 
178         } else if (type == IMAGE_TLV_SHA256) {
179             /* Get the image's hash value from the manifest section. */
180             if (len > sizeof(image_hash)) {
181                 return -1;
182             }
183             rc = flash_area_read(fap, offset, image_hash, len);
184             if (rc) {
185                 return -1;
186             }
187 
188             hash_found = true;
189 
190             /* The boot record TLV is part of the protected TLV area which is
191              * located before the other parts of the TLV area (including the
192              * image hash) so at this point it is okay to break the loop
193              * as the boot record TLV should have already been found.
194              */
195             break;
196         }
197     }
198 
199 
200     if (!boot_record_found || !hash_found) {
201         return -1;
202     }
203 
204     /* Update the measurement value (hash of the image) data item in the
205      * boot record. It is always the last item in the structure to make
206      * it easy to calculate its position.
207      * The image hash is computed over the image header, the image itself and
208      * the protected TLV area (which should already include the image hash as
209      * part of the boot record TLV). For this reason this field has been
210      * filled with zeros during the image signing process.
211      */
212     offset = record_len - sizeof(image_hash);
213     /* The size of 'buf' has already been checked when
214      * the BOOT_RECORD TLV was read, it won't overflow.
215      */
216     memcpy(buf + offset, image_hash, sizeof(image_hash));
217 
218     /* Add the CBOR encoded boot record to the shared data area. */
219     ias_minor = SET_IAS_MINOR(sw_module, SW_BOOT_RECORD);
220     rc = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
221                                       ias_minor,
222                                       record_len,
223                                       buf);
224     if (rc != SHARED_MEMORY_OK) {
225         return rc;
226     }
227 
228     return 0;
229 }
230 #endif /* MCUBOOT_MEASURED_BOOT */
231