]> git.baikalelectronics.ru Git - arm-tf.git/commitdiff
feat(drtm): add a few DRTM DMA protection APIs
authorManish V Badarkhe <Manish.Badarkhe@arm.com>
Tue, 21 Jun 2022 17:08:50 +0000 (18:08 +0100)
committerManish V Badarkhe <Manish.Badarkhe@arm.com>
Wed, 5 Oct 2022 14:25:28 +0000 (15:25 +0100)
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>
services/std_svc/drtm/drtm_dma_prot.c
services/std_svc/drtm/drtm_dma_prot.h
services/std_svc/drtm/drtm_main.c
services/std_svc/drtm/drtm_main.h

index 9d014a0c3272c831b597b8e500004a283b101421..48317fdc4cf96d2d26e870b6c83b996f7143293b 100644 (file)
 #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);
+}
index e9b1fe94ddd129ffc1aa92a6c7a0cb4844cf1f4b..79dc9cb49e765038fd0cda022a795beebbb516ef 100644 (file)
@@ -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 */
index 9878887e555f4d42cd409f3204b62ba9d325001e..53aafeb4bd6a4b29a3f972fcb4a13fbadffe49bf 100644 (file)
@@ -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:
index cccc14f968875622926fed570cb1096e9d69329a..9bf5400b88f2fd5e80a4da88045a8a4166dd196b 100644 (file)
@@ -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))