summaryrefslogtreecommitdiff
path: root/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
blob: 884240b863758bb7b6a1fe62888c5b56781df96a (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
/*
 * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <cactus_def.h>
#include <cactus_platform_def.h>
#include "cactus_message_loop.h"
#include "cactus_test_cmds.h"
#include "cactus_tests.h"
#include <debug.h>
#include <ffa_helpers.h>
#include <sp_helpers.h>
#include <xlat_tables_defs.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <sync.h>

static volatile uint32_t data_abort_gpf_triggered;

static bool data_abort_gpf_handler(void)
{
	uint64_t esr_el1 = read_esr_el1();

	VERBOSE("%s count %u esr_el1 %llx elr_el1 %lx\n",
		__func__, data_abort_gpf_triggered, esr_el1,
		read_elr_el1());

	/* Expect a data abort because of a GPF. */
	if ((EC_BITS(esr_el1) == EC_DABORT_CUR_EL) &&
	    ((ISS_BITS(esr_el1) & ISS_DFSC_MASK) == DFSC_GPF_DABORT)) {
		data_abort_gpf_triggered++;
		return true;
	}

	return false;
}

/**
 * Each Cactus SP has a memory region dedicated to memory sharing tests
 * described in their partition manifest.
 * This function returns the expected base address depending on the
 * SP ID (should be the same as the manifest).
 */
static void *share_page(ffa_id_t cactus_sp_id)
{
	switch (cactus_sp_id) {
	case SP_ID(1):
		return (void *)CACTUS_SP1_MEM_SHARE_BASE;
	case SP_ID(2):
		return (void *)CACTUS_SP2_MEM_SHARE_BASE;
	case SP_ID(3):
		return (void *)CACTUS_SP3_MEM_SHARE_BASE;
	default:
		ERROR("Helper function expecting a valid Cactus SP ID!\n");
		panic();
	}
}

static void *share_page_non_secure(ffa_id_t cactus_sp_id)
{
	if (cactus_sp_id != SP_ID(3)) {
		ERROR("Helper function expecting a valid Cactus SP ID!\n");
		panic();
	}

	return (void *)CACTUS_SP3_NS_MEM_SHARE_BASE;
}

CACTUS_CMD_HANDLER(mem_send_cmd, CACTUS_MEM_SEND_CMD)
{
	struct ffa_memory_region *m;
	struct ffa_composite_memory_region *composite;
	int ret;
	unsigned int mem_attrs;
	uint32_t *ptr;
	ffa_id_t source = ffa_dir_msg_source(*args);
	ffa_id_t vm_id = ffa_dir_msg_dest(*args);
	uint32_t mem_func = cactus_req_mem_send_get_mem_func(*args);
	uint64_t handle = cactus_mem_send_get_handle(*args);
	ffa_memory_region_flags_t retrv_flags =
					 cactus_mem_send_get_retrv_flags(*args);
	uint32_t words_to_write = cactus_mem_send_words_to_write(*args);
	bool non_secure = cactus_mem_send_get_non_secure(*args);

	expect(memory_retrieve(mb, &m, handle, source, vm_id,
			       retrv_flags), true);

	composite = ffa_memory_region_get_composite(m, 0);

	VERBOSE("Address: %p; page_count: %x %x\n",
		composite->constituents[0].address,
		composite->constituents[0].page_count, PAGE_SIZE);

	/* This test is only concerned with RW permissions. */
	if (ffa_get_data_access_attr(
			m->receivers[0].receiver_permissions.permissions) !=
		FFA_DATA_ACCESS_RW) {
		ERROR("Permissions not expected!\n");
		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
	}

	mem_attrs = MT_RW_DATA | MT_EXECUTE_NEVER;

	if (non_secure) {
		mem_attrs |= MT_NS;
	}

	ret = mmap_add_dynamic_region(
			(uint64_t)composite->constituents[0].address,
			(uint64_t)composite->constituents[0].address,
			composite->constituents[0].page_count * PAGE_SIZE,
			mem_attrs);

	if (ret != 0) {
		ERROR("Failed to map received memory region(%d)!\n", ret);
		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
	}

	VERBOSE("Memory has been mapped\n");

	ptr = (uint32_t *) composite->constituents[0].address;

	/* Check that memory has been cleared by the SPMC before using it. */
	if ((retrv_flags & FFA_MEMORY_REGION_FLAG_CLEAR) != 0U) {
		VERBOSE("Check if memory has been cleared!\n");
		for (uint32_t i = 0; i < words_to_write; i++) {
			if (ptr[i] != 0) {
				/*
				 * If it hasn't been cleared, shouldn't be used.
				 */
				ERROR("Memory should have been cleared!\n");
				return cactus_error_resp(
					vm_id, source, CACTUS_ERROR_TEST);
			}
		}
	}

	data_abort_gpf_triggered = 0;
	register_custom_sync_exception_handler(data_abort_gpf_handler);

	/* Write mem_func to retrieved memory region for validation purposes. */
	VERBOSE("Writing: %x\n", mem_func);
	for (unsigned int i = 0U; i < words_to_write; i++) {
		ptr[i] = mem_func;
	}

	unregister_custom_sync_exception_handler();

	/*
	 * A FFA_MEM_DONATE changes the ownership of the page, as such no
	 * relinquish is needed.
	 */
	if (mem_func != FFA_MEM_DONATE_SMC32) {
		ret = mmap_remove_dynamic_region(
			(uint64_t)composite->constituents[0].address,
			composite->constituents[0].page_count * PAGE_SIZE);

		if (ret != 0) {
			ERROR("Failed to unmap received memory region(%d)!\n", ret);
			return cactus_error_resp(vm_id, source,
						 CACTUS_ERROR_TEST);
		}

		if (!memory_relinquish((struct ffa_mem_relinquish *)mb->send,
					m->handle, vm_id)) {
			return cactus_error_resp(vm_id, source,
						 CACTUS_ERROR_TEST);
		}
	}

	if (ffa_func_id(ffa_rx_release()) != FFA_SUCCESS_SMC32) {
		ERROR("Failed to release buffer!\n");
		return cactus_error_resp(vm_id, source,
					 CACTUS_ERROR_FFA_CALL);
	}

	return cactus_success_resp(vm_id,
				   source, data_abort_gpf_triggered);
}

