diff --git a/services/std_svc/drtm/drtm_dma_prot.h b/services/std_svc/drtm/drtm_dma_prot.h index e0c58b510..e9b1fe94d 100644 --- a/services/std_svc/drtm/drtm_dma_prot.h +++ b/services/std_svc/drtm/drtm_dma_prot.h @@ -9,6 +9,14 @@ #include +struct __packed drtm_dl_dma_prot_args_v1 { + uint64_t dma_prot_table_paddr; + uint64_t dma_prot_table_size; +}; + +/* Opaque / encapsulated type. */ +typedef struct drtm_dl_dma_prot_args_v1 drtm_dl_dma_prot_args_v1_t; + bool drtm_dma_prot_init(void); #endif /* DRTM_DMA_PROT_H */ diff --git a/services/std_svc/drtm/drtm_main.c b/services/std_svc/drtm/drtm_main.c index f05a231f7..d971901a7 100644 --- a/services/std_svc/drtm/drtm_main.c +++ b/services/std_svc/drtm/drtm_main.c @@ -194,9 +194,170 @@ static enum drtm_retc drtm_dl_check_cores(void) return SUCCESS; } +static enum drtm_retc drtm_dl_prepare_dlme_data(const struct_drtm_dl_args *args, + size_t *dlme_data_size_out) +{ + size_t dlme_data_total_bytes_req = 0; + + *dlme_data_size_out = dlme_data_total_bytes_req; + + return SUCCESS; +} + +/* + * Note: accesses to the dynamic launch args, and to the DLME data are + * little-endian as required, thanks to TF-A BL31 init requirements. + */ +static enum drtm_retc drtm_dl_check_args(uint64_t x1, + struct_drtm_dl_args *a_out) +{ + uint64_t dlme_start, dlme_end; + uint64_t dlme_img_start, dlme_img_ep, dlme_img_end; + uint64_t dlme_data_start, dlme_data_end; + uintptr_t args_mapping; + size_t args_mapping_size; + struct_drtm_dl_args *a; + struct_drtm_dl_args args_buf; + size_t dlme_data_size_req; + int rc; + + if (x1 % DRTM_PAGE_SIZE != 0) { + ERROR("DRTM: parameters structure is not " + DRTM_PAGE_SIZE_STR "-aligned\n"); + return INVALID_PARAMETERS; + } + + args_mapping_size = ALIGNED_UP(sizeof(struct_drtm_dl_args), DRTM_PAGE_SIZE); + rc = mmap_add_dynamic_region_alloc_va(x1, &args_mapping, args_mapping_size, + MT_MEMORY | MT_NS | MT_RO | + MT_SHAREABILITY_ISH); + if (rc != 0) { + WARN("DRTM: %s: mmap_add_dynamic_region() failed rc=%d\n", + __func__, rc); + return INTERNAL_ERROR; + } + a = (struct_drtm_dl_args *)args_mapping; + /* + * TODO: invalidate all data cache before reading the data passed by the + * DCE Preamble. This is required to avoid / defend against racing with + * cache evictions. + */ + args_buf = *a; + + rc = mmap_remove_dynamic_region(args_mapping, args_mapping_size); + if (rc) { + ERROR("%s(): mmap_remove_dynamic_region() failed unexpectedly" + " rc=%d\n", __func__, rc); + panic(); + } + a = &args_buf; + + if (a->version != 1) { + ERROR("DRTM: parameters structure incompatible with major version %d\n", + ARM_DRTM_VERSION_MAJOR); + return NOT_SUPPORTED; + } + + if (!(a->dlme_img_off < a->dlme_size && + a->dlme_data_off < a->dlme_size)) { + ERROR("DRTM: argument offset is outside of the DLME region\n"); + return INVALID_PARAMETERS; + } + dlme_start = a->dlme_paddr; + dlme_end = a->dlme_paddr + a->dlme_size; + dlme_img_start = a->dlme_paddr + a->dlme_img_off; + dlme_img_ep = dlme_img_start + a->dlme_img_ep_off; + dlme_img_end = dlme_img_start + a->dlme_img_size; + dlme_data_start = a->dlme_paddr + a->dlme_data_off; + dlme_data_end = dlme_end; + + /* + * TODO: validate that the DLME physical address range is all NS memory, + * return INVALID_PARAMETERS if it is not. + * Note that this check relies on platform-specific information. For + * examples, see psci_plat_pm_ops->validate_ns_entrypoint() or + * arm_validate_ns_entrypoint(). + */ + + /* Check the DLME regions arguments. */ + if ((dlme_start % DRTM_PAGE_SIZE) != 0) { + ERROR("DRTM: argument DLME region is not " + DRTM_PAGE_SIZE_STR "-aligned\n"); + return INVALID_PARAMETERS; + } + + if (!(dlme_start < dlme_end && + dlme_start <= dlme_img_start && dlme_img_start < dlme_img_end && + dlme_start <= dlme_data_start && dlme_data_start < dlme_data_end)) { + ERROR("DRTM: argument DLME region is discontiguous\n"); + return INVALID_PARAMETERS; + } + + if (dlme_img_start < dlme_data_end && dlme_data_start < dlme_img_end) { + ERROR("DRTM: argument DLME regions overlap\n"); + return INVALID_PARAMETERS; + } + + /* Check the DLME image region arguments. */ + if ((dlme_img_start % DRTM_PAGE_SIZE) != 0) { + ERROR("DRTM: argument DLME image region is not " + DRTM_PAGE_SIZE_STR "-aligned\n"); + return INVALID_PARAMETERS; + } + + if (!(dlme_img_start <= dlme_img_ep && dlme_img_ep < dlme_img_end)) { + ERROR("DRTM: DLME entry point is outside of the DLME image region\n"); + return INVALID_PARAMETERS; + } + + if ((dlme_img_ep % 4) != 0) { + ERROR("DRTM: DLME image entry point is not 4-byte-aligned\n"); + return INVALID_PARAMETERS; + } + + /* Check the DLME data region arguments. */ + if ((dlme_data_start % DRTM_PAGE_SIZE) != 0) { + ERROR("DRTM: argument DLME data region is not " + DRTM_PAGE_SIZE_STR "-aligned\n"); + return INVALID_PARAMETERS; + } + + rc = drtm_dl_prepare_dlme_data(NULL, &dlme_data_size_req); + if (rc) { + ERROR("%s: drtm_dl_prepare_dlme_data() failed unexpectedly rc=%d\n", + __func__, rc); + panic(); + } + if (dlme_data_end - dlme_data_start < dlme_data_size_req) { + ERROR("DRTM: argument DLME data region is short of %lu bytes\n", + dlme_data_size_req - (size_t)(dlme_data_end - dlme_data_start)); + return INVALID_PARAMETERS; + } + + /* Check the Normal World DCE region arguments. */ + if (a->dce_nwd_paddr != 0) { + uint32_t dce_nwd_start = a->dce_nwd_paddr; + uint32_t dce_nwd_end = dce_nwd_start + a->dce_nwd_size; + + if (!(dce_nwd_start < dce_nwd_end)) { + ERROR("DRTM: argument Normal World DCE region is dicontiguous\n"); + return INVALID_PARAMETERS; + } + + if (dce_nwd_start < dlme_end && dlme_start < dce_nwd_end) { + ERROR("DRTM: argument Normal World DCE regions overlap\n"); + return INVALID_PARAMETERS; + } + } + + *a_out = *a; + return SUCCESS; +} + static uint64_t drtm_dynamic_launch(uint64_t x1, void *handle) { enum drtm_retc ret = SUCCESS; + struct_drtm_dl_args args; /* Ensure that only boot PE is powered on */ ret = drtm_dl_check_cores(); @@ -213,6 +374,11 @@ static uint64_t drtm_dynamic_launch(uint64_t x1, void *handle) SMC_RET1(handle, ret); } + ret = drtm_dl_check_args(x1, &args); + if (ret != SUCCESS) { + SMC_RET1(handle, ret); + } + SMC_RET1(handle, ret); } diff --git a/services/std_svc/drtm/drtm_main.h b/services/std_svc/drtm/drtm_main.h index b60d95458..59e56080a 100644 --- a/services/std_svc/drtm/drtm_main.h +++ b/services/std_svc/drtm/drtm_main.h @@ -9,10 +9,28 @@ #include +#include #include #include "drtm_dma_prot.h" +#define ALIGNED_UP(x, a) __extension__ ({ \ + __typeof__(a) _a = (a); \ + __typeof__(a) _one = 1; \ + assert(IS_POWER_OF_TWO(_a)); \ + ((x) + (_a - _one)) & ~(_a - _one); \ +}) + +#define ALIGNED_DOWN(x, a) __extension__ ({ \ + __typeof__(a) _a = (a); \ + __typeof__(a) _one = 1; \ + assert(IS_POWER_OF_TWO(_a)); \ + (x) & ~(_a - _one); \ +}) + +#define DRTM_PAGE_SIZE (4 * (1 << 10)) +#define DRTM_PAGE_SIZE_STR "4-KiB" + enum drtm_retc { SUCCESS = SMC_OK, NOT_SUPPORTED = SMC_UNK, @@ -31,7 +49,28 @@ typedef struct { uint64_t tcb_hash_features; } drtm_features_t; +struct __packed drtm_dl_args_v1 { + uint16_t version; /* Must be 1. */ + uint8_t __res[2]; + uint32_t features; + uint64_t dlme_paddr; + uint64_t dlme_size; + uint64_t dlme_img_off; + uint64_t dlme_img_ep_off; + uint64_t dlme_img_size; + uint64_t dlme_data_off; + uint64_t dce_nwd_paddr; + uint64_t dce_nwd_size; + drtm_dl_dma_prot_args_v1_t dma_prot_args; +} __aligned(__alignof(uint16_t /* First member's type, `uint16_t version' */)); + drtm_memory_region_descriptor_table_t *drtm_build_address_map(void); uint64_t drtm_get_address_map_size(void); +/* + * Version-independent type. May be used to avoid excessive line of code + * changes when migrating to new struct versions. + */ +typedef struct drtm_dl_args_v1 struct_drtm_dl_args; + #endif /* DRTM_MAIN_H */