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