162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * FF-A v1.0 proxy to filter out invalid memory-sharing SMC calls issued by 462306a36Sopenharmony_ci * the host. FF-A is a slightly more palatable abbreviation of "Arm Firmware 562306a36Sopenharmony_ci * Framework for Arm A-profile", which is specified by Arm in document 662306a36Sopenharmony_ci * number DEN0077. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2022 - Google LLC 962306a36Sopenharmony_ci * Author: Andrew Walbran <qwandor@google.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This driver hooks into the SMC trapping logic for the host and intercepts 1262306a36Sopenharmony_ci * all calls falling within the FF-A range. Each call is either: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Forwarded on unmodified to the SPMD at EL3 1562306a36Sopenharmony_ci * - Rejected as "unsupported" 1662306a36Sopenharmony_ci * - Accompanied by a host stage-2 page-table check/update and reissued 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Consequently, any attempts by the host to make guest memory pages 1962306a36Sopenharmony_ci * accessible to the secure world using FF-A will be detected either here 2062306a36Sopenharmony_ci * (in the case that the memory is already owned by the guest) or during 2162306a36Sopenharmony_ci * donation to the guest (in the case that the memory was previously shared 2262306a36Sopenharmony_ci * with the secure world). 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * To allow the rolling-back of page-table updates and FF-A calls in the 2562306a36Sopenharmony_ci * event of failure, operations involving the RXTX buffers are locked for 2662306a36Sopenharmony_ci * the duration and are therefore serialised. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/arm-smccc.h> 3062306a36Sopenharmony_ci#include <linux/arm_ffa.h> 3162306a36Sopenharmony_ci#include <asm/kvm_pkvm.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <nvhe/ffa.h> 3462306a36Sopenharmony_ci#include <nvhe/mem_protect.h> 3562306a36Sopenharmony_ci#include <nvhe/memory.h> 3662306a36Sopenharmony_ci#include <nvhe/trap_handler.h> 3762306a36Sopenharmony_ci#include <nvhe/spinlock.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * "ID value 0 must be returned at the Non-secure physical FF-A instance" 4162306a36Sopenharmony_ci * We share this ID with the host. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci#define HOST_FFA_ID 0 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * A buffer to hold the maximum descriptor size we can see from the host, 4762306a36Sopenharmony_ci * which is required when the SPMD returns a fragmented FFA_MEM_RETRIEVE_RESP 4862306a36Sopenharmony_ci * when resolving the handle on the reclaim path. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistruct kvm_ffa_descriptor_buffer { 5162306a36Sopenharmony_ci void *buf; 5262306a36Sopenharmony_ci size_t len; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct kvm_ffa_descriptor_buffer ffa_desc_buf; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct kvm_ffa_buffers { 5862306a36Sopenharmony_ci hyp_spinlock_t lock; 5962306a36Sopenharmony_ci void *tx; 6062306a36Sopenharmony_ci void *rx; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Note that we don't currently lock these buffers explicitly, instead 6562306a36Sopenharmony_ci * relying on the locking of the host FFA buffers as we only have one 6662306a36Sopenharmony_ci * client. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic struct kvm_ffa_buffers hyp_buffers; 6962306a36Sopenharmony_cistatic struct kvm_ffa_buffers host_buffers; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci *res = (struct arm_smccc_res) { 7462306a36Sopenharmony_ci .a0 = FFA_ERROR, 7562306a36Sopenharmony_ci .a2 = ffa_errno, 7662306a36Sopenharmony_ci }; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void ffa_to_smccc_res_prop(struct arm_smccc_res *res, int ret, u64 prop) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci if (ret == FFA_RET_SUCCESS) { 8262306a36Sopenharmony_ci *res = (struct arm_smccc_res) { .a0 = FFA_SUCCESS, 8362306a36Sopenharmony_ci .a2 = prop }; 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci ffa_to_smccc_error(res, ret); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void ffa_to_smccc_res(struct arm_smccc_res *res, int ret) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci ffa_to_smccc_res_prop(res, ret, 0); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void ffa_set_retval(struct kvm_cpu_context *ctxt, 9562306a36Sopenharmony_ci struct arm_smccc_res *res) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci cpu_reg(ctxt, 0) = res->a0; 9862306a36Sopenharmony_ci cpu_reg(ctxt, 1) = res->a1; 9962306a36Sopenharmony_ci cpu_reg(ctxt, 2) = res->a2; 10062306a36Sopenharmony_ci cpu_reg(ctxt, 3) = res->a3; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic bool is_ffa_call(u64 func_id) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci return ARM_SMCCC_IS_FAST_CALL(func_id) && 10662306a36Sopenharmony_ci ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD && 10762306a36Sopenharmony_ci ARM_SMCCC_FUNC_NUM(func_id) >= FFA_MIN_FUNC_NUM && 10862306a36Sopenharmony_ci ARM_SMCCC_FUNC_NUM(func_id) <= FFA_MAX_FUNC_NUM; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int ffa_map_hyp_buffers(u64 ffa_page_count) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct arm_smccc_res res; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_FN64_RXTX_MAP, 11662306a36Sopenharmony_ci hyp_virt_to_phys(hyp_buffers.tx), 11762306a36Sopenharmony_ci hyp_virt_to_phys(hyp_buffers.rx), 11862306a36Sopenharmony_ci ffa_page_count, 11962306a36Sopenharmony_ci 0, 0, 0, 0, 12062306a36Sopenharmony_ci &res); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return res.a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res.a2; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int ffa_unmap_hyp_buffers(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct arm_smccc_res res; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_RXTX_UNMAP, 13062306a36Sopenharmony_ci HOST_FFA_ID, 13162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, 13262306a36Sopenharmony_ci &res); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return res.a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res.a2; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void ffa_mem_frag_tx(struct arm_smccc_res *res, u32 handle_lo, 13862306a36Sopenharmony_ci u32 handle_hi, u32 fraglen, u32 endpoint_id) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_MEM_FRAG_TX, 14162306a36Sopenharmony_ci handle_lo, handle_hi, fraglen, endpoint_id, 14262306a36Sopenharmony_ci 0, 0, 0, 14362306a36Sopenharmony_ci res); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void ffa_mem_frag_rx(struct arm_smccc_res *res, u32 handle_lo, 14762306a36Sopenharmony_ci u32 handle_hi, u32 fragoff) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_MEM_FRAG_RX, 15062306a36Sopenharmony_ci handle_lo, handle_hi, fragoff, HOST_FFA_ID, 15162306a36Sopenharmony_ci 0, 0, 0, 15262306a36Sopenharmony_ci res); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void ffa_mem_xfer(struct arm_smccc_res *res, u64 func_id, u32 len, 15662306a36Sopenharmony_ci u32 fraglen) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci arm_smccc_1_1_smc(func_id, len, fraglen, 15962306a36Sopenharmony_ci 0, 0, 0, 0, 0, 16062306a36Sopenharmony_ci res); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void ffa_mem_reclaim(struct arm_smccc_res *res, u32 handle_lo, 16462306a36Sopenharmony_ci u32 handle_hi, u32 flags) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_MEM_RECLAIM, 16762306a36Sopenharmony_ci handle_lo, handle_hi, flags, 16862306a36Sopenharmony_ci 0, 0, 0, 0, 16962306a36Sopenharmony_ci res); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void ffa_retrieve_req(struct arm_smccc_res *res, u32 len) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_FN64_MEM_RETRIEVE_REQ, 17562306a36Sopenharmony_ci len, len, 17662306a36Sopenharmony_ci 0, 0, 0, 0, 0, 17762306a36Sopenharmony_ci res); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void do_ffa_rxtx_map(struct arm_smccc_res *res, 18162306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci DECLARE_REG(phys_addr_t, tx, ctxt, 1); 18462306a36Sopenharmony_ci DECLARE_REG(phys_addr_t, rx, ctxt, 2); 18562306a36Sopenharmony_ci DECLARE_REG(u32, npages, ctxt, 3); 18662306a36Sopenharmony_ci int ret = 0; 18762306a36Sopenharmony_ci void *rx_virt, *tx_virt; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (npages != (KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) / FFA_PAGE_SIZE) { 19062306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 19162306a36Sopenharmony_ci goto out; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!PAGE_ALIGNED(tx) || !PAGE_ALIGNED(rx)) { 19562306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 19662306a36Sopenharmony_ci goto out; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci hyp_spin_lock(&host_buffers.lock); 20062306a36Sopenharmony_ci if (host_buffers.tx) { 20162306a36Sopenharmony_ci ret = FFA_RET_DENIED; 20262306a36Sopenharmony_ci goto out_unlock; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Map our hypervisor buffers into the SPMD before mapping and 20762306a36Sopenharmony_ci * pinning the host buffers in our own address space. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci ret = ffa_map_hyp_buffers(npages); 21062306a36Sopenharmony_ci if (ret) 21162306a36Sopenharmony_ci goto out_unlock; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = __pkvm_host_share_hyp(hyp_phys_to_pfn(tx)); 21462306a36Sopenharmony_ci if (ret) { 21562306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 21662306a36Sopenharmony_ci goto err_unmap; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ret = __pkvm_host_share_hyp(hyp_phys_to_pfn(rx)); 22062306a36Sopenharmony_ci if (ret) { 22162306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 22262306a36Sopenharmony_ci goto err_unshare_tx; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci tx_virt = hyp_phys_to_virt(tx); 22662306a36Sopenharmony_ci ret = hyp_pin_shared_mem(tx_virt, tx_virt + 1); 22762306a36Sopenharmony_ci if (ret) { 22862306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 22962306a36Sopenharmony_ci goto err_unshare_rx; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci rx_virt = hyp_phys_to_virt(rx); 23362306a36Sopenharmony_ci ret = hyp_pin_shared_mem(rx_virt, rx_virt + 1); 23462306a36Sopenharmony_ci if (ret) { 23562306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 23662306a36Sopenharmony_ci goto err_unpin_tx; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci host_buffers.tx = tx_virt; 24062306a36Sopenharmony_ci host_buffers.rx = rx_virt; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciout_unlock: 24362306a36Sopenharmony_ci hyp_spin_unlock(&host_buffers.lock); 24462306a36Sopenharmony_ciout: 24562306a36Sopenharmony_ci ffa_to_smccc_res(res, ret); 24662306a36Sopenharmony_ci return; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cierr_unpin_tx: 24962306a36Sopenharmony_ci hyp_unpin_shared_mem(tx_virt, tx_virt + 1); 25062306a36Sopenharmony_cierr_unshare_rx: 25162306a36Sopenharmony_ci __pkvm_host_unshare_hyp(hyp_phys_to_pfn(rx)); 25262306a36Sopenharmony_cierr_unshare_tx: 25362306a36Sopenharmony_ci __pkvm_host_unshare_hyp(hyp_phys_to_pfn(tx)); 25462306a36Sopenharmony_cierr_unmap: 25562306a36Sopenharmony_ci ffa_unmap_hyp_buffers(); 25662306a36Sopenharmony_ci goto out_unlock; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void do_ffa_rxtx_unmap(struct arm_smccc_res *res, 26062306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci DECLARE_REG(u32, id, ctxt, 1); 26362306a36Sopenharmony_ci int ret = 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (id != HOST_FFA_ID) { 26662306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci hyp_spin_lock(&host_buffers.lock); 27162306a36Sopenharmony_ci if (!host_buffers.tx) { 27262306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 27362306a36Sopenharmony_ci goto out_unlock; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci hyp_unpin_shared_mem(host_buffers.tx, host_buffers.tx + 1); 27762306a36Sopenharmony_ci WARN_ON(__pkvm_host_unshare_hyp(hyp_virt_to_pfn(host_buffers.tx))); 27862306a36Sopenharmony_ci host_buffers.tx = NULL; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci hyp_unpin_shared_mem(host_buffers.rx, host_buffers.rx + 1); 28162306a36Sopenharmony_ci WARN_ON(__pkvm_host_unshare_hyp(hyp_virt_to_pfn(host_buffers.rx))); 28262306a36Sopenharmony_ci host_buffers.rx = NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ffa_unmap_hyp_buffers(); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciout_unlock: 28762306a36Sopenharmony_ci hyp_spin_unlock(&host_buffers.lock); 28862306a36Sopenharmony_ciout: 28962306a36Sopenharmony_ci ffa_to_smccc_res(res, ret); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic u32 __ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges, 29362306a36Sopenharmony_ci u32 nranges) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci u32 i; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci for (i = 0; i < nranges; ++i) { 29862306a36Sopenharmony_ci struct ffa_mem_region_addr_range *range = &ranges[i]; 29962306a36Sopenharmony_ci u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE; 30062306a36Sopenharmony_ci u64 pfn = hyp_phys_to_pfn(range->address); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!PAGE_ALIGNED(sz)) 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (__pkvm_host_share_ffa(pfn, sz / PAGE_SIZE)) 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return i; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic u32 __ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges, 31362306a36Sopenharmony_ci u32 nranges) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci u32 i; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for (i = 0; i < nranges; ++i) { 31862306a36Sopenharmony_ci struct ffa_mem_region_addr_range *range = &ranges[i]; 31962306a36Sopenharmony_ci u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE; 32062306a36Sopenharmony_ci u64 pfn = hyp_phys_to_pfn(range->address); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!PAGE_ALIGNED(sz)) 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (__pkvm_host_unshare_ffa(pfn, sz / PAGE_SIZE)) 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return i; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges, 33362306a36Sopenharmony_ci u32 nranges) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci u32 nshared = __ffa_host_share_ranges(ranges, nranges); 33662306a36Sopenharmony_ci int ret = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (nshared != nranges) { 33962306a36Sopenharmony_ci WARN_ON(__ffa_host_unshare_ranges(ranges, nshared) != nshared); 34062306a36Sopenharmony_ci ret = FFA_RET_DENIED; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges, 34762306a36Sopenharmony_ci u32 nranges) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci u32 nunshared = __ffa_host_unshare_ranges(ranges, nranges); 35062306a36Sopenharmony_ci int ret = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (nunshared != nranges) { 35362306a36Sopenharmony_ci WARN_ON(__ffa_host_share_ranges(ranges, nunshared) != nunshared); 35462306a36Sopenharmony_ci ret = FFA_RET_DENIED; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic void do_ffa_mem_frag_tx(struct arm_smccc_res *res, 36162306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci DECLARE_REG(u32, handle_lo, ctxt, 1); 36462306a36Sopenharmony_ci DECLARE_REG(u32, handle_hi, ctxt, 2); 36562306a36Sopenharmony_ci DECLARE_REG(u32, fraglen, ctxt, 3); 36662306a36Sopenharmony_ci DECLARE_REG(u32, endpoint_id, ctxt, 4); 36762306a36Sopenharmony_ci struct ffa_mem_region_addr_range *buf; 36862306a36Sopenharmony_ci int ret = FFA_RET_INVALID_PARAMETERS; 36962306a36Sopenharmony_ci u32 nr_ranges; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) 37262306a36Sopenharmony_ci goto out; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (fraglen % sizeof(*buf)) 37562306a36Sopenharmony_ci goto out; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci hyp_spin_lock(&host_buffers.lock); 37862306a36Sopenharmony_ci if (!host_buffers.tx) 37962306a36Sopenharmony_ci goto out_unlock; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci buf = hyp_buffers.tx; 38262306a36Sopenharmony_ci memcpy(buf, host_buffers.tx, fraglen); 38362306a36Sopenharmony_ci nr_ranges = fraglen / sizeof(*buf); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ret = ffa_host_share_ranges(buf, nr_ranges); 38662306a36Sopenharmony_ci if (ret) { 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * We're effectively aborting the transaction, so we need 38962306a36Sopenharmony_ci * to restore the global state back to what it was prior to 39062306a36Sopenharmony_ci * transmission of the first fragment. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci ffa_mem_reclaim(res, handle_lo, handle_hi, 0); 39362306a36Sopenharmony_ci WARN_ON(res->a0 != FFA_SUCCESS); 39462306a36Sopenharmony_ci goto out_unlock; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ffa_mem_frag_tx(res, handle_lo, handle_hi, fraglen, endpoint_id); 39862306a36Sopenharmony_ci if (res->a0 != FFA_SUCCESS && res->a0 != FFA_MEM_FRAG_RX) 39962306a36Sopenharmony_ci WARN_ON(ffa_host_unshare_ranges(buf, nr_ranges)); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ciout_unlock: 40262306a36Sopenharmony_ci hyp_spin_unlock(&host_buffers.lock); 40362306a36Sopenharmony_ciout: 40462306a36Sopenharmony_ci if (ret) 40562306a36Sopenharmony_ci ffa_to_smccc_res(res, ret); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * If for any reason this did not succeed, we're in trouble as we have 40962306a36Sopenharmony_ci * now lost the content of the previous fragments and we can't rollback 41062306a36Sopenharmony_ci * the host stage-2 changes. The pages previously marked as shared will 41162306a36Sopenharmony_ci * remain stuck in that state forever, hence preventing the host from 41262306a36Sopenharmony_ci * sharing/donating them again and may possibly lead to subsequent 41362306a36Sopenharmony_ci * failures, but this will not compromise confidentiality. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci return; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic __always_inline void do_ffa_mem_xfer(const u64 func_id, 41962306a36Sopenharmony_ci struct arm_smccc_res *res, 42062306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci DECLARE_REG(u32, len, ctxt, 1); 42362306a36Sopenharmony_ci DECLARE_REG(u32, fraglen, ctxt, 2); 42462306a36Sopenharmony_ci DECLARE_REG(u64, addr_mbz, ctxt, 3); 42562306a36Sopenharmony_ci DECLARE_REG(u32, npages_mbz, ctxt, 4); 42662306a36Sopenharmony_ci struct ffa_composite_mem_region *reg; 42762306a36Sopenharmony_ci struct ffa_mem_region *buf; 42862306a36Sopenharmony_ci u32 offset, nr_ranges; 42962306a36Sopenharmony_ci int ret = 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci BUILD_BUG_ON(func_id != FFA_FN64_MEM_SHARE && 43262306a36Sopenharmony_ci func_id != FFA_FN64_MEM_LEND); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (addr_mbz || npages_mbz || fraglen > len || 43562306a36Sopenharmony_ci fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) { 43662306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 43762306a36Sopenharmony_ci goto out; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (fraglen < sizeof(struct ffa_mem_region) + 44162306a36Sopenharmony_ci sizeof(struct ffa_mem_region_attributes)) { 44262306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 44362306a36Sopenharmony_ci goto out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci hyp_spin_lock(&host_buffers.lock); 44762306a36Sopenharmony_ci if (!host_buffers.tx) { 44862306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 44962306a36Sopenharmony_ci goto out_unlock; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci buf = hyp_buffers.tx; 45362306a36Sopenharmony_ci memcpy(buf, host_buffers.tx, fraglen); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci offset = buf->ep_mem_access[0].composite_off; 45662306a36Sopenharmony_ci if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) { 45762306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 45862306a36Sopenharmony_ci goto out_unlock; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (fraglen < offset + sizeof(struct ffa_composite_mem_region)) { 46262306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 46362306a36Sopenharmony_ci goto out_unlock; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci reg = (void *)buf + offset; 46762306a36Sopenharmony_ci nr_ranges = ((void *)buf + fraglen) - (void *)reg->constituents; 46862306a36Sopenharmony_ci if (nr_ranges % sizeof(reg->constituents[0])) { 46962306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 47062306a36Sopenharmony_ci goto out_unlock; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci nr_ranges /= sizeof(reg->constituents[0]); 47462306a36Sopenharmony_ci ret = ffa_host_share_ranges(reg->constituents, nr_ranges); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci goto out_unlock; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ffa_mem_xfer(res, func_id, len, fraglen); 47962306a36Sopenharmony_ci if (fraglen != len) { 48062306a36Sopenharmony_ci if (res->a0 != FFA_MEM_FRAG_RX) 48162306a36Sopenharmony_ci goto err_unshare; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (res->a3 != fraglen) 48462306a36Sopenharmony_ci goto err_unshare; 48562306a36Sopenharmony_ci } else if (res->a0 != FFA_SUCCESS) { 48662306a36Sopenharmony_ci goto err_unshare; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ciout_unlock: 49062306a36Sopenharmony_ci hyp_spin_unlock(&host_buffers.lock); 49162306a36Sopenharmony_ciout: 49262306a36Sopenharmony_ci if (ret) 49362306a36Sopenharmony_ci ffa_to_smccc_res(res, ret); 49462306a36Sopenharmony_ci return; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cierr_unshare: 49762306a36Sopenharmony_ci WARN_ON(ffa_host_unshare_ranges(reg->constituents, nr_ranges)); 49862306a36Sopenharmony_ci goto out_unlock; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void do_ffa_mem_reclaim(struct arm_smccc_res *res, 50262306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci DECLARE_REG(u32, handle_lo, ctxt, 1); 50562306a36Sopenharmony_ci DECLARE_REG(u32, handle_hi, ctxt, 2); 50662306a36Sopenharmony_ci DECLARE_REG(u32, flags, ctxt, 3); 50762306a36Sopenharmony_ci struct ffa_composite_mem_region *reg; 50862306a36Sopenharmony_ci u32 offset, len, fraglen, fragoff; 50962306a36Sopenharmony_ci struct ffa_mem_region *buf; 51062306a36Sopenharmony_ci int ret = 0; 51162306a36Sopenharmony_ci u64 handle; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci handle = PACK_HANDLE(handle_lo, handle_hi); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci hyp_spin_lock(&host_buffers.lock); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci buf = hyp_buffers.tx; 51862306a36Sopenharmony_ci *buf = (struct ffa_mem_region) { 51962306a36Sopenharmony_ci .sender_id = HOST_FFA_ID, 52062306a36Sopenharmony_ci .handle = handle, 52162306a36Sopenharmony_ci }; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ffa_retrieve_req(res, sizeof(*buf)); 52462306a36Sopenharmony_ci buf = hyp_buffers.rx; 52562306a36Sopenharmony_ci if (res->a0 != FFA_MEM_RETRIEVE_RESP) 52662306a36Sopenharmony_ci goto out_unlock; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci len = res->a1; 52962306a36Sopenharmony_ci fraglen = res->a2; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci offset = buf->ep_mem_access[0].composite_off; 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * We can trust the SPMD to get this right, but let's at least 53462306a36Sopenharmony_ci * check that we end up with something that doesn't look _completely_ 53562306a36Sopenharmony_ci * bogus. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci if (WARN_ON(offset > len || 53862306a36Sopenharmony_ci fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)) { 53962306a36Sopenharmony_ci ret = FFA_RET_ABORTED; 54062306a36Sopenharmony_ci goto out_unlock; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (len > ffa_desc_buf.len) { 54462306a36Sopenharmony_ci ret = FFA_RET_NO_MEMORY; 54562306a36Sopenharmony_ci goto out_unlock; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci buf = ffa_desc_buf.buf; 54962306a36Sopenharmony_ci memcpy(buf, hyp_buffers.rx, fraglen); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci for (fragoff = fraglen; fragoff < len; fragoff += fraglen) { 55262306a36Sopenharmony_ci ffa_mem_frag_rx(res, handle_lo, handle_hi, fragoff); 55362306a36Sopenharmony_ci if (res->a0 != FFA_MEM_FRAG_TX) { 55462306a36Sopenharmony_ci ret = FFA_RET_INVALID_PARAMETERS; 55562306a36Sopenharmony_ci goto out_unlock; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci fraglen = res->a3; 55962306a36Sopenharmony_ci memcpy((void *)buf + fragoff, hyp_buffers.rx, fraglen); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ffa_mem_reclaim(res, handle_lo, handle_hi, flags); 56362306a36Sopenharmony_ci if (res->a0 != FFA_SUCCESS) 56462306a36Sopenharmony_ci goto out_unlock; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci reg = (void *)buf + offset; 56762306a36Sopenharmony_ci /* If the SPMD was happy, then we should be too. */ 56862306a36Sopenharmony_ci WARN_ON(ffa_host_unshare_ranges(reg->constituents, 56962306a36Sopenharmony_ci reg->addr_range_cnt)); 57062306a36Sopenharmony_ciout_unlock: 57162306a36Sopenharmony_ci hyp_spin_unlock(&host_buffers.lock); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (ret) 57462306a36Sopenharmony_ci ffa_to_smccc_res(res, ret); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* 57862306a36Sopenharmony_ci * Is a given FFA function supported, either by forwarding on directly 57962306a36Sopenharmony_ci * or by handling at EL2? 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistatic bool ffa_call_supported(u64 func_id) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci switch (func_id) { 58462306a36Sopenharmony_ci /* Unsupported memory management calls */ 58562306a36Sopenharmony_ci case FFA_FN64_MEM_RETRIEVE_REQ: 58662306a36Sopenharmony_ci case FFA_MEM_RETRIEVE_RESP: 58762306a36Sopenharmony_ci case FFA_MEM_RELINQUISH: 58862306a36Sopenharmony_ci case FFA_MEM_OP_PAUSE: 58962306a36Sopenharmony_ci case FFA_MEM_OP_RESUME: 59062306a36Sopenharmony_ci case FFA_MEM_FRAG_RX: 59162306a36Sopenharmony_ci case FFA_FN64_MEM_DONATE: 59262306a36Sopenharmony_ci /* Indirect message passing via RX/TX buffers */ 59362306a36Sopenharmony_ci case FFA_MSG_SEND: 59462306a36Sopenharmony_ci case FFA_MSG_POLL: 59562306a36Sopenharmony_ci case FFA_MSG_WAIT: 59662306a36Sopenharmony_ci /* 32-bit variants of 64-bit calls */ 59762306a36Sopenharmony_ci case FFA_MSG_SEND_DIRECT_REQ: 59862306a36Sopenharmony_ci case FFA_MSG_SEND_DIRECT_RESP: 59962306a36Sopenharmony_ci case FFA_RXTX_MAP: 60062306a36Sopenharmony_ci case FFA_MEM_DONATE: 60162306a36Sopenharmony_ci case FFA_MEM_RETRIEVE_REQ: 60262306a36Sopenharmony_ci return false; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return true; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic bool do_ffa_features(struct arm_smccc_res *res, 60962306a36Sopenharmony_ci struct kvm_cpu_context *ctxt) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci DECLARE_REG(u32, id, ctxt, 1); 61262306a36Sopenharmony_ci u64 prop = 0; 61362306a36Sopenharmony_ci int ret = 0; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (!ffa_call_supported(id)) { 61662306a36Sopenharmony_ci ret = FFA_RET_NOT_SUPPORTED; 61762306a36Sopenharmony_ci goto out_handled; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci switch (id) { 62162306a36Sopenharmony_ci case FFA_MEM_SHARE: 62262306a36Sopenharmony_ci case FFA_FN64_MEM_SHARE: 62362306a36Sopenharmony_ci case FFA_MEM_LEND: 62462306a36Sopenharmony_ci case FFA_FN64_MEM_LEND: 62562306a36Sopenharmony_ci ret = FFA_RET_SUCCESS; 62662306a36Sopenharmony_ci prop = 0; /* No support for dynamic buffers */ 62762306a36Sopenharmony_ci goto out_handled; 62862306a36Sopenharmony_ci default: 62962306a36Sopenharmony_ci return false; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciout_handled: 63362306a36Sopenharmony_ci ffa_to_smccc_res_prop(res, ret, prop); 63462306a36Sopenharmony_ci return true; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cibool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct arm_smccc_res res; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * There's no way we can tell what a non-standard SMC call might 64362306a36Sopenharmony_ci * be up to. Ideally, we would terminate these here and return 64462306a36Sopenharmony_ci * an error to the host, but sadly devices make use of custom 64562306a36Sopenharmony_ci * firmware calls for things like power management, debugging, 64662306a36Sopenharmony_ci * RNG access and crash reporting. 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * Given that the architecture requires us to trust EL3 anyway, 64962306a36Sopenharmony_ci * we forward unrecognised calls on under the assumption that 65062306a36Sopenharmony_ci * the firmware doesn't expose a mechanism to access arbitrary 65162306a36Sopenharmony_ci * non-secure memory. Short of a per-device table of SMCs, this 65262306a36Sopenharmony_ci * is the best we can do. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci if (!is_ffa_call(func_id)) 65562306a36Sopenharmony_ci return false; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci switch (func_id) { 65862306a36Sopenharmony_ci case FFA_FEATURES: 65962306a36Sopenharmony_ci if (!do_ffa_features(&res, host_ctxt)) 66062306a36Sopenharmony_ci return false; 66162306a36Sopenharmony_ci goto out_handled; 66262306a36Sopenharmony_ci /* Memory management */ 66362306a36Sopenharmony_ci case FFA_FN64_RXTX_MAP: 66462306a36Sopenharmony_ci do_ffa_rxtx_map(&res, host_ctxt); 66562306a36Sopenharmony_ci goto out_handled; 66662306a36Sopenharmony_ci case FFA_RXTX_UNMAP: 66762306a36Sopenharmony_ci do_ffa_rxtx_unmap(&res, host_ctxt); 66862306a36Sopenharmony_ci goto out_handled; 66962306a36Sopenharmony_ci case FFA_MEM_SHARE: 67062306a36Sopenharmony_ci case FFA_FN64_MEM_SHARE: 67162306a36Sopenharmony_ci do_ffa_mem_xfer(FFA_FN64_MEM_SHARE, &res, host_ctxt); 67262306a36Sopenharmony_ci goto out_handled; 67362306a36Sopenharmony_ci case FFA_MEM_RECLAIM: 67462306a36Sopenharmony_ci do_ffa_mem_reclaim(&res, host_ctxt); 67562306a36Sopenharmony_ci goto out_handled; 67662306a36Sopenharmony_ci case FFA_MEM_LEND: 67762306a36Sopenharmony_ci case FFA_FN64_MEM_LEND: 67862306a36Sopenharmony_ci do_ffa_mem_xfer(FFA_FN64_MEM_LEND, &res, host_ctxt); 67962306a36Sopenharmony_ci goto out_handled; 68062306a36Sopenharmony_ci case FFA_MEM_FRAG_TX: 68162306a36Sopenharmony_ci do_ffa_mem_frag_tx(&res, host_ctxt); 68262306a36Sopenharmony_ci goto out_handled; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (ffa_call_supported(func_id)) 68662306a36Sopenharmony_ci return false; /* Pass through */ 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED); 68962306a36Sopenharmony_ciout_handled: 69062306a36Sopenharmony_ci ffa_set_retval(host_ctxt, &res); 69162306a36Sopenharmony_ci return true; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ciint hyp_ffa_init(void *pages) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct arm_smccc_res res; 69762306a36Sopenharmony_ci size_t min_rxtx_sz; 69862306a36Sopenharmony_ci void *tx, *rx; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (kvm_host_psci_config.smccc_version < ARM_SMCCC_VERSION_1_2) 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_VERSION, FFA_VERSION_1_0, 0, 0, 0, 0, 0, 0, &res); 70462306a36Sopenharmony_ci if (res.a0 == FFA_RET_NOT_SUPPORTED) 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* 70862306a36Sopenharmony_ci * Firmware returns the maximum supported version of the FF-A 70962306a36Sopenharmony_ci * implementation. Check that the returned version is 71062306a36Sopenharmony_ci * backwards-compatible with the hyp according to the rules in DEN0077A 71162306a36Sopenharmony_ci * v1.1 REL0 13.2.1. 71262306a36Sopenharmony_ci * 71362306a36Sopenharmony_ci * Of course, things are never simple when dealing with firmware. v1.1 71462306a36Sopenharmony_ci * broke ABI with v1.0 on several structures, which is itself 71562306a36Sopenharmony_ci * incompatible with the aforementioned versioning scheme. The 71662306a36Sopenharmony_ci * expectation is that v1.x implementations that do not support the v1.0 71762306a36Sopenharmony_ci * ABI return NOT_SUPPORTED rather than a version number, according to 71862306a36Sopenharmony_ci * DEN0077A v1.1 REL0 18.6.4. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci if (FFA_MAJOR_VERSION(res.a0) != 1) 72162306a36Sopenharmony_ci return -EOPNOTSUPP; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0, &res); 72462306a36Sopenharmony_ci if (res.a0 != FFA_SUCCESS) 72562306a36Sopenharmony_ci return -EOPNOTSUPP; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (res.a2 != HOST_FFA_ID) 72862306a36Sopenharmony_ci return -EINVAL; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci arm_smccc_1_1_smc(FFA_FEATURES, FFA_FN64_RXTX_MAP, 73162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 73262306a36Sopenharmony_ci if (res.a0 != FFA_SUCCESS) 73362306a36Sopenharmony_ci return -EOPNOTSUPP; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci switch (res.a2) { 73662306a36Sopenharmony_ci case FFA_FEAT_RXTX_MIN_SZ_4K: 73762306a36Sopenharmony_ci min_rxtx_sz = SZ_4K; 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci case FFA_FEAT_RXTX_MIN_SZ_16K: 74062306a36Sopenharmony_ci min_rxtx_sz = SZ_16K; 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case FFA_FEAT_RXTX_MIN_SZ_64K: 74362306a36Sopenharmony_ci min_rxtx_sz = SZ_64K; 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci default: 74662306a36Sopenharmony_ci return -EINVAL; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (min_rxtx_sz > PAGE_SIZE) 75062306a36Sopenharmony_ci return -EOPNOTSUPP; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci tx = pages; 75362306a36Sopenharmony_ci pages += KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE; 75462306a36Sopenharmony_ci rx = pages; 75562306a36Sopenharmony_ci pages += KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ffa_desc_buf = (struct kvm_ffa_descriptor_buffer) { 75862306a36Sopenharmony_ci .buf = pages, 75962306a36Sopenharmony_ci .len = PAGE_SIZE * 76062306a36Sopenharmony_ci (hyp_ffa_proxy_pages() - (2 * KVM_FFA_MBOX_NR_PAGES)), 76162306a36Sopenharmony_ci }; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci hyp_buffers = (struct kvm_ffa_buffers) { 76462306a36Sopenharmony_ci .lock = __HYP_SPIN_LOCK_UNLOCKED, 76562306a36Sopenharmony_ci .tx = tx, 76662306a36Sopenharmony_ci .rx = rx, 76762306a36Sopenharmony_ci }; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci host_buffers = (struct kvm_ffa_buffers) { 77062306a36Sopenharmony_ci .lock = __HYP_SPIN_LOCK_UNLOCKED, 77162306a36Sopenharmony_ci }; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 775