summaryrefslogtreecommitdiff
path: root/services/std_svc/drtm/drtm_dma_prot.c
blob: e41f3607346c5b58b0d883054873342a9de34660 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
 * Copyright (c) 2021 Arm Limited and Contributors. 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 <services/drtm_svc_plat.h>
#include <smccc_helpers.h>

#include "drtm_dma_prot.h"
#include "drtm_remediation.h"
#include "drtm_main.h"


/* Values for DRTM_PROTECT_MEMORY */
enum dma_prot_type {
	PROTECT_NONE            = -1,
	PROTECT_MEM_ALL         = 0,
	PROTECT_MEM_REGION      = 1,
};

struct dma_prot {
	enum dma_prot_type type;
};

/*
 *  ________________________  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;


int drtm_dma_prot_init(void)
{
	bool must_init_fail = false;
	const uintptr_t *smmus;
	size_t num_smmus = 0;
	unsigned int num_smmus_total;

	/* Report presence of non-host platforms, for info only. */
	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.
	 */
	num_smmus_total = plat_get_total_num_smmus();
	plat_enumerate_smmus((const uintptr_t (*)[])&smmus, &num_smmus);
	if (num_smmus != num_smmus_total) {
		ERROR("DRTM: could not discover all SMMUs\n");
		must_init_fail = true;
	}

	/* Check any SMMUs enumerated. */
	for (const uintptr_t *smmu = smmus; smmu < smmus + num_smmus; smmu++) {
		if (*smmu == 0) {
			WARN("DRTM: SMMU reported at unusual PA 0x0\n");
		}
	}

	return (int)must_init_fail;
}

uint64_t drtm_features_dma_prot(void *ctx)
{
	SMC_RET2(ctx, 1ULL,  /* DMA protection feature is supported */
		1u  /* DMA protection support:  Complete DMA protection. */
	);
}

/*
 * Checks that the DMA protection arguments are valid and that the given
 * protected regions would be 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,
                                        struct __protected_regions 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((const uintptr_t (*)[])&smmus, &num_smmus);
	for (const uintptr_t *smmu = smmus; smmu < smmus+num_smmus; smmu++) {
		int rc;

		/*
		 * 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.
		 */

		if ((rc = smmuv3_ns_set_abort_all(*smmu))) {
			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;

	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(1, "cannot undo PROTECT_MEM_ALL SMMU configuration");

	/* 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(char *dst, size_t *size_out)
{
	if (active_prot.type == PROTECT_NONE) {
		if (size_out) {
			*size_out = 0;
		}
		return;
	} else if (active_prot.type != PROTECT_MEM_ALL) {
		ERROR("%s(): unimplemented DMA protection type\n", __func__);
		panic();
	}

	struct __packed descr_table_1 {
		struct_drtm_mem_region_descr_table header;
		struct_drtm_mem_region_descr regions[1];
	} prot_table = {
		.header = {
			.version = 1,
			.num_regions = sizeof(((struct descr_table_1 *)NULL)->regions) /
						   sizeof(((struct descr_table_1 *)NULL)->regions[0])
		},
			#define PAGES_AND_TYPE(pages, type) \
			.pages_and_type = DRTM_MEM_REGION_PAGES_AND_TYPE(pages, type)
		.regions = {
			{.paddr = 0, PAGES_AND_TYPE(UINT64_MAX, 0x3)},
		}
	};

	if (dst) {
		(void)memcpy(dst, &prot_table, sizeof(prot_table));
	}
	if (size_out) {
		*size_out = sizeof(prot_table);
	}
}