|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | +/* |
| 3 | + * FF-A v1.0 proxy to filter out invalid memory-sharing SMC calls issued by |
| 4 | + * the host. FF-A is a slightly more palatable abbreviation of "Arm Firmware |
| 5 | + * Framework for Arm A-profile", which is specified by Arm in document |
| 6 | + * number DEN0077. |
| 7 | + * |
| 8 | + * Copyright (C) 2022 - Google LLC |
| 9 | + * Author: Andrew Walbran <qwandor@google.com> |
| 10 | + * |
| 11 | + * This driver hooks into the SMC trapping logic for the host and intercepts |
| 12 | + * all calls falling within the FF-A range. Each call is either: |
| 13 | + * |
| 14 | + * - Forwarded on unmodified to the SPMD at EL3 |
| 15 | + * - Rejected as "unsupported" |
| 16 | + * - Accompanied by a host stage-2 page-table check/update and reissued |
| 17 | + * |
| 18 | + * Consequently, any attempts by the host to make guest memory pages |
| 19 | + * accessible to the secure world using FF-A will be detected either here |
| 20 | + * (in the case that the memory is already owned by the guest) or during |
| 21 | + * donation to the guest (in the case that the memory was previously shared |
| 22 | + * with the secure world). |
| 23 | + * |
| 24 | + * To allow the rolling-back of page-table updates and FF-A calls in the |
| 25 | + * event of failure, operations involving the RXTX buffers are locked for |
| 26 | + * the duration and are therefore serialised. |
| 27 | + */ |
| 28 | + |
| 29 | +#include <linux/arm-smccc.h> |
| 30 | +#include <linux/arm_ffa.h> |
| 31 | +#include <nvhe/ffa.h> |
| 32 | +#include <nvhe/trap_handler.h> |
| 33 | + |
| 34 | +static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno) |
| 35 | +{ |
| 36 | + *res = (struct arm_smccc_res) { |
| 37 | + .a0 = FFA_ERROR, |
| 38 | + .a2 = ffa_errno, |
| 39 | + }; |
| 40 | +} |
| 41 | + |
| 42 | +static void ffa_set_retval(struct kvm_cpu_context *ctxt, |
| 43 | + struct arm_smccc_res *res) |
| 44 | +{ |
| 45 | + cpu_reg(ctxt, 0) = res->a0; |
| 46 | + cpu_reg(ctxt, 1) = res->a1; |
| 47 | + cpu_reg(ctxt, 2) = res->a2; |
| 48 | + cpu_reg(ctxt, 3) = res->a3; |
| 49 | +} |
| 50 | + |
| 51 | +static bool is_ffa_call(u64 func_id) |
| 52 | +{ |
| 53 | + return ARM_SMCCC_IS_FAST_CALL(func_id) && |
| 54 | + ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD && |
| 55 | + ARM_SMCCC_FUNC_NUM(func_id) >= FFA_MIN_FUNC_NUM && |
| 56 | + ARM_SMCCC_FUNC_NUM(func_id) <= FFA_MAX_FUNC_NUM; |
| 57 | +} |
| 58 | + |
| 59 | +/* |
| 60 | + * Is a given FFA function supported, either by forwarding on directly |
| 61 | + * or by handling at EL2? |
| 62 | + */ |
| 63 | +static bool ffa_call_supported(u64 func_id) |
| 64 | +{ |
| 65 | + switch (func_id) { |
| 66 | + /* Unsupported memory management calls */ |
| 67 | + case FFA_FN64_MEM_RETRIEVE_REQ: |
| 68 | + case FFA_MEM_RETRIEVE_RESP: |
| 69 | + case FFA_MEM_RELINQUISH: |
| 70 | + case FFA_MEM_OP_PAUSE: |
| 71 | + case FFA_MEM_OP_RESUME: |
| 72 | + case FFA_MEM_FRAG_RX: |
| 73 | + case FFA_FN64_MEM_DONATE: |
| 74 | + /* Indirect message passing via RX/TX buffers */ |
| 75 | + case FFA_MSG_SEND: |
| 76 | + case FFA_MSG_POLL: |
| 77 | + case FFA_MSG_WAIT: |
| 78 | + /* 32-bit variants of 64-bit calls */ |
| 79 | + case FFA_MSG_SEND_DIRECT_REQ: |
| 80 | + case FFA_MSG_SEND_DIRECT_RESP: |
| 81 | + case FFA_RXTX_MAP: |
| 82 | + case FFA_MEM_DONATE: |
| 83 | + case FFA_MEM_RETRIEVE_REQ: |
| 84 | + /* Don't advertise any features just yet */ |
| 85 | + case FFA_FEATURES: |
| 86 | + return false; |
| 87 | + } |
| 88 | + |
| 89 | + return true; |
| 90 | +} |
| 91 | + |
| 92 | +bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt) |
| 93 | +{ |
| 94 | + DECLARE_REG(u64, func_id, host_ctxt, 0); |
| 95 | + struct arm_smccc_res res; |
| 96 | + |
| 97 | + /* |
| 98 | + * There's no way we can tell what a non-standard SMC call might |
| 99 | + * be up to. Ideally, we would terminate these here and return |
| 100 | + * an error to the host, but sadly devices make use of custom |
| 101 | + * firmware calls for things like power management, debugging, |
| 102 | + * RNG access and crash reporting. |
| 103 | + * |
| 104 | + * Given that the architecture requires us to trust EL3 anyway, |
| 105 | + * we forward unrecognised calls on under the assumption that |
| 106 | + * the firmware doesn't expose a mechanism to access arbitrary |
| 107 | + * non-secure memory. Short of a per-device table of SMCs, this |
| 108 | + * is the best we can do. |
| 109 | + */ |
| 110 | + if (!is_ffa_call(func_id)) |
| 111 | + return false; |
| 112 | + |
| 113 | + if (ffa_call_supported(func_id)) |
| 114 | + return false; /* Pass through */ |
| 115 | + |
| 116 | + ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED); |
| 117 | + ffa_set_retval(host_ctxt, &res); |
| 118 | + return true; |
| 119 | +} |
0 commit comments