mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-25 22:35:42 +00:00

Child software components are inheriting their first valid DPE context handle from their parent components (who loaded and measured them). The context handle is shared through the device tree object the following way: - BL1 -> BL2 via TB_FW_CONFIG - BL2 -> BL33 via NT_FW_CONFIG Signed-off-by: Tamas Ban <tamas.ban@arm.com> Change-Id: I9bf7808fb13a310ad7ca1895674a0c7e6725e08b
541 lines
14 KiB
C
541 lines
14 KiB
C
/*
|
|
* Copyright (c) 2018-2024, Arm Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include <common/debug.h>
|
|
#if MEASURED_BOOT
|
|
#include <common/desc_image_load.h>
|
|
#endif
|
|
#include <common/fdt_wrappers.h>
|
|
|
|
#include <lib/fconf/fconf.h>
|
|
#include <lib/fconf/fconf_dyn_cfg_getter.h>
|
|
#include <libfdt.h>
|
|
|
|
#include <plat/arm/common/arm_dyn_cfg_helpers.h>
|
|
#include <plat/arm/common/plat_arm.h>
|
|
|
|
#define DTB_PROP_MBEDTLS_HEAP_ADDR "mbedtls_heap_addr"
|
|
#define DTB_PROP_MBEDTLS_HEAP_SIZE "mbedtls_heap_size"
|
|
|
|
#if MEASURED_BOOT
|
|
#ifdef SPD_opteed
|
|
/*
|
|
* Currently OP-TEE does not support reading DTBs from Secure memory
|
|
* and this property should be removed when this feature is supported.
|
|
*/
|
|
#define DTB_PROP_HW_SM_LOG_ADDR "tpm_event_log_sm_addr"
|
|
#endif /* SPD_opteed */
|
|
#define DTB_PROP_HW_LOG_ADDR "tpm_event_log_addr"
|
|
#define DTB_PROP_HW_LOG_SIZE "tpm_event_log_size"
|
|
#define DTB_PROP_HW_LOG_MAX_SIZE "tpm_event_log_max_size"
|
|
#endif /* MEASURED_BOOT */
|
|
|
|
static size_t event_log_max_size __unused;
|
|
|
|
/*******************************************************************************
|
|
* Validate the tb_fw_config is a valid DTB file and returns the node offset
|
|
* to "arm,tb_fw" property.
|
|
* Arguments:
|
|
* void *dtb - pointer to the TB_FW_CONFIG in memory
|
|
* int *node - Returns the node offset to "arm,tb_fw" property if found.
|
|
*
|
|
* Returns 0 on success and -1 on error.
|
|
******************************************************************************/
|
|
int arm_dyn_tb_fw_cfg_init(void *dtb, int *node)
|
|
{
|
|
assert(dtb != NULL);
|
|
assert(node != NULL);
|
|
|
|
/* Check if the pointer to DT is correct */
|
|
if (fdt_check_header(dtb) != 0) {
|
|
WARN("Invalid DTB file passed as%s\n", " TB_FW_CONFIG");
|
|
return -1;
|
|
}
|
|
|
|
/* Assert the node offset point to "arm,tb_fw" compatible property */
|
|
*node = fdt_node_offset_by_compatible(dtb, -1, "arm,tb_fw");
|
|
if (*node < 0) {
|
|
WARN("The compatible property '%s' not%s", "arm,tb_fw",
|
|
" found in the config\n");
|
|
return -1;
|
|
}
|
|
|
|
VERBOSE("Dyn cfg: '%s'%s", "arm,tb_fw", " found in the config\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function writes the Mbed TLS heap address and size in the DTB. When it
|
|
* is called, it is guaranteed that a DTB is available. However it is not
|
|
* guaranteed that the shared Mbed TLS heap implementation is used. Thus we
|
|
* return error code from here and it's the responsibility of the caller to
|
|
* determine the action upon error.
|
|
*
|
|
* This function is supposed to be called only by BL1.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* -1 = error
|
|
*/
|
|
int arm_set_dtb_mbedtls_heap_info(void *dtb, void *heap_addr, size_t heap_size)
|
|
{
|
|
int dtb_root;
|
|
|
|
/*
|
|
* Verify that the DTB is valid, before attempting to write to it,
|
|
* and get the DTB root node.
|
|
*/
|
|
int err = arm_dyn_tb_fw_cfg_init(dtb, &dtb_root);
|
|
if (err < 0) {
|
|
ERROR("Invalid%s loaded. Unable to get root node\n",
|
|
" TB_FW_CONFIG");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Write the heap address and size in the DTB.
|
|
*
|
|
* NOTE: The variables heap_addr and heap_size are corrupted
|
|
* by the "fdtw_write_inplace_cells" function. After the
|
|
* function calls they must NOT be reused.
|
|
*/
|
|
err = fdtw_write_inplace_cells(dtb, dtb_root,
|
|
DTB_PROP_MBEDTLS_HEAP_ADDR, 2, &heap_addr);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_MBEDTLS_HEAP_ADDR);
|
|
return -1;
|
|
}
|
|
|
|
err = fdtw_write_inplace_cells(dtb, dtb_root,
|
|
DTB_PROP_MBEDTLS_HEAP_SIZE, 1, &heap_size);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_MBEDTLS_HEAP_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if MEASURED_BOOT
|
|
#if DICE_PROTECTION_ENVIRONMENT
|
|
|
|
#include <common/desc_image_load.h>
|
|
|
|
#define DTB_PROP_DPE_CTX_HANDLE "dpe_ctx_handle"
|
|
|
|
static int arm_set_dpe_context_handle(uintptr_t config_base,
|
|
int *ctx_handle)
|
|
{
|
|
/* As libfdt uses void *, we can't avoid this cast */
|
|
void *dtb = (void *)config_base;
|
|
const char *compatible = "arm,dpe_ctx_handle";
|
|
int err, node;
|
|
|
|
/*
|
|
* Verify that the DTB is valid, before attempting to write to it,
|
|
* and get the DTB root node.
|
|
*/
|
|
|
|
/* Check if the pointer to DT is correct */
|
|
err = fdt_check_header(dtb);
|
|
if (err < 0) {
|
|
WARN("Invalid DTB file passed\n");
|
|
return err;
|
|
}
|
|
|
|
/* Assert the node offset point to compatible property */
|
|
node = fdt_node_offset_by_compatible(dtb, -1, compatible);
|
|
if (node < 0) {
|
|
WARN("The compatible property '%s' not%s", compatible,
|
|
" found in the config\n");
|
|
return node;
|
|
}
|
|
|
|
VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
|
|
|
|
err = fdtw_write_inplace_cells(dtb, node,
|
|
DTB_PROP_DPE_CTX_HANDLE, 1, ctx_handle);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_DPE_CTX_HANDLE);
|
|
} else {
|
|
/*
|
|
* Ensure that the info written to the DTB is visible
|
|
* to other images.
|
|
*/
|
|
flush_dcache_range(config_base, fdt_totalsize(dtb));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function writes the DPE context handle value to the NT_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL2.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
int arm_set_nt_fw_info(int *ctx_handle)
|
|
{
|
|
uintptr_t config_base;
|
|
const bl_mem_params_node_t *cfg_mem_params;
|
|
|
|
/* Get the config load address and size from NT_FW_CONFIG */
|
|
cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
|
|
assert(cfg_mem_params != NULL);
|
|
|
|
config_base = cfg_mem_params->image_info.image_base;
|
|
|
|
/* Write the context handle value in the DTB */
|
|
return arm_set_dpe_context_handle(config_base, ctx_handle);
|
|
}
|
|
|
|
/*
|
|
* This function writes the DPE context handle value to the TB_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL1.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
int arm_set_tb_fw_info(int *ctx_handle)
|
|
{
|
|
/*
|
|
* Read tb_fw_config device tree for Event Log properties
|
|
* and write the Event Log address and its size in the DTB
|
|
*/
|
|
const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
|
|
uintptr_t tb_fw_cfg_dtb;
|
|
|
|
tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
|
|
assert(tb_fw_config_info != NULL);
|
|
|
|
tb_fw_cfg_dtb = tb_fw_config_info->config_addr;
|
|
|
|
/* Write the context handle value in the DTB */
|
|
return arm_set_dpe_context_handle(tb_fw_cfg_dtb, ctx_handle);
|
|
}
|
|
|
|
/*
|
|
* This function reads the initial DPE context handle from TB_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL2.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
|
|
int arm_get_tb_fw_info(int *ctx_handle)
|
|
{
|
|
/* As libfdt uses void *, we can't avoid this cast */
|
|
const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
|
|
int node, rc;
|
|
|
|
tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
|
|
assert(tb_fw_config_info != NULL);
|
|
|
|
void *dtb = (void *)tb_fw_config_info->config_addr;
|
|
const char *compatible = "arm,dpe_ctx_handle";
|
|
|
|
/* Assert the node offset point to compatible property */
|
|
node = fdt_node_offset_by_compatible(dtb, -1, compatible);
|
|
if (node < 0) {
|
|
WARN("The compatible property '%s'%s", compatible,
|
|
" not specified in TB_FW config.\n");
|
|
return node;
|
|
}
|
|
|
|
VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
|
|
|
|
rc = fdt_read_uint32(dtb, node, DTB_PROP_DPE_CTX_HANDLE, (uint32_t *)ctx_handle);
|
|
if (rc != 0) {
|
|
ERROR("%s%s", DTB_PROP_DPE_CTX_HANDLE,
|
|
" not specified in TB_FW config.\n");
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#else
|
|
/*
|
|
* Write the Event Log address and its size in the DTB.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
static int arm_set_event_log_info(uintptr_t config_base,
|
|
#ifdef SPD_opteed
|
|
uintptr_t sm_log_addr,
|
|
#endif
|
|
uintptr_t log_addr, size_t log_size)
|
|
{
|
|
/* As libfdt uses void *, we can't avoid this cast */
|
|
void *dtb = (void *)config_base;
|
|
const char *compatible = "arm,tpm_event_log";
|
|
int err, node;
|
|
|
|
/*
|
|
* Verify that the DTB is valid, before attempting to write to it,
|
|
* and get the DTB root node.
|
|
*/
|
|
|
|
/* Check if the pointer to DT is correct */
|
|
err = fdt_check_header(dtb);
|
|
if (err < 0) {
|
|
WARN("Invalid DTB file passed\n");
|
|
return err;
|
|
}
|
|
|
|
/* Assert the node offset point to compatible property */
|
|
node = fdt_node_offset_by_compatible(dtb, -1, compatible);
|
|
if (node < 0) {
|
|
WARN("The compatible property '%s' not%s", compatible,
|
|
" found in the config\n");
|
|
return node;
|
|
}
|
|
|
|
VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
|
|
|
|
#ifdef SPD_opteed
|
|
if (sm_log_addr != 0UL) {
|
|
err = fdtw_write_inplace_cells(dtb, node,
|
|
DTB_PROP_HW_SM_LOG_ADDR, 2, &sm_log_addr);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_HW_SM_LOG_ADDR);
|
|
return err;
|
|
}
|
|
}
|
|
#endif
|
|
err = fdtw_write_inplace_cells(dtb, node,
|
|
DTB_PROP_HW_LOG_ADDR, 2, &log_addr);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_HW_LOG_ADDR);
|
|
return err;
|
|
}
|
|
|
|
assert(event_log_max_size != 0U);
|
|
err = fdtw_write_inplace_cells(dtb, node,
|
|
DTB_PROP_HW_LOG_MAX_SIZE, 1,
|
|
&event_log_max_size);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_HW_LOG_MAX_SIZE);
|
|
return err;
|
|
}
|
|
|
|
err = fdtw_write_inplace_cells(dtb, node,
|
|
DTB_PROP_HW_LOG_SIZE, 1, &log_size);
|
|
if (err < 0) {
|
|
ERROR("%sDTB property '%s'\n",
|
|
"Unable to write ", DTB_PROP_HW_LOG_SIZE);
|
|
} else {
|
|
/*
|
|
* Ensure that the info written to the DTB is visible
|
|
* to other images.
|
|
*/
|
|
flush_dcache_range(config_base, fdt_totalsize(dtb));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function writes the Event Log address and its size
|
|
* in the TOS_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL2.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
int arm_set_tos_fw_info(uintptr_t log_addr, size_t log_size)
|
|
{
|
|
uintptr_t config_base;
|
|
const bl_mem_params_node_t *cfg_mem_params;
|
|
int err;
|
|
|
|
assert(log_addr != 0UL);
|
|
|
|
/* Get the config load address and size of TOS_FW_CONFIG */
|
|
cfg_mem_params = get_bl_mem_params_node(TOS_FW_CONFIG_ID);
|
|
assert(cfg_mem_params != NULL);
|
|
|
|
config_base = cfg_mem_params->image_info.image_base;
|
|
|
|
/* Write the Event Log address and its size in the DTB */
|
|
err = arm_set_event_log_info(config_base,
|
|
#ifdef SPD_opteed
|
|
0UL,
|
|
#endif
|
|
log_addr, log_size);
|
|
if (err < 0) {
|
|
ERROR("%sEvent Log data to TOS_FW_CONFIG\n",
|
|
"Unable to write ");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function writes the Event Log address and its size
|
|
* in the NT_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL2.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
int arm_set_nt_fw_info(
|
|
#ifdef SPD_opteed
|
|
uintptr_t log_addr,
|
|
#endif
|
|
size_t log_size, uintptr_t *ns_log_addr)
|
|
{
|
|
uintptr_t config_base;
|
|
uintptr_t ns_addr;
|
|
const bl_mem_params_node_t *cfg_mem_params;
|
|
int err;
|
|
|
|
assert(ns_log_addr != NULL);
|
|
|
|
/* Get the config load address and size from NT_FW_CONFIG */
|
|
cfg_mem_params = get_bl_mem_params_node(NT_FW_CONFIG_ID);
|
|
assert(cfg_mem_params != NULL);
|
|
|
|
config_base = cfg_mem_params->image_info.image_base;
|
|
|
|
/* Calculate Event Log address in Non-secure memory */
|
|
ns_addr = cfg_mem_params->image_info.image_base +
|
|
cfg_mem_params->image_info.image_max_size;
|
|
|
|
/* Check for memory space */
|
|
if ((uint64_t)(ns_addr + log_size) > ARM_NS_DRAM1_END) {
|
|
return -1;
|
|
}
|
|
|
|
/* Write the Event Log address and its size in the DTB */
|
|
err = arm_set_event_log_info(config_base,
|
|
#ifdef SPD_opteed
|
|
log_addr,
|
|
#endif
|
|
ns_addr, log_size);
|
|
|
|
/* Return Event Log address in Non-secure memory */
|
|
*ns_log_addr = (err < 0) ? 0UL : ns_addr;
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function writes the Event Log address and its size
|
|
* in the TB_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL1.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
*/
|
|
int arm_set_tb_fw_info(uintptr_t log_addr, size_t log_size, size_t log_max_size)
|
|
{
|
|
/*
|
|
* Read tb_fw_config device tree for Event Log properties
|
|
* and write the Event Log address and its size in the DTB
|
|
*/
|
|
const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
|
|
uintptr_t tb_fw_cfg_dtb;
|
|
int err;
|
|
|
|
tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
|
|
assert(tb_fw_config_info != NULL);
|
|
|
|
tb_fw_cfg_dtb = tb_fw_config_info->config_addr;
|
|
|
|
event_log_max_size = log_max_size;
|
|
|
|
err = arm_set_event_log_info(tb_fw_cfg_dtb,
|
|
#ifdef SPD_opteed
|
|
0UL,
|
|
#endif
|
|
log_addr, log_size);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function reads the Event Log address and its size
|
|
* properties present in TB_FW_CONFIG DTB.
|
|
*
|
|
* This function is supposed to be called only by BL2.
|
|
*
|
|
* Returns:
|
|
* 0 = success
|
|
* < 0 = error
|
|
* Alongside returns Event Log address and its size.
|
|
*/
|
|
|
|
int arm_get_tb_fw_info(uint64_t *log_addr, size_t *log_size,
|
|
size_t *log_max_size)
|
|
{
|
|
/* As libfdt uses void *, we can't avoid this cast */
|
|
const struct dyn_cfg_dtb_info_t *tb_fw_config_info;
|
|
int node, rc;
|
|
|
|
tb_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TB_FW_CONFIG_ID);
|
|
assert(tb_fw_config_info != NULL);
|
|
|
|
void *dtb = (void *)tb_fw_config_info->config_addr;
|
|
const char *compatible = "arm,tpm_event_log";
|
|
|
|
/* Assert the node offset point to compatible property */
|
|
node = fdt_node_offset_by_compatible(dtb, -1, compatible);
|
|
if (node < 0) {
|
|
WARN("The compatible property '%s'%s", compatible,
|
|
" not specified in TB_FW config.\n");
|
|
return node;
|
|
}
|
|
|
|
VERBOSE("Dyn cfg: '%s'%s", compatible, " found in the config\n");
|
|
|
|
rc = fdt_read_uint64(dtb, node, DTB_PROP_HW_LOG_ADDR, log_addr);
|
|
if (rc != 0) {
|
|
ERROR("%s%s", DTB_PROP_HW_LOG_ADDR,
|
|
" not specified in TB_FW config.\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_SIZE, (uint32_t *)log_size);
|
|
if (rc != 0) {
|
|
ERROR("%s%s", DTB_PROP_HW_LOG_SIZE,
|
|
" not specified in TB_FW config.\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = fdt_read_uint32(dtb, node, DTB_PROP_HW_LOG_MAX_SIZE,
|
|
(uint32_t *)log_max_size);
|
|
if (rc != 0) {
|
|
ERROR("%s%s", DTB_PROP_HW_LOG_MAX_SIZE,
|
|
" not specified in TB_FW config.\n");
|
|
return rc;
|
|
} else {
|
|
event_log_max_size = *log_max_size;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif /* DICE_PROTECTION_ENVIRONMENT */
|
|
#endif /* MEASURED_BOOT */
|