123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- /*
- * Copyright (c) 2022 Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * DRTM DMA protection.
- *
- * Authors:
- * Lucian Paul-Trifu <lucian.paultrifu@gmail.com>
- *
- */
- #include <stdint.h>
- #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.
- * and returns false - if the platform supports complete DMA protection.
- * and returns true - if the platform does not support complete DMA protection.
- */
- bool drtm_dma_prot_init(void)
- {
- bool must_init_fail = false;
- const uintptr_t *smmus;
- size_t num_smmus = 0;
- unsigned int total_smmus;
- /* Warns presence of non-host platforms */
- if (plat_has_non_host_platforms()) {
- WARN("DRTM: the platform includes trusted DMA-capable devices"
- " (non-host platforms)\n");
- }
- /*
- * DLME protection is uncertain on platforms with peripherals whose
- * DMA is not managed by an SMMU. DRTM doesn't work on such platforms.
- */
- if (plat_has_unmanaged_dma_peripherals()) {
- ERROR("DRTM: this platform does not provide DMA protection\n");
- must_init_fail = true;
- }
- /*
- * Check that the platform reported all SMMUs.
- * It is acceptable if the platform doesn't have any SMMUs when it
- * doesn't have any DMA-capable devices.
- */
- total_smmus = plat_get_total_smmus();
- plat_enumerate_smmus(&smmus, &num_smmus);
- if (num_smmus != total_smmus) {
- ERROR("DRTM: could not discover all SMMUs\n");
- must_init_fail = true;
- }
- 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);
- }
|