xref: /arm-trusted-firmware/services/std_svc/drtm/drtm_dma_prot.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
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