Skip to content

Commit 6dcf731

Browse files
author
Marc Zyngier
committed
Merge branch kvm-arm64/smccc-filtering into kvmarm-master/next
* kvm-arm64/smccc-filtering: : . : SMCCC call filtering and forwarding to userspace, courtesy of : Oliver Upton. From the cover letter: : : "The Arm SMCCC is rather prescriptive in regards to the allocation of : SMCCC function ID ranges. Many of the hypercall ranges have an : associated specification from Arm (FF-A, PSCI, SDEI, etc.) with some : room for vendor-specific implementations. : : The ever-expanding SMCCC surface leaves a lot of work within KVM for : providing new features. Furthermore, KVM implements its own : vendor-specific ABI, with little room for other implementations (like : Hyper-V, for example). Rather than cramming it all into the kernel we : should provide a way for userspace to handle hypercalls." : . KVM: selftests: Fix spelling mistake "KVM_HYPERCAL_EXIT_SMC" -> "KVM_HYPERCALL_EXIT_SMC" KVM: arm64: Test that SMC64 arch calls are reserved KVM: arm64: Prevent userspace from handling SMC64 arch range KVM: arm64: Expose SMC/HVC width to userspace KVM: selftests: Add test for SMCCC filter KVM: selftests: Add a helper for SMCCC calls with SMC instruction KVM: arm64: Let errors from SMCCC emulation to reach userspace KVM: arm64: Return NOT_SUPPORTED to guest for unknown PSCI version KVM: arm64: Introduce support for userspace SMCCC filtering KVM: arm64: Add support for KVM_EXIT_HYPERCALL KVM: arm64: Use a maple tree to represent the SMCCC filter KVM: arm64: Refactor hvc filtering to support different actions KVM: arm64: Start handling SMCs from EL1 KVM: arm64: Rename SMC/HVC call handler to reflect reality KVM: arm64: Add vm fd device attribute accessors KVM: arm64: Add a helper to check if a VM has ran once KVM: x86: Redefine 'longmode' as a flag for KVM_EXIT_HYPERCALL Signed-off-by: Marc Zyngier <maz@kernel.org>
2 parents 367eb09 + c5284f6 commit 6dcf731

18 files changed

Lines changed: 712 additions & 63 deletions

File tree

Documentation/virt/kvm/api.rst

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6256,15 +6256,40 @@ to the byte array.
62566256
__u64 nr;
62576257
__u64 args[6];
62586258
__u64 ret;
6259-
__u32 longmode;
6260-
__u32 pad;
6259+
__u64 flags;
62616260
} hypercall;
62626261

6263-
Unused. This was once used for 'hypercall to userspace'. To implement
6264-
such functionality, use KVM_EXIT_IO (x86) or KVM_EXIT_MMIO (all except s390).
6262+
6263+
It is strongly recommended that userspace use ``KVM_EXIT_IO`` (x86) or
6264+
``KVM_EXIT_MMIO`` (all except s390) to implement functionality that
6265+
requires a guest to interact with host userpace.
62656266

62666267
.. note:: KVM_EXIT_IO is significantly faster than KVM_EXIT_MMIO.
62676268

6269+
For arm64:
6270+
----------
6271+
6272+
SMCCC exits can be enabled depending on the configuration of the SMCCC
6273+
filter. See the Documentation/virt/kvm/devices/vm.rst
6274+
``KVM_ARM_SMCCC_FILTER`` for more details.
6275+
6276+
``nr`` contains the function ID of the guest's SMCCC call. Userspace is
6277+
expected to use the ``KVM_GET_ONE_REG`` ioctl to retrieve the call
6278+
parameters from the vCPU's GPRs.
6279+
6280+
Definition of ``flags``:
6281+
- ``KVM_HYPERCALL_EXIT_SMC``: Indicates that the guest used the SMC
6282+
conduit to initiate the SMCCC call. If this bit is 0 then the guest
6283+
used the HVC conduit for the SMCCC call.
6284+
6285+
- ``KVM_HYPERCALL_EXIT_16BIT``: Indicates that the guest used a 16bit
6286+
instruction to initiate the SMCCC call. If this bit is 0 then the
6287+
guest used a 32bit instruction. An AArch64 guest always has this
6288+
bit set to 0.
6289+
6290+
At the point of exit, PC points to the instruction immediately following
6291+
the trapping instruction.
6292+
62686293
::
62696294

