summaryrefslogtreecommitdiffstats
path: root/bl32/tsp/ffa_helpers.c
blob: 3639c221ca3a4045abc407d1001a26bbae558c4c (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
/*
 * Copyright (c) 2022, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <common/debug.h>
#include "ffa_helpers.h"
#include <services/ffa_svc.h>
#include "tsp_private.h"

/*******************************************************************************
 * Wrapper function to send a direct request.
 ******************************************************************************/
smc_args_t ffa_msg_send_direct_req(ffa_endpoint_id16_t sender,
				   ffa_endpoint_id16_t receiver,
				   uint32_t arg3,
				   uint32_t arg4,
				   uint32_t arg5,
				   uint32_t arg6,
				   uint32_t arg7)
{
	uint32_t src_dst_ids = (sender << FFA_DIRECT_MSG_SOURCE_SHIFT) |
			       (receiver << FFA_DIRECT_MSG_DESTINATION_SHIFT);


	/* Send Direct Request. */
	return smc_helper(FFA_MSG_SEND_DIRECT_REQ_SMC64, src_dst_ids,
			0, arg3, arg4, arg5, arg6, arg7);
}

/*******************************************************************************
 * Wrapper function to send a direct response.
 ******************************************************************************/
smc_args_t *ffa_msg_send_direct_resp(ffa_endpoint_id16_t sender,
				     ffa_endpoint_id16_t receiver,
				     uint32_t arg3,
				     uint32_t arg4,
				     uint32_t arg5,
				     uint32_t arg6,
				     uint32_t arg7)
{
	uint32_t src_dst_ids = (sender << FFA_DIRECT_MSG_SOURCE_SHIFT) |
			       (receiver << FFA_DIRECT_MSG_DESTINATION_SHIFT);

	return set_smc_args(FFA_MSG_SEND_DIRECT_RESP_SMC64, src_dst_ids,
			    0, arg3, arg4, arg5, arg6, arg7);
}

/*******************************************************************************
 * Memory Management Helpers.
 ******************************************************************************/

/**
 * Initialises the header of the given `ffa_mtd`, not including the
 * composite memory region offset.
 */
static void ffa_memory_region_init_header(
	struct ffa_mtd *memory_region, ffa_endpoint_id16_t sender,
	ffa_mem_attr16_t attributes, ffa_mtd_flag32_t flags,
	uint64_t handle, uint64_t tag, ffa_endpoint_id16_t *receivers,
	uint32_t receiver_count, ffa_mem_perm8_t permissions)
{
	struct ffa_emad_v1_0 *emad;

	memory_region->emad_offset = sizeof(struct ffa_mtd);
	memory_region->emad_size = sizeof(struct ffa_emad_v1_0);
	emad = (struct ffa_emad_v1_0 *)
		    ((uint8_t *) memory_region +
		     memory_region->emad_offset);
	memory_region->sender_id = sender;
	memory_region->memory_region_attributes = attributes;
	memory_region->reserved_36_39 = 0;
	memory_region->flags = flags;
	memory_region->handle = handle;
	memory_region->tag = tag;
	memory_region->reserved_40_47 = 0;
	memory_region->emad_count = receiver_count;
	for (uint32_t i = 0U; i < receiver_count; i++) {
		emad[i].mapd.endpoint_id = receivers[i];
		emad[i].mapd.memory_access_permissions = permissions;
		emad[i].mapd.flags = 0;
		emad[i].comp_mrd_offset = 0;
		emad[i].reserved_8_15 = 0;
	}
}
/**
 * Initialises the given `ffa_mtd` to be used for an
 * `FFA_MEM_RETRIEVE_REQ` by the receiver of a memory transaction.
 * TODO: Support differing attributes per receiver.
 *
 * Returns the size of the descriptor written.
 */
static uint32_t ffa_memory_retrieve_request_init(
	struct ffa_mtd *memory_region, uint64_t handle,
	ffa_endpoint_id16_t sender, ffa_endpoint_id16_t *receivers, uint32_t receiver_count,
	uint64_t tag, ffa_mtd_flag32_t flags,
	ffa_mem_perm8_t permissions,
	ffa_mem_attr16_t attributes)
{
	ffa_memory_region_init_header(memory_region, sender, attributes, flags,
				      handle, tag, receivers,
				      receiver_count, permissions);

	return sizeof(struct ffa_mtd) +
	       memory_region->emad_count * sizeof(struct ffa_emad_v1_0);
}

/* Relinquish access to memory region. */
bool ffa_mem_relinquish(void)
{
	smc_args_t ret;

	ret = smc_helper(FFA_MEM_RELINQUISH, 0, 0, 0, 0, 0, 0, 0);
	if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
		ERROR("%s failed to relinquish memory! error: (%x) %x\n",
		      __func__, ffa_func_id(ret), ffa_error_code(ret));
		return false;
	}
	return true;
}

