blob: 0fd2b6cd2bcd487e91e689c5a06ba5ff7da2e00d [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"
14#include "hf/ffa_internal.h"
15#include "hf/vm.h"
16
17bool plat_ffa_is_indirect_msg_supported(struct vm_locked sender_locked,
18 struct vm_locked receiver_locked)
19{
20 (void)sender_locked;
21 (void)receiver_locked;
22
23 /*
24 * Hypervisor is only for testing purposes, always allow indirect
25 * messages from VM.
26 */
27 return true;
28}
29
30bool plat_ffa_msg_send2_forward(ffa_id_t receiver_vm_id, ffa_id_t sender_vm_id,
31 struct ffa_value *ret)
32{
33 /* FFA_MSG_SEND2 is forwarded to SPMC when the receiver is an SP. */
Karl Meakin940a8d72024-10-15 14:13:30 +010034 if (vm_id_is_current_world(receiver_vm_id)) {
35 return false;
Karl Meakin9724b362024-10-15 14:35:02 +010036 }
37
Karl Meakin940a8d72024-10-15 14:13:30 +010038 /*
39 * Set the sender in arg1 to allow the SPMC to retrieve
40 * VM's TX buffer to copy in SP's RX buffer.
41 */
42 *ret = arch_other_world_call((struct ffa_value){
43 .func = FFA_MSG_SEND2_32,
44 .arg1 = sender_vm_id << 16,
45 });
46
47 if (ffa_func_id(*ret) != FFA_SUCCESS_32) {
48 dlog_verbose(
49 "Failed forwarding FFA_MSG_SEND2_32 to the "
50 "SPMC, got error %s (%d).\n",
51 ffa_error_name(ffa_error_code(*ret)),
52 ffa_error_code(*ret));
53 }
54
55 return true;
Karl Meakin9724b362024-10-15 14:35:02 +010056}
57
58/**
59 * Checks whether the vCPU's attempt to wait for a message has already been
60 * interrupted or whether it is allowed to block.
61 */
62static bool plat_ffa_msg_recv_block_interrupted(
63 struct vcpu_locked current_locked)
64{
65 bool interrupted;
66
67 /*
68 * Don't block if there are enabled and pending interrupts, to match
69 * behaviour of wait_for_interrupt.
70 */
71 interrupted = (vcpu_interrupt_count_get(current_locked) > 0);
72
73 return interrupted;
74}
75
76/**
77 * Returns true if there is something in the return code, either a v1.0
78 * FFA_MSG_SEND, or an FFA_ERROR.
79 */
80static bool plat_ffa_return_pending_messages(struct vm_locked vm_locked,
81 struct ffa_value *ret)
82{
83 /* Return pending messages without blocking. */
84 if (vm_locked.vm->mailbox.state == MAILBOX_STATE_FULL) {
85 *ret = ffa_msg_recv_return(vm_locked.vm);
86 if (ret->func == FFA_MSG_SEND_32) {
87 vm_locked.vm->mailbox.state = MAILBOX_STATE_EMPTY;
88 }
89 return true;
90 }
91
92 return false;
93}
94
95/**
96 * Receives a message from the mailbox. If one isn't available, this function
97 * can optionally block the caller until one becomes available.
98 *
99 * No new messages can be received until the mailbox has been cleared.
100 */
101struct ffa_value plat_ffa_msg_recv(bool block,
102 struct vcpu_locked current_locked,
103 struct vcpu **next)
104{
Karl Meakin9724b362024-10-15 14:35:02 +0100105 struct vm *vm = current_locked.vcpu->vm;
106 struct vcpu *current = current_locked.vcpu;
107 struct vm_locked vm_locked;
108 struct ffa_value return_code;
109
110 /*
111 * The primary VM will receive messages as a status code from running
112 * vCPUs and must not call this function.
113 */
114 if (vm_is_primary(vm)) {
115 return ffa_error(FFA_NOT_SUPPORTED);
116 }
117
118 /*
119 * Deny if vCPU is executing in context of an FFA_MSG_SEND_DIRECT_REQ
120 * invocation.
121 */
Karl Meakin940a8d72024-10-15 14:13:30 +0100122 if (is_ffa_direct_msg_request_ongoing(current_locked)) {
Karl Meakin9724b362024-10-15 14:35:02 +0100123 return ffa_error(FFA_DENIED);
124 }
125
126 vcpu_unlock(&current_locked);
127 vm_locked = vm_lock(vm);
128 current_locked = vcpu_lock(current);
129
130 if (plat_ffa_return_pending_messages(vm_locked, &return_code)) {
131 goto out;
132 }
133
134 /* No pending message so fail if not allowed to block. */
135 if (!block) {
136 return_code = ffa_error(FFA_RETRY);
137 goto out;
138 }
139
140 /*
141 * From this point onward this call can only be interrupted or a message
142 * received. If a message is received the return value will be set at
143 * that time to FFA_SUCCESS.
144 */
145 return_code = ffa_error(FFA_INTERRUPTED);
146 if (plat_ffa_msg_recv_block_interrupted(current_locked)) {
147 goto out;
148 }
149
150 {
151 /* Switch back to primary VM to block. */
152 struct ffa_value run_return = {
153 .func = FFA_MSG_WAIT_32,
154 .arg1 = ffa_vm_vcpu(vm->id,
155 vcpu_index(current_locked.vcpu)),
156 };
157
158 *next = api_switch_to_primary(current_locked, run_return,
159 VCPU_STATE_WAITING);
160 }
161out:
162 vm_unlock(&vm_locked);
163
164 return return_code;
165}