62706295
/* KVM_EXIT_TPR_ACCESS */

Documentation/virt/kvm/devices/vm.rst

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,82 @@ Allows userspace to query the status of migration mode.
321321
if it is enabled
322322
:Returns: -EFAULT if the given address is not accessible from kernel space;
323323
0 in case of success.
324+
325+
6. GROUP: KVM_ARM_VM_SMCCC_CTRL
326+
===============================
327+
328+
:Architectures: arm64
329+
330+
6.1. ATTRIBUTE: KVM_ARM_VM_SMCCC_FILTER (w/o)
331+
---------------------------------------------
332+
333+
:Parameters: Pointer to a ``struct kvm_smccc_filter``
334+
335+
:Returns:
336+
337+
====== ===========================================
338+
EEXIST Range intersects with a previously inserted
339+
or reserved range
340+
EBUSY A vCPU in the VM has already run
341+
EINVAL Invalid filter configuration
342+
ENOMEM Failed to allocate memory for the in-kernel
343+
representation of the SMCCC filter
344+
====== ===========================================
345+
346+
Requests the installation of an SMCCC call filter described as follows::
347+
348+
enum kvm_smccc_filter_action {
349+
KVM_SMCCC_FILTER_HANDLE = 0,
350+
KVM_SMCCC_FILTER_DENY,
351+
KVM_SMCCC_FILTER_FWD_TO_USER,
352+
};
353+
354+
struct kvm_smccc_filter {
355+
__u32 base;
356+
__u32 nr_functions;
357+
__u8 action;
358+
__u8 pad[15];
359+
};
360+
361+
The filter is defined as a set of non-overlapping ranges. Each
362+
range defines an action to be applied to SMCCC calls within the range.
363+
Userspace can insert multiple ranges into the filter by using
364+
successive calls to this attribute.
365+
366+
The default configuration of KVM is such that all implemented SMCCC
367+
calls are allowed. Thus, the SMCCC filter can be defined sparsely
368+
by userspace, only describing ranges that modify the default behavior.
369+
370+
The range expressed by ``struct kvm_smccc_filter`` is
371+
[``base``, ``base + nr_functions``). The range is not allowed to wrap,
372+
i.e. userspace cannot rely on ``base + nr_functions`` overflowing.
373+
374+
The SMCCC filter applies to both SMC and HVC calls initiated by the
375+
guest. The SMCCC filter gates the in-kernel emulation of SMCCC calls
376+
and as such takes effect before other interfaces that interact with
377+
SMCCC calls (e.g. hypercall bitmap registers).
378+
379+
Actions:
380+
381+
- ``KVM_SMCCC_FILTER_HANDLE``: Allows the guest SMCCC call to be
382+
handled in-kernel. It is strongly recommended that userspace *not*
383+
explicitly describe the allowed SMCCC call ranges.
384+
385+
- ``KVM_SMCCC_FILTER_DENY``: Rejects the guest SMCCC call in-kernel
386+
and returns to the guest.
387+
388+
- ``KVM_SMCCC_FILTER_FWD_TO_USER``: The guest SMCCC call is forwarded
389+
to userspace with an exit reason of ``KVM_EXIT_HYPERCALL``.
390+
391+
The ``pad`` field is reserved for future use and must be zero. KVM may
392+
return ``-EINVAL`` if the field is nonzero.
393+
394+
KVM reserves the 'Arm Architecture Calls' range of function IDs and
395+
will reject attempts to define a filter for any portion of these ranges:
396+
397+
=========== ===============
398+
Start End (inclusive)
399+
=========== ===============
400+
0x8000_0000 0x8000_FFFF
401+
0xC000_0000 0xC000_FFFF
402+
=========== ===============

