blob: 8aa56ddb9390cde0f114eac1719d942804fceb08 [file] [log] [blame]
Jamie Fox34681992023-09-04 18:14:06 +01001/*
2 * Copyright (c) 2023, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include "dpe_boot_data.h"
9
10#include <stdbool.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <string.h>
14
15#include "boot_hal.h"
16#include "boot_measurement.h"
17#include "dpe_context_mngr.h"
18#include "service_api.h"
19#include "tfm_boot_status.h"
20#include "tfm_plat_otp.h"
21
22/* Maximum measurement size is size of SHA-512 hash */
23#define MEASUREMENT_VALUE_MAX_SIZE 64
24
25/* Size of 1 complete measurement (value + metadata) in TLV format. */
26#define SHARED_BOOT_MEASUREMENT_SIZE \
27 ((2 * SHARED_DATA_ENTRY_HEADER_SIZE) \
28 + sizeof(struct boot_measurement_metadata) \
29 + MEASUREMENT_VALUE_MAX_SIZE)
30
31/* 2 measurements from the BL1 stages and 1 measurement per image from BL2. */
32#define MAX_SHARED_BOOT_DATA_LENGTH \
33 ((2 + MCUBOOT_IMAGE_NUMBER) * SHARED_BOOT_MEASUREMENT_SIZE)
34
35/**
36 * \struct boot_measurement_data
37 *
38 * \brief Contains all the measurement and related metadata (from BL1_x and BL2).
39 *
40 * \details This is a redefinition of \ref tfm_boot_data to allocate the
41 * appropriate, service dependent size of \ref boot_data.
42 */
43struct boot_measurement_data {
44 struct shared_data_tlv_header header;
45 uint8_t data[MAX_SHARED_BOOT_DATA_LENGTH];
46};
47
48/**
49 * \var boot_measurements
50 *
51 * \brief Store the boot measurements in the service's memory.
52 *
53 * \details Boot measurements come from the BL1 and BL2 boot stages and stored
54 * on a memory area which is shared between the bootloaders and SPM.
55 * SPM provides the \ref tfm_core_get_boot_data() API to retrieve
56 * the service related data from shared area.
57 */
58__attribute__ ((aligned(4)))
59static struct boot_measurement_data boot_measurements;
60
61/**
62 * \brief Get the current DICE mode based on HW state.
63 *
64 * \return DICE mode.
65 */
66static DiceMode get_dice_mode(void)
67{
68 enum tfm_plat_err_t err;
69 enum plat_otp_lcs_t otp_lcs;
70
71 err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(otp_lcs),
72 (uint8_t *)&otp_lcs);
73 if (err != TFM_PLAT_ERR_SUCCESS) {
74 return kDiceModeNotInitialized;
75 }
76
77 /* FIXME consider DCU state as well */
78 switch (otp_lcs) {
79 case PLAT_OTP_LCS_SECURED:
80 return kDiceModeNormal;
81 case PLAT_OTP_LCS_DECOMMISSIONED:
82 return kDiceModeMaintenance;
83 default:
84 return kDiceModeNotInitialized;
85 }
86}
87
88/**
89 * \brief Convert boot measurement data to DICE input values.
90 *
91 * \param[in] metadata Boot measurement metadata.
92 * \param[in] measurement Boot measurement.
93 * \param[in] measurement_len Length of measurement input.
94 * \param[out] dice_inputs DICE input values.
95 *
96 * \return Returns 0 on success, -1 on failure.
97 */
98static int measurement_to_dice_inputs(const struct boot_measurement_metadata *metadata,
99 const uint8_t *measurement,
100 size_t measurement_len,
101 DiceInputValues *dice_inputs)
102{
103 uint8_t *cfg_p;
104
105 if (measurement_len > sizeof(dice_inputs->code_hash) ||
106 metadata->signer_id_size > sizeof(metadata->signer_id) ||
107 metadata->signer_id_size > sizeof(dice_inputs->authority_hash)) {
108 return -1;
109 }
110
111 /* Zero DICE inputs to ensure unused values are zero */
112 memset(dice_inputs, 0, sizeof(*dice_inputs));
113
114 /* Code hash */
115 memcpy(dice_inputs->code_hash, measurement, measurement_len);
116
117 /* Code descriptor */
118 dice_inputs->code_descriptor = (uint8_t *)&metadata->measurement_type;
119 dice_inputs->code_descriptor_size = sizeof(metadata->measurement_type);
120
121 /* Config value */
122 dice_inputs->config_type = kDiceConfigTypeInline;
123 cfg_p = dice_inputs->config_value;
124
125 memcpy(cfg_p, &metadata->sw_version.build_num,
126 sizeof(metadata->sw_version.build_num));
127 cfg_p += sizeof(metadata->sw_version.build_num);
128
129 memcpy(cfg_p, &metadata->sw_version.revision,
130 sizeof(metadata->sw_version.revision));
131 cfg_p += sizeof(metadata->sw_version.revision);
132
133 memcpy(cfg_p, &metadata->sw_version.minor,
134 sizeof(metadata->sw_version.minor));
135 cfg_p += sizeof(metadata->sw_version.minor);
136
137 memcpy(cfg_p, &metadata->sw_version.major,
138 sizeof(metadata->sw_version.major));
139
140 /* Authority hash */
141 memcpy(dice_inputs->authority_hash, metadata->signer_id,
142 metadata->signer_id_size);
143
144 /* Mode */
145 dice_inputs->mode = get_dice_mode();
146
147 return 0;
148}
149
150/**
151 * \brief Function pointer type that indicates whether slot meets condition.
152 */
153typedef bool (*slot_cond_t)(uint8_t slot);
154
155/**
156 * \brief Iteratively get measurements whose slot values meet the condition of
157 * the supplied condition function.
158 *
159 * \param[in] slot_cond Slot condition function.
160 * \param[in,out] itr Pointer to iterator. If the pointed-to value is
161 * NULL then the function searches from the
162 * beginning of the data area. The iterator value is
163 * updated by the function and should be supplied to
164 * subsequent calls to continue the search.
165 * \param[out] dice_inputs DICE input values.
166 *
167 * \return Returns integer error code.
168 * \retval -1 Failure.
169 * \retval 0 Success, reached end of data area without finding a component.
170 * \retval 1 Success, component found.
171 */
172static int get_measurement_for_slot_cond(slot_cond_t slot_cond,
173 void **itr,
174 DiceInputValues *dice_inputs)
175{
176 struct boot_measurement_metadata *metadata;
177 uint8_t *measurement;
178 size_t measurement_len;
179 struct shared_data_tlv_entry tlv_entry;
180 uint8_t *tlv_curr;
181 uint8_t *tlv_end;
182 uint8_t slot;
183 uint8_t claim;
184
185 if (boot_measurements.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
186 /* Boot measurement information is malformed. */
187 return -1;
188 }
189
190 /* Get the boundaries of TLV section where to lookup. */
191 tlv_curr = (*itr == NULL) ? boot_measurements.data : (uint8_t *)*itr;
192 tlv_end = (uint8_t *)&boot_measurements
193 + boot_measurements.header.tlv_tot_len;
194
195 while (tlv_curr < tlv_end) {
196 /* Copy TLV entry header - the measurement metadata must come first. */
197 memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
198 slot = GET_MBS_SLOT(tlv_entry.tlv_type);
199
200 if ((*slot_cond)(slot)) {
201 if ((GET_MBS_CLAIM(tlv_entry.tlv_type) != SW_MEASURE_METADATA) ||
202 (tlv_entry.tlv_len != sizeof(struct boot_measurement_metadata))) {
203 /* Boot measurement information is malformed. */
204 return -1;
205 }
206
207 metadata = (struct boot_measurement_metadata *)
208 (tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE);
209
210 /* Copy next TLV entry header - it must belong to the measurement. */
211 tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
212 memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
213 claim = GET_MBS_CLAIM(tlv_entry.tlv_type);
214
215 if ((claim != SW_MEASURE_VALUE) &&
216 (claim != SW_MEASURE_VALUE_NON_EXTENDABLE)) {
217 /* Boot measurement information is malformed. */
218 return -1;
219 }
220
221 measurement = tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE;
222 measurement_len = tlv_entry.tlv_len;
223
224 if (measurement_to_dice_inputs(metadata, measurement,
225 measurement_len, dice_inputs) != 0) {
226 return -1;
227 }
228
229 /* Set iterator to point to next TLV entry */
230 *itr = tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len;
231
232 return 1;
233 }
234
235 /* Move to the next TLV entry. */
236 tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
237 }
238
239 return 0;
240}
241
242dpe_error_t initialise_boot_data(void)
243{
244 psa_status_t status;
245
246 /* Collect the measurements from the shared data area and store them. */
247 status = tfm_core_get_boot_data(TLV_MAJOR_MBS,
248 (struct tfm_boot_data *)&boot_measurements,
249 sizeof(boot_measurements));
250 if (status != PSA_SUCCESS) {
251 return DPE_INTERNAL_ERROR;
252 }
253
254 return DPE_NO_ERROR;
255}
256
257static bool bl1_2_cond(uint8_t slot)
258{
259 return slot == BOOT_MEASUREMENT_SLOT_BL1_2;
260}
261
262static bool bl2_cond(uint8_t slot)
263{
264 return slot == BOOT_MEASUREMENT_SLOT_BL2;
265}
266
267static bool plat_cond(uint8_t slot)
268{
269 return slot >= BOOT_MEASUREMENT_SLOT_RT_0 &&
270 slot <= BOOT_MEASUREMENT_SLOT_MAX &&
271 slot != BOOT_MEASUREMENT_SLOT_RT_2;
272}
273
274static bool ap_cond(uint8_t slot)
275{
276 return slot == BOOT_MEASUREMENT_SLOT_RT_2; /* FIXME: This may vary */
277}
278
279dpe_error_t derive_boot_data_contexts(int rot_ctx_handle,
Maulik Patela81605b2023-10-24 12:17:03 +0100280 int *new_ctx_handle)
Jamie Fox34681992023-09-04 18:14:06 +0100281{
282 int ret;
283 dpe_error_t err;
284 void *itr;
285 DiceInputValues dice_inputs;
286 int plat_ctx_handle;
287 int invalid_ctx_handle;
288
289 /* Only the BL1_2 measurement is included in the RoT layer */
290 itr = NULL;
291 ret = get_measurement_for_slot_cond(&bl1_2_cond, &itr, &dice_inputs);
292 if (ret != 1) {
293 /* RoT layer measurement is either malformed or missing, fatal error */
294 return DPE_INTERNAL_ERROR;
295 }
296
297 /* Derive RoT layer */
Maulik Patela81605b2023-10-24 12:17:03 +0100298 err = derive_context_request(rot_ctx_handle,
299 false,
300 true,
301 true, /* create certificate */
302 &dice_inputs,
303 0,
304 &plat_ctx_handle,
305 &invalid_ctx_handle);
Jamie Fox34681992023-09-04 18:14:06 +0100306 if (err != DPE_NO_ERROR) {
307 return err;
308 }
309
310 /* Get BL2 measurement */
311 itr = NULL;
312 ret = get_measurement_for_slot_cond(&bl2_cond, &itr, &dice_inputs);
313 if (ret != 1) {
314 /* BL2 measurement is either malformed or missing, fatal error */
315 return DPE_INTERNAL_ERROR;
316 }
317
318 /* Derive BL2 context */
Maulik Patela81605b2023-10-24 12:17:03 +0100319 err = derive_context_request(plat_ctx_handle,
320 false, /* close parent context */
321 true, /* allow BL2 to derive further */
322 false,
323 &dice_inputs,
324 0,
325 &plat_ctx_handle,
326 &invalid_ctx_handle);
Jamie Fox34681992023-09-04 18:14:06 +0100327 if (err != DPE_NO_ERROR) {
328 return err;
329 }
330
331 /* Get measurements for the rest of platform layer, except AP */
332 itr = NULL;
333 while ((ret = get_measurement_for_slot_cond(&plat_cond, &itr,
334 &dice_inputs)) == 1) {
335 /* Derive rest of platform contexts from retained BL2 context */
Maulik Patela81605b2023-10-24 12:17:03 +0100336 err = derive_context_request(plat_ctx_handle,
337 true, /* retain parent context */
338 false, /* do not allow derived context to derive */
339 false,
340 &dice_inputs,
341 0,
342 &invalid_ctx_handle,
343 &plat_ctx_handle);
Jamie Fox34681992023-09-04 18:14:06 +0100344 if (err != DPE_NO_ERROR) {
345 return err;
346 }
347 }
348
349 /* Check termination was due to reaching end of boot data area */
350 if (ret != 0) {
351 return DPE_INTERNAL_ERROR;
352 }
353
354 /* Get AP measurement */
355 itr = NULL;
356 ret = get_measurement_for_slot_cond(&ap_cond, &itr, &dice_inputs);
357 if (ret != 1) {
358 /* AP measurement is either malformed or missing, fatal error */
359 return DPE_INTERNAL_ERROR;
360 }
361
Maulik Patela81605b2023-10-24 12:17:03 +0100362 /* Derive AP context, with the new derived context handle returned to the
363 * caller in the new_ctx_handle output parameter.
Jamie Fox34681992023-09-04 18:14:06 +0100364 */
Maulik Patela81605b2023-10-24 12:17:03 +0100365 return derive_context_request(plat_ctx_handle,
366 false, /* close parent context */
367 true, /* allow AP to derive */
368 false,
369 &dice_inputs,
370 0,
371 new_ctx_handle,
372 &invalid_ctx_handle);
Jamie Fox34681992023-09-04 18:14:06 +0100373}