/* * Copyright (c) 2022 Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * * DRTM DMA protection. * * Authors: * Lucian Paul-Trifu * */ #include #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. * 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); }