arch/arm64/include/asm/kvm_host.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/types.h>
1717
#include <linux/jump_label.h>
1818
#include <linux/kvm_types.h>
19+
#include <linux/maple_tree.h>
1920
#include <linux/percpu.h>
2021
#include <linux/psci.h>
2122
#include <asm/arch_gicv3.h>
@@ -228,7 +229,8 @@ struct kvm_arch {
228229
#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET 6
229230
/* Timer PPIs made immutable */
230231
#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE 7
231-
232+
/* SMCCC filter initialized for the VM */
233+
#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED 8
232234
unsigned long flags;
233235

234236
/*
@@ -249,6 +251,7 @@ struct kvm_arch {
249251

250252
/* Hypercall features firmware registers' descriptor */
251253
struct kvm_smccc_features smccc_feat;
254+
struct maple_tree smccc_filter;
252255

253256
/*
254257
* For an untrusted host VM, 'pkvm.handle' is used to lookup
@@ -1078,6 +1081,9 @@ bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu);
10781081
(system_supports_32bit_el0() && \
10791082
!static_branch_unlikely(&arm64_mismatched_32bit_el0))
10801083

1084+
#define kvm_vm_has_ran_once(kvm) \
1085+
(test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &(kvm)->arch.flags))
1086+
10811087
int kvm_trng_call(struct kvm_vcpu *vcpu);
10821088
#ifdef CONFIG_KVM
10831089
extern phys_addr_t hyp_mem_base;

arch/arm64/include/uapi/asm/kvm.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ enum {
381381
#endif
382382
};
383383

384+
/* Device Control API on vm fd */
385+
#define KVM_ARM_VM_SMCCC_CTRL 0
386+
#define KVM_ARM_VM_SMCCC_FILTER 0
387+
384388
/* Device Control API: ARM VGIC */
385389
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
386390
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
@@ -480,6 +484,27 @@ enum {
480484
/* run->fail_entry.hardware_entry_failure_reason codes. */
481485
#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0)
482486

487+
enum kvm_smccc_filter_action {
488+
KVM_SMCCC_FILTER_HANDLE = 0,
489+
KVM_SMCCC_FILTER_DENY,
490+
KVM_SMCCC_FILTER_FWD_TO_USER,
491+
492+
#ifdef __KERNEL__
493+
NR_SMCCC_FILTER_ACTIONS
494+
#endif
495+
};
496+
497+
struct kvm_smccc_filter {
498+
__u32 base;
499+
__u32 nr_functions;
500+
__u8 action;
501+
__u8 pad[15];
502+
};
503+
504+
/* arm64-specific KVM_EXIT_HYPERCALL flags */
505+
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
506+
#define KVM_HYPERCALL_EXIT_16BIT (1U << 1)
507+
483508
#endif
484509

485510
#endif /* __ARM_KVM_H__ */

arch/arm64/kvm/arm.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
204204
kvm_destroy_vcpus(kvm);
205205

206206
kvm_unshare_hyp(kvm, kvm + 1);
207+
208+
kvm_arm_teardown_hypercalls(kvm);
207209
}
208210

209211
int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
@@ -1477,11 +1479,32 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
14771479
}
14781480
}
14791481

