KVM: arm64: FF-A proxy for pKVM
Subsystems (3)
Will Deacon
maintainerv2 cover KVM: arm64: FF-A proxy for pKVM
Hi folks,
This is the second version of the pKVM FF-A proxy patches that were originally posted by Quentin a while back:
https://lore.kernel.org/r/[email protected]
As described in the original cover letter, these patches provide a mechanism for pKVM to intercept memory being shared between the host and Trustzone in order to prevent "confused deputy" attacks where the host can ask Trustzone to access protected guest pages.
Changes since v1 include:
* Remove double negatives by inverting ffa_call_unsupported() into ffa_call_supported()
* Re-order patch series so that FFA_FEATURES doesn't advertise unimplemented functions during bisection
* Rework patches to introduce function switch case by case
* Dropped the first two changes as they have been merged upstream
* Dropped stray "ANDROID:" prefix from the final patch
Thanks to Oliver for his comments on the initial posting.
Patches based on -rc7 and also pushed here:
git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git kvm/ffa-proxy
Cheers,
Will
--->8
Fuad Tabba (1):
KVM: arm64: Handle FFA_FEATURES call from the host
Quentin Perret (1):
KVM: arm64: pkvm: Add support for fragmented FF-A descriptors
Will Deacon (8):
KVM: arm64: Block unsafe FF-A calls from the host
KVM: arm64: Probe FF-A version and host/hyp partition ID during init
KVM: arm64: Allocate pages for hypervisor FF-A mailboxes
KVM: arm64: Handle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host
KVM: arm64: Add FF-A helpers to share/unshare memory with secure world
KVM: arm64: Handle FFA_MEM_SHARE calls from the host
KVM: arm64: Handle FFA_MEM_RECLAIM calls from the host
KVM: arm64: Handle FFA_MEM_LEND calls from the host
arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/asm/kvm_pkvm.h | 21 + arch/arm64/kvm/arm.c | 1 + arch/arm64/kvm/hyp/include/nvhe/ffa.h | 17 + arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 3 + arch/arm64/kvm/hyp/nvhe/Makefile | 2 +- arch/arm64/kvm/hyp/nvhe/ffa.c | 745 ++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 68 ++ arch/arm64/kvm/hyp/nvhe/setup.c | 11 + arch/arm64/kvm/pkvm.c | 1 + include/linux/arm_ffa.h | 8 + 12 files changed, 880 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kvm/hyp/include/nvhe/ffa.h create mode 100644 arch/arm64/kvm/hyp/nvhe/ffa.c
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 1/10 KVM: arm64: Block unsafe FF-A calls from the host
When KVM is initialised in protected mode, we must take care to filter certain FFA calls from the host kernel so that the integrity of guest and hypervisor memory is maintained and is not made available to the secure world.
As a first step, intercept and block all memory-related FF-A SMC calls from the host to EL3 and don't advertise any FF-A features. This puts the framework in place for handling them properly.
--- arch/arm64/kvm/hyp/include/nvhe/ffa.h | 16 ++++ arch/arm64/kvm/hyp/nvhe/Makefile | 2 +- arch/arm64/kvm/hyp/nvhe/ffa.c | 106 ++++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 + 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kvm/hyp/include/nvhe/ffa.h create mode 100644 arch/arm64/kvm/hyp/nvhe/ffa.c
arch/arm64/kvm/hyp/include/nvhe/ffa.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Copyright (C) 2022 - Google LLC +* Author: Andrew Walbran <[email protected]> +*/ +#ifndef __KVM_HYP_FFA_H +#define __KVM_HYP_FFA_H + +#include <asm/kvm_host.h> + +#define FFA_MIN_FUNC_NUM 0x60 +#define FFA_MAX_FUNC_NUM 0x7F + +bool kvm_host_ffa_handler (struct kvm_cpu_context *host_ctxt); + +#endif /* __KVM_HYP_FFA_H */
arch/arm64/kvm/hyp/nvhe/Makefile +1 -1
@@ -22,7 +22,7 @@ lib-objs := $(addprefix ../../../lib/, $(lib-objs)) hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \ hyp-main.o hyp-smp.o psci-relay.o early_alloc.o page_alloc.o \ - cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o + cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o
arch/arm64/kvm/hyp/nvhe/ffa.c new file +106 -0
106 lines not on this page · raw message
arch/arm64/kvm/hyp/nvhe/hyp-main.c +3 -0
@@ -13,6 +13,7 @@#include <asm/kvm_hyp.h> #include <asm/kvm_mmu.h> +#include <nvhe/ffa.h> #include <nvhe/mem_protect.h> #include <nvhe/mm.h> #include <nvhe/pkvm.h>
@@ -373,6 +374,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)bool handled; handled =kvm_host_psci_handler (host_ctxt); +if (!handled) + handled =kvm_host_ffa_handler (host_ctxt);if (!handled)default_host_smc_handler (host_ctxt);
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Re: [PATCH v2 01/10] KVM: arm64: Block unsafe FF-A calls from the host
Hi Will,
On Wed, Apr 19, 2023 at 01:20:42PM +0100, Will Deacon wrote:
[...]
Quoted 22 lines
arch/arm64/kvm/hyp/nvhe/ffa.c new file +22
-0+/* +* Is a given FFA function supported, either by forwarding on directly +* or by handling at EL2? +*/ +static bool ffa_call_supported (u64 func_id) +{ +switch (func_id) { +/* Unsupported memory management calls */ +case FFA_FN64_MEM_RETRIEVE_REQ : +case FFA_MEM_RETRIEVE_RESP : +case FFA_MEM_RELINQUISH : +case FFA_MEM_OP_PAUSE : +case FFA_MEM_OP_RESUME : +case FFA_MEM_FRAG_RX : +case FFA_FN64_MEM_DONATE : +/* Indirect message passing via RX/TX buffers */ +case FFA_MSG_SEND : +case FFA_MSG_POLL : +case FFA_MSG_WAIT : +/* 32-bit variants of 64-bit calls */ +case FFA_MSG_SEND_DIRECT_REQ : +case FFA_MSG_SEND_DIRECT_RESP :
arch/arm64/kvm/hyp/nvhe/ffa.c line 90
Quoted 10 lines
arch/arm64/kvm/hyp/nvhe/ffa.c new file +10
-0+case FFA_RXTX_MAP : +case FFA_MEM_DONATE : +case FFA_MEM_RETRIEVE_REQ : +/* Don't advertise any features just yet */ +case FFA_FEATURES : +return false; + } + +return true; +}
Apologies for rehashing something we dicussed in v1...
Enforcing the pKVM policy as a denylist rather than an allowlist deserves a bit more elaboration, at least in the form of a comment. I understand that we must trust EL3 by construction, but it is fuzzy why it gets extended to what EL1 might do with FF-A calls that are unknown to pKVM.
Broadening the scope for a moment, is my understanding correct that limiting 'unknown' SMCs from host EL1 are an explicit non-goal of pKVM's security model? Assuming a well-intentioned EL3, I'm just a bit worried about any vendor-specific junkware that could be used by a malicious EL1.
Signature · 2 lines
Thanks, Oliver
Will Deacon
maintainerRe: [PATCH v2 01/10] KVM: arm64: Block unsafe FF-A calls from the host
Hi Oliver,
Quoted 36 lines · Oliver Upton
Quoted 22 lines · Will Deacon
arch/arm64/kvm/hyp/nvhe/ffa.c new file +22
-0+/* +* Is a given FFA function supported, either by forwarding on directly +* or by handling at EL2? +*/ +static bool ffa_call_supported (u64 func_id) +{ +switch (func_id) { +/* Unsupported memory management calls */ +case FFA_FN64_MEM_RETRIEVE_REQ : +case FFA_MEM_RETRIEVE_RESP : +case FFA_MEM_RELINQUISH : +case FFA_MEM_OP_PAUSE : +case FFA_MEM_OP_RESUME : +case FFA_MEM_FRAG_RX : +case FFA_FN64_MEM_DONATE : +/* Indirect message passing via RX/TX buffers */ +case FFA_MSG_SEND : +case FFA_MSG_POLL : +case FFA_MSG_WAIT : +/* 32-bit variants of 64-bit calls */ +case FFA_MSG_SEND_DIRECT_REQ : +case FFA_MSG_SEND_DIRECT_RESP :arch/arm64/kvm/hyp/nvhe/ffa.c line 90
Quoted 10 lines
arch/arm64/kvm/hyp/nvhe/ffa.c new file +10
-0+case FFA_RXTX_MAP : +case FFA_MEM_DONATE : +case FFA_MEM_RETRIEVE_REQ : +/* Don't advertise any features just yet */ +case FFA_FEATURES : +return false; + } + +return true; +}Apologies for rehashing something we dicussed in v1...
Quoted 5 lines
Enforcing the pKVM policy as a denylist rather than an allowlist deserves a bit more elaboration, at least in the form of a comment. I understand that we must trust EL3 by construction, but it is fuzzy why it gets extended to what EL1 might do with FF-A calls that are unknown to pKVM.
Sure thing, I'll add a comment for the next version.
Quoted 5 lines
Broadening the scope for a moment, is my understanding correct that limiting 'unknown' SMCs from host EL1 are an explicit non-goal of pKVM's security model? Assuming a well-intentioned EL3, I'm just a bit worried about any vendor-specific junkware that could be used by a malicious EL1.
It's a valid concern, but the sad reality is that every shipping Android device makes use of 'unknown' SMCs and if we reject them at EL2 then the device won't function properly and we've shot ourselves in the foot.
So we basically have two options:
(1) Add per-device logic to EL2 which knows how to introspect the 'unknown' SMCs and filter out bad requests from EL1
(2) Let them through by default, intercepting standard requests such as PSCI and FF-A at EL2 and rely on the firmware not to expose non-standard memory sharing calls
We have patches in Android to support modules loading code into EL2, which makes option (1) practical, but without that support upstream, (2) is the best we can do for now.
Will
Re: [PATCH v2 01/10] KVM: arm64: Block unsafe FF-A calls from the host
Quoted 62 lines · Will Deacon
Hi Oliver,
Quoted 36 lines · Oliver Upton
Quoted 22 lines · Will Deacon
arch/arm64/kvm/hyp/nvhe/ffa.c new file +22
-0+/* +* Is a given FFA function supported, either by forwarding on directly +* or by handling at EL2? +*/ +static bool ffa_call_supported (u64 func_id) +{ +switch (func_id) { +/* Unsupported memory management calls */ +case FFA_FN64_MEM_RETRIEVE_REQ : +case FFA_MEM_RETRIEVE_RESP : +case FFA_MEM_RELINQUISH : +case FFA_MEM_OP_PAUSE : +case FFA_MEM_OP_RESUME : +case FFA_MEM_FRAG_RX : +case FFA_FN64_MEM_DONATE : +/* Indirect message passing via RX/TX buffers */ +case FFA_MSG_SEND : +case FFA_MSG_POLL : +case FFA_MSG_WAIT : +/* 32-bit variants of 64-bit calls */ +case FFA_MSG_SEND_DIRECT_REQ : +case FFA_MSG_SEND_DIRECT_RESP :arch/arm64/kvm/hyp/nvhe/ffa.c line 90
Quoted 10 lines
arch/arm64/kvm/hyp/nvhe/ffa.c new file +10
-0+case FFA_RXTX_MAP : +case FFA_MEM_DONATE : +case FFA_MEM_RETRIEVE_REQ : +/* Don't advertise any features just yet */ +case FFA_FEATURES : +return false; + } + +return true; +}Apologies for rehashing something we dicussed in v1...
Quoted 5 lines
Enforcing the pKVM policy as a denylist rather than an allowlist deserves a bit more elaboration, at least in the form of a comment. I understand that we must trust EL3 by construction, but it is fuzzy why it gets extended to what EL1 might do with FF-A calls that are unknown to pKVM.
Sure thing, I'll add a comment for the next version.
Quoted 5 lines
Broadening the scope for a moment, is my understanding correct that limiting 'unknown' SMCs from host EL1 are an explicit non-goal of pKVM's security model? Assuming a well-intentioned EL3, I'm just a bit worried about any vendor-specific junkware that could be used by a malicious EL1.
It's a valid concern, but the sad reality is that every shipping Android device makes use of 'unknown' SMCs and if we reject them at EL2 then the device won't function properly and we've shot ourselves in the foot.
So we basically have two options:
(1) Add per-device logic to EL2 which knows how to introspect the 'unknown' SMCs and filter out bad requests from EL1
Quoted 7 lines
(2) Let them through by default, intercepting standard requests such as PSCI and FF-A at EL2 and rely on the firmware not to expose non-standard memory sharing calls
We have patches in Android to support modules loading code into EL2, which makes option (1) practical, but without that support upstream, (2) is the best we can do for now.
Thanks, and fully understand what led to the policy you folks have implemented. Perhaps in the future we can add an optional, restrictive model to satisfy users that have a more adversarial relationship with firmware interfaces. Not worth addressing in this series, though.
Going forwared, can the goals/non-goals of the current pKVM design be captured somewhere in documentation? I think this can be done organically as more bits and pieces are upstreamed, but it'd help reviewers who haven't spent time in the trenches with pKVM to at least grasp the intent. At least personally, I really like to see the pKVM stuff landing upstream but haven't had first-hand experience with the horrors of client devices :)
Signature · 2 lines
Thanks, Oliver
Will Deacon
maintainerPATCH v2 2/10 KVM: arm64: Probe FF-A version and host/hyp partition ID during init
In reply to Will DeaconProbe FF-A during pKVM initialisation so that we can detect any inconsistencies in the version or partition ID early on.
--- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/kvm/arm.c | 1 + arch/arm64/kvm/hyp/include/nvhe/ffa.h | 1 + arch/arm64/kvm/hyp/nvhe/ffa.c | 30 +++++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/setup.c | 5 +++++ 5 files changed, 38 insertions(+)
arch/arm64/include/asm/kvm_host.h +1 -0
@@ -391,6 +391,7 @@ struct kvm_host_data {struct kvm_host_psci_config {/* PSCI version used by host. */ u32 version; +u32 smccc_version;/* Function IDs used by host if version is v0.1. */ struct psci_0_1_function_ids function_ids_0_1;
arch/arm64/kvm/arm.c +1 -0
@@ -1790,6 +1790,7 @@ static bool __init init_psci_relay(void) } kvm_host_psci_config.version = psci_ops.get_version (); + kvm_host_psci_config.smccc_version =arm_smccc_get_version ();if (kvm_host_psci_config.version ==PSCI_VERSION (0 ,1 )) { kvm_host_psci_config.function_ids_0_1 =get_psci_0_1_function_ids ();
arch/arm64/kvm/hyp/include/nvhe/ffa.h +1 -0
@@ -11,6 +11,7 @@#define FFA_MIN_FUNC_NUM 0x60#define FFA_MAX_FUNC_NUM 0x7F +int hyp_ffa_init (void );bool kvm_host_ffa_handler (struct kvm_cpu_context *host_ctxt);#endif /* __KVM_HYP_FFA_H */
arch/arm64/kvm/hyp/nvhe/ffa.c +30 -0
@@ -31,6 +31,12 @@#include <nvhe/ffa.h> #include <nvhe/trap_handler.h> +/* +* "ID value 0 must be returned at the Non-secure physical FF-A instance" +* We share this ID with the host. +*/ +#define HOST_FFA_ID 0 +static void ffa_to_smccc_error (struct arm_smccc_res *res,u64 ffa_errno) { *res = (struct arm_smccc_res ) {
@@ -104,3 +110,27 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)ffa_set_retval (host_ctxt, &res);return true; } + +int hyp_ffa_init (void ) +{ +struct arm_smccc_res res; + +if (kvm_host_psci_config.smccc_version <ARM_SMCCC_VERSION_1_2 ) +return 0 ; + +arm_smccc_1_1_smc (FFA_VERSION ,FFA_VERSION_1_0 ,0 ,0 ,0 ,0 ,0 ,0 , &res); +if (res.a0 ==FFA_RET_NOT_SUPPORTED ) +return 0 ; + +if (res.a0 !=FFA_VERSION_1_0 ) +return -EOPNOTSUPP ; + +arm_smccc_1_1_smc (FFA_ID_GET ,0 ,0 ,0 ,0 ,0 ,0 ,0 , &res); +if (res.a0 !=FFA_SUCCESS ) +return -EOPNOTSUPP ; + +if (res.a2 !=HOST_FFA_ID ) +return -EINVAL ; + +return 0 ; +}
arch/arm64/kvm/hyp/nvhe/setup.c +5 -0
@@ -11,6 +11,7 @@#include <asm/kvm_pkvm.h> #include <nvhe/early_alloc.h> +#include <nvhe/ffa.h> #include <nvhe/fixed_config.h> #include <nvhe/gfp.h> #include <nvhe/memory.h>
@@ -314,6 +315,10 @@ void __noreturn __pkvm_init_finalise(void)if (ret) goto out; + ret =hyp_ffa_init (); +if (ret) + goto out; +pkvm_hyp_vm_table_init (vm_table_base); out:/*
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 3/10 KVM: arm64: Allocate pages for hypervisor FF-A mailboxes
In reply to Will DeaconThe FF-A proxy code needs to allocate its own buffer pair for communication with EL3 and for forwarding calls from the host at EL1.
Reserve a couple of pages for this purpose and use them to initialise the hypervisor's FF-A buffer structure.
--- arch/arm64/include/asm/kvm_pkvm.h | 8 ++++++++ arch/arm64/kvm/hyp/include/nvhe/ffa.h | 2 +- arch/arm64/kvm/hyp/nvhe/ffa.c | 24 +++++++++++++++++++++++- arch/arm64/kvm/hyp/nvhe/setup.c | 8 +++++++- arch/arm64/kvm/pkvm.c | 1 + 5 files changed, 40 insertions(+), 3 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +8 -0
@@ -106,4 +106,12 @@ static inline unsigned long host_s2_pgtable_pages(void) return res; } +#define KVM_FFA_MBOX_NR_PAGES 1 + +static inline unsigned long hyp_ffa_proxy_pages (void ) +{ +/* A page each for the hypervisor's RX and TX mailboxes. */ +return 2 *KVM_FFA_MBOX_NR_PAGES ; +} +#endif /* __ARM64_KVM_PKVM_H__ */
arch/arm64/kvm/hyp/include/nvhe/ffa.h +1 -1
@@ -11,7 +11,7 @@#define FFA_MIN_FUNC_NUM 0x60#define FFA_MAX_FUNC_NUM 0x7F -int hyp_ffa_init (void ); +int hyp_ffa_init (void *pages);bool kvm_host_ffa_handler (struct kvm_cpu_context *host_ctxt);#endif /* __KVM_HYP_FFA_H */
arch/arm64/kvm/hyp/nvhe/ffa.c +23 -1
@@ -28,8 +28,11 @@#include <linux/arm-smccc.h> #include <linux/arm_ffa.h> +#include <asm/kvm_pkvm.h> +#include <nvhe/ffa.h> #include <nvhe/trap_handler.h> +#include <nvhe/spinlock.h> /* * "ID value 0 must be returned at the Non-secure physical FF-A instance"
@@ -37,6 +40,19 @@*/ #define HOST_FFA_ID 0 +struct kvm_ffa_buffers { +hyp_spinlock_t lock; +void *tx; +void *rx; +}; + +/* +* Note that we don't currently lock these buffers explicitly, instead +* relying on the locking of the host FFA buffers as we only have one +* client. +*/ +static struct kvm_ffa_buffers hyp_buffers; +static void ffa_to_smccc_error (struct arm_smccc_res *res,u64 ffa_errno) { *res = (struct arm_smccc_res ) {
@@ -111,7 +127,7 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)return true; } -int hyp_ffa_init (void ) +int hyp_ffa_init (void *pages) {struct arm_smccc_res res;
@@ -132,5 +148,11 @@ int hyp_ffa_init(void)if (res.a2 !=HOST_FFA_ID )return -EINVAL ; + hyp_buffers = (struct kvm_ffa_buffers ) { + .lock = __HYP_SPIN_LOCK_UNLOCKED, + .tx = pages, + .rx = pages + (KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ), + }; +return 0 ; }
arch/arm64/kvm/hyp/nvhe/setup.c +7 -1
@@ -29,6 +29,7 @@ static void *vmemmap_base;static void *vm_table_base;static void *hyp_pgt_base;static void *host_s2_pgt_base; +static void *ffa_proxy_pages;static struct kvm_pgtable_mm_ops pkvm_pgtable_mm_ops;static struct hyp_pool hpool;
@@ -58,6 +59,11 @@ static int divide_memory_pool(void *virt, unsigned long size)if (!host_s2_pgt_base)return -ENOMEM ; + nr_pages =hyp_ffa_proxy_pages (); + ffa_proxy_pages =hyp_early_alloc_contig (nr_pages); +if (!ffa_proxy_pages) +return -ENOMEM ; +return 0 ; }
@@ -315,7 +321,7 @@ void __noreturn __pkvm_init_finalise(void)if (ret) goto out; - ret =hyp_ffa_init (); + ret =hyp_ffa_init (ffa_proxy_pages);if (ret) goto out;
arch/arm64/kvm/pkvm.c +1 -0
@@ -74,6 +74,7 @@ void __init kvm_hyp_reserve(void) hyp_mem_pages +=host_s2_pgtable_pages (); hyp_mem_pages +=hyp_vm_table_pages (); hyp_mem_pages +=hyp_vmemmap_pages (STRUCT_HYP_PAGE_SIZE ); + hyp_mem_pages +=hyp_ffa_proxy_pages ();/* * Try to allocate a PMD-aligned region to reduce TLB pressure once
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 4/10 KVM: arm64: Handle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host
In reply to Will DeaconHandle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host by sharing the host's mailbox memory with the hypervisor and establishing a separate pair of mailboxes between the hypervisor and the SPMD at EL3.
--- arch/arm64/kvm/hyp/nvhe/ffa.c | 184 ++++++++++++++++++++++++++++++++++ include/linux/arm_ffa.h | 8 ++ 2 files changed, 192 insertions(+)
arch/arm64/kvm/hyp/nvhe/ffa.c +184 -0
@@ -31,6 +31,8 @@#include <asm/kvm_pkvm.h> #include <nvhe/ffa.h> +#include <nvhe/mem_protect.h> +#include <nvhe/memory.h> #include <nvhe/trap_handler.h> #include <nvhe/spinlock.h>
@@ -52,6 +54,7 @@ struct kvm_ffa_buffers {* client. */ static struct kvm_ffa_buffers hyp_buffers; +static struct kvm_ffa_buffers host_buffers;static void ffa_to_smccc_error (struct arm_smccc_res *res,u64 ffa_errno) {
@@ -61,6 +64,15 @@ static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno) }; } +static void ffa_to_smccc_res (struct arm_smccc_res *res,int ret) +{ +if (ret ==FFA_RET_SUCCESS ) { + *res = (struct arm_smccc_res ) { .a0 =FFA_SUCCESS }; + }else { +ffa_to_smccc_error (res, ret); + } +} +static void ffa_set_retval (struct kvm_cpu_context *ctxt,struct arm_smccc_res *res) {
@@ -78,6 +90,140 @@ static bool is_ffa_call(u64 func_id)ARM_SMCCC_FUNC_NUM (func_id) <=FFA_MAX_FUNC_NUM ; } +static int spmd_map_ffa_buffers (u64 ffa_page_count) +{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_FN64_RXTX_MAP , +hyp_virt_to_phys (hyp_buffers.tx), +hyp_virt_to_phys (hyp_buffers.rx), + ffa_page_count, +0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +} + +static int spmd_unmap_ffa_buffers (void ) +{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_RXTX_UNMAP , +HOST_FFA_ID , +0 ,0 ,0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +} + +static void do_ffa_rxtx_map (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) +{ +DECLARE_REG (phys_addr_t, tx, ctxt,1 ); +DECLARE_REG (phys_addr_t, rx, ctxt,2 ); +DECLARE_REG (u32, npages, ctxt,3 ); +int ret =0 ; +void *rx_virt, *tx_virt; + +if (npages != (KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ) /FFA_PAGE_SIZE ) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +if (!PAGE_ALIGNED (tx) || !PAGE_ALIGNED (rx)) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +hyp_spin_lock (&host_buffers.lock); +if (host_buffers.tx) { + ret =FFA_RET_DENIED ; + goto out_unlock; + } + + ret =spmd_map_ffa_buffers (npages); +if (ret) + goto out_unlock; + + ret =__pkvm_host_share_hyp (hyp_phys_to_pfn (tx)); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unmap; + } + + ret =__pkvm_host_share_hyp (hyp_phys_to_pfn (rx)); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unshare_tx; + } + + tx_virt =hyp_phys_to_virt (tx); + ret =hyp_pin_shared_mem (tx_virt, tx_virt +1 ); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unshare_rx; + } + + rx_virt =hyp_phys_to_virt (rx); + ret =hyp_pin_shared_mem (rx_virt, rx_virt +1 ); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unpin_tx; + } + + host_buffers.tx = tx_virt; + host_buffers.rx = rx_virt; + +out_unlock: +hyp_spin_unlock (&host_buffers.lock); +out: +ffa_to_smccc_res (res, ret); +return ; + +err_unpin_tx: +hyp_unpin_shared_mem (tx_virt, tx_virt +1 ); +err_unshare_rx: +__pkvm_host_unshare_hyp (hyp_phys_to_pfn (rx)); +err_unshare_tx: +__pkvm_host_unshare_hyp (hyp_phys_to_pfn (tx)); +err_unmap: +spmd_unmap_ffa_buffers (); + goto out_unlock; +} + +static void do_ffa_rxtx_unmap (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) +{ +DECLARE_REG (u32, id, ctxt,1 ); +int ret =0 ; + +if (id !=HOST_FFA_ID ) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +hyp_spin_lock (&host_buffers.lock); +if (!host_buffers.tx) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out_unlock; + } + +hyp_unpin_shared_mem (host_buffers.tx, host_buffers.tx +1 ); +WARN_ON (__pkvm_host_unshare_hyp (hyp_virt_to_pfn (host_buffers.tx))); + host_buffers.tx =NULL ; + +hyp_unpin_shared_mem (host_buffers.rx, host_buffers.rx +1 ); +WARN_ON (__pkvm_host_unshare_hyp (hyp_virt_to_pfn (host_buffers.rx))); + host_buffers.rx =NULL ; + +spmd_unmap_ffa_buffers (); + +out_unlock: +hyp_spin_unlock (&host_buffers.lock); +out: +ffa_to_smccc_res (res, ret); +} +/* * Is a given FFA function supported, either by forwarding on directly * or by handling at EL2?
@@ -119,10 +265,21 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)if (!is_ffa_call (func_id))return false; +switch (func_id) { +/* Memory management */ +case FFA_FN64_RXTX_MAP : +do_ffa_rxtx_map (&res, host_ctxt); + goto out_handled; +case FFA_RXTX_UNMAP : +do_ffa_rxtx_unmap (&res, host_ctxt); + goto out_handled; + } +if (ffa_call_supported (func_id))return false;/* Pass through */ ffa_to_smccc_error (&res,FFA_RET_NOT_SUPPORTED ); +out_handled:ffa_set_retval (host_ctxt, &res);return true; }
@@ -130,6 +287,7 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)int hyp_ffa_init (void *pages) {struct arm_smccc_res res; +size_t min_rxtx_sz;if (kvm_host_psci_config.smccc_version <ARM_SMCCC_VERSION_1_2 )return 0 ;
@@ -148,11 +306,37 @@ int hyp_ffa_init(void *pages)if (res.a2 !=HOST_FFA_ID )return -EINVAL ; +arm_smccc_1_1_smc (FFA_FEATURES ,FFA_FN64_RXTX_MAP , +0 ,0 ,0 ,0 ,0 ,0 , &res); +if (res.a0 !=FFA_SUCCESS ) +return -EOPNOTSUPP ; + +switch (res.a2) { +case FFA_FEAT_RXTX_MIN_SZ_4K : + min_rxtx_sz =SZ_4K ; +break ; +case FFA_FEAT_RXTX_MIN_SZ_16K : + min_rxtx_sz =SZ_16K ; +break ; +case FFA_FEAT_RXTX_MIN_SZ_64K : + min_rxtx_sz =SZ_64K ; +break ; +default : +return -EINVAL ; + } + +if (min_rxtx_sz >PAGE_SIZE ) +return -EOPNOTSUPP ; + hyp_buffers = (struct kvm_ffa_buffers ) { .lock = __HYP_SPIN_LOCK_UNLOCKED, .tx = pages, .rx = pages + (KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ), }; + host_buffers = (struct kvm_ffa_buffers ) { + .lock = __HYP_SPIN_LOCK_UNLOCKED, + }; +return 0 ; }
include/linux/arm_ffa.h +8 -0
@@ -94,6 +94,14 @@*/ #define FFA_PAGE_SIZE SZ_4K +/* +* Minimum buffer size/alignment encodings returned by an FFA_FEATURES +* query for FFA_RXTX_MAP. +*/ +#define FFA_FEAT_RXTX_MIN_SZ_4K 0 +#define FFA_FEAT_RXTX_MIN_SZ_64K 1 +#define FFA_FEAT_RXTX_MIN_SZ_16K 2 +/* FFA Bus/Device/Driver related */ struct ffa_device {int vm_id;
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Re: [PATCH v2 04/10] KVM: arm64: Handle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host
Hi Will,
Quoted 52 lines · Will Deacon
Handle
FFA_RXTX_MAPandFFA_RXTX_UNMAPcalls from the host by sharing the host's mailbox memory with the hypervisor and establishing a separate pair of mailboxes between the hypervisor and the SPMD at EL3.Co-developed-by Andrew Walbran <[email protected]>Signed-off-by Andrew Walbran <[email protected]>Signed-off-by Will Deacon <[email protected]>--- arch/arm64/kvm/hyp/nvhe/ffa.c | 184 ++++++++++++++++++++++++++++++++++ include/linux/arm_ffa.h | 8 ++ 2 files changed, 192 insertions(+)arch/arm64/kvm/hyp/nvhe/ffa.c +12
-0@@ -31,6 +31,8 @@#include <asm/kvm_pkvm.h> #include <nvhe/ffa.h> +#include <nvhe/mem_protect.h> +#include <nvhe/memory.h> #include <nvhe/trap_handler.h> #include <nvhe/spinlock.h> @@ -52,6 +54,7 @@ struct kvm_ffa_buffers {* client. */ static struct kvm_ffa_buffers hyp_buffers; +static struct kvm_ffa_buffers host_buffers;static void ffa_to_smccc_error (struct arm_smccc_res *res,u64 ffa_errno) {@@ -61,6 +64,15 @@ static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno) }; } +static void ffa_to_smccc_res (struct arm_smccc_res *res,int ret) +{ +if (ret ==FFA_RET_SUCCESS ) { + *res = (struct arm_smccc_res ) { .a0 =FFA_SUCCESS }; + }else { +ffa_to_smccc_error (res, ret); + } +} +static void ffa_set_retval (struct kvm_cpu_context *ctxt,struct arm_smccc_res *res) {@@ -78,6 +90,140 @@ static bool is_ffa_call(u64 func_id)ARM_SMCCC_FUNC_NUM (func_id) <=FFA_MAX_FUNC_NUM ; }
<snip>
Quoted 15 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +15
-0+static int spmd_map_ffa_buffers (u64 ffa_page_count) +{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_FN64_RXTX_MAP , +hyp_virt_to_phys (hyp_buffers.tx), +hyp_virt_to_phys (hyp_buffers.rx), + ffa_page_count, +0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +} + +static int spmd_unmap_ffa_buffers (void )
arch/arm64/kvm/hyp/nvhe/ffa.c static bool is_ffa_call(u64 func_id) · line 117
Quoted 10 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +10
-0+{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_RXTX_UNMAP , +HOST_FFA_ID , +0 ,0 ,0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +}
</snip>
I understand SPMD is significant in the context of the whole FF-A spec, but I don't think the degree of precision is necessary to get the point across for someone coming at this from a more KVM-centric background.
So, for the sake of readability, could these helpers be named ffa_map_hyp_buffers()/ffa_unmap_hyp_buffers() (or similar)? Sticking 'hyp' in there somewhere helps the reader understand exactly what is getting mapped, since there are both hyp and host buffers.
Apologies for bikeshedding, but I want to make sure folks w/o knowledge of FF-A can at least grasp some of the mechanics here.
arch/arm64/kvm/hyp/nvhe/ffa.c static bool is_ffa_call(u64 func_id) · line 120
Quoted 2 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +2
-0+static void do_ffa_rxtx_map (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt)
This could do with some clarifying comments as well, since there is rather significant interaction between host MAP calls and how we handle host/hyp buffers.
Quoted 72 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +72
-0+{ +DECLARE_REG (phys_addr_t, tx, ctxt,1 ); +DECLARE_REG (phys_addr_t, rx, ctxt,2 ); +DECLARE_REG (u32, npages, ctxt,3 ); +int ret =0 ; +void *rx_virt, *tx_virt; + +if (npages != (KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ) /FFA_PAGE_SIZE ) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +if (!PAGE_ALIGNED (tx) || !PAGE_ALIGNED (rx)) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +hyp_spin_lock (&host_buffers.lock); +if (host_buffers.tx) { + ret =FFA_RET_DENIED ; + goto out_unlock; + } + + ret =spmd_map_ffa_buffers (npages); +if (ret) + goto out_unlock; + + ret =__pkvm_host_share_hyp (hyp_phys_to_pfn (tx)); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unmap; + } + + ret =__pkvm_host_share_hyp (hyp_phys_to_pfn (rx)); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unshare_tx; + } + + tx_virt =hyp_phys_to_virt (tx); + ret =hyp_pin_shared_mem (tx_virt, tx_virt +1 ); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unshare_rx; + } + + rx_virt =hyp_phys_to_virt (rx); + ret =hyp_pin_shared_mem (rx_virt, rx_virt +1 ); +if (ret) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto err_unpin_tx; + } + + host_buffers.tx = tx_virt; + host_buffers.rx = rx_virt; + +out_unlock: +hyp_spin_unlock (&host_buffers.lock); +out: +ffa_to_smccc_res (res, ret); +return ; + +err_unpin_tx: +hyp_unpin_shared_mem (tx_virt, tx_virt +1 ); +err_unshare_rx: +__pkvm_host_unshare_hyp (hyp_phys_to_pfn (rx)); +err_unshare_tx: +__pkvm_host_unshare_hyp (hyp_phys_to_pfn (tx)); +err_unmap: +spmd_unmap_ffa_buffers (); + goto out_unlock; +}
Signature · 2 lines
Thanks, Oliver
Will Deacon
maintainerRe: [PATCH v2 04/10] KVM: arm64: Handle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host
Hey Oliver,
Quoted 12 lines · Oliver Upton
Quoted 11 lines · Will Deacon
Handle
FFA_RXTX_MAPandFFA_RXTX_UNMAPcalls from the host by sharing the host's mailbox memory with the hypervisor and establishing a separate pair of mailboxes between the hypervisor and the SPMD at EL3.Co-developed-by Andrew Walbran <[email protected]>Signed-off-by Andrew Walbran <[email protected]>Signed-off-by Will Deacon <[email protected]>--- arch/arm64/kvm/hyp/nvhe/ffa.c | 184 ++++++++++++++++++++++++++++++++++ include/linux/arm_ffa.h | 8 ++ 2 files changed, 192 insertions(+)
[...]
Quoted 32 lines
Quoted 15 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +15
-0+static int spmd_map_ffa_buffers (u64 ffa_page_count) +{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_FN64_RXTX_MAP , +hyp_virt_to_phys (hyp_buffers.tx), +hyp_virt_to_phys (hyp_buffers.rx), + ffa_page_count, +0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +} + +static int spmd_unmap_ffa_buffers (void )arch/arm64/kvm/hyp/nvhe/ffa.c static bool is_ffa_call(u64 func_id) · line 117
Quoted 10 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +10
-0+{ +struct arm_smccc_res res; + +arm_smccc_1_1_smc (FFA_RXTX_UNMAP , +HOST_FFA_ID , +0 ,0 ,0 ,0 ,0 ,0 , + &res); + +return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; +}</snip>
I understand SPMD is significant in the context of the whole FF-A spec, but I don't think the degree of precision is necessary to get the point across for someone coming at this from a more KVM-centric background.
Quoted 7 lines
So, for the sake of readability, could these helpers be named ffa_map_hyp_buffers()/ffa_unmap_hyp_buffers() (or similar)? Sticking 'hyp' in there somewhere helps the reader understand exactly what is getting mapped, since there are both hyp and host buffers.
Apologies for bikeshedding, but I want to make sure folks w/o knowledge of FF-A can at least grasp some of the mechanics here.
It's no skin off my back, we can name them whatever you like!
The only thing I ask is that we're consistent, so if it's ok with you, I'll rename all the 'spmd_' functions to 'ffa_'.
Quoted 6 lines
arch/arm64/kvm/hyp/nvhe/ffa.c static bool is_ffa_call(u64 func_id) · line 120
Quoted 2 lines
arch/arm64/kvm/hyp/nvhe/ffa.c +2
-0+static void do_ffa_rxtx_map (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt)This could do with some clarifying comments as well, since there is rather significant interaction between host MAP calls and how we handle host/hyp buffers.
Is there something in particular you think needs to be clarified? The function looks pretty straightforward to me, but then I'm used to it by now. I'd just like to avoid writing a comment that basically describes how the code works because that doesn't help anybody.
Will
Will Deacon
maintainerPATCH v2 5/10 KVM: arm64: Add FF-A helpers to share/unshare memory with secure world
In reply to Will DeaconExtend pKVM's memory protection code so that we can update the host's stage-2 page-table to track pages shared with secure world by the host using FF-A and prevent those pages from being mapped into a guest.
--- arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 3 + arch/arm64/kvm/hyp/nvhe/mem_protect.c | 68 +++++++++++++++++++ 2 files changed, 71 insertions(+)
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h +3 -0
@@ -57,6 +57,7 @@ extern struct host_mmu host_mmu;enum pkvm_component_id {PKVM_ID_HOST ,PKVM_ID_HYP , +PKVM_ID_FFA , };extern unsigned long hyp_nr_cpus;
@@ -66,6 +67,8 @@ int __pkvm_host_share_hyp(u64 pfn);int __pkvm_host_unshare_hyp (u64 pfn);int __pkvm_host_donate_hyp (u64 pfn,u64 nr_pages);int __pkvm_hyp_donate_host (u64 pfn,u64 nr_pages); +int __pkvm_host_share_ffa (u64 pfn,u64 nr_pages); +int __pkvm_host_unshare_ffa (u64 pfn,u64 nr_pages);bool addr_is_memory (phys_addr_t phys);int host_stage2_idmap_locked (phys_addr_t addr,u64 size,enum kvm_pgtable_prot prot);
arch/arm64/kvm/hyp/nvhe/mem_protect.c +68 -0
95 lines not on this page · raw message
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 6/10 KVM: arm64: Handle FFA_MEM_SHARE calls from the host
In reply to Will DeaconIntercept FFA_MEM_SHARE/FFA_FN64_MEM_SHARE calls from the host and transition the host stage-2 page-table entries from the OWNED state to the SHARED_OWNED state prior to forwarding the call onto EL3.
--- arch/arm64/kvm/hyp/nvhe/ffa.c | 155 ++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+)
arch/arm64/kvm/hyp/nvhe/ffa.c +155 -0
@@ -116,6 +116,14 @@ static int spmd_unmap_ffa_buffers(void)return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; } +static void spmd_mem_share (struct arm_smccc_res *res,u32 len,u32 fraglen) +{ +arm_smccc_1_1_smc (FFA_FN64_MEM_SHARE , + len, fraglen, +0 ,0 ,0 ,0 ,0 , + res); +} +static void do_ffa_rxtx_map (struct arm_smccc_res *res,struct kvm_cpu_context *ctxt) {
@@ -224,6 +232,149 @@ static void do_ffa_rxtx_unmap(struct arm_smccc_res *res,ffa_to_smccc_res (res, ret); } +static u32 __ffa_host_share_ranges (struct ffa_mem_region_addr_range *ranges, +u32 nranges) +{ +u32 i; + +for (i =0 ; i < nranges; ++i) { +struct ffa_mem_region_addr_range *range = &ranges[i]; +u64 sz = (u64 )range->pg_cnt *FFA_PAGE_SIZE ; +u64 pfn =hyp_phys_to_pfn (range->address); + +if (!PAGE_ALIGNED (sz)) +break ; + +if (__pkvm_host_share_ffa (pfn, sz /PAGE_SIZE )) +break ; + } + +return i; +} + +static u32 __ffa_host_unshare_ranges (struct ffa_mem_region_addr_range *ranges, +u32 nranges) +{ +u32 i; + +for (i =0 ; i < nranges; ++i) { +struct ffa_mem_region_addr_range *range = &ranges[i]; +u64 sz = (u64 )range->pg_cnt *FFA_PAGE_SIZE ; +u64 pfn =hyp_phys_to_pfn (range->address); + +if (!PAGE_ALIGNED (sz)) +break ; + +if (__pkvm_host_unshare_ffa (pfn, sz /PAGE_SIZE )) +break ; + } + +return i; +} + +static int ffa_host_share_ranges (struct ffa_mem_region_addr_range *ranges, +u32 nranges) +{ +u32 nshared =__ffa_host_share_ranges (ranges, nranges); +int ret =0 ; + +if (nshared != nranges) { +WARN_ON (__ffa_host_unshare_ranges (ranges, nshared) != nshared); + ret =FFA_RET_DENIED ; + } + +return ret; +} + +static int ffa_host_unshare_ranges (struct ffa_mem_region_addr_range *ranges, +u32 nranges) +{ +u32 nunshared =__ffa_host_unshare_ranges (ranges, nranges); +int ret =0 ; + +if (nunshared != nranges) { +WARN_ON (__ffa_host_share_ranges (ranges, nunshared) != nunshared); + ret =FFA_RET_DENIED ; + } + +return ret; +} + +static void do_ffa_mem_share (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) +{ +DECLARE_REG (u32, len, ctxt,1 ); +DECLARE_REG (u32, fraglen, ctxt,2 ); +DECLARE_REG (u64, addr_mbz, ctxt,3 ); +DECLARE_REG (u32, npages_mbz, ctxt,4 ); +struct ffa_composite_mem_region *reg; +struct ffa_mem_region *buf; +int ret =0 ; +u32 offset; + +if (addr_mbz || npages_mbz || fraglen > len || + fraglen >KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +if (fraglen < len) { + ret =FFA_RET_ABORTED ; + goto out; + } + +if (fraglen <sizeof (struct ffa_mem_region ) + +sizeof (struct ffa_mem_region_attributes )) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out; + } + +hyp_spin_lock (&host_buffers.lock); +if (!host_buffers.tx) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out_unlock; + } + + buf = hyp_buffers.tx; +memcpy (buf, host_buffers.tx, fraglen); + + offset = buf->ep_mem_access[0 ].composite_off; +if (!offset || buf->ep_count !=1 || buf->sender_id !=HOST_FFA_ID ) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out_unlock; + } + +if (fraglen < offset +sizeof (struct ffa_composite_mem_region )) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out_unlock; + } + + reg = (void *)buf + offset; +if (fraglen < offset +sizeof (struct ffa_composite_mem_region ) + + reg->addr_range_cnt * +sizeof (struct ffa_mem_region_addr_range )) { + ret =FFA_RET_INVALID_PARAMETERS ; + goto out_unlock; + } + + ret =ffa_host_share_ranges (reg->constituents, reg->addr_range_cnt); +if (ret) + goto out_unlock; + +spmd_mem_share (res, len, fraglen); +if (res->a0 !=FFA_SUCCESS ) { +WARN_ON (ffa_host_unshare_ranges (reg->constituents, + reg->addr_range_cnt)); + } + +out_unlock: +hyp_spin_unlock (&host_buffers.lock); +out: +if (ret) +ffa_to_smccc_res (res, ret); +return ; +} +/* * Is a given FFA function supported, either by forwarding on directly * or by handling at EL2?
@@ -273,6 +424,10 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)case FFA_RXTX_UNMAP :do_ffa_rxtx_unmap (&res, host_ctxt); goto out_handled; +case FFA_MEM_SHARE : +case FFA_FN64_MEM_SHARE : +do_ffa_mem_share (&res, host_ctxt); + goto out_handled; }if (ffa_call_supported (func_id))
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 7/10 KVM: arm64: Handle FFA_MEM_RECLAIM calls from the host
In reply to Will DeaconIntecept FFA_MEM_RECLAIM calls from the host and transition the host stage-2 page-table entries from the SHARED_OWNED state back to the OWNED state once EL3 has confirmed that the secure mapping has been reclaimed.
--- arch/arm64/kvm/hyp/nvhe/ffa.c | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+)
arch/arm64/kvm/hyp/nvhe/ffa.c +79 -0
@@ -124,6 +124,23 @@ static void spmd_mem_share(struct arm_smccc_res *res, u32 len, u32 fraglen) res); } +static void spmd_mem_reclaim (struct arm_smccc_res *res,u32 handle_lo, +u32 handle_hi,u32 flags) +{ +arm_smccc_1_1_smc (FFA_MEM_RECLAIM , + handle_lo, handle_hi, flags, +0 ,0 ,0 ,0 , + res); +} + +static void spmd_retrieve_req (struct arm_smccc_res *res,u32 len) +{ +arm_smccc_1_1_smc (FFA_FN64_MEM_RETRIEVE_REQ , + len, len, +0 ,0 ,0 ,0 ,0 , + res); +} +static void do_ffa_rxtx_map (struct arm_smccc_res *res,struct kvm_cpu_context *ctxt) {
@@ -375,6 +392,65 @@ static void do_ffa_mem_share(struct arm_smccc_res *res,return ; } +static void do_ffa_mem_reclaim (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) +{ +DECLARE_REG (u32, handle_lo, ctxt,1 ); +DECLARE_REG (u32, handle_hi, ctxt,2 ); +DECLARE_REG (u32, flags, ctxt,3 ); +struct ffa_composite_mem_region *reg; +struct ffa_mem_region *buf; +int ret =0 ; +u32 offset; +u64 handle; + + handle =PACK_HANDLE (handle_lo, handle_hi); + +hyp_spin_lock (&host_buffers.lock); + + buf = hyp_buffers.tx; + *buf = (struct ffa_mem_region ) { + .sender_id =HOST_FFA_ID , + .handle = handle, + }; + +spmd_retrieve_req (res,sizeof (*buf)); + buf = hyp_buffers.rx; +if (res->a0 !=FFA_MEM_RETRIEVE_RESP ) + goto out_unlock; + +/* Check for fragmentation */ +if (res->a1 != res->a2) { + ret =FFA_RET_ABORTED ; + goto out_unlock; + } + + offset = buf->ep_mem_access[0 ].composite_off; +/* +* We can trust the SPMD to get this right, but let's at least +* check that we end up with something that doesn't look _completely_ +* bogus. +*/ +if (WARN_ON (offset >KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE )) { + ret =FFA_RET_ABORTED ; + goto out_unlock; + } + + reg = (void *)buf + offset; +spmd_mem_reclaim (res, handle_lo, handle_hi, flags); +if (res->a0 !=FFA_SUCCESS ) + goto out_unlock; + +/* If the SPMD was happy, then we should be too. */ +WARN_ON (ffa_host_unshare_ranges (reg->constituents, + reg->addr_range_cnt)); +out_unlock: +hyp_spin_unlock (&host_buffers.lock); + +if (ret) +ffa_to_smccc_res (res, ret); +} +/* * Is a given FFA function supported, either by forwarding on directly * or by handling at EL2?
@@ -428,6 +504,9 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)case FFA_FN64_MEM_SHARE :do_ffa_mem_share (&res, host_ctxt); goto out_handled; +case FFA_MEM_RECLAIM : +do_ffa_mem_reclaim (&res, host_ctxt); + goto out_handled; }if (ffa_call_supported (func_id))
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 8/10 KVM: arm64: Handle FFA_MEM_LEND calls from the host
In reply to Will DeaconHandle FFA_MEM_LEND calls from the host by treating them identically to FFA_MEM_SHARE calls for the purposes of the host stage-2 page-table, but forwarding on the original request to EL3.
--- arch/arm64/kvm/hyp/nvhe/ffa.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
arch/arm64/kvm/hyp/nvhe/ffa.c +15 -7
@@ -116,10 +116,10 @@ static int spmd_unmap_ffa_buffers(void)return res.a0 ==FFA_SUCCESS ?FFA_RET_SUCCESS : res.a2; } -static void spmd_mem_share (struct arm_smccc_res *res,u32 len,u32 fraglen) +static void spmd_mem_xfer (struct arm_smccc_res *res,u64 func_id,u32 len, +u32 fraglen) { -arm_smccc_1_1_smc (FFA_FN64_MEM_SHARE , - len, fraglen, +arm_smccc_1_1_smc (func_id, len, fraglen,0 ,0 ,0 ,0 ,0 , res); }
@@ -317,8 +317,9 @@ static int ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges,return ret; } -static void do_ffa_mem_share (struct arm_smccc_res *res, -struct kvm_cpu_context *ctxt) +static __always_inline void do_ffa_mem_xfer(const u64 func_id, +struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) {DECLARE_REG (u32, len, ctxt,1 );DECLARE_REG (u32, fraglen, ctxt,2 );
@@ -329,6 +330,9 @@ static void do_ffa_mem_share(struct arm_smccc_res *res,int ret =0 ;u32 offset; +BUILD_BUG_ON (func_id !=FFA_FN64_MEM_SHARE && + func_id !=FFA_FN64_MEM_LEND ); +if (addr_mbz || npages_mbz || fraglen > len || fraglen >KVM_FFA_MBOX_NR_PAGES *PAGE_SIZE ) { ret =FFA_RET_INVALID_PARAMETERS ;
@@ -378,7 +382,7 @@ static void do_ffa_mem_share(struct arm_smccc_res *res,if (ret) goto out_unlock; -spmd_mem_share (res, len, fraglen); +spmd_mem_xfer (res, func_id, len, fraglen);if (res->a0 !=FFA_SUCCESS ) {WARN_ON (ffa_host_unshare_ranges (reg->constituents, reg->addr_range_cnt));
@@ -502,11 +506,15 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt) goto out_handled;case FFA_MEM_SHARE :case FFA_FN64_MEM_SHARE : -do_ffa_mem_share (&res, host_ctxt); +do_ffa_mem_xfer (FFA_FN64_MEM_SHARE , &res, host_ctxt); goto out_handled;case FFA_MEM_RECLAIM :do_ffa_mem_reclaim (&res, host_ctxt); goto out_handled; +case FFA_MEM_LEND : +case FFA_FN64_MEM_LEND : +do_ffa_mem_xfer (FFA_FN64_MEM_LEND , &res, host_ctxt); + goto out_handled; }if (ffa_call_supported (func_id))
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 9/10 KVM: arm64: Handle FFA_FEATURES call from the host
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
Filter out advertising unsupported features, and only advertise features and properties that are supported by the hypervisor proxy.
--- arch/arm64/kvm/hyp/nvhe/ffa.c | 45 +++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-)
arch/arm64/kvm/hyp/nvhe/ffa.c +41 -4
@@ -64,15 +64,21 @@ static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno) }; } -static void ffa_to_smccc_res (struct arm_smccc_res *res,int ret) +static void ffa_to_smccc_res_prop (struct arm_smccc_res *res,int ret,u64 prop) {if (ret ==FFA_RET_SUCCESS ) { - *res = (struct arm_smccc_res ) { .a0 =FFA_SUCCESS }; + *res = (struct arm_smccc_res ) { .a0 =FFA_SUCCESS , + .a2 = prop }; }else {ffa_to_smccc_error (res, ret); } } +static void ffa_to_smccc_res (struct arm_smccc_res *res,int ret) +{ +ffa_to_smccc_res_prop (res, ret,0 ); +} +static void ffa_set_retval (struct kvm_cpu_context *ctxt,struct arm_smccc_res *res) {
@@ -480,14 +486,41 @@ static bool ffa_call_supported(u64 func_id)case FFA_RXTX_MAP :case FFA_MEM_DONATE :case FFA_MEM_RETRIEVE_REQ : -/* Don't advertise any features just yet */ -case FFA_FEATURES :return false; }return true; } +static bool do_ffa_features (struct arm_smccc_res *res, +struct kvm_cpu_context *ctxt) +{ +DECLARE_REG (u32, id, ctxt,1 ); +u64 prop =0 ; +int ret =0 ; + +if (!ffa_call_supported (id)) { + ret =FFA_RET_NOT_SUPPORTED ; + goto out_handled; + } + +switch (id) { +case FFA_MEM_SHARE : +case FFA_FN64_MEM_SHARE : +case FFA_MEM_LEND : +case FFA_FN64_MEM_LEND : + ret =FFA_RET_SUCCESS ; + prop =0 ;/* No support for dynamic buffers */ + goto out_handled; +default : +return false; + } + +out_handled: +ffa_to_smccc_res_prop (res, ret, prop); +return true; +} +bool kvm_host_ffa_handler (struct kvm_cpu_context *host_ctxt) {DECLARE_REG (u64, func_id, host_ctxt,0 );
@@ -497,6 +530,10 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)return false;switch (func_id) { +case FFA_FEATURES : +if (!do_ffa_features (&res, host_ctxt)) +return false; + goto out_handled;/* Memory management */ case FFA_FN64_RXTX_MAP :do_ffa_rxtx_map (&res, host_ctxt);
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog
Will Deacon
maintainerPATCH v2 10/10 KVM: arm64: pkvm: Add support for fragmented FF-A descriptors
In reply to Will DeaconFrom: Quentin Perret <[email protected]>
FF-A memory descriptors may need to be sent in fragments when they don't fit in the mailboxes. Doing so involves using the FRAG_TX and FRAG_RX primitives defined in the FF-A protocol.
Add support in the pKVM FF-A relayer for fragmented descriptors by monitoring outgoing FRAG_TX transactions and by buffering large descriptors on the reclaim path.
--- arch/arm64/include/asm/kvm_pkvm.h | 17 ++- arch/arm64/kvm/hyp/nvhe/ffa.c | 170 ++++++++++++++++++++++++++---- 2 files changed, 162 insertions(+), 25 deletions(-)
arch/arm64/include/asm/kvm_pkvm.h +15 -2
@@ -6,7 +6,9 @@#ifndef __ARM64_KVM_PKVM_H__#define __ARM64_KVM_PKVM_H__ +#include <linux/arm_ffa.h> #include <linux/memblock.h> +#include <linux/scatterlist.h> #include <asm/kvm_pgtable.h> /* Maximum number of VMs that can co-exist under pKVM. */
@@ -110,8 +112,19 @@ static inline unsigned long host_s2_pgtable_pages(void)static inline unsigned long hyp_ffa_proxy_pages (void ) { -/* A page each for the hypervisor's RX and TX mailboxes. */ -return 2 *KVM_FFA_MBOX_NR_PAGES ; +size_t desc_max; + +/* +* The hypervisor FFA proxy needs enough memory to buffer a fragmented +* descriptor returned from EL3 in response to a RETRIEVE_REQ call. +*/ + desc_max =sizeof (struct ffa_mem_region ) + +sizeof (struct ffa_mem_region_attributes ) + +sizeof (struct ffa_composite_mem_region ) + +SG_MAX_SEGMENTS *sizeof (struct ffa_mem_region_addr_range ); + +/* Plus a page each for the hypervisor's RX and TX mailboxes. */ +return (2 *KVM_FFA_MBOX_NR_PAGES ) +DIV_ROUND_UP (desc_max,PAGE_SIZE ); }#endif /* __ARM64_KVM_PKVM_H__ */
arch/arm64/kvm/hyp/nvhe/ffa.c +147 -23
269 lines not on this page · raw message
Signature · 1 line
2.40.0.634.g4ca3ef3211-goog