| Karl Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 1 | /* |
| 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 Meakin | 902af08 | 2024-11-28 14:58:38 +0000 | [diff] [blame^] | 9 | #include "hf/ffa/indirect_messaging.h" |
| Karl Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 10 | |
| 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 | |
| 17 | bool 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 | |
| 30 | bool 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 Meakin | 940a8d7 | 2024-10-15 14:13:30 +0100 | [diff] [blame] | 34 | if (vm_id_is_current_world(receiver_vm_id)) { |
| 35 | return false; |
| Karl Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 36 | } |
| 37 | |
| Karl Meakin | 940a8d7 | 2024-10-15 14:13:30 +0100 | [diff] [blame] | 38 | /* |
| 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 Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 56 | } |
| 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 | */ |
| 62 | static 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 | */ |
| 80 | static 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 | */ |
| 101 | struct ffa_value plat_ffa_msg_recv(bool block, |
| 102 | struct vcpu_locked current_locked, |
| 103 | struct vcpu **next) |
| 104 | { |
| Karl Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 105 | 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 Meakin | 940a8d7 | 2024-10-15 14:13:30 +0100 | [diff] [blame] | 122 | if (is_ffa_direct_msg_request_ongoing(current_locked)) { |
| Karl Meakin | 9724b36 | 2024-10-15 14:35:02 +0100 | [diff] [blame] | 123 | return ffa_error(FFA_DENIED); |
| 124 | } |
| 125 | |
| 126 | vcpu_unlock(¤t_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 | } |
| 161 | out: |
| 162 | vm_unlock(&vm_locked); |
| 163 | |
| 164 | return return_code; |
| 165 | } |