1482+
static int kvm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
1483+
{
1484+
switch (attr->group) {
1485+
case KVM_ARM_VM_SMCCC_CTRL:
1486+
return kvm_vm_smccc_has_attr(kvm, attr);
1487+
default:
1488+
return -ENXIO;
1489+
}
1490+
}
1491+
1492+
static int kvm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
1493+
{
1494+
switch (attr->group) {
1495+
case KVM_ARM_VM_SMCCC_CTRL:
1496+
return kvm_vm_smccc_set_attr(kvm, attr);
1497+
default:
1498+
return -ENXIO;
1499+
}
1500+
}
1501+
14801502
long kvm_arch_vm_ioctl(struct file *filp,
14811503
unsigned int ioctl, unsigned long arg)
14821504
{
14831505
struct kvm *kvm = filp->private_data;
14841506
void __user *argp = (void __user *)arg;
1507+
struct kvm_device_attr attr;
14851508

14861509
switch (ioctl) {
14871510
case KVM_CREATE_IRQCHIP: {
@@ -1524,6 +1547,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
15241547
return -EFAULT;
15251548
return kvm_vm_ioctl_set_counter_offset(kvm, &offset);
15261549
}
1550+
case KVM_HAS_DEVICE_ATTR: {
1551+
if (copy_from_user(&attr, argp, sizeof(attr)))
1552+
return -EFAULT;
1553+
1554+
return kvm_vm_has_attr(kvm, &attr);
1555+
}
1556+
case KVM_SET_DEVICE_ATTR: {
1557+
if (copy_from_user(&attr, argp, sizeof(attr)))
1558+
return -EFAULT;
1559+
1560+
return kvm_vm_set_attr(kvm, &attr);
1561+
}
15271562
default:
15281563
return -EINVAL;
15291564
}

arch/arm64/kvm/handle_exit.c

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ static void kvm_handle_guest_serror(struct kvm_vcpu *vcpu, u64 esr)
3636

3737
static int handle_hvc(struct kvm_vcpu *vcpu)
3838
{
39-
int ret;
40-
4139
trace_kvm_hvc_arm64(*vcpu_pc(vcpu), vcpu_get_reg(vcpu, 0),
4240
kvm_vcpu_hvc_get_imm(vcpu));
4341
vcpu->stat.hvc_exit_stat++;
@@ -52,33 +50,29 @@ static int handle_hvc(struct kvm_vcpu *vcpu)
5250
return 1;
5351
}
5452

55-
ret = kvm_hvc_call_handler(vcpu);
56-
if (ret < 0) {
57-
vcpu_set_reg(vcpu, 0, ~0UL);
58-
return 1;
59-
}
60-
61-
return ret;
53+
return kvm_smccc_call_handler(vcpu);
6254
}
6355

6456
static int handle_smc(struct kvm_vcpu *vcpu)
6557
{
66-
int ret;
67-
6858
/*
6959
* "If an SMC instruction executed at Non-secure EL1 is
7060
* trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
7161
* Trap exception, not a Secure Monitor Call exception [...]"
7262
*
7363
* We need to advance the PC after the trap, as it would
74-
* otherwise return to the same address...
75-
*
76-
* Only handle SMCs from the virtual EL2 with an immediate of zero and
77-
* skip it otherwise.
64+
* otherwise return to the same address. Furthermore, pre-incrementing
65+
* the PC before potentially exiting to userspace maintains the same
66+
* abstraction for both SMCs and HVCs.
67+
*/
68+
kvm_incr_pc(vcpu);
69+
70+
/*
71+
* SMCs with a nonzero immediate are reserved according to DEN0028E 2.9
72+
* "SMC and HVC immediate value".
7873
*/
79-
if (!vcpu_is_el2(vcpu) || kvm_vcpu_hvc_get_imm(vcpu)) {
74+
if (kvm_vcpu_hvc_get_imm(vcpu)) {
8075
vcpu_set_reg(vcpu, 0, ~0UL);
81-
kvm_incr_pc(vcpu);
8276
return 1;
8377
}
8478

@@ -89,13 +83,7 @@ static int handle_smc(struct kvm_vcpu *vcpu)
8983
* at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than
9084
* being treated as UNDEFINED.
9185
*/
92-
ret = kvm_hvc_call_handler(vcpu);
93-
if (ret < 0)
94-
vcpu_set_reg(vcpu, 0, ~0UL);
95-
96-
kvm_incr_pc(vcpu);
97-
98-
return ret;
86+
return kvm_smccc_call_handler(vcpu);
9987
}
10088

10189
/*

0 commit comments

Comments
 (0)