Browse Source

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 <Manish.Badarkhe@arm.com>
pull/1988/merge
Manish V Badarkhe 2 years ago
parent
commit
2b13a98599
  1. 204
      services/std_svc/drtm/drtm_dma_prot.c
  2. 28
      services/std_svc/drtm/drtm_dma_prot.h
  3. 14
      services/std_svc/drtm/drtm_main.c
  4. 1
      services/std_svc/drtm/drtm_main.h

204
services/std_svc/drtm/drtm_dma_prot.c

@ -14,9 +14,30 @@
#include <string.h>
#include <common/debug.h>
#include <drivers/arm/smmu_v3.h>
#include "drtm_dma_prot.h"
#include "drtm_main.h"
#include "drtm_remediation.h"
#include <plat/common/platform.h>
#include <smccc_helpers.h>
/*
* ________________________ 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);
}

28
services/std_svc/drtm/drtm_dma_prot.h

@ -8,15 +8,43 @@
#define DRTM_DMA_PROT_H
#include <stdint.h>
#include <plat/common/platform.h>
#include <services/drtm_svc.h>
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 */

14
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:

1
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))

Loading…
Cancel
Save