MeasuredBoot: Adding new measured boot partition
Create new partition for measured boot services for storing and
retrieving measurements for software components.
Measured boot partition disabled by default.
Measurement hash values and metadata are stored in local RAM.
Measurement hash for each slot is initialised with default pattern
which is then extended on subsequent store requests.
Measured boot services use stateless service model for better
performance in case of multiple concurrent requests.
The minimum signer id and measurement value size required is 32 bytes.
The maximum signer id and measurement value size supported is 64 bytes.
The maximum version size supported is 14 bytes.
Signed-off-by: Maulik Patel <maulik.patel@arm.com>
Signed-off-by: David Vincze <david.vincze@arm.com>
Change-Id: I4584d28cb9e845dd9aff475aa840f11f97ae3baa
diff --git a/partitions/measured_boot/CMakeLists.txt b/partitions/measured_boot/CMakeLists.txt
new file mode 100644
index 0000000..5cb1638
--- /dev/null
+++ b/partitions/measured_boot/CMakeLists.txt
@@ -0,0 +1,112 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+if (NOT TFM_PARTITION_MEASURED_BOOT)
+ return()
+endif()
+
+cmake_minimum_required(VERSION 3.15)
+cmake_policy(SET CMP0079 NEW)
+
+# The name of the target is required to be of the pattern
+# tfm_app_rot_partition_x or tfm_psa_rot_partition_x, as it affects how the
+# linker script will lay the partition in memory.
+add_library(tfm_psa_rot_partition_measured_boot STATIC)
+
+target_sources(tfm_psa_rot_partition_measured_boot
+ PRIVATE
+ measured_boot.c
+ measured_boot_req_mngr.c
+ measured_boot_utils.c
+)
+
+# The generated sources
+target_sources(tfm_psa_rot_partition_measured_boot
+ PRIVATE
+ $<$<BOOL:${TFM_PSA_API}>:
+ ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/measured_boot/auto_generated/intermedia_tfm_measured_boot.c>
+)
+target_sources(tfm_partitions
+ INTERFACE
+ $<$<BOOL:${TFM_PSA_API}>:
+ ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/measured_boot/auto_generated/load_info_tfm_measured_boot.c>
+)
+
+# Set include directory
+target_include_directories(tfm_psa_rot_partition_measured_boot
+ PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}/generated/secure_fw/partitions/measured_boot
+)
+
+
+target_link_libraries(tfm_psa_rot_partition_measured_boot
+ PRIVATE
+ tfm_secure_api
+ psa_interface
+ platform_s
+ tfm_sprt
+)
+
+if(${MEASURED_BOOT_HASH_ALG} STREQUAL "PSA_ALG_SHA_512")
+ set(MEASUREMENT_VALUE_SIZE 64)
+elseif(${MEASURED_BOOT_HASH_ALG} STREQUAL "PSA_ALG_SHA_256")
+ set(MEASUREMENT_VALUE_SIZE 32)
+else()
+ message(FATAL_ERROR "Unknown hash algorithm")
+endif()
+
+target_compile_definitions(tfm_psa_rot_partition_measured_boot
+ PUBLIC
+ TFM_MEASURED_BOOT_HASH_ALG=${MEASURED_BOOT_HASH_ALG}
+ MEASUREMENT_VALUE_SIZE=${MEASUREMENT_VALUE_SIZE}
+ $<$<BOOL:${TFM_BOOT_STORE_MEASUREMENTS}>:TFM_BOOT_STORE_MEASUREMENTS>
+)
+
+############################ Secure API ########################################
+
+set(INTERFACE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/interface/src)
+set(INTERFACE_INC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/interface/include)
+
+target_sources(tfm_sprt
+ INTERFACE
+ ${INTERFACE_SRC_DIR}/measured_boot_api.c
+)
+
+target_include_directories(tfm_sprt
+ PUBLIC
+ ${INTERFACE_INC_DIR}
+)
+
+# The veneers give warnings about not being properly declared so they get hidden
+# to not overshadow _real_ warnings.
+set_source_files_properties(measured_boot_api.c
+ PROPERTIES
+ COMPILE_FLAGS -Wno-implicit-function-declaration
+)
+
+############################ Partition Defs ####################################
+
+target_link_libraries(tfm_partitions
+ INTERFACE
+ tfm_psa_rot_partition_measured_boot
+)
+
+target_compile_definitions(tfm_partition_defs
+ INTERFACE
+ TFM_PARTITION_MEASURED_BOOT
+)
+
+set(INSTALL_INTERFACE_SRC_DIR ${TFM_INSTALL_PATH}/interface/src)
+set(INSTALL_INTERFACE_INC_DIR ${TFM_INSTALL_PATH}/interface/include)
+
+install(FILES ${INTERFACE_SRC_DIR}/measured_boot_api.c
+ DESTINATION ${INSTALL_INTERFACE_SRC_DIR})
+
+install(FILES ${INTERFACE_INC_DIR}/measured_boot_api.h
+ ${INTERFACE_INC_DIR}/measured_boot_defs.h
+ DESTINATION ${INSTALL_INTERFACE_INC_DIR})
diff --git a/partitions/measured_boot/interface/include/measured_boot_api.h b/partitions/measured_boot/interface/include/measured_boot_api.h
new file mode 100644
index 0000000..8939592
--- /dev/null
+++ b/partitions/measured_boot/interface/include/measured_boot_api.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/* This file describes the TFM Measured Boot API */
+
+#ifndef __MEASURED_BOOT_API__
+#define __MEASURED_BOOT_API__
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "psa/error.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Minimum measurement value size that can be requested to store */
+#define MEASUREMENT_VALUE_MIN_SIZE 32U
+/* Maximum measurement value size that can be requested to store */
+#define MEASUREMENT_VALUE_MAX_SIZE 64U
+/* Minimum signer id size that can be requested to store */
+#define SIGNER_ID_MIN_SIZE MEASUREMENT_VALUE_MIN_SIZE
+/* Maximum signer id size that can be requested to store */
+#define SIGNER_ID_MAX_SIZE MEASUREMENT_VALUE_MAX_SIZE
+/* The theoretical maximum image version is: "255.255.65535\0" */
+#define VERSION_MAX_SIZE 14U
+/* Example sw_type: "TFM_BLX, AP_BL1, etc." */
+#define SW_TYPE_MAX_SIZE 20U
+
+#define NUM_OF_MEASUREMENT_SLOTS 32U
+
+#define MEASUREMENT_VALUE_INIT_PATTERN 0
+
+/**
+ * \brief Retrieves a measurement from the requested slot.
+ *
+ * \param[in] index Slot number from which measurement is
+ * to be retrieved.
+ * \param[out] signer_id Pointer to \p signer_id buffer.
+ * \param[in] signer_id_size Size of the \p signer_id buffer in
+ * bytes.
+ * \param[out] signer_id_len On success, number of bytes that make
+ * up \p signer_id.
+ * \param[out] version Pointer to \p version buffer.
+ * \param[in] version_size Size of the \p version buffer in bytes.
+ * \param[out] version_len On success, number of bytes that make
+ * up the \p version.
+ * \param[out] measurement_algo Pointer to \p measurement_algo.
+ * \param[out] sw_type Pointer to \p sw_type buffer.
+ * \param[in] sw_type_size Size of the \p sw_type buffer in bytes.
+ * \param[out] sw_type_len On success, number of bytes that make
+ * up the \p sw_type.
+ * \param[out] measurement_value Pointer to \p measurement_value buffer.
+ * \param[in] measurement_value_size Size of the \p measurement_value
+ * buffer in bytes.
+ * \param[out] measurement_value_len On success, number of bytes that make
+ * up the \p measurement_value.
+ * \param[out] is_locked Pointer to lock status of requested
+ * measurement slot.
+ *
+ * \retval #PSA_SUCCESS
+ * Success.
+ * \retval #PSA_ERROR_PROGRAMMER_ERROR
+ * Fails to get caller id.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * When requested slot index is invalid.
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ * When Measurement is requested by unauthorised client.
+ * i.e. if client is not the owner of measurement OR
+ * measurement is not accessible to all.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ * The requested slot is empty, does not contain a measurement.
+ */
+psa_status_t tfm_measured_boot_read_measurement(uint8_t index,
+ uint8_t *signer_id,
+ size_t signer_id_size,
+ size_t *signer_id_len,
+ uint8_t *version,
+ size_t version_size,
+ size_t *version_len,
+ uint32_t *measurement_algo,
+ uint8_t *sw_type,
+ size_t sw_type_size,
+ size_t *sw_type_len,
+ uint8_t *measurement_value,
+ size_t measurement_value_size,
+ size_t *measurement_value_len,
+ bool *is_locked);
+
+/**
+ * \brief Extends and stores a measurement to the requested slot.
+ *
+ * \param[in] index Slot number in which measurement is
+ * to be stored.
+ * \param[in] signer_id Pointer to \p signer_id buffer.
+ * \param[in] signer_id_size Size of the \p signer_id buffer in
+ * bytes.
+ * \param[in] version Pointer to \p version buffer.
+ * \param[in] version_size Size of the \p version buffer in bytes.
+ * \param[in] measurement_algo Algorithm identifier used for
+ * measurement.
+ * \param[in] sw_type Pointer to \p sw_type buffer.
+ * \param[in] sw_type_size Size of the \p sw_type buffer in bytes.
+ * \param[in] measurement_value Pointer to \p measurement_value buffer.
+ * \param[in] measurement_value_size Size of the \p measurement_value
+ * buffer in bytes.
+ * \param[in] lock_measurement Boolean flag requesting whether the
+ * measurement is to be locked.
+ *
+ * \retval #PSA_SUCCESS
+ * Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * The size of any argument is invalid OR
+ * Input Measurement value is NULL OR
+ * Input Signer ID is NULL OR
+ * Requested slot index is invalid.
+ * \retval #PSA_ERROR_BAD_STATE
+ * Request to lock, when slot is already locked.
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ * When the requested slot is not accessible to the caller.
+ *
+ * \note \p measurement_algo value must be a valid PSA hash algorithm
+ * identifier.
+*/
+psa_status_t tfm_measured_boot_extend_measurement(
+ uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ size_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ size_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ bool lock_measurement);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MEASURED_BOOT_API__ */
diff --git a/partitions/measured_boot/interface/include/measured_boot_defs.h b/partitions/measured_boot/interface/include/measured_boot_defs.h
new file mode 100644
index 0000000..600536f
--- /dev/null
+++ b/partitions/measured_boot/interface/include/measured_boot_defs.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __TFM_MEASURED_BOOT_DEFS_H__
+#define __TFM_MEASURED_BOOT_DEFS_H__
+
+#include "measured_boot_api.h"
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Measured boot message types that distinguish its services */
+#define TFM_MEASURED_BOOT_READ 1001U
+#define TFM_MEASURED_BOOT_EXTEND 1002U
+
+struct measured_boot_read_iovec_in_t {
+ uint8_t index;
+ uint8_t sw_type_size;
+ uint8_t version_size;
+};
+
+struct measured_boot_read_iovec_out_t {
+ uint8_t is_locked;
+ uint32_t measurement_algo;
+ uint8_t sw_type[SW_TYPE_MAX_SIZE];
+ uint8_t sw_type_len;
+ uint8_t version[VERSION_MAX_SIZE];
+ uint8_t version_len;
+};
+
+struct measured_boot_extend_iovec_t {
+ uint8_t index;
+ uint8_t lock_measurement;
+ uint32_t measurement_algo;
+ uint8_t sw_type[SW_TYPE_MAX_SIZE];
+ uint8_t sw_type_size;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TFM_MEASURED_BOOT_DEFS_H__ */
diff --git a/partitions/measured_boot/interface/src/measured_boot_api.c b/partitions/measured_boot/interface/src/measured_boot_api.c
new file mode 100644
index 0000000..8263aba
--- /dev/null
+++ b/partitions/measured_boot/interface/src/measured_boot_api.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "measured_boot_api.h"
+#include "measured_boot_defs.h"
+#include "psa/client.h"
+#include "psa_manifest/sid.h"
+#include <string.h>
+
+psa_status_t tfm_measured_boot_read_measurement(uint8_t index,
+ uint8_t *signer_id,
+ size_t signer_id_size,
+ size_t *signer_id_len,
+ uint8_t *version,
+ size_t version_size,
+ size_t *version_len,
+ uint32_t *measurement_algo,
+ uint8_t *sw_type,
+ size_t sw_type_size,
+ size_t *sw_type_len,
+ uint8_t *measurement_value,
+ size_t measurement_value_size,
+ size_t *measurement_value_len,
+ bool *is_locked)
+{
+ psa_status_t status;
+ struct measured_boot_read_iovec_in_t read_iov_in = {
+ .index = index,
+ .sw_type_size = sw_type_size,
+ .version_size = version_size,
+ };
+
+ struct measured_boot_read_iovec_out_t read_iov_out;
+
+ psa_invec in_vec[] = {
+ {.base = &read_iov_in,
+ .len = sizeof(struct measured_boot_read_iovec_in_t)},
+ };
+
+ psa_outvec out_vec[] = {
+ {.base = &read_iov_out,
+ .len = sizeof(struct measured_boot_read_iovec_out_t)},
+ {.base = signer_id, .len = signer_id_size},
+ {.base = measurement_value, .len = measurement_value_size}
+ };
+
+ status = psa_call(TFM_MEASURED_BOOT_HANDLE, TFM_MEASURED_BOOT_READ,
+ in_vec, IOVEC_LEN(in_vec),
+ out_vec, IOVEC_LEN(out_vec));
+
+ if (status == PSA_SUCCESS) {
+ *is_locked = read_iov_out.is_locked;
+ *measurement_algo = read_iov_out.measurement_algo;
+ *sw_type_len = read_iov_out.sw_type_len;
+ *version_len = read_iov_out.version_len;
+ memcpy(sw_type, read_iov_out.sw_type, read_iov_out.sw_type_len);
+ memcpy(version, read_iov_out.version, read_iov_out.version_len);
+ *signer_id_len = out_vec[1].len;
+ *measurement_value_len = out_vec[2].len;
+ }
+
+ return status;
+}
+
+psa_status_t tfm_measured_boot_extend_measurement(
+ uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ size_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ size_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ bool lock_measurement)
+{
+ struct measured_boot_extend_iovec_t extend_iov = {
+ .index = index,
+ .lock_measurement = lock_measurement,
+ .measurement_algo = measurement_algo,
+ .sw_type = {0},
+ .sw_type_size = sw_type_size,
+ };
+
+ psa_invec in_vec[] = {
+ {.base = &extend_iov,
+ .len = sizeof(struct measured_boot_extend_iovec_t)},
+ {.base = signer_id, .len = signer_id_size},
+ {.base = version, .len = version_size},
+ {.base = measurement_value, .len = measurement_value_size}
+ };
+
+ if (sw_type != NULL) {
+ if (sw_type_size > SW_TYPE_MAX_SIZE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ memcpy(extend_iov.sw_type, sw_type, sw_type_size);
+ }
+
+ return psa_call(TFM_MEASURED_BOOT_HANDLE,
+ TFM_MEASURED_BOOT_EXTEND,
+ in_vec, IOVEC_LEN(in_vec),
+ NULL, 0);
+}
diff --git a/partitions/measured_boot/measured_boot.c b/partitions/measured_boot/measured_boot.c
new file mode 100644
index 0000000..7fd1a52
--- /dev/null
+++ b/partitions/measured_boot/measured_boot.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "measured_boot.h"
+#include "measured_boot_utils.h"
+#include "measured_boot_api.h"
+#include "psa/crypto.h"
+#include "tfm_api.h"
+#include "tfm_boot_status.h"
+#include "boot_hal.h"
+#include "service_api.h"
+#include "tfm_strnlen.h"
+#include "tfm_sp_log.h"
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define TEMP_BUFFER_SIZE (MEASUREMENT_VALUE_SIZE + MEASUREMENT_VALUE_MAX_SIZE)
+
+#ifdef TFM_BOOT_STORE_MEASUREMENTS
+/* 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 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;
+#endif /* TFM_BOOT_STORE_MEASUREMENTS */
+
+struct measurement_metadata_t {
+ uint8_t signer_id[SIGNER_ID_MAX_SIZE];
+ size_t signer_id_size;
+ uint8_t version[VERSION_MAX_SIZE];
+ uint8_t version_size;
+ uint32_t measurement_algo;
+ uint8_t sw_type[SW_TYPE_MAX_SIZE];
+ uint8_t sw_type_size;
+};
+
+struct measurement_value_t {
+ uint8_t hash_buf[MEASUREMENT_VALUE_MAX_SIZE];
+ uint8_t hash_buf_size;
+};
+
+struct measurement_t {
+ struct measurement_value_t value; /* measurement value */
+ struct measurement_metadata_t metadata; /* metadata */
+};
+
+struct measured_boot_slot_t {
+ bool is_locked;
+ bool is_populated;
+ bool is_common;
+ struct measurement_t measurement;
+};
+
+static struct measured_boot_slot_t measurement_slot[NUM_OF_MEASUREMENT_SLOTS];
+
+static bool is_signer_id_different(uint8_t index, const uint8_t *signer_id)
+{
+ uint8_t *stored_signer_id =
+ &measurement_slot[index].measurement.metadata.signer_id[0];
+ uint8_t stored_signer_id_size =
+ measurement_slot[index].measurement.metadata.signer_id_size;
+
+ if (memcmp(signer_id, stored_signer_id, stored_signer_id_size) != 0) {
+ return true;
+ }
+ return false;
+}
+
+/* TODO: Access control strategy to be updated */
+static bool is_slot_access_prohibited(uint8_t slot_index,
+ const uint8_t *signer_id)
+{
+ if (is_signer_id_different(slot_index, signer_id)) {
+ /* The client signer id is different from the current slot signer id */
+ if (measurement_slot[slot_index].is_common) {
+ /* This slot holds common measurement and must be accessible to
+ * all
+ */
+ return false;
+ } else {
+ /* Check for read/extend permissions; deny access for now */
+ return true;
+ }
+ }
+
+ /* The client signer id is same as the current slot signer id; hence it
+ * must be allowed full access
+ */
+ return false;
+}
+
+/* TODO: Implement updates for access control strategy here. */
+static bool is_read_access_prohibited(uint8_t slot_index)
+{
+ return false;
+}
+
+static inline bool is_measurement_slot_populated(const uint8_t index)
+{
+ /* Extension is required if any previous measurement value already exists
+ * in this slot
+ */
+ return (measurement_slot[index].is_populated);
+}
+
+psa_status_t measured_boot_read_measurement(uint8_t index,
+ uint8_t *signer_id,
+ size_t signer_id_size,
+ size_t *signer_id_len,
+ uint8_t *version,
+ size_t version_size,
+ uint8_t *version_len,
+ uint32_t *measurement_algo,
+ uint8_t *sw_type,
+ size_t sw_type_size,
+ uint8_t *sw_type_len,
+ uint8_t *measurement_value,
+ size_t measurement_value_size,
+ size_t *measurement_value_len,
+ uint8_t *is_locked)
+{
+ struct measurement_t *src_measurement =
+ &measurement_slot[index].measurement;
+
+ if (is_read_access_prohibited(index)) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+
+ if (is_measurement_slot_populated(index)) {
+ if ((version_size < src_measurement->metadata.version_size) ||
+ (sw_type_size < src_measurement->metadata.sw_type_size) ||
+ (signer_id_size < src_measurement->metadata.signer_id_size) ||
+ (measurement_value_size < src_measurement->value.hash_buf_size)) {
+ /* The size of one of the arguments is incorrect */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ *signer_id_len = src_measurement->metadata.signer_id_size;
+ memcpy(signer_id, src_measurement->metadata.signer_id, *signer_id_len);
+
+ *version_len = src_measurement->metadata.version_size;
+ memcpy(version, src_measurement->metadata.version, *version_len);
+
+ *sw_type_len = src_measurement->metadata.sw_type_size;
+ memcpy(sw_type, src_measurement->metadata.sw_type, *sw_type_len);
+
+ *measurement_algo = src_measurement->metadata.measurement_algo;
+
+ *measurement_value_len = src_measurement->value.hash_buf_size;
+ memcpy(measurement_value, src_measurement->value.hash_buf,
+ *measurement_value_len);
+
+ *is_locked = measurement_slot[index].is_locked;
+ } else {
+ /* Measurement slot is not populated. */
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ return PSA_SUCCESS;
+}
+
+static void update_metadata(const uint8_t index,
+ const uint8_t *signer_id,
+ const size_t signer_id_size,
+ const uint8_t *version,
+ const size_t version_size,
+ const uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ const size_t sw_type_size)
+{
+ struct measurement_metadata_t *dest_metadata =
+ &measurement_slot[index].measurement.metadata;
+
+ /* Copy metadata for corresponding measurement slot */
+ dest_metadata->signer_id_size = signer_id_size;
+ dest_metadata->version_size = version_size;
+ dest_metadata->sw_type_size = sw_type_size;
+
+ memcpy(dest_metadata->signer_id, signer_id, signer_id_size);
+ memcpy(dest_metadata->version, version, version_size);
+ dest_metadata->measurement_algo = measurement_algo;
+ memcpy(dest_metadata->sw_type, sw_type, sw_type_size);
+}
+
+static void extend_metadata(const uint8_t index)
+{
+ struct measurement_metadata_t *dest_metadata =
+ &measurement_slot[index].measurement.metadata;
+
+ /* Do not update Signer ID as it should be the same */
+ /* Do not update Measurement algo as it should be the same */
+ /* Clear Version info and Software component description */
+ dest_metadata->version_size = 0;
+ dest_metadata->sw_type_size = 0;
+ (void)memset(dest_metadata->version, 0, VERSION_MAX_SIZE);
+ (void)memset(dest_metadata->sw_type, 0, SW_TYPE_MAX_SIZE);
+}
+
+static void store_measurement_value(const uint8_t index,
+ const uint8_t *src_value,
+ const size_t measurement_size)
+{
+ memcpy(&measurement_slot[index].measurement.value.hash_buf[0], src_value,
+ measurement_size);
+ measurement_slot[index].measurement.value.hash_buf_size = measurement_size;
+}
+
+static inline void lock_measurement_slot(const uint8_t index)
+{
+ measurement_slot[index].is_locked = true;
+}
+
+static inline void get_stored_measurement_value(const uint8_t index,
+ uint8_t *output_buffer,
+ const size_t measurement_size)
+{
+ memcpy(output_buffer,
+ &measurement_slot[index].measurement.value.hash_buf[0],
+ MEASUREMENT_VALUE_SIZE);
+}
+
+static psa_status_t extend_measurement_value(const uint8_t index,
+ const uint8_t *measurement,
+ const size_t measurement_size,
+ uint8_t *hash_result,
+ size_t *hash_len)
+{
+ uint8_t temp_buffer[TEMP_BUFFER_SIZE];
+ size_t total_size;
+
+ /* Read previous measurement */
+ get_stored_measurement_value(index, temp_buffer, MEASUREMENT_VALUE_SIZE);
+
+ /* concatenate new measurement */
+ memcpy(&temp_buffer[MEASUREMENT_VALUE_SIZE], measurement, measurement_size);
+ total_size = measurement_size + MEASUREMENT_VALUE_SIZE;
+
+ /* Perform hash calculation */
+ return psa_hash_compute(TFM_MEASURED_BOOT_HASH_ALG, temp_buffer,
+ total_size, hash_result, MEASUREMENT_VALUE_SIZE,
+ hash_len);
+}
+
+static inline bool is_measurement_slot_locked(const uint8_t index)
+{
+ return measurement_slot[index].is_locked;
+}
+
+static inline void mark_slot_as_occupied(const uint8_t index)
+{
+ measurement_slot[index].is_populated = true;
+}
+
+/* This API is used to extend and store measurement and the corresponding
+ * metadata / boot-record (if provided).
+ */
+psa_status_t measured_boot_extend_measurement(uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ uint8_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ uint8_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ uint8_t lock_measurement)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t hash_result[MEASUREMENT_VALUE_SIZE] = {0};
+ size_t hash_len;
+
+ log_extend_measurement(index,
+ signer_id, signer_id_size,
+ version, version_size,
+ measurement_algo,
+ sw_type, sw_type_size,
+ measurement_value, measurement_value_size,
+ lock_measurement);
+
+ if (is_slot_access_prohibited(index, signer_id)) {
+ status = PSA_ERROR_NOT_PERMITTED;
+ goto error;
+ }
+
+ if (is_measurement_slot_locked(index)) {
+ /* Cannot write to measurement slot once locked */
+ status = PSA_ERROR_BAD_STATE;
+ goto error;
+ }
+
+ /* Check how metadata needs updating for the requested slot */
+ if (is_measurement_slot_populated(index)) {
+ /* Extend metadata */
+ extend_metadata(index);
+ } else {
+ /* Store the corresponding metadata */
+ update_metadata(index,
+ signer_id, signer_id_size,
+ version, version_size,
+ measurement_algo,
+ sw_type, sw_type_size);
+ /* Indicate that the slot is not empty anymore */
+ mark_slot_as_occupied(index);
+ }
+
+ /* Extend current measurement with new measured value */
+ status = extend_measurement_value(index,
+ measurement_value,
+ measurement_value_size,
+ hash_result, &hash_len);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ /* Store calculated extended value */
+ store_measurement_value(index, hash_result, hash_len);
+
+ if (lock_measurement) {
+ /* lock measurement slot if requested */
+ lock_measurement_slot(index);
+ }
+
+error:
+ if (status != PSA_SUCCESS) {
+ LOG_DBGFMT("Measured Boot : measurement extension failed.\r\n");
+ } else {
+ LOG_DBGFMT("Measured Boot : measurement extended successfully.\r\n");
+ }
+
+ return status;
+}
+
+void initialise_all_measurements(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < NUM_OF_MEASUREMENT_SLOTS; i++) {
+ measurement_slot[i].is_locked = false;
+ measurement_slot[i].is_populated = false;
+ /* By default, mark all slots as "not common" to avoid accidental extend
+ * and write by a different signer id
+ */
+ measurement_slot[i].is_common = false;
+
+ /* Clear all metadata for corresponding measurement slot */
+ (void)memset(&measurement_slot[i].measurement.metadata, 0,
+ sizeof(struct measurement_metadata_t));
+ /* Initialise measurement values to default pattern */
+ (void)memset(&measurement_slot[i].measurement.value.hash_buf[0],
+ MEASUREMENT_VALUE_INIT_PATTERN,
+ sizeof(measurement_slot[i].measurement.value.hash_buf));
+ }
+}
+
+#ifdef TFM_BOOT_STORE_MEASUREMENTS
+psa_status_t collect_shared_measurements(void)
+{
+ struct shared_data_tlv_entry tlv_entry;
+ struct boot_measurement_metadata *metadata_ptr;
+ uint8_t *tlv_end;
+ uint8_t *tlv_curr;
+ uint8_t claim;
+ psa_status_t status = PSA_ERROR_GENERIC_ERROR;
+ int32_t rc;
+
+ /* Collect the measurements from the shared data area and store them. */
+ rc = tfm_core_get_boot_data(TLV_MAJOR_MBS,
+ (struct tfm_boot_data *)&boot_measurements,
+ sizeof(boot_measurements));
+ if (rc != (int32_t)TFM_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (boot_measurements.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
+ /* Boot measurement information is malformed. */
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Get the boundaries of TLV section where to lookup. */
+ tlv_curr = boot_measurements.data;
+ 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. */
+ (void)memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
+ 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. */
+ status = PSA_ERROR_GENERIC_ERROR;
+ break;
+ }
+
+ metadata_ptr = (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);
+ (void)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. */
+ status = PSA_ERROR_GENERIC_ERROR;
+ break;
+ } else {
+ /* Validate size limits of metadata items (if applicable) and
+ * the size limits of measurement value before storing it.
+ */
+ if ((metadata_ptr->signer_id_size < SIGNER_ID_MIN_SIZE) ||
+ (metadata_ptr->signer_id_size > SIGNER_ID_MAX_SIZE) ||
+ (tlv_entry.tlv_len < MEASUREMENT_VALUE_MIN_SIZE) ||
+ (tlv_entry.tlv_len > MEASUREMENT_VALUE_MAX_SIZE)) {
+ status = PSA_ERROR_GENERIC_ERROR;
+ break;
+ }
+
+ /* Store the measurement and associated metadata. */
+ status = measured_boot_extend_measurement(
+ (uint8_t)GET_MBS_SLOT(tlv_entry.tlv_type),
+ metadata_ptr->signer_id,
+ metadata_ptr->signer_id_size,
+ (const uint8_t*)metadata_ptr->sw_version,
+ tfm_strnlen(metadata_ptr->sw_version,
+ sizeof(metadata_ptr->sw_version)),
+ metadata_ptr->measurement_type,
+ (const uint8_t*)metadata_ptr->sw_type,
+ tfm_strnlen(metadata_ptr->sw_type,
+ sizeof(metadata_ptr->sw_type)),
+ tlv_curr + SHARED_DATA_ENTRY_HEADER_SIZE,
+ tlv_entry.tlv_len,
+ (claim == SW_MEASURE_VALUE_NON_EXTENDABLE) ? true : false);
+ if (status != PSA_SUCCESS) {
+ /* Failed to store measurement. */
+ break;
+ }
+ }
+ /* Move to the next TLV entry. */
+ tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
+ }
+
+ return status;
+}
+#endif /* TFM_BOOT_STORE_MEASUREMENTS */
diff --git a/partitions/measured_boot/measured_boot.h b/partitions/measured_boot/measured_boot.h
new file mode 100644
index 0000000..fbd68ea
--- /dev/null
+++ b/partitions/measured_boot/measured_boot.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __MEASURED_BOOT_H__
+#define __MEASURED_BOOT_H__
+
+#include "psa/error.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Retrieves a measurement from the requested slot.
+ *
+ * \param[in] index Slot number from which measurement is
+ * to be retrieved.
+ * \param[out] signer_id Pointer to \p signer_id buffer.
+ * \param[in] signer_id_size Size of the \p signer_id buffer in
+ * bytes.
+ * \param[out] signer_id_len On success, number of bytes that make
+ * up \p signer_id.
+ * \param[out] version Pointer to \p version buffer.
+ * \param[in] version_size Size of the \p version buffer in bytes.
+ * \param[out] version_len On success, number of bytes that make
+ * up the \p version.
+ * \param[out] measurement_algo Pointer to \p measurement_algo.
+ * \param[out] sw_type Pointer to \p sw_type buffer.
+ * \param[in] sw_type_size Size of the \p sw_type buffer in bytes.
+ * \param[out] sw_type_len On success, number of bytes that make
+ * up the \p sw_type.
+ * \param[out] measurement_value Pointer to \p measurement_value buffer.
+ * \param[in] measurement_value_size Size of the \p measurement_value
+ * buffer in bytes.
+ * \param[out] measurement_value_len On success, number of bytes that make
+ * up the \p measurement_value.
+ * \param[out] is_locked Pointer to lock status of requested
+ * measurement slot.
+ *
+ * \retval #PSA_SUCCESS
+ * Success.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * The size of at least one of the output buffers is incorrect.
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ * When the requested slot is not accessible to the caller.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ * The requested slot is empty, does not contain a measurement.
+ */
+psa_status_t measured_boot_read_measurement(uint8_t index,
+ uint8_t *signer_id,
+ size_t signer_id_size,
+ size_t *signer_id_len,
+ uint8_t *version,
+ size_t version_size,
+ uint8_t *version_len,
+ uint32_t *measurement_algo,
+ uint8_t *sw_type,
+ size_t sw_type_size,
+ uint8_t *sw_type_len,
+ uint8_t *measurement_value,
+ size_t measurement_value_size,
+ size_t *measurement_value_len,
+ uint8_t *is_locked);
+
+/**
+ * \brief Extends and stores a measurement to the requested slot.
+ *
+ * \param[in] index Slot number in which measurement is
+ * to be stored.
+ * \param[in] signer_id Pointer to \p signer_id buffer.
+ * \param[in] signer_id_size Size of the \p signer_id buffer in
+ * bytes.
+ * \param[in] version Pointer to \p version buffer.
+ * \param[in] version_size Size of the \p version buffer in bytes.
+ * \param[in] measurement_algo Algorithm identifier used for
+ * measurement.
+ * \param[in] sw_type Pointer to \p sw_type buffer.
+ * \param[in] sw_type_size Size of the \p sw_type buffer in bytes.
+ * \param[in] measurement_value Pointer to \p measurement_value buffer.
+ * \param[in] measurement_value_size Size of the \p measurement_value
+ * buffer in bytes.
+ * \param[in] lock_measurement Flag requesting whether the
+ * measurement is to be locked.
+ *
+ * \retval #PSA_SUCCESS
+ * Success.
+ * \retval #PSA_ERROR_BAD_STATE
+ * Request to lock, when slot is already locked.
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ * When the requested slot is not accessible to the caller.
+ */
+psa_status_t measured_boot_extend_measurement(uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ uint8_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ uint8_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ uint8_t lock_measurement);
+
+/**
+ * \brief Initialise all measurements & related metadata
+ *
+ */
+void initialise_all_measurements(void);
+
+/**
+ * \brief Collect and store every measurement from the shared memory.
+ *
+ * \return Returns values as specified by the \ref psa_status_t
+ */
+psa_status_t collect_shared_measurements(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MEASURED_BOOT_H__ */
diff --git a/partitions/measured_boot/measured_boot_integration_guide.rst b/partitions/measured_boot/measured_boot_integration_guide.rst
new file mode 100644
index 0000000..5478e23
--- /dev/null
+++ b/partitions/measured_boot/measured_boot_integration_guide.rst
@@ -0,0 +1,132 @@
+#######################################
+Measured Boot Service Integration Guide
+#######################################
+
+Introduction
+************
+Measured Boot partition provides services to extend and read
+measurements (hash values and metadata) during various stages of a power cycle.
+These measurements can be extended and read by any application/service
+(secure or non-secure).
+
+************
+Measurements
+************
+The initial attestation token (required by attestation service) is formed of
+various claims. Each software component claim comprises of the following
+measurements which are extended and read by Measured Boot services.
+
+ - **Measurement type**: It represents the role of the
+ software component. Value is encoded as a short(!) text string.
+
+ - **Measurement value**: It represents a hash of the invariant software
+ component in memory at start-up time. The value must be a cryptographic
+ hash of 256 bits or stronger. Value is encoded as a byte string.
+
+ - **Version**: It represents the issued software version. Value is encoded
+ as a text string.
+
+ - **Signer ID**: It represents the hash of a signing authority public key.
+ Value is encoded as a byte string.
+
+ - **Measurement description**: It represents the way in which the
+ measurement value of the software component is computed. Value is
+ encoded as text string containing an abbreviated description (name) of
+ the measurement method.
+
+**************
+Code structure
+**************
+
+The TF-M Measured Boot Service source and header files are located in current
+directory. The interfaces for the measured boot service are located in the
+``interface/include``. The headers to be included by applications that want
+to use functions from the API is ``measured_boot_api.h`` and
+``measured_boot_defs.h``.
+
+Service source files
+====================
+
+- Measured Boot Service:
+ - ``measured_boot.c`` : Implements core functionalities such as
+ implementation of APIs, extension and reading of measurements.
+ - ``measured_boot_api.c``: Implements the secure API layer to
+ allow other services in the secure domain to request functionalities
+ from the measured boot service using the PSA API interface.
+ - ``measured_boot_req_mngr.c``: Includes the initialization entry of
+ measured boot service and handles service requests in IPC model.
+
+Measured Boot Interfaces
+========================
+
+The TF-M Measured Boot service exposes the following interfaces:
+
+.. code-block:: c
+
+ psa_status_t tfm_measured_boot_read_measurement(
+ uint8_t index,
+ uint8_t *signer_id,
+ size_t signer_id_size,
+ size_t *signer_id_len,
+ uint8_t *version,
+ size_t version_size,
+ size_t *version_len,
+ uint32_t *measurement_algo,
+ uint8_t *sw_type,
+ size_t sw_type_size,
+ size_t *sw_type_len,
+ uint8_t *measurement_value,
+ size_t measurement_value_size,
+ size_t *measurement_value_len,
+ bool *is_locked);
+ psa_status_t tfm_measured_boot_extend_measurement(
+ uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ size_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ size_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ bool lock_measurement);
+
+When reading measurement, the caller must allocate large enough
+buffers to accommodate data for all the output measurement parameters.
+The definitions ``SIGNER_ID_MAX_SIZE``, ``VERSION_MAX_SIZE``,
+``SW_TYPE_MAX_SIZE``, and ``MEASUREMENT_VALUE_MAX_SIZE`` can be used to
+determine the required size of the buffers.
+
+System integrators might need to port these interfaces to a custom secure
+partition manager implementation (SPM). Implementations in TF-M project can be
+found in tf-m-extras repository.
+
+- ``partitions/measured_boot/interface/src/measured_boot_api.c``:
+ non-secure as well as secure interface implementation
+
+Related compile time options for out of tree build
+--------------------------------------------------
+- ``TFM_PARTITION_MEASURED_BOOT``: To include measured boot secure partition
+ and its services, its value should be ON. By default, it is switched OFF.
+
+- ``MEASURED_BOOT_HASH_ALG``: This option selects the hash algorithm used
+ for extension of measurement hashes. Its default value is PSA_ALG_SHA_256.
+
+- ``TFM_EXTRA_MANIFEST_LIST_FILES``: <tf-m-extras-repo>/partitions/
+ measured_boot/measured_boot_manifest_list.yaml
+
+- ``TFM_EXTRA_PARTITION_PATHS``: <tf-m-extras-repo>/partitions/measured_boot
+
+************
+Verification
+************
+
+Regression test
+===============
+
+To be implemented.
+
+--------------
+
+*Copyright (c) 2022, Arm Limited. All rights reserved.*
diff --git a/partitions/measured_boot/measured_boot_manifest_list.yaml b/partitions/measured_boot/measured_boot_manifest_list.yaml
new file mode 100644
index 0000000..d4df93e
--- /dev/null
+++ b/partitions/measured_boot/measured_boot_manifest_list.yaml
@@ -0,0 +1,29 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+{
+ "name": "Measured boot secure partition",
+ "type": "manifest_list",
+ "version_major": 0,
+ "version_minor": 1,
+ "manifest_list": [
+ {
+ "name": "TFM Measured Boot Service",
+ "short_name": "TFM_SP_MEASURED_BOOT",
+ "manifest": "tfm_measured_boot.yaml",
+ "output_path": "secure_fw/partitions/measured_boot",
+ "conditional": "@TFM_PARTITION_MEASURED_BOOT@",
+ "version_major": 0,
+ "version_minor": 1,
+ "pid": 275,
+ "linker_pattern": {
+ "library_list": [
+ "*tfm_*partition_measured_boot.*"
+ ]
+ }
+ }
+ ]
+}
diff --git a/partitions/measured_boot/measured_boot_req_mngr.c b/partitions/measured_boot/measured_boot_req_mngr.c
new file mode 100644
index 0000000..8590d69
--- /dev/null
+++ b/partitions/measured_boot/measured_boot_req_mngr.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <string.h>
+#include "measured_boot.h"
+#include "measured_boot_defs.h"
+#include "tfm_sp_log.h"
+
+#include "psa/service.h"
+#include "psa_manifest/pid.h"
+#include "psa/crypto.h"
+#include "psa_manifest/tfm_measured_boot.h"
+
+/* TODO: This info will be used later on as input to decide access control */
+static int32_t g_measured_boot_caller_id;
+
+static psa_status_t read_measurements(const psa_msg_t *msg)
+{
+ struct measured_boot_read_iovec_in_t read_iov_in;
+ struct measured_boot_read_iovec_out_t read_iov_out;
+ size_t signer_id_len, measurement_value_len;
+ size_t num;
+ size_t signer_id_size, measurement_value_size;
+ uint8_t signer_id[SIGNER_ID_MAX_SIZE] = {0};
+ uint8_t measurement_value[MEASUREMENT_VALUE_SIZE] = {0};
+ psa_status_t status;
+
+ /* store the client ID here for later use in service */
+ g_measured_boot_caller_id = msg->client_id;
+
+ signer_id_size = msg->out_size[1];
+ measurement_value_size = msg->out_size[2];
+
+ /* Check input parameter */
+ if ((msg->in_size[0] != sizeof(struct measured_boot_read_iovec_in_t)) ||
+ (msg->out_size[0] != sizeof(struct measured_boot_read_iovec_out_t))) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ num = psa_read(msg->handle, 0, &read_iov_in, sizeof(read_iov_in));
+ if (num != sizeof(read_iov_in)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ /* Validate requested slot number */
+ if (read_iov_in.index >= NUM_OF_MEASUREMENT_SLOTS) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ memset(&read_iov_out, 0, sizeof(read_iov_out));
+
+ status = measured_boot_read_measurement(read_iov_in.index,
+ &signer_id[0],
+ signer_id_size,
+ &signer_id_len,
+ &read_iov_out.version[0],
+ read_iov_in.version_size,
+ &read_iov_out.version_len,
+ &read_iov_out.measurement_algo,
+ &read_iov_out.sw_type[0],
+ read_iov_in.sw_type_size,
+ &read_iov_out.sw_type_len,
+ &measurement_value[0],
+ measurement_value_size,
+ &measurement_value_len,
+ &read_iov_out.is_locked);
+
+ if (status == PSA_SUCCESS) {
+ psa_write(msg->handle, 0, &read_iov_out, sizeof(read_iov_out));
+ psa_write(msg->handle, 1, &signer_id[0], signer_id_len);
+ psa_write(msg->handle, 2, &measurement_value[0], measurement_value_len);
+ }
+
+ return status;
+}
+
+static psa_status_t extend_measurement(const psa_msg_t *msg)
+{
+ struct measured_boot_extend_iovec_t extend_iov;
+ size_t num;
+ size_t signer_id_size;
+ size_t version_size;
+ size_t measurement_value_size;
+ uint8_t signer_id[SIGNER_ID_MAX_SIZE] = {0};
+ uint8_t version[VERSION_MAX_SIZE] = {0};
+ uint8_t measurement_value[MEASUREMENT_VALUE_MAX_SIZE] = {0};
+
+ /* store the client ID here for later use in service */
+ g_measured_boot_caller_id = msg->client_id;
+
+ /* Check input parameter */
+ if (msg->in_size[0] != sizeof(struct measured_boot_extend_iovec_t)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ signer_id_size = msg->in_size[1];
+ version_size = msg->in_size[2];
+ measurement_value_size = msg->in_size[3];
+
+ memset(&extend_iov, 0, sizeof(extend_iov));
+ num = psa_read(msg->handle, 0, &extend_iov,
+ sizeof(struct measured_boot_extend_iovec_t));
+ if (num != sizeof(struct measured_boot_extend_iovec_t)) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ /* Validate size limits of input parameters */
+ if ((signer_id_size < SIGNER_ID_MIN_SIZE) ||
+ (signer_id_size > SIGNER_ID_MAX_SIZE) ||
+ (version_size > VERSION_MAX_SIZE) ||
+ (measurement_value_size < MEASUREMENT_VALUE_MIN_SIZE) ||
+ (measurement_value_size > MEASUREMENT_VALUE_MAX_SIZE) ||
+ (extend_iov.sw_type_size > SW_TYPE_MAX_SIZE)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Validate requested slot number */
+ if (extend_iov.index >= NUM_OF_MEASUREMENT_SLOTS) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ num = psa_read(msg->handle, 1, signer_id, signer_id_size);
+ if (num != signer_id_size) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ num = psa_read(msg->handle, 2, version, version_size);
+ if (num != version_size) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ num = psa_read(msg->handle, 3, &measurement_value[0],
+ measurement_value_size);
+ if (num != measurement_value_size) {
+ return PSA_ERROR_PROGRAMMER_ERROR;
+ }
+
+ return measured_boot_extend_measurement(extend_iov.index,
+ signer_id,
+ signer_id_size,
+ version,
+ version_size,
+ extend_iov.measurement_algo,
+ &extend_iov.sw_type[0],
+ extend_iov.sw_type_size,
+ measurement_value,
+ measurement_value_size,
+ extend_iov.lock_measurement);
+}
+
+static void measured_boot_signal_handle(psa_signal_t signal)
+{
+ psa_status_t status;
+ psa_msg_t msg;
+
+ /* Retrieve the message corresponding to the measured_boot service signal */
+ status = psa_get(signal, &msg);
+ if (status != PSA_SUCCESS) {
+ return;
+ }
+
+ /* Decode the message */
+ switch (msg.type) {
+ case TFM_MEASURED_BOOT_READ:
+ status = read_measurements(&msg);
+ /* Reply with the message result status to unblock the client */
+ psa_reply(msg.handle, status);
+ break;
+ case TFM_MEASURED_BOOT_EXTEND:
+ status = extend_measurement(&msg);
+ /* Reply with the message result status to unblock the client */
+ psa_reply(msg.handle, status);
+ break;
+ default:
+ /* Invalid message type */
+ status = PSA_ERROR_NOT_SUPPORTED;
+ break;
+ }
+}
+
+/**
+ * \brief The measured_boot partition's entry function.
+ */
+psa_status_t tfm_measured_boot_init(void)
+{
+ psa_status_t status = PSA_SUCCESS;
+
+ /* Initialise all measurements & related metadata */
+ initialise_all_measurements();
+
+ LOG_DBGFMT("Measured Boot : selected algorithm: %x\r\n",
+ TFM_MEASURED_BOOT_HASH_ALG);
+
+#ifdef TFM_BOOT_STORE_MEASUREMENTS
+ status = collect_shared_measurements();
+#endif
+
+ if (status != PSA_SUCCESS) {
+ psa_panic();
+ }
+
+ psa_signal_t signals = 0;
+
+ while (1) {
+ signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK);
+ if (signals & TFM_MEASURED_BOOT_SIGNAL) {
+ measured_boot_signal_handle(TFM_MEASURED_BOOT_SIGNAL);
+ } else {
+ psa_panic();
+ }
+ }
+}
diff --git a/partitions/measured_boot/measured_boot_utils.c b/partitions/measured_boot/measured_boot_utils.c
new file mode 100644
index 0000000..4f13c31
--- /dev/null
+++ b/partitions/measured_boot/measured_boot_utils.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "measured_boot_utils.h"
+#include "measured_boot_api.h"
+#include "tfm_sp_log.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+static void print_byte_array(const uint8_t *array, size_t len)
+{
+ size_t i;
+
+ if (array == NULL || len == 0) {
+ LOG_DBGFMT("\r\n");
+ } else {
+ for (i = 0; i < len; ++i) {
+ if (array[i] < 0x10) {
+ LOG_DBGFMT(" 0%x", array[i]);
+ } else {
+ LOG_DBGFMT(" %x", array[i]);
+ }
+ if ((i & 0xFu) == 0xFu) {
+ LOG_DBGFMT("\r\n");
+ if (i < (len - 1)) {
+ LOG_DBGFMT(" :");
+ }
+ }
+ }
+ }
+}
+
+static inline void add_null_terminator(uint8_t *dest,
+ const uint8_t *src,
+ size_t src_len)
+{
+ memcpy(dest, src, src_len);
+ *(dest + src_len) = '\0';
+}
+
+void log_extend_measurement(uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ uint8_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ uint8_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ uint8_t lock_measurement)
+{
+ uint8_t string_buf[((SW_TYPE_MAX_SIZE > VERSION_MAX_SIZE) ?
+ SW_TYPE_MAX_SIZE : VERSION_MAX_SIZE) + 1];
+
+ LOG_DBGFMT("Measured Boot : store and extend measurement:\r\n");
+ LOG_DBGFMT(" - slot : %u\r\n", index);
+ LOG_DBGFMT(" - signer_id :");
+ print_byte_array(signer_id, signer_id_size);
+ add_null_terminator(string_buf, version, version_size);
+ LOG_DBGFMT(" - version : %s\r\n", string_buf);
+ LOG_DBGFMT(" - algorithm : %x\r\n", measurement_algo);
+ add_null_terminator(string_buf, sw_type, sw_type_size);
+ LOG_DBGFMT(" - sw_type : %s\r\n", string_buf);
+ LOG_DBGFMT(" - measurement :");
+ print_byte_array(measurement_value, measurement_value_size);
+ LOG_DBGFMT(" - locking : %s\r\n", lock_measurement ? "true" : "false");
+}
diff --git a/partitions/measured_boot/measured_boot_utils.h b/partitions/measured_boot/measured_boot_utils.h
new file mode 100644
index 0000000..f2c43f7
--- /dev/null
+++ b/partitions/measured_boot/measured_boot_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __MEASURED_BOOT_UTILS_H__
+#define __MEASURED_BOOT_UTILS_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Logs all the measurement parameters used to extend the requested slot.
+ *
+ * \param[in] index Slot number in which measurement is
+ * to be stored.
+ * \param[in] signer_id Pointer to \p signer_id buffer.
+ * \param[in] signer_id_size Size of the \p signer_id buffer in
+ * bytes.
+ * \param[in] version Pointer to \p version buffer.
+ * \param[in] version_size Size of the \p version buffer in bytes.
+ * \param[in] measurement_algo Algorithm identifier used for
+ * measurement.
+ * \param[in] sw_type Pointer to \p sw_type buffer.
+ * \param[in] sw_type_size Size of the \p sw_type buffer in bytes.
+ * \param[in] measurement_value Pointer to \p measurement_value buffer.
+ * \param[in] measurement_value_size Size of the \p measurement_value
+ * buffer in bytes.
+ * \param[in] lock_measurement Flag requesting whether the
+ * measurement is to be locked.
+ */
+void log_extend_measurement(uint8_t index,
+ const uint8_t *signer_id,
+ size_t signer_id_size,
+ const uint8_t *version,
+ uint8_t version_size,
+ uint32_t measurement_algo,
+ const uint8_t *sw_type,
+ uint8_t sw_type_size,
+ const uint8_t *measurement_value,
+ size_t measurement_value_size,
+ uint8_t lock_measurement);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MEASURED_BOOT_UTILS_H__ */
diff --git a/partitions/measured_boot/tfm_measured_boot.yaml b/partitions/measured_boot/tfm_measured_boot.yaml
new file mode 100644
index 0000000..6fd8eba
--- /dev/null
+++ b/partitions/measured_boot/tfm_measured_boot.yaml
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+{
+ "psa_framework_version": 1.1,
+ "name": "TFM_SP_MEASURED_BOOT",
+ "type": "PSA-ROT",
+ "priority": "NORMAL",
+ "model": "IPC",
+ "entry_point": "tfm_measured_boot_init",
+ "stack_size": "0x400",
+ "services": [
+ {
+ "name": "TFM_MEASURED_BOOT",
+ "sid": "0x000000C0",
+ "non_secure_clients": true,
+ "connection_based": false,
+ "stateless_handle": auto,
+ "version": 1,
+ "version_policy": "STRICT"
+ }
+ ],
+ "dependencies": [
+ "TFM_CRYPTO"
+ ]
+}