blob: 3e433496795eed20b68349f633c47968b7a3ac85 [file] [log] [blame]
Karl Meakin9724b362024-10-15 14:35:02 +01001/*
2 * Copyright 2024 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
Karl Meakin902af082024-11-28 14:58:38 +00009#include "hf/ffa/indirect_messaging.h"
Karl Meakin9724b362024-10-15 14:35:02 +010010
11#include "hf/arch/other_world.h"
12
13#include "hf/api.h"
Karl Meakind8c9fff2025-02-10 12:26:48 +000014#include "hf/ffa/init.h"
Karl Meakin9724b362024-10-15 14:35:02 +010015#include "hf/ffa_internal.h"
16#include "hf/vm.h"
17
Karl Meakin117c8082024-12-04 16:03:28 +000018bool ffa_indirect_msg_is_supported(struct vm_locked sender_locked,
19 struct vm_locked receiver_locked)
Karl Meakin9724b362024-10-15 14:35:02 +010020{
21 (void)sender_locked;
22 (void)receiver_locked;
23
24 /*
25 * Hypervisor is only for testing purposes, always allow indirect
26 * messages from VM.
27 */
28 return true;
29}
30
Karl Meakin117c8082024-12-04 16:03:28 +000031bool ffa_indirect_msg_send2_forward(ffa_id_t receiver_vm_id,
32 ffa_id_t sender_vm_id,
33 struct ffa_value *ret)
Karl Meakin9724b362024-10-15 14:35:02 +010034{
35 /* FFA_MSG_SEND2 is forwarded to SPMC when the receiver is an SP. */
Karl Meakin940a8d72024-10-15 14:13:30 +010036 if (vm_id_is_current_world(receiver_vm_id)) {
37 return false;
Karl Meakin9724b362024-10-15 14:35:02 +010038 }
39
Karl Meakin940a8d72024-10-15 14:13:30 +010040 /*
41 * Set the sender in arg1 to allow the SPMC to retrieve
42 * VM's TX buffer to copy in SP's RX buffer.
43 */
44 *ret = arch_other_world_call((struct ffa_value){
45 .func = FFA_MSG_SEND2_32,
46 .arg1 = sender_vm_id << 16,
47 });
48
49 if (ffa_func_id(*ret) != FFA_SUCCESS_32) {
50 dlog_verbose(
51 "Failed forwarding FFA_MSG_SEND2_32 to the "
52 "SPMC, got error %s (%d).\n",
53 ffa_error_name(ffa_error_code(*ret)),
54 ffa_error_code(*ret));
55 }
56
57 return true;
Karl Meakin9724b362024-10-15 14:35:02 +010058}
59
60/**
61 * Checks whether the vCPU's attempt to wait for a message has already been
62 * interrupted or whether it is allowed to block.
63 */
Karl Meakin117c8082024-12-04 16:03:28 +000064static bool ffa_indirect_msg_recv_block_interrupted(
Karl Meakin9724b362024-10-15 14:35:02 +010065 struct vcpu_locked current_locked)
66{
67 bool interrupted;
68
69 /*
70 * Don't block if there are enabled and pending interrupts, to match
71 * behaviour of wait_for_interrupt.
72 */
Daniel Boulbyd21e9b32025-02-13 15:53:21 +000073 interrupted = (vcpu_virt_interrupt_count_get(current_locked) > 0);
Karl Meakin9724b362024-10-15 14:35:02 +010074
75 return interrupted;
76}
77
78/**
79 * Returns true if there is something in the return code, either a v1.0
80 * FFA_MSG_SEND, or an FFA_ERROR.
81 */
Karl Meakinca38ef92025-02-13 14:20:23 +000082static bool ffa_indirect_msg_return_pending_messages(struct vm_locked vm_locked,
83 struct ffa_value *ret)
Karl Meakin9724b362024-10-15 14:35:02 +010084{
85 /* Return pending messages without blocking. */
86 if (vm_locked.vm->mailbox.state == MAILBOX_STATE_FULL) {
87 *ret = ffa_msg_recv_return(vm_locked.vm);
88 if (ret->func == FFA_MSG_SEND_32) {
89 vm_locked.vm->mailbox.state = MAILBOX_STATE_EMPTY;
90 }
91 return true;
92 }
93
94 return false;
95}
96
97/**
98 * Receives a message from the mailbox. If one isn't available, this function
99 * can optionally block the caller until one becomes available.
100 *
101 * No new messages can be received until the mailbox has been cleared.
102 */
Karl Meakin117c8082024-12-04 16:03:28 +0000103struct ffa_value ffa_indirect_msg_recv(bool block,
104 struct vcpu_locked current_locked,
105 struct vcpu **next)
Karl Meakin9724b362024-10-15 14:35:02 +0100106{
Karl Meakin9724b362024-10-15 14:35:02 +0100107 struct vm *vm = current_locked.vcpu->vm;
108 struct vcpu *current = current_locked.vcpu;
109 struct vm_locked vm_locked;
110 struct ffa_value return_code;
111
112 /*
113 * The primary VM will receive messages as a status code from running
114 * vCPUs and must not call this function.
115 */
116 if (vm_is_primary(vm)) {
117 return ffa_error(FFA_NOT_SUPPORTED);
118 }
119
120 /*
121 * Deny if vCPU is executing in context of an FFA_MSG_SEND_DIRECT_REQ
122 * invocation.
123 */
Karl Meakin940a8d72024-10-15 14:13:30 +0100124 if (is_ffa_direct_msg_request_ongoing(current_locked)) {
Karl Meakin9724b362024-10-15 14:35:02 +0100125 return ffa_error(FFA_DENIED);
126 }
127
128 vcpu_unlock(&current_locked);
129 vm_locked = vm_lock(vm);
130 current_locked = vcpu_lock(current);
131
Karl Meakinca38ef92025-02-13 14:20:23 +0000132 if (ffa_indirect_msg_return_pending_messages(vm_locked, &return_code)) {
Karl Meakin9724b362024-10-15 14:35:02 +0100133 goto out;
134 }
135
136 /* No pending message so fail if not allowed to block. */
137 if (!block) {
138 return_code = ffa_error(FFA_RETRY);
139 goto out;
140 }
141
142 /*
143 * From this point onward this call can only be interrupted or a message
144 * received. If a message is received the return value will be set at
145 * that time to FFA_SUCCESS.
146 */
147 return_code = ffa_error(FFA_INTERRUPTED);
Karl Meakin117c8082024-12-04 16:03:28 +0000148 if (ffa_indirect_msg_recv_block_interrupted(current_locked)) {
Karl Meakin9724b362024-10-15 14:35:02 +0100149 goto out;
150 }
151
152 {
153 /* Switch back to primary VM to block. */
154 struct ffa_value run_return = {
155 .func = FFA_MSG_WAIT_32,
156 .arg1 = ffa_vm_vcpu(vm->id,
157 vcpu_index(current_locked.vcpu)),
158 };
159
160 *next = api_switch_to_primary(current_locked, run_return,
161 VCPU_STATE_WAITING);
162 }
163out:
164 vm_unlock(&vm_locked);
165
166 return return_code;
167}
Karl Meakin2ae1abb2025-01-31 13:09:35 +0000168
169/**
170 * Notifies the `to` VM about the message currently in its mailbox, possibly
171 * with the help of the primary VM.
172 */
173static struct ffa_value deliver_msg(struct vm_locked to, ffa_id_t from_id,
174 struct vcpu_locked current_locked,
175 struct vcpu **next)
176{
177 struct ffa_value ret = (struct ffa_value){.func = FFA_SUCCESS_32};
178 struct ffa_value primary_ret = {
179 .func = FFA_MSG_SEND_32,
180 .arg1 = ((uint32_t)from_id << 16) | to.vm->id,
181 };
182
183 /* Messages for the primary VM are delivered directly. */
184 if (vm_is_primary(to.vm)) {
185 /*
186 * Only tell the primary VM the size and other details if the
187 * message is for it, to avoid leaking data about messages for
188 * other VMs.
189 */
190 primary_ret = ffa_msg_recv_return(to.vm);
191
192 *next = api_switch_to_primary(current_locked, primary_ret,
193 VCPU_STATE_BLOCKED);
194 return ret;
195 }
196
197 to.vm->mailbox.state = MAILBOX_STATE_FULL;
198
199 /* Messages for the TEE are sent on via the dispatcher. */
200 if (to.vm->id == HF_TEE_VM_ID) {
201 struct ffa_value call = ffa_msg_recv_return(to.vm);
202
203 ret = arch_other_world_call(call);
204 /*
205 * After the call to the TEE completes it must have finished
206 * reading its RX buffer, so it is ready for another message.
207 */
208 to.vm->mailbox.state = MAILBOX_STATE_EMPTY;
209 /*
210 * Don't return to the primary VM in this case, as the TEE is
211 * not (yet) scheduled via FF-A.
212 */
213 return ret;
214 }
215
216 /* Return to the primary VM directly or with a switch. */
217 if (from_id != HF_PRIMARY_VM_ID) {
218 *next = api_switch_to_primary(current_locked, primary_ret,
219 VCPU_STATE_BLOCKED);
220 }
221
222 return ret;
223}
224
225/*
226 * Copies data from the sender's send buffer to the recipient's receive buffer
227 * and notifies the recipient.
228 *
229 * If the recipient's receive buffer is busy, it can optionally register the
230 * caller to be notified when the recipient's receive buffer becomes available.
231 */
232struct ffa_value ffa_indirect_msg_send(ffa_id_t sender_vm_id,
233 ffa_id_t receiver_vm_id, uint32_t size,
234 struct vcpu *current, struct vcpu **next)
235{
236 struct vm *from = current->vm;
237 struct vm *to;
238 struct vm_locked to_locked;
239 const void *from_msg;
240 struct ffa_value ret;
241 struct vcpu_locked current_locked;
242 bool is_direct_request_ongoing;
243
244 /* Ensure sender VM ID corresponds to the current VM. */
245 if (sender_vm_id != from->id) {
246 return ffa_error(FFA_INVALID_PARAMETERS);
247 }
248
249 /* Disallow reflexive requests as this suggests an error in the VM. */
250 if (receiver_vm_id == from->id) {
251 return ffa_error(FFA_INVALID_PARAMETERS);
252 }
253
254 /* Limit the size of transfer. */
255 if (size > FFA_MSG_PAYLOAD_MAX) {
256 return ffa_error(FFA_INVALID_PARAMETERS);
257 }
258
259 /* Ensure the receiver VM exists. */
260 to = vm_find(receiver_vm_id);
261 if (to == NULL) {
262 return ffa_error(FFA_INVALID_PARAMETERS);
263 }
264
265 /*
266 * Deny if vCPU is executing in context of an FFA_MSG_SEND_DIRECT_REQ
267 * invocation.
268 */
269 current_locked = vcpu_lock(current);
270 is_direct_request_ongoing =
271 is_ffa_direct_msg_request_ongoing(current_locked);
272
273 if (is_direct_request_ongoing) {
274 ret = ffa_error(FFA_DENIED);
275 goto out_current;
276 }
277
278 /*
279 * Check that the sender has configured its send buffer. If the tx
280 * mailbox at from_msg is configured (i.e. from_msg != NULL) then it can
281 * be safely accessed after releasing the lock since the tx mailbox
282 * address can only be configured once.
283 * A VM's lock must be acquired before any of its vCPU's lock. Hence,
284 * unlock current vCPU and acquire it immediately after its VM's lock.
285 */
286 vcpu_unlock(&current_locked);
287 sl_lock(&from->lock);
288 current_locked = vcpu_lock(current);
289 from_msg = from->mailbox.send;
290 sl_unlock(&from->lock);
291
292 if (from_msg == NULL) {
293 ret = ffa_error(FFA_INVALID_PARAMETERS);
294 goto out_current;
295 }
296
297 to_locked = vm_lock(to);
298
299 if (vm_is_mailbox_busy(to_locked)) {
300 ret = ffa_error(FFA_BUSY);
301 goto out;
302 }
303
304 /* Copy data. */
305 memcpy_s(to->mailbox.recv, FFA_MSG_PAYLOAD_MAX, from_msg, size);
306 to->mailbox.recv_size = size;
307 to->mailbox.recv_sender = sender_vm_id;
308 to->mailbox.recv_func = FFA_MSG_SEND_32;
309 to->mailbox.state = MAILBOX_STATE_FULL;
310 ret = deliver_msg(to_locked, sender_vm_id, current_locked, next);
311
312out:
313 vm_unlock(&to_locked);
314
315out_current:
316 vcpu_unlock(&current_locked);
317
318 return ret;
319}