1*91f16700Schasinglulu /* 2*91f16700Schasinglulu * Copyright (c) 2022 Arm Limited. All rights reserved. 3*91f16700Schasinglulu * 4*91f16700Schasinglulu * SPDX-License-Identifier: BSD-3-Clause 5*91f16700Schasinglulu * 6*91f16700Schasinglulu * DRTM DMA protection. 7*91f16700Schasinglulu * 8*91f16700Schasinglulu * Authors: 9*91f16700Schasinglulu * Lucian Paul-Trifu <lucian.paultrifu@gmail.com> 10*91f16700Schasinglulu * 11*91f16700Schasinglulu */ 12*91f16700Schasinglulu 13*91f16700Schasinglulu #include <stdint.h> 14*91f16700Schasinglulu #include <string.h> 15*91f16700Schasinglulu 16*91f16700Schasinglulu #include <common/debug.h> 17*91f16700Schasinglulu #include <drivers/arm/smmu_v3.h> 18*91f16700Schasinglulu #include "drtm_dma_prot.h" 19*91f16700Schasinglulu #include "drtm_main.h" 20*91f16700Schasinglulu #include "drtm_remediation.h" 21*91f16700Schasinglulu #include <plat/common/platform.h> 22*91f16700Schasinglulu #include <smccc_helpers.h> 23*91f16700Schasinglulu 24*91f16700Schasinglulu /* 25*91f16700Schasinglulu * ________________________ LAUNCH success ________________________ 26*91f16700Schasinglulu * | Initial | -------------------> | Prot engaged | 27*91f16700Schasinglulu * |````````````````````````| |````````````````````````| 28*91f16700Schasinglulu * | request.type == NONE | | request.type != NONE | 29*91f16700Schasinglulu * | | <------------------- | | 30*91f16700Schasinglulu * `________________________' UNPROTECT_MEM `________________________' 31*91f16700Schasinglulu * 32*91f16700Schasinglulu * Transitions that are not shown correspond to ABI calls that do not change 33*91f16700Schasinglulu * state and result in an error being returned to the caller. 34*91f16700Schasinglulu */ 35*91f16700Schasinglulu static struct dma_prot active_prot = { 36*91f16700Schasinglulu .type = PROTECT_NONE, 37*91f16700Schasinglulu }; 38*91f16700Schasinglulu 39*91f16700Schasinglulu /* Version-independent type. */ 40*91f16700Schasinglulu typedef struct drtm_dl_dma_prot_args_v1 struct_drtm_dl_dma_prot_args; 41*91f16700Schasinglulu 42*91f16700Schasinglulu /* 43*91f16700Schasinglulu * This function checks that platform supports complete DMA protection. 44*91f16700Schasinglulu * and returns false - if the platform supports complete DMA protection. 45*91f16700Schasinglulu * and returns true - if the platform does not support complete DMA protection. 46*91f16700Schasinglulu */ 47*91f16700Schasinglulu bool drtm_dma_prot_init(void) 48*91f16700Schasinglulu { 49*91f16700Schasinglulu bool must_init_fail = false; 50*91f16700Schasinglulu const uintptr_t *smmus; 51*91f16700Schasinglulu size_t num_smmus = 0; 52*91f16700Schasinglulu unsigned int total_smmus; 53*91f16700Schasinglulu 54*91f16700Schasinglulu /* Warns presence of non-host platforms */ 55*91f16700Schasinglulu if (plat_has_non_host_platforms()) { 56*91f16700Schasinglulu WARN("DRTM: the platform includes trusted DMA-capable devices" 57*91f16700Schasinglulu " (non-host platforms)\n"); 58*91f16700Schasinglulu } 59*91f16700Schasinglulu 60*91f16700Schasinglulu /* 61*91f16700Schasinglulu * DLME protection is uncertain on platforms with peripherals whose 62*91f16700Schasinglulu * DMA is not managed by an SMMU. DRTM doesn't work on such platforms. 63*91f16700Schasinglulu */ 64*91f16700Schasinglulu if (plat_has_unmanaged_dma_peripherals()) { 65*91f16700Schasinglulu ERROR("DRTM: this platform does not provide DMA protection\n"); 66*91f16700Schasinglulu must_init_fail = true; 67*91f16700Schasinglulu } 68*91f16700Schasinglulu 69*91f16700Schasinglulu /* 70*91f16700Schasinglulu * Check that the platform reported all SMMUs. 71*91f16700Schasinglulu * It is acceptable if the platform doesn't have any SMMUs when it 72*91f16700Schasinglulu * doesn't have any DMA-capable devices. 73*91f16700Schasinglulu */ 74*91f16700Schasinglulu total_smmus = plat_get_total_smmus(); 75*91f16700Schasinglulu plat_enumerate_smmus(&smmus, &num_smmus); 76*91f16700Schasinglulu if (num_smmus != total_smmus) { 77*91f16700Schasinglulu ERROR("DRTM: could not discover all SMMUs\n"); 78*91f16700Schasinglulu must_init_fail = true; 79*91f16700Schasinglulu } 80*91f16700Schasinglulu 81*91f16700Schasinglulu return must_init_fail; 82*91f16700Schasinglulu } 83*91f16700Schasinglulu 84*91f16700Schasinglulu /* 85*91f16700Schasinglulu * Checks that the DMA protection arguments are valid and that the given 86*91f16700Schasinglulu * protected regions are covered by DMA protection. 87*91f16700Schasinglulu */ 88*91f16700Schasinglulu enum drtm_retc drtm_dma_prot_check_args(const struct_drtm_dl_dma_prot_args *a, 89*91f16700Schasinglulu int a_dma_prot_type, 90*91f16700Schasinglulu drtm_mem_region_t p) 91*91f16700Schasinglulu { 92*91f16700Schasinglulu switch ((enum dma_prot_type)a_dma_prot_type) { 93*91f16700Schasinglulu case PROTECT_MEM_ALL: 94*91f16700Schasinglulu if (a->dma_prot_table_paddr || a->dma_prot_table_size) { 95*91f16700Schasinglulu ERROR("DRTM: invalid launch due to inconsistent" 96*91f16700Schasinglulu " DMA protection arguments\n"); 97*91f16700Schasinglulu return MEM_PROTECT_INVALID; 98*91f16700Schasinglulu } 99*91f16700Schasinglulu /* 100*91f16700Schasinglulu * Full DMA protection ought to ensure that the DLME and NWd 101*91f16700Schasinglulu * DCE regions are protected, no further checks required. 102*91f16700Schasinglulu */ 103*91f16700Schasinglulu return SUCCESS; 104*91f16700Schasinglulu 105*91f16700Schasinglulu default: 106*91f16700Schasinglulu ERROR("DRTM: invalid launch due to unsupported DMA protection type\n"); 107*91f16700Schasinglulu return MEM_PROTECT_INVALID; 108*91f16700Schasinglulu } 109*91f16700Schasinglulu } 110*91f16700Schasinglulu 111*91f16700Schasinglulu enum drtm_retc drtm_dma_prot_engage(const struct_drtm_dl_dma_prot_args *a, 112*91f16700Schasinglulu int a_dma_prot_type) 113*91f16700Schasinglulu { 114*91f16700Schasinglulu const uintptr_t *smmus; 115*91f16700Schasinglulu size_t num_smmus = 0; 116*91f16700Schasinglulu 117*91f16700Schasinglulu if (active_prot.type != PROTECT_NONE) { 118*91f16700Schasinglulu ERROR("DRTM: launch denied as previous DMA protection" 119*91f16700Schasinglulu " is still engaged\n"); 120*91f16700Schasinglulu return DENIED; 121*91f16700Schasinglulu } 122*91f16700Schasinglulu 123*91f16700Schasinglulu if (a_dma_prot_type == PROTECT_NONE) { 124*91f16700Schasinglulu return SUCCESS; 125*91f16700Schasinglulu /* Only PROTECT_MEM_ALL is supported currently. */ 126*91f16700Schasinglulu } else if (a_dma_prot_type != PROTECT_MEM_ALL) { 127*91f16700Schasinglulu ERROR("%s(): unimplemented DMA protection type\n", __func__); 128*91f16700Schasinglulu panic(); 129*91f16700Schasinglulu } 130*91f16700Schasinglulu 131*91f16700Schasinglulu /* 132*91f16700Schasinglulu * Engage SMMUs in accordance with the request we have previously received. 133*91f16700Schasinglulu * Only PROTECT_MEM_ALL is implemented currently. 134*91f16700Schasinglulu */ 135*91f16700Schasinglulu plat_enumerate_smmus(&smmus, &num_smmus); 136*91f16700Schasinglulu for (const uintptr_t *smmu = smmus; smmu < smmus+num_smmus; smmu++) { 137*91f16700Schasinglulu /* 138*91f16700Schasinglulu * TODO: Invalidate SMMU's Stage-1 and Stage-2 TLB entries. This ensures 139*91f16700Schasinglulu * that any outstanding device transactions are completed, see Section 140*91f16700Schasinglulu * 3.21.1, specification IHI_0070_C_a for an approximate reference. 141*91f16700Schasinglulu */ 142*91f16700Schasinglulu int rc = smmuv3_ns_set_abort_all(*smmu); 143*91f16700Schasinglulu if (rc != 0) { 144*91f16700Schasinglulu ERROR("DRTM: SMMU at PA 0x%lx failed to engage DMA protection" 145*91f16700Schasinglulu " rc=%d\n", *smmu, rc); 146*91f16700Schasinglulu return INTERNAL_ERROR; 147*91f16700Schasinglulu } 148*91f16700Schasinglulu } 149*91f16700Schasinglulu 150*91f16700Schasinglulu /* 151*91f16700Schasinglulu * TODO: Restrict DMA from the GIC. 152*91f16700Schasinglulu * 153*91f16700Schasinglulu * Full DMA protection may be achieved as follows: 154*91f16700Schasinglulu * 155*91f16700Schasinglulu * With a GICv3: 156*91f16700Schasinglulu * - Set GICR_CTLR.EnableLPIs to 0, for each GICR; 157*91f16700Schasinglulu * GICR_CTLR.RWP == 0 must be the case before finishing, for each GICR. 158*91f16700Schasinglulu * - Set GITS_CTLR.Enabled to 0; 159*91f16700Schasinglulu * GITS_CTLR.Quiescent == 1 must be the case before finishing. 160*91f16700Schasinglulu * 161*91f16700Schasinglulu * In addition, with a GICv4: 162*91f16700Schasinglulu * - Set GICR_VPENDBASER.Valid to 0, for each GICR; 163*91f16700Schasinglulu * GICR_CTLR.RWP == 0 must be the case before finishing, for each GICR. 164*91f16700Schasinglulu * 165*91f16700Schasinglulu * Alternatively, e.g. if some bit values cannot be changed at runtime, 166*91f16700Schasinglulu * this procedure should return an error if the LPI Pending and 167*91f16700Schasinglulu * Configuration tables overlap the regions being protected. 168*91f16700Schasinglulu */ 169*91f16700Schasinglulu 170*91f16700Schasinglulu active_prot.type = a_dma_prot_type; 171*91f16700Schasinglulu 172*91f16700Schasinglulu return SUCCESS; 173*91f16700Schasinglulu } 174*91f16700Schasinglulu 175*91f16700Schasinglulu /* 176*91f16700Schasinglulu * Undo what has previously been done in drtm_dma_prot_engage(), or enter 177*91f16700Schasinglulu * remediation if it is not possible. 178*91f16700Schasinglulu */ 179*91f16700Schasinglulu enum drtm_retc drtm_dma_prot_disengage(void) 180*91f16700Schasinglulu { 181*91f16700Schasinglulu const uintptr_t *smmus; 182*91f16700Schasinglulu size_t num_smmus = 0; 183*91f16700Schasinglulu const char *err_str = "cannot undo PROTECT_MEM_ALL SMMU config"; 184*91f16700Schasinglulu 185*91f16700Schasinglulu if (active_prot.type == PROTECT_NONE) { 186*91f16700Schasinglulu return SUCCESS; 187*91f16700Schasinglulu /* Only PROTECT_MEM_ALL is supported currently. */ 188*91f16700Schasinglulu } else if (active_prot.type != PROTECT_MEM_ALL) { 189*91f16700Schasinglulu ERROR("%s(): unimplemented DMA protection type\n", __func__); 190*91f16700Schasinglulu panic(); 191*91f16700Schasinglulu } 192*91f16700Schasinglulu 193*91f16700Schasinglulu /* 194*91f16700Schasinglulu * For PROTECT_MEM_ALL, undo the SMMU configuration for "abort all" mode 195*91f16700Schasinglulu * done during engage(). 196*91f16700Schasinglulu */ 197*91f16700Schasinglulu /* Simply enter remediation for now. */ 198*91f16700Schasinglulu (void)smmus; 199*91f16700Schasinglulu (void)num_smmus; 200*91f16700Schasinglulu drtm_enter_remediation(1ULL, err_str); 201*91f16700Schasinglulu 202*91f16700Schasinglulu /* TODO: Undo GIC DMA restrictions. */ 203*91f16700Schasinglulu 204*91f16700Schasinglulu active_prot.type = PROTECT_NONE; 205*91f16700Schasinglulu 206*91f16700Schasinglulu return SUCCESS; 207*91f16700Schasinglulu } 208*91f16700Schasinglulu 209*91f16700Schasinglulu uint64_t drtm_unprotect_mem(void *ctx) 210*91f16700Schasinglulu { 211*91f16700Schasinglulu enum drtm_retc ret; 212*91f16700Schasinglulu 213*91f16700Schasinglulu switch (active_prot.type) { 214*91f16700Schasinglulu case PROTECT_NONE: 215*91f16700Schasinglulu ERROR("DRTM: invalid UNPROTECT_MEM, no DMA protection has" 216*91f16700Schasinglulu " previously been engaged\n"); 217*91f16700Schasinglulu ret = DENIED; 218*91f16700Schasinglulu break; 219*91f16700Schasinglulu 220*91f16700Schasinglulu case PROTECT_MEM_ALL: 221*91f16700Schasinglulu /* 222*91f16700Schasinglulu * UNPROTECT_MEM is a no-op for PROTECT_MEM_ALL: DRTM must not touch 223*91f16700Schasinglulu * the NS SMMU as it is expected that the DLME has configured it. 224*91f16700Schasinglulu */ 225*91f16700Schasinglulu active_prot.type = PROTECT_NONE; 226*91f16700Schasinglulu 227*91f16700Schasinglulu ret = SUCCESS; 228*91f16700Schasinglulu break; 229*91f16700Schasinglulu 230*91f16700Schasinglulu default: 231*91f16700Schasinglulu ret = drtm_dma_prot_disengage(); 232*91f16700Schasinglulu break; 233*91f16700Schasinglulu } 234*91f16700Schasinglulu 235*91f16700Schasinglulu SMC_RET1(ctx, ret); 236*91f16700Schasinglulu } 237*91f16700Schasinglulu 238*91f16700Schasinglulu void drtm_dma_prot_serialise_table(uint8_t *dst, size_t *size_out) 239*91f16700Schasinglulu { 240*91f16700Schasinglulu if (active_prot.type == PROTECT_NONE) { 241*91f16700Schasinglulu return; 242*91f16700Schasinglulu } else if (active_prot.type != PROTECT_MEM_ALL) { 243*91f16700Schasinglulu ERROR("%s(): unimplemented DMA protection type\n", __func__); 244*91f16700Schasinglulu panic(); 245*91f16700Schasinglulu } 246*91f16700Schasinglulu 247*91f16700Schasinglulu struct __packed descr_table_1 { 248*91f16700Schasinglulu drtm_memory_region_descriptor_table_t header; 249*91f16700Schasinglulu drtm_mem_region_t regions[1]; 250*91f16700Schasinglulu } prot_table = { 251*91f16700Schasinglulu .header = { 252*91f16700Schasinglulu .revision = 1, 253*91f16700Schasinglulu .num_regions = sizeof(((struct descr_table_1 *)NULL)->regions) / 254*91f16700Schasinglulu sizeof(((struct descr_table_1 *)NULL)->regions[0]) 255*91f16700Schasinglulu }, 256*91f16700Schasinglulu .regions = { 257*91f16700Schasinglulu {.region_address = 0, PAGES_AND_TYPE(UINT64_MAX, 0x3)}, 258*91f16700Schasinglulu } 259*91f16700Schasinglulu }; 260*91f16700Schasinglulu 261*91f16700Schasinglulu memcpy(dst, &prot_table, sizeof(prot_table)); 262*91f16700Schasinglulu *size_out = sizeof(prot_table); 263*91f16700Schasinglulu } 264