Skip to content

Commit 9d0c6a9

Browse files
willdeaconoupton
authored andcommitted
KVM: arm64: Handle FFA_RXTX_MAP and FFA_RXTX_UNMAP calls from the host
Handle 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. Co-developed-by: Andrew Walbran <qwandor@google.com> Signed-off-by: Andrew Walbran <qwandor@google.com> Signed-off-by: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20230523101828.7328-5-will@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent bc3888a commit 9d0c6a9

2 files changed

Lines changed: 196 additions & 0 deletions

File tree

arch/arm64/kvm/hyp/nvhe/ffa.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
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
*/
5456
static struct kvm_ffa_buffers hyp_buffers;
57+
static struct kvm_ffa_buffers host_buffers;
5558

5659
static 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+
6476
static 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

143304
int 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
}

include/linux/arm_ffa.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@
9494
*/
9595
#define FFA_PAGE_SIZE SZ_4K
9696

97+
/*
98+
* Minimum buffer size/alignment encodings returned by an FFA_FEATURES
99+
* query for FFA_RXTX_MAP.
100+
*/
101+
#define FFA_FEAT_RXTX_MIN_SZ_4K 0
102+
#define FFA_FEAT_RXTX_MIN_SZ_64K 1
103+
#define FFA_FEAT_RXTX_MIN_SZ_16K 2
104+
97105
/* FFA Bus/Device/Driver related */
98106
struct ffa_device {
99107
int vm_id;

0 commit comments

Comments
 (0)