3131#include <asm/kvm_pkvm.h>
3232
3333#include <nvhe/ffa.h>
34+ #include <nvhe/mem_protect.h>
35+ #include <nvhe/memory.h>
3436#include <nvhe/trap_handler.h>
3537#include <nvhe/spinlock.h>
3638
@@ -52,6 +54,7 @@ struct kvm_ffa_buffers {
5254 * client.
5355 */
5456static struct kvm_ffa_buffers hyp_buffers ;
57+ static struct kvm_ffa_buffers host_buffers ;
5558
5659static void ffa_to_smccc_error (struct arm_smccc_res * res , u64 ffa_errno )
5760{
@@ -61,6 +64,15 @@ static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno)
6164 };
6265}
6366
67+ static void ffa_to_smccc_res (struct arm_smccc_res * res , int ret )
68+ {
69+ if (ret == FFA_RET_SUCCESS ) {
70+ * res = (struct arm_smccc_res ) { .a0 = FFA_SUCCESS };
71+ } else {
72+ ffa_to_smccc_error (res , ret );
73+ }
74+ }
75+
6476static void ffa_set_retval (struct kvm_cpu_context * ctxt ,
6577 struct arm_smccc_res * res )
6678{
@@ -78,6 +90,144 @@ static bool is_ffa_call(u64 func_id)
7890 ARM_SMCCC_FUNC_NUM (func_id ) <= FFA_MAX_FUNC_NUM ;
7991}
8092
93+ static int ffa_map_hyp_buffers (u64 ffa_page_count )
94+ {
95+ struct arm_smccc_res res ;
96+
97+ arm_smccc_1_1_smc (FFA_FN64_RXTX_MAP ,
98+ hyp_virt_to_phys (hyp_buffers .tx ),
99+ hyp_virt_to_phys (hyp_buffers .rx ),
100+ ffa_page_count ,
101+ 0 , 0 , 0 , 0 ,
102+ & res );
103+
104+ return res .a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res .a2 ;
105+ }
106+
107+ static int ffa_unmap_hyp_buffers (void )
108+ {
109+ struct arm_smccc_res res ;
110+
111+ arm_smccc_1_1_smc (FFA_RXTX_UNMAP ,
112+ HOST_FFA_ID ,
113+ 0 , 0 , 0 , 0 , 0 , 0 ,
114+ & res );
115+
116+ return res .a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res .a2 ;
117+ }
118+
119+ static void do_ffa_rxtx_map (struct arm_smccc_res * res ,
120+ struct kvm_cpu_context * ctxt )
121+ {
122+ DECLARE_REG (phys_addr_t , tx , ctxt , 1 );
123+ DECLARE_REG (phys_addr_t , rx , ctxt , 2 );
124+ DECLARE_REG (u32 , npages , ctxt , 3 );
125+ int ret = 0 ;
126+ void * rx_virt , * tx_virt ;
127+
128+ if (npages != (KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE ) / FFA_PAGE_SIZE ) {
129+ ret = FFA_RET_INVALID_PARAMETERS ;
130+ goto out ;
131+ }
132+
133+ if (!PAGE_ALIGNED (tx ) || !PAGE_ALIGNED (rx )) {
134+ ret = FFA_RET_INVALID_PARAMETERS ;
135+ goto out ;
136+ }
137+
138+ hyp_spin_lock (& host_buffers .lock );
139+ if (host_buffers .tx ) {
140+ ret = FFA_RET_DENIED ;
141+ goto out_unlock ;
142+ }
143+
144+ /*
145+ * Map our hypervisor buffers into the SPMD before mapping and
146+ * pinning the host buffers in our own address space.
147+ */
148+ ret = ffa_map_hyp_buffers (npages );
149+ if (ret )
150+ goto out_unlock ;
151+
152+ ret = __pkvm_host_share_hyp (hyp_phys_to_pfn (tx ));
153+ if (ret ) {
154+ ret = FFA_RET_INVALID_PARAMETERS ;
155+ goto err_unmap ;
156+ }
157+
158+ ret = __pkvm_host_share_hyp (hyp_phys_to_pfn (rx ));
159+ if (ret ) {
160+ ret = FFA_RET_INVALID_PARAMETERS ;
161+ goto err_unshare_tx ;
162+ }
163+
164+ tx_virt = hyp_phys_to_virt (tx );
165+ ret = hyp_pin_shared_mem (tx_virt , tx_virt + 1 );
166+ if (ret ) {
167+ ret = FFA_RET_INVALID_PARAMETERS ;
168+ goto err_unshare_rx ;
169+ }
170+
171+ rx_virt = hyp_phys_to_virt (rx );
172+ ret = hyp_pin_shared_mem (rx_virt , rx_virt + 1 );
173+ if (ret ) {
174+ ret = FFA_RET_INVALID_PARAMETERS ;
175+ goto err_unpin_tx ;
176+ }
177+
178+ host_buffers .tx = tx_virt ;
179+ host_buffers .rx = rx_virt ;
180+
181+ out_unlock :
182+ hyp_spin_unlock (& host_buffers .lock );
183+ out :
184+ ffa_to_smccc_res (res , ret );
185+ return ;
186+
187+ err_unpin_tx :
188+ hyp_unpin_shared_mem (tx_virt , tx_virt + 1 );
189+ err_unshare_rx :
190+ __pkvm_host_unshare_hyp (hyp_phys_to_pfn (rx ));
191+ err_unshare_tx :
192+ __pkvm_host_unshare_hyp (hyp_phys_to_pfn (tx ));
193+ err_unmap :
194+ ffa_unmap_hyp_buffers ();
195+ goto out_unlock ;
196+ }
197+
198+ static void do_ffa_rxtx_unmap (struct arm_smccc_res * res ,
199+ struct kvm_cpu_context * ctxt )
200+ {
201+ DECLARE_REG (u32 , id , ctxt , 1 );
202+ int ret = 0 ;
203+
204+ if (id != HOST_FFA_ID ) {
205+ ret = FFA_RET_INVALID_PARAMETERS ;
206+ goto out ;
207+ }
208+
209+ hyp_spin_lock (& host_buffers .lock );
210+ if (!host_buffers .tx ) {
211+ ret = FFA_RET_INVALID_PARAMETERS ;
212+ goto out_unlock ;
213+ }
214+
215+ hyp_unpin_shared_mem (host_buffers .tx , host_buffers .tx + 1 );
216+ WARN_ON (__pkvm_host_unshare_hyp (hyp_virt_to_pfn (host_buffers .tx )));
217+ host_buffers .tx = NULL ;
218+
219+ hyp_unpin_shared_mem (host_buffers .rx , host_buffers .rx + 1 );
220+ WARN_ON (__pkvm_host_unshare_hyp (hyp_virt_to_pfn (host_buffers .rx )));
221+ host_buffers .rx = NULL ;
222+
223+ ffa_unmap_hyp_buffers ();
224+
225+ out_unlock :
226+ hyp_spin_unlock (& host_buffers .lock );
227+ out :
228+ ffa_to_smccc_res (res , ret );
229+ }
230+
81231/*
82232 * Is a given FFA function supported, either by forwarding on directly
83233 * or by handling at EL2?
@@ -132,17 +282,29 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt)
132282 if (!is_ffa_call (func_id ))
133283 return false;
134284
285+ switch (func_id ) {
286+ /* Memory management */
287+ case FFA_FN64_RXTX_MAP :
288+ do_ffa_rxtx_map (& res , host_ctxt );
289+ goto out_handled ;
290+ case FFA_RXTX_UNMAP :
291+ do_ffa_rxtx_unmap (& res , host_ctxt );
292+ goto out_handled ;
293+ }
294+
135295 if (ffa_call_supported (func_id ))
136296 return false; /* Pass through */
137297
138298 ffa_to_smccc_error (& res , FFA_RET_NOT_SUPPORTED );
299+ out_handled :
139300 ffa_set_retval (host_ctxt , & res );
140301 return true;
141302}
142303
143304int hyp_ffa_init (void * pages )
144305{
145306 struct arm_smccc_res res ;
307+ size_t min_rxtx_sz ;
146308
147309 if (kvm_host_psci_config .smccc_version < ARM_SMCCC_VERSION_1_2 )
148310 return 0 ;
@@ -161,11 +323,37 @@ int hyp_ffa_init(void *pages)
161323 if (res .a2 != HOST_FFA_ID )
162324 return - EINVAL ;
163325
326+ arm_smccc_1_1_smc (FFA_FEATURES , FFA_FN64_RXTX_MAP ,
327+ 0 , 0 , 0 , 0 , 0 , 0 , & res );
328+ if (res .a0 != FFA_SUCCESS )
329+ return - EOPNOTSUPP ;
330+
331+ switch (res .a2 ) {
332+ case FFA_FEAT_RXTX_MIN_SZ_4K :
333+ min_rxtx_sz = SZ_4K ;
334+ break ;
335+ case FFA_FEAT_RXTX_MIN_SZ_16K :
336+ min_rxtx_sz = SZ_16K ;
337+ break ;
338+ case FFA_FEAT_RXTX_MIN_SZ_64K :
339+ min_rxtx_sz = SZ_64K ;
340+ break ;
341+ default :
342+ return - EINVAL ;
343+ }
344+
345+ if (min_rxtx_sz > PAGE_SIZE )
346+ return - EOPNOTSUPP ;
347+
164348 hyp_buffers = (struct kvm_ffa_buffers ) {
165349 .lock = __HYP_SPIN_LOCK_UNLOCKED ,
166350 .tx = pages ,
167351 .rx = pages + (KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE ),
168352 };
169353
354+ host_buffers = (struct kvm_ffa_buffers ) {
355+ .lock = __HYP_SPIN_LOCK_UNLOCKED ,
356+ };
357+
170358 return 0 ;
171359}
0 commit comments