blob: 8aa56ddb9390cde0f114eac1719d942804fceb08 [file] [log] [blame]
/*
* Copyright (c) 2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include "dpe_boot_data.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "boot_hal.h"
#include "boot_measurement.h"
#include "dpe_context_mngr.h"
#include "service_api.h"
#include "tfm_boot_status.h"
#include "tfm_plat_otp.h"
/* Maximum measurement size is size of SHA-512 hash */
#define MEASUREMENT_VALUE_MAX_SIZE 64
/* Size of 1 complete measurement (value + metadata) in TLV format. */
#define SHARED_BOOT_MEASUREMENT_SIZE \
((2 * SHARED_DATA_ENTRY_HEADER_SIZE) \
+ sizeof(struct boot_measurement_metadata) \
+ MEASUREMENT_VALUE_MAX_SIZE)
/* 2 measurements from the BL1 stages and 1 measurement per image from BL2. */
#define MAX_SHARED_BOOT_DATA_LENGTH \
((2 + MCUBOOT_IMAGE_NUMBER) * SHARED_BOOT_MEASUREMENT_SIZE)
/**
* \struct boot_measurement_data
*
* \brief Contains all the measurement and related metadata (from BL1_x and BL2).
*
* \details This is a redefinition of \ref tfm_boot_data to allocate the
* appropriate, service dependent size of \ref boot_data.
*/
struct boot_measurement_data {
struct shared_data_tlv_header header;
uint8_t data[MAX_SHARED_BOOT_DATA_LENGTH];
};
/**
* \var boot_measurements
*
* \brief Store the boot measurements in the service's memory.
*
* \details Boot measurements come from the BL1 and BL2 boot stages and stored
* on a memory area which is shared between the bootloaders and SPM.
* SPM provides the \ref tfm_core_get_boot_data() API to retrieve
* the service related data from shared area.
*/
__attribute__ ((aligned(4)))
static struct boot_measurement_data boot_measurements;
/**
* \brief Get the current DICE mode based on HW state.
*
* \return DICE mode.
*/
static DiceMode get_dice_mode(void)
{
enum tfm_plat_err_t err;
enum plat_otp_lcs_t otp_lcs;
err = tfm_plat_otp_read(PLAT_OTP_ID_LCS, sizeof(otp_lcs),
(uint8_t *)&otp_lcs);
if (err != TFM_PLAT_ERR_SUCCESS) {
return kDiceModeNotInitialized;
}
/* FIXME consider DCU state as well */
switch (otp_lcs) {
case PLAT_OTP_LCS_SECURED:
return kDiceModeNormal;
case PLAT_OTP_LCS_DECOMMISSIONED:
return kDiceModeMaintenance;
default:
return kDiceModeNotInitialized;
}
}
/**
* \brief Convert boot measurement data to DICE input values.
*
* \param[in] metadata Boot measurement metadata.
* \param[in] measurement Boot measurement.
* \param[in] measurement_len Length of measurement input.
* \param[out] dice_inputs DICE input values.
*
* \return Returns 0 on success, -1 on failure.
*/
static int measurement_to_dice_inputs(const struct boot_measurement_metadata *metadata,
const uint8_t *measurement,
size_t measurement_len,
DiceInputValues *dice_inputs)
{
uint8_t *cfg_p;
if (measurement_len > sizeof(dice_inputs->code_hash) ||
metadata->signer_id_size > sizeof(metadata->signer_id) ||
metadata->signer_id_size > sizeof(dice_inputs->authority_hash)) {
return -1;
}
/* Zero DICE inputs to ensure unused values are zero */
memset(dice_inputs, 0, sizeof(*dice_inputs));
/* Code hash */
memcpy(dice_inputs->code_hash, measurement, measurement_len);
/* Code descriptor */
dice_inputs->code_descriptor = (uint8_t *)&metadata->measurement_type;
dice_inputs->code_descriptor_size = sizeof(metadata->measurement_type);
/* Config value */
dice_inputs->config_type = kDiceConfigTypeInline;
cfg_p = dice_inputs->config_value;
memcpy(cfg_p, &metadata->sw_version.build_num,
sizeof(metadata->sw_version.build_num));
cfg_p += sizeof(metadata->sw_version.build_num);
memcpy(cfg_p, &metadata->sw_version.revision,
sizeof(metadata->sw_version.revision));
cfg_p += sizeof(metadata->sw_version.revision);
memcpy(cfg_p, &metadata->sw_version.minor,
sizeof(metadata->sw_version.minor));
cfg_p += sizeof(metadata->sw_version.minor);
memcpy(cfg_p, &metadata->sw_version.major,
sizeof(metadata->sw_version.major));
/* Authority hash */
memcpy(dice_inputs->authority_hash, metadata->signer_id,
metadata->signer_id_size);
/* Mode */
dice_inputs->mode = get_dice_mode();
return 0;
}
/**
* \brief Function pointer type that indicates whether slot meets condition.
*/
typedef bool (*slot_cond_t)(uint8_t slot);
/**
* \brief Iteratively get measurements whose slot values meet the condition of
* the supplied condition function.
*
* \param[in] slot_cond Slot condition function.
* \param[in,out] itr Pointer to iterator. If the pointed-to value is
* NULL then the function searches from the
* beginning of the data area. The iterator value is
* updated by the function and should be supplied to
* subsequent calls to continue the search.
* \param[out] dice_inputs DICE input values.
*
* \return Returns integer error code.
* \retval -1 Failure.
* \retval 0 Success, reached end of data area without finding a component.
* \retval 1 Success, component found.
*/
static int get_measurement_for_slot_cond(slot_cond_t slot_cond,
void **itr,
DiceInputValues *dice_inputs)
{
struct boot_measurement_metadata *metadata;
uint8_t *measurement;
size_t measurement_len;
struct shared_data_tlv_entry tlv_entry;
uint8_t *tlv_curr;
uint8_t *tlv_end;
uint8_t slot;
uint8_t claim;
if (boot_measurements.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
/* Boot measurement information is malformed. */
return -1;
}
/* Get the boundaries of TLV section where to lookup. */
tlv_curr = (*itr == NULL) ? boot_measurements.data : (uint8_t *)*itr;
tlv_end = (uint8_t *)&boot_measurements
+ boot_measurements.header.tlv_tot_len;
while (tlv_curr < tlv_end) {
/* Copy TLV entry header - the measurement metadata must come first. */
memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
slot = GET_MBS_SLOT(tlv_entry.tlv_type);
if ((*slot_cond)(slot)) {
if ((GET_MBS_CLAIM(tlv_entry.tlv_type) != SW_MEASURE_METADATA) ||
(tlv_entry.tlv_len != sizeof(struct boot_measurement_metadata))) {
/* Boot measurement information is malformed. */
return -1;
}
metadata = (struct boot_measurement_metadata *)
(tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE);
/* Copy next TLV entry header - it must belong to the measurement. */
tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
claim = GET_MBS_CLAIM(tlv_entry.tlv_type);
if ((claim != SW_MEASURE_VALUE) &&
(claim != SW_MEASURE_VALUE_NON_EXTENDABLE)) {
/* Boot measurement information is malformed. */
return -1;
}
measurement = tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE;
measurement_len = tlv_entry.tlv_len;
if (measurement_to_dice_inputs(metadata, measurement,
measurement_len, dice_inputs) != 0) {
return -1;
}
/* Set iterator to point to next TLV entry */
*itr = tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len;
return 1;
}
/* Move to the next TLV entry. */
tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
}
return 0;
}
dpe_error_t initialise_boot_data(void)
{
psa_status_t status;
/* Collect the measurements from the shared data area and store them. */
status = tfm_core_get_boot_data(TLV_MAJOR_MBS,
(struct tfm_boot_data *)&boot_measurements,
sizeof(boot_measurements));
if (status != PSA_SUCCESS) {
return DPE_INTERNAL_ERROR;
}
return DPE_NO_ERROR;
}
static bool bl1_2_cond(uint8_t slot)
{
return slot == BOOT_MEASUREMENT_SLOT_BL1_2;
}
static bool bl2_cond(uint8_t slot)
{
return slot == BOOT_MEASUREMENT_SLOT_BL2;
}
static bool plat_cond(uint8_t slot)
{
return slot >= BOOT_MEASUREMENT_SLOT_RT_0 &&
slot <= BOOT_MEASUREMENT_SLOT_MAX &&
slot != BOOT_MEASUREMENT_SLOT_RT_2;
}
static bool ap_cond(uint8_t slot)
{
return slot == BOOT_MEASUREMENT_SLOT_RT_2; /* FIXME: This may vary */
}
dpe_error_t derive_boot_data_contexts(int rot_ctx_handle,
int *new_ctx_handle)
{
int ret;
dpe_error_t err;
void *itr;
DiceInputValues dice_inputs;
int plat_ctx_handle;
int invalid_ctx_handle;
/* Only the BL1_2 measurement is included in the RoT layer */
itr = NULL;
ret = get_measurement_for_slot_cond(&bl1_2_cond, &itr, &dice_inputs);
if (ret != 1) {
/* RoT layer measurement is either malformed or missing, fatal error */
return DPE_INTERNAL_ERROR;
}
/* Derive RoT layer */
err = derive_context_request(rot_ctx_handle,
false,
true,
true, /* create certificate */
&dice_inputs,
0,
&plat_ctx_handle,
&invalid_ctx_handle);
if (err != DPE_NO_ERROR) {
return err;
}
/* Get BL2 measurement */
itr = NULL;
ret = get_measurement_for_slot_cond(&bl2_cond, &itr, &dice_inputs);
if (ret != 1) {
/* BL2 measurement is either malformed or missing, fatal error */
return DPE_INTERNAL_ERROR;
}
/* Derive BL2 context */
err = derive_context_request(plat_ctx_handle,
false, /* close parent context */
true, /* allow BL2 to derive further */
false,
&dice_inputs,
0,
&plat_ctx_handle,
&invalid_ctx_handle);
if (err != DPE_NO_ERROR) {
return err;
}
/* Get measurements for the rest of platform layer, except AP */
itr = NULL;
while ((ret = get_measurement_for_slot_cond(&plat_cond, &itr,
&dice_inputs)) == 1) {
/* Derive rest of platform contexts from retained BL2 context */
err = derive_context_request(plat_ctx_handle,
true, /* retain parent context */
false, /* do not allow derived context to derive */
false,
&dice_inputs,
0,
&invalid_ctx_handle,
&plat_ctx_handle);
if (err != DPE_NO_ERROR) {
return err;
}
}
/* Check termination was due to reaching end of boot data area */
if (ret != 0) {
return DPE_INTERNAL_ERROR;
}
/* Get AP measurement */
itr = NULL;
ret = get_measurement_for_slot_cond(&ap_cond, &itr, &dice_inputs);
if (ret != 1) {
/* AP measurement is either malformed or missing, fatal error */
return DPE_INTERNAL_ERROR;
}
/* Derive AP context, with the new derived context handle returned to the
* caller in the new_ctx_handle output parameter.
*/
return derive_context_request(plat_ctx_handle,
false, /* close parent context */
true, /* allow AP to derive */
false,
&dice_inputs,
0,
new_ctx_handle,
&invalid_ctx_handle);
}