CACTUS_CMD_HANDLER(req_mem_send_cmd, CACTUS_REQ_MEM_SEND_CMD)
{
	smc_ret_values ffa_ret;
	uint32_t mem_func = cactus_req_mem_send_get_mem_func(*args);
	ffa_id_t receiver = cactus_req_mem_send_get_receiver(*args);
	ffa_memory_handle_t handle;
	ffa_id_t vm_id = ffa_dir_msg_dest(*args);
	ffa_id_t source = ffa_dir_msg_source(*args);
	bool non_secure = cactus_req_mem_send_get_non_secure(*args);
	void *share_page_addr =
		non_secure ? share_page_non_secure(vm_id) : share_page(vm_id);
	unsigned int mem_attrs;
	int ret;

	VERBOSE("%x requested to send memory to %x (func: %x), page: %llx\n",
		source, receiver, mem_func, (uint64_t)share_page_addr);

	const struct ffa_memory_region_constituent constituents[] = {
		{share_page_addr, 1, 0}
	};

	const uint32_t constituents_count = (sizeof(constituents) /
					     sizeof(constituents[0]));

	VERBOSE("Sharing at 0x%llx\n", (uint64_t)constituents[0].address);
	mem_attrs = MT_RW_DATA;
	if (non_secure)
		mem_attrs |= MT_NS;
	ret = mmap_add_dynamic_region(
		(uint64_t)constituents[0].address,
		(uint64_t)constituents[0].address,
		constituents[0].page_count * PAGE_SIZE,
		mem_attrs);

	if (ret != 0) {
		ERROR("Failed map share memory before sending (%d)!\n",
		      ret);
		return cactus_error_resp(vm_id, source,
					 CACTUS_ERROR_TEST);
	}

	handle = memory_init_and_send(
		(struct ffa_memory_region *)mb->send, PAGE_SIZE,
		vm_id, receiver, constituents,
		constituents_count, mem_func, &ffa_ret);

	/*
	 * If returned an invalid handle, we should break the test.
	 */
	if (handle == FFA_MEMORY_HANDLE_INVALID) {
		VERBOSE("Received an invalid FF-A memory Handle!\n");
		return cactus_error_resp(vm_id, source,
					 ffa_error_code(ffa_ret));
	}

	ffa_ret = cactus_mem_send_cmd(vm_id, receiver, mem_func, handle,
				      0, non_secure, 10);

	if (!is_ffa_direct_response(ffa_ret)) {
		return cactus_error_resp(vm_id, source, CACTUS_ERROR_FFA_CALL);
	}

	/* If anything went bad on the receiver's end. */
	if (cactus_get_response(ffa_ret) == CACTUS_ERROR) {
		ERROR("Received error from receiver!\n");
		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
	}

	if (mem_func != FFA_MEM_DONATE_SMC32) {
		/*
		 * Do a memory reclaim only if the mem_func regards to memory
		 * share or lend operations, as with a donate the owner is
		 * permanently given up access to the memory region.
		 */
		ffa_ret = ffa_mem_reclaim(handle, 0);
		if (is_ffa_call_error(ffa_ret)) {
			return cactus_error_resp(vm_id, source,
						 CACTUS_ERROR_TEST);
		}

		/**
		 * Read Content that has been written to memory to validate
		 * access to memory segment has been reestablished, and receiver
		 * made use of memory region.
		 */
		#if (LOG_LEVEL >= LOG_LEVEL_VERBOSE)
			uint32_t *ptr = (uint32_t *)constituents->address;

			VERBOSE("Memory contents after receiver SP's use:\n");
			for (unsigned int i = 0U; i < 5U; i++) {
				VERBOSE("      %u: %x\n", i, ptr[i]);
			}
		#endif
	}

	/* Always unmap the sent memory region, will be remapped by another
	 * test if needed. */
	ret = mmap_remove_dynamic_region(
		(uint64_t)constituents[0].address,
		constituents[0].page_count * PAGE_SIZE);

	if (ret != 0) {
		ERROR("Failed to unmap share memory region (%d)!\n", ret);
		return cactus_error_resp(vm_id, source,
					 CACTUS_ERROR_TEST);
	}

	return cactus_success_resp(vm_id, source, 0);
}