From 2b13a985994213f766ada197427f96e064f1b59b Mon Sep 17 00:00:00 2001 From: Manish V Badarkhe Date: Tue, 21 Jun 2022 18:08:50 +0100 Subject: [PATCH] feat(drtm): add a few DRTM DMA protection APIs Added DRTM DMA protections APIs, and called them during the DLME launch and DRTM SMC handling. Change-Id: I29e7238c04e2ca9f26600276c5c05bff5387789e Signed-off-by: Manish V Badarkhe --- services/std_svc/drtm/drtm_dma_prot.c | 204 +++++++++++++++++++++++++- services/std_svc/drtm/drtm_dma_prot.h | 28 ++++ services/std_svc/drtm/drtm_main.c | 14 +- services/std_svc/drtm/drtm_main.h | 1 + 4 files changed, 245 insertions(+), 2 deletions(-) diff --git a/services/std_svc/drtm/drtm_dma_prot.c b/services/std_svc/drtm/drtm_dma_prot.c index 9d014a0c3..48317fdc4 100644 --- a/services/std_svc/drtm/drtm_dma_prot.c +++ b/services/std_svc/drtm/drtm_dma_prot.c @@ -14,9 +14,30 @@ #include #include - +#include #include "drtm_dma_prot.h" +#include "drtm_main.h" +#include "drtm_remediation.h" #include +#include + +/* + * ________________________ LAUNCH success ________________________ + * | Initial | -------------------> | Prot engaged | + * |````````````````````````| |````````````````````````| + * | request.type == NONE | | request.type != NONE | + * | | <------------------- | | + * `________________________' UNPROTECT_MEM `________________________' + * + * Transitions that are not shown correspond to ABI calls that do not change + * state and result in an error being returned to the caller. + */ +static struct dma_prot active_prot = { + .type = PROTECT_NONE, +}; + +/* Version-independent type. */ +typedef struct drtm_dl_dma_prot_args_v1 struct_drtm_dl_dma_prot_args; /* * This function checks that platform supports complete DMA protection. @@ -59,3 +80,184 @@ bool drtm_dma_prot_init(void) return must_init_fail; } + +/* + * Checks that the DMA protection arguments are valid and that the given + * protected regions are covered by DMA protection. + */ +enum drtm_retc drtm_dma_prot_check_args(const struct_drtm_dl_dma_prot_args *a, + int a_dma_prot_type, + drtm_mem_region_t p) +{ + switch ((enum dma_prot_type)a_dma_prot_type) { + case PROTECT_MEM_ALL: + if (a->dma_prot_table_paddr || a->dma_prot_table_size) { + ERROR("DRTM: invalid launch due to inconsistent" + " DMA protection arguments\n"); + return MEM_PROTECT_INVALID; + } + /* + * Full DMA protection ought to ensure that the DLME and NWd + * DCE regions are protected, no further checks required. + */ + return SUCCESS; + + default: + ERROR("DRTM: invalid launch due to unsupported DMA protection type\n"); + return MEM_PROTECT_INVALID; + } +} + +enum drtm_retc drtm_dma_prot_engage(const struct_drtm_dl_dma_prot_args *a, + int a_dma_prot_type) +{ + const uintptr_t *smmus; + size_t num_smmus = 0; + + if (active_prot.type != PROTECT_NONE) { + ERROR("DRTM: launch denied as previous DMA protection" + " is still engaged\n"); + return DENIED; + } + + if (a_dma_prot_type == PROTECT_NONE) { + return SUCCESS; + /* Only PROTECT_MEM_ALL is supported currently. */ + } else if (a_dma_prot_type != PROTECT_MEM_ALL) { + ERROR("%s(): unimplemented DMA protection type\n", __func__); + panic(); + } + + /* + * Engage SMMUs in accordance with the request we have previously received. + * Only PROTECT_MEM_ALL is implemented currently. + */ + plat_enumerate_smmus(&smmus, &num_smmus); + for (const uintptr_t *smmu = smmus; smmu < smmus+num_smmus; smmu++) { + /* + * TODO: Invalidate SMMU's Stage-1 and Stage-2 TLB entries. This ensures + * that any outstanding device transactions are completed, see Section + * 3.21.1, specification IHI_0070_C_a for an approximate reference. + */ + int rc = smmuv3_ns_set_abort_all(*smmu); + if (rc != 0) { + ERROR("DRTM: SMMU at PA 0x%lx failed to engage DMA protection" + " rc=%d\n", *smmu, rc); + return INTERNAL_ERROR; + } + } + + /* + * TODO: Restrict DMA from the GIC. + * + * Full DMA protection may be achieved as follows: + * + * With a GICv3: + * - Set GICR_CTLR.EnableLPIs to 0, for each GICR; + * GICR_CTLR.RWP == 0 must be the case before finishing, for each GICR. + * - Set GITS_CTLR.Enabled to 0; + * GITS_CTLR.Quiescent == 1 must be the case before finishing. + * + * In addition, with a GICv4: + * - Set GICR_VPENDBASER.Valid to 0, for each GICR; + * GICR_CTLR.RWP == 0 must be the case before finishing, for each GICR. + * + * Alternatively, e.g. if some bit values cannot be changed at runtime, + * this procedure should return an error if the LPI Pending and + * Configuration tables overlap the regions being protected. + */ + + active_prot.type = a_dma_prot_type; + + return SUCCESS; +} + +/* + * Undo what has previously been done in drtm_dma_prot_engage(), or enter + * remediation if it is not possible. + */ +enum drtm_retc drtm_dma_prot_disengage(void) +{ + const uintptr_t *smmus; + size_t num_smmus = 0; + const char *err_str = "cannot undo PROTECT_MEM_ALL SMMU config"; + + if (active_prot.type == PROTECT_NONE) { + return SUCCESS; + /* Only PROTECT_MEM_ALL is supported currently. */ + } else if (active_prot.type != PROTECT_MEM_ALL) { + ERROR("%s(): unimplemented DMA protection type\n", __func__); + panic(); + } + + /* + * For PROTECT_MEM_ALL, undo the SMMU configuration for "abort all" mode + * done during engage(). + */ + /* Simply enter remediation for now. */ + (void)smmus; + (void)num_smmus; + drtm_enter_remediation(1ULL, err_str); + + /* TODO: Undo GIC DMA restrictions. */ + + active_prot.type = PROTECT_NONE; + + return SUCCESS; +} + +uint64_t drtm_unprotect_mem(void *ctx) +{ + enum drtm_retc ret; + + switch (active_prot.type) { + case PROTECT_NONE: + ERROR("DRTM: invalid UNPROTECT_MEM, no DMA protection has" + " previously been engaged\n"); + ret = DENIED; + break; + + case PROTECT_MEM_ALL: + /* + * UNPROTECT_MEM is a no-op for PROTECT_MEM_ALL: DRTM must not touch + * the NS SMMU as it is expected that the DLME has configured it. + */ + active_prot.type = PROTECT_NONE; + + ret = SUCCESS; + break; + + default: + ret = drtm_dma_prot_disengage(); + break; + } + + SMC_RET1(ctx, ret); +} + +void drtm_dma_prot_serialise_table(uint8_t *dst, size_t *size_out) +{ + if (active_prot.type == PROTECT_NONE) { + return; + } else if (active_prot.type != PROTECT_MEM_ALL) { + ERROR("%s(): unimplemented DMA protection type\n", __func__); + panic(); + } + + struct __packed descr_table_1 { + drtm_memory_region_descriptor_table_t header; + drtm_mem_region_t regions[1]; + } prot_table = { + .header = { + .revision = 1, + .num_regions = sizeof(((struct descr_table_1 *)NULL)->regions) / + sizeof(((struct descr_table_1 *)NULL)->regions[0]) + }, + .regions = { + {.region_address = 0, PAGES_AND_TYPE(UINT64_MAX, 0x3)}, + } + }; + + memcpy(dst, &prot_table, sizeof(prot_table)); + *size_out = sizeof(prot_table); +} diff --git a/services/std_svc/drtm/drtm_dma_prot.h b/services/std_svc/drtm/drtm_dma_prot.h index e9b1fe94d..79dc9cb49 100644 --- a/services/std_svc/drtm/drtm_dma_prot.h +++ b/services/std_svc/drtm/drtm_dma_prot.h @@ -8,15 +8,43 @@ #define DRTM_DMA_PROT_H #include +#include +#include struct __packed drtm_dl_dma_prot_args_v1 { uint64_t dma_prot_table_paddr; uint64_t dma_prot_table_size; }; +/* Values for DRTM_PROTECT_MEMORY */ +enum dma_prot_type { + PROTECT_NONE = -1, + PROTECT_MEM_ALL = 0, + PROTECT_MEM_REGION = 2, +}; + +struct dma_prot { + enum dma_prot_type type; +}; + +#define DRTM_MEM_REGION_PAGES_AND_TYPE(pages, type) \ + (((uint64_t)(pages) & (((uint64_t)1 << 52) - 1)) \ + | (((uint64_t)(type) & 0x7) << 52)) + +#define PAGES_AND_TYPE(pages, type) \ + .region_size_type = DRTM_MEM_REGION_PAGES_AND_TYPE(pages, type) + /* Opaque / encapsulated type. */ typedef struct drtm_dl_dma_prot_args_v1 drtm_dl_dma_prot_args_v1_t; bool drtm_dma_prot_init(void); +enum drtm_retc drtm_dma_prot_check_args(const drtm_dl_dma_prot_args_v1_t *a, + int a_dma_prot_type, + drtm_mem_region_t p); +enum drtm_retc drtm_dma_prot_engage(const drtm_dl_dma_prot_args_v1_t *a, + int a_dma_prot_type); +enum drtm_retc drtm_dma_prot_disengage(void); +uint64_t drtm_unprotect_mem(void *ctx); +void drtm_dma_prot_serialise_table(uint8_t *dst, size_t *size_out); #endif /* DRTM_DMA_PROT_H */ diff --git a/services/std_svc/drtm/drtm_main.c b/services/std_svc/drtm/drtm_main.c index 9878887e5..53aafeb4b 100644 --- a/services/std_svc/drtm/drtm_main.c +++ b/services/std_svc/drtm/drtm_main.c @@ -358,6 +358,7 @@ static enum drtm_retc drtm_dl_check_args(uint64_t x1, static uint64_t drtm_dynamic_launch(uint64_t x1, void *handle) { enum drtm_retc ret = SUCCESS; + enum drtm_retc dma_prot_ret; struct_drtm_dl_args args; /* Ensure that only boot PE is powered on */ @@ -380,6 +381,17 @@ static uint64_t drtm_dynamic_launch(uint64_t x1, void *handle) SMC_RET1(handle, ret); } + /* + * Engage the DMA protections. The launch cannot proceed without the DMA + * protections due to potential TOC/TOU vulnerabilities w.r.t. the DLME + * region (and to the NWd DCE region). + */ + ret = drtm_dma_prot_engage(&args.dma_prot_args, + DL_ARGS_GET_DMA_PROT_TYPE(&args)); + if (ret != SUCCESS) { + SMC_RET1(handle, ret); + } + SMC_RET1(handle, ret); } @@ -497,7 +509,7 @@ uint64_t drtm_smc_handler(uint32_t smc_fid, case ARM_DRTM_SVC_UNPROTECT_MEM: INFO("DRTM service handler: unprotect mem\n"); - SMC_RET1(handle, SMC_OK); + return drtm_unprotect_mem(handle); break; /* not reached */ case ARM_DRTM_SVC_DYNAMIC_LAUNCH: diff --git a/services/std_svc/drtm/drtm_main.h b/services/std_svc/drtm/drtm_main.h index cccc14f96..9bf5400b8 100644 --- a/services/std_svc/drtm/drtm_main.h +++ b/services/std_svc/drtm/drtm_main.h @@ -31,6 +31,7 @@ #define DRTM_PAGE_SIZE (4 * (1 << 10)) #define DRTM_PAGE_SIZE_STR "4-KiB" +#define DL_ARGS_GET_DMA_PROT_TYPE(a) (((a)->features >> 3) & 0x7U) #define DL_ARGS_GET_PCR_SCHEMA(a) (((a)->features >> 1) & 0x3U) #define DL_ARGS_GET_DLME_ENTRY_POINT(a) \ (((a)->dlme_paddr + (a)->dlme_img_off + (a)->dlme_img_ep_off)) -- 2.39.5