Skip to content

Commit 821d935

Browse files
ouptonMarc Zyngier
authored andcommitted
KVM: arm64: Introduce support for userspace SMCCC filtering
As the SMCCC (and related specifications) march towards an 'everything and the kitchen sink' interface for interacting with a system it becomes less likely that KVM will support every related feature. We could do better by letting userspace have a crack at it instead. Allow userspace to define an 'SMCCC filter' that applies to both HVCs and SMCs initiated by the guest. Supporting both conduits with this interface is important for a couple of reasons. Guest SMC usage is table stakes for a nested guest, as HVCs are always taken to the virtual EL2. Additionally, guests may want to interact with a service on the secure side which can now be proxied by userspace. Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20230404154050.2270077-10-oliver.upton@linux.dev
1 parent d824dff commit 821d935

6 files changed

Lines changed: 161 additions & 0 deletions

File tree

Documentation/virt/kvm/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6231,6 +6231,10 @@ requires a guest to interact with host userpace.
62316231
For arm64:
62326232
----------
62336233

6234+
SMCCC exits can be enabled depending on the configuration of the SMCCC
6235+
filter. See the Documentation/virt/kvm/devices/vm.rst
6236+
``KVM_ARM_SMCCC_FILTER`` for more details.
6237+
62346238
``nr`` contains the function ID of the guest's SMCCC call. Userspace is
62356239
expected to use the ``KVM_GET_ONE_REG`` ioctl to retrieve the call
62366240
parameters from the vCPU's GPRs.

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/uapi/asm/kvm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ enum {
372372
#endif
373373
};
374374

375+
/* Device Control API on vm fd */
376+
#define KVM_ARM_VM_SMCCC_CTRL 0
377+
#define KVM_ARM_VM_SMCCC_FILTER 0
378+
375379
/* Device Control API: ARM VGIC */
376380
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
377381
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
@@ -479,6 +483,13 @@ enum kvm_smccc_filter_action {
479483
#endif
480484
};
481485

486+
struct kvm_smccc_filter {
487+
__u32 base;
488+
__u32 nr_functions;
489+
__u8 action;
490+
__u8 pad[15];
491+
};
492+
482493
/* arm64-specific KVM_EXIT_HYPERCALL flags */
483494
#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
484495

arch/arm64/kvm/arm.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,8 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
14441444
static int kvm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
14451445
{
14461446
switch (attr->group) {
1447+
case KVM_ARM_VM_SMCCC_CTRL:
1448+
return kvm_vm_smccc_has_attr(kvm, attr);
14471449
default:
14481450
return -ENXIO;
14491451
}
@@ -1452,6 +1454,8 @@ static int kvm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
14521454
static int kvm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
14531455
{
14541456
switch (attr->group) {
1457+
case KVM_ARM_VM_SMCCC_CTRL:
1458+
return kvm_vm_smccc_set_attr(kvm, attr);
14551459
default:
14561460
return -ENXIO;
14571461
}

arch/arm64/kvm/hypercalls.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,44 @@ static void init_smccc_filter(struct kvm *kvm)
145145
WARN_ON_ONCE(r);
146146
}
147147

148+
static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user *uaddr)
149+
{
150+
const void *zero_page = page_to_virt(ZERO_PAGE(0));
151+
struct kvm_smccc_filter filter;
152+
u32 start, end;
153+
int r;
154+
155+
if (copy_from_user(&filter, uaddr, sizeof(filter)))
156+
return -EFAULT;
157+
158+
if (memcmp(filter.pad, zero_page, sizeof(filter.pad)))
159+
return -EINVAL;
160+
161+
start = filter.base;
162+
end = start + filter.nr_functions - 1;
163+
164+
if (end < start || filter.action >= NR_SMCCC_FILTER_ACTIONS)
165+
return -EINVAL;
166+
167+
mutex_lock(&kvm->lock);
168+
169+
if (kvm_vm_has_ran_once(kvm)) {
170+
r = -EBUSY;
171+
goto out_unlock;
172+
}
173+
174+
r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
175+
xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
176+
if (r)
177+
goto out_unlock;
178+
179+
set_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags);
180+
181+
out_unlock:
182+
mutex_unlock(&kvm->lock);
183+
return r;
184+
}
185+
148186
static u8 kvm_smccc_filter_get_action(struct kvm *kvm, u32 func_id)
149187
{
150188
unsigned long idx = func_id;
@@ -569,3 +607,25 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
569607

570608
return -EINVAL;
571609
}
610+
611+
int kvm_vm_smccc_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
612+
{
613+
switch (attr->attr) {
614+
case KVM_ARM_VM_SMCCC_FILTER:
615+
return 0;
616+
default:
617+
return -ENXIO;
618+
}
619+
}
620+
621+
int kvm_vm_smccc_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
622+
{
623+
void __user *uaddr = (void __user *)attr->addr;
624+
625+
switch (attr->attr) {
626+
case KVM_ARM_VM_SMCCC_FILTER:
627+
return kvm_smccc_set_filter(kvm, uaddr);
628+
default:
629+
return -ENXIO;
630+
}
631+
}

include/kvm/arm_hypercalls.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,7 @@ int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
4949
int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
5050
int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
5151

52+
int kvm_vm_smccc_has_attr(struct kvm *kvm, struct kvm_device_attr *attr);
53+
int kvm_vm_smccc_set_attr(struct kvm *kvm, struct kvm_device_attr *attr);
54+
5255
#endif

0 commit comments

Comments
 (0)