/* Retrieve memory shared by another partition. */
smc_args_t ffa_mem_retrieve_req(uint32_t descriptor_length,
				uint32_t fragment_length)
{
	return smc_helper(FFA_MEM_RETRIEVE_REQ_SMC32,
		      descriptor_length,
		      fragment_length,
		      0, 0, 0, 0, 0);
}

/* Retrieve the next memory descriptor fragment. */
smc_args_t ffa_mem_frag_rx(uint64_t handle, uint32_t recv_length)
{
	return smc_helper(FFA_MEM_FRAG_RX,
		       FFA_MEM_HANDLE_LOW(handle),
		       FFA_MEM_HANDLE_HIGH(handle),
		       recv_length,
		       0, 0, 0, 0);
}

bool memory_retrieve(struct mailbox *mb,
			    struct ffa_mtd **retrieved,
			    uint64_t handle, ffa_endpoint_id16_t sender,
			    ffa_endpoint_id16_t *receivers, uint32_t receiver_count,
			    ffa_mtd_flag32_t flags, uint32_t *frag_length,
			    uint32_t *total_length)
{
	smc_args_t ret;
	uint32_t descriptor_size;
	struct ffa_mtd *memory_region = (struct ffa_mtd *)mb->tx_buffer;

	if (retrieved == NULL || mb == NULL) {
		ERROR("Invalid parameters!\n");
		return false;
	}

	/* Clear TX buffer. */
	memset(memory_region, 0, PAGE_SIZE);

	/* Clear local buffer. */
	memset(mem_region_buffer, 0, REGION_BUF_SIZE);

	descriptor_size = ffa_memory_retrieve_request_init(
	    memory_region, handle, sender, receivers, receiver_count, 0, flags,
	    FFA_MEM_PERM_RW | FFA_MEM_PERM_NX,
	    FFA_MEM_ATTR_NORMAL_MEMORY_CACHED_WB |
	    FFA_MEM_ATTR_INNER_SHAREABLE);

	ret = ffa_mem_retrieve_req(descriptor_size, descriptor_size);

	if (ffa_func_id(ret) == FFA_ERROR) {
		ERROR("Couldn't retrieve the memory page. Error: %x\n",
		      ffa_error_code(ret));
		return false;
	}

	/*
	 * Following total_size and fragment_size are useful to keep track
	 * of the state of transaction. When the sum of all fragment_size of all
	 * fragments is equal to total_size, the memory transaction has been
	 * completed.
	 */
	*total_length = ret._regs[1];
	*frag_length = ret._regs[2];

	/* Validate frag_length is less than total_length and mailbox size. */
	if (*frag_length == 0U || *total_length == 0U ||
	    *frag_length > *total_length || *frag_length > (mb->rxtx_page_count * PAGE_SIZE)) {
		ERROR("Invalid parameters!\n");
		return false;
	}

	/* Copy response to local buffer. */
	memcpy(mem_region_buffer, mb->rx_buffer, *frag_length);

	if (ffa_rx_release()) {
		ERROR("Failed to release buffer!\n");
		return false;
	}

	*retrieved = (struct ffa_mtd *) mem_region_buffer;

	if ((*retrieved)->emad_count > MAX_MEM_SHARE_RECIPIENTS) {
		VERBOSE("SPMC memory sharing supports max of %u receivers!\n",
			MAX_MEM_SHARE_RECIPIENTS);
		return false;
	}

	/*
	 * We are sharing memory from the normal world therefore validate the NS
	 * bit was set by the SPMC.
	 */
	if (((*retrieved)->memory_region_attributes & FFA_MEM_ATTR_NS_BIT) == 0U) {
		ERROR("SPMC has not set the NS bit! 0x%x\n",
		      (*retrieved)->memory_region_attributes);
		return false;
	}

	VERBOSE("Memory Descriptor Retrieved!\n");

	return true;
}

/* Relinquish the memory region. */
bool memory_relinquish(struct ffa_mem_relinquish_descriptor *m, uint64_t handle,
		       ffa_endpoint_id16_t id)
{
	ffa_mem_relinquish_init(m, handle, 0, id);
	return ffa_mem_relinquish();
}

/* Query SPMC that the rx buffer of the partition can be released. */
bool ffa_rx_release(void)
{
	smc_args_t ret;

	ret = smc_helper(FFA_RX_RELEASE, 0, 0, 0, 0, 0, 0, 0);
	return ret._regs[SMC_ARG0] != FFA_SUCCESS_SMC32;
}

/* Map the provided buffers with the SPMC. */
bool ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages)
{
	smc_args_t ret;

	ret = smc_helper(FFA_RXTX_MAP_SMC64, send, recv, pages, 0, 0, 0, 0);
	return ret._regs[0] != FFA_SUCCESS_SMC32;
}