Skip to content

Commit 048be5f

Browse files
willdeaconoupton
authored andcommitted
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. 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-2-will@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent f1fcbaa commit 048be5f

4 files changed

Lines changed: 139 additions & 1 deletion

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (C) 2022 - Google LLC
4+
* Author: Andrew Walbran <qwandor@google.com>
5+
*/
6+
#ifndef __KVM_HYP_FFA_H
7+
#define __KVM_HYP_FFA_H
8+
9+
#include <asm/kvm_host.h>
10+
11+
#define FFA_MIN_FUNC_NUM 0x60
12+
#define FFA_MAX_FUNC_NUM 0x7F
13+
14+
bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt);
15+
16+
#endif /* __KVM_HYP_FFA_H */

arch/arm64/kvm/hyp/nvhe/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ lib-objs := $(addprefix ../../../lib/, $(lib-objs))
2222

2323
hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \
2424
hyp-main.o hyp-smp.o psci-relay.o early_alloc.o page_alloc.o \
25-
cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o
25+
cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o ffa.o
2626
hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
2727
../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o
2828
hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o

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

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

arch/arm64/kvm/hyp/nvhe/hyp-main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/kvm_hyp.h>
1414
#include <asm/kvm_mmu.h>
1515

16+
#include <nvhe/ffa.h>
1617
#include <nvhe/mem_protect.h>
1718
#include <nvhe/mm.h>
1819
#include <nvhe/pkvm.h>
@@ -373,6 +374,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
373374
bool handled;
374375

375376
handled = kvm_host_psci_handler(host_ctxt);
377+
if (!handled)
378+
handled = kvm_host_ffa_handler(host_ctxt);
376379
if (!handled)
377380
default_host_smc_handler(host_ctxt);
378381

0 commit comments

Comments
 (0)