Commit 6dcf7316 authored by Marc Zyngier's avatar Marc Zyngier
Browse files

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: default avatarMarc Zyngier <maz@kernel.org>
parents 367eb095 c5284f6d
Loading
Loading
Loading
Loading
+29 −4
Original line number Diff line number Diff line
@@ -6256,15 +6256,40 @@ to the byte array.
			__u64 nr;
			__u64 args[6];
			__u64 ret;
			__u32 longmode;
			__u32 pad;
			__u64 flags;
		} hypercall;

Unused.  This was once used for 'hypercall to userspace'.  To implement
such functionality, use KVM_EXIT_IO (x86) or KVM_EXIT_MMIO (all except s390).

It is strongly recommended that userspace use ``KVM_EXIT_IO`` (x86) or
``KVM_EXIT_MMIO`` (all except s390) to implement functionality that
requires a guest to interact with host userpace.

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

For arm64:
----------

SMCCC exits can be enabled depending on the configuration of the SMCCC
filter. See the Documentation/virt/kvm/devices/vm.rst
``KVM_ARM_SMCCC_FILTER`` for more details.

``nr`` contains the function ID of the guest's SMCCC call. Userspace is
expected to use the ``KVM_GET_ONE_REG`` ioctl to retrieve the call
parameters from the vCPU's GPRs.

Definition of ``flags``:
 - ``KVM_HYPERCALL_EXIT_SMC``: Indicates that the guest used the SMC
   conduit to initiate the SMCCC call. If this bit is 0 then the guest
   used the HVC conduit for the SMCCC call.

 - ``KVM_HYPERCALL_EXIT_16BIT``: Indicates that the guest used a 16bit
   instruction to initiate the SMCCC call. If this bit is 0 then the
   guest used a 32bit instruction. An AArch64 guest always has this
   bit set to 0.

At the point of exit, PC points to the instruction immediately following
the trapping instruction.

::

		/* KVM_EXIT_TPR_ACCESS */
+79 −0
Original line number Diff line number Diff line
@@ -321,3 +321,82 @@ Allows userspace to query the status of migration mode.
	     if it is enabled
:Returns:   -EFAULT if the given address is not accessible from kernel space;
	    0 in case of success.

6. GROUP: KVM_ARM_VM_SMCCC_CTRL
===============================

:Architectures: arm64

6.1. ATTRIBUTE: KVM_ARM_VM_SMCCC_FILTER (w/o)
---------------------------------------------

:Parameters: Pointer to a ``struct kvm_smccc_filter``

:Returns:

        ======  ===========================================
        EEXIST  Range intersects with a previously inserted
                or reserved range
        EBUSY   A vCPU in the VM has already run
        EINVAL  Invalid filter configuration
        ENOMEM  Failed to allocate memory for the in-kernel
                representation of the SMCCC filter
        ======  ===========================================

Requests the installation of an SMCCC call filter described as follows::

    enum kvm_smccc_filter_action {
            KVM_SMCCC_FILTER_HANDLE = 0,
            KVM_SMCCC_FILTER_DENY,
            KVM_SMCCC_FILTER_FWD_TO_USER,
    };

    struct kvm_smccc_filter {
            __u32 base;
            __u32 nr_functions;
            __u8 action;
            __u8 pad[15];
    };

The filter is defined as a set of non-overlapping ranges. Each
range defines an action to be applied to SMCCC calls within the range.
Userspace can insert multiple ranges into the filter by using
successive calls to this attribute.

The default configuration of KVM is such that all implemented SMCCC
calls are allowed. Thus, the SMCCC filter can be defined sparsely
by userspace, only describing ranges that modify the default behavior.

The range expressed by ``struct kvm_smccc_filter`` is
[``base``, ``base + nr_functions``). The range is not allowed to wrap,
i.e. userspace cannot rely on ``base + nr_functions`` overflowing.

The SMCCC filter applies to both SMC and HVC calls initiated by the
guest. The SMCCC filter gates the in-kernel emulation of SMCCC calls
and as such takes effect before other interfaces that interact with
SMCCC calls (e.g. hypercall bitmap registers).

Actions:

 - ``KVM_SMCCC_FILTER_HANDLE``: Allows the guest SMCCC call to be
   handled in-kernel. It is strongly recommended that userspace *not*
   explicitly describe the allowed SMCCC call ranges.

 - ``KVM_SMCCC_FILTER_DENY``: Rejects the guest SMCCC call in-kernel
   and returns to the guest.

 - ``KVM_SMCCC_FILTER_FWD_TO_USER``: The guest SMCCC call is forwarded
   to userspace with an exit reason of ``KVM_EXIT_HYPERCALL``.

The ``pad`` field is reserved for future use and must be zero. KVM may
return ``-EINVAL`` if the field is nonzero.

KVM reserves the 'Arm Architecture Calls' range of function IDs and
will reject attempts to define a filter for any portion of these ranges:

        =========== ===============
        Start       End (inclusive)
        =========== ===============
        0x8000_0000 0x8000_FFFF
        0xC000_0000 0xC000_FFFF
        =========== ===============
+7 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/jump_label.h>
#include <linux/kvm_types.h>
#include <linux/maple_tree.h>
#include <linux/percpu.h>
#include <linux/psci.h>
#include <asm/arch_gicv3.h>
@@ -228,7 +229,8 @@ struct kvm_arch {
#define KVM_ARCH_FLAG_VM_COUNTER_OFFSET			6
	/* Timer PPIs made immutable */
#define KVM_ARCH_FLAG_TIMER_PPIS_IMMUTABLE		7

	/* SMCCC filter initialized for the VM */
#define KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED		8
	unsigned long flags;

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

	/* Hypercall features firmware registers' descriptor */
	struct kvm_smccc_features smccc_feat;
	struct maple_tree smccc_filter;

	/*
	 * 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);
	(system_supports_32bit_el0() &&				\
	 !static_branch_unlikely(&arm64_mismatched_32bit_el0))

#define kvm_vm_has_ran_once(kvm)					\
	(test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &(kvm)->arch.flags))

int kvm_trng_call(struct kvm_vcpu *vcpu);
#ifdef CONFIG_KVM
extern phys_addr_t hyp_mem_base;
+25 −0
Original line number Diff line number Diff line
@@ -381,6 +381,10 @@ enum {
#endif
};

/* Device Control API on vm fd */
#define KVM_ARM_VM_SMCCC_CTRL		0
#define   KVM_ARM_VM_SMCCC_FILTER	0

/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR	0
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1
@@ -480,6 +484,27 @@ enum {
/* run->fail_entry.hardware_entry_failure_reason codes. */
#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED	(1ULL << 0)

enum kvm_smccc_filter_action {
	KVM_SMCCC_FILTER_HANDLE = 0,
	KVM_SMCCC_FILTER_DENY,
	KVM_SMCCC_FILTER_FWD_TO_USER,

#ifdef __KERNEL__
	NR_SMCCC_FILTER_ACTIONS
#endif
};

struct kvm_smccc_filter {
	__u32 base;
	__u32 nr_functions;
	__u8 action;
	__u8 pad[15];
};

/* arm64-specific KVM_EXIT_HYPERCALL flags */
#define KVM_HYPERCALL_EXIT_SMC		(1U << 0)
#define KVM_HYPERCALL_EXIT_16BIT	(1U << 1)

#endif

#endif /* __ARM_KVM_H__ */
+35 −0
Original line number Diff line number Diff line
@@ -204,6 +204,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
	kvm_destroy_vcpus(kvm);

	kvm_unshare_hyp(kvm, kvm + 1);

	kvm_arm_teardown_hypercalls(kvm);
}

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,
	}
}

static int kvm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
{
	switch (attr->group) {
	case KVM_ARM_VM_SMCCC_CTRL:
		return kvm_vm_smccc_has_attr(kvm, attr);
	default:
		return -ENXIO;
	}
}

static int kvm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
{
	switch (attr->group) {
	case KVM_ARM_VM_SMCCC_CTRL:
		return kvm_vm_smccc_set_attr(kvm, attr);
	default:
		return -ENXIO;
	}
}

long kvm_arch_vm_ioctl(struct file *filp,
		       unsigned int ioctl, unsigned long arg)
{
	struct kvm *kvm = filp->private_data;
	void __user *argp = (void __user *)arg;
	struct kvm_device_attr attr;

	switch (ioctl) {
	case KVM_CREATE_IRQCHIP: {
@@ -1524,6 +1547,18 @@ long kvm_arch_vm_ioctl(struct file *filp,
			return -EFAULT;
		return kvm_vm_ioctl_set_counter_offset(kvm, &offset);
	}
	case KVM_HAS_DEVICE_ATTR: {
		if (copy_from_user(&attr, argp, sizeof(attr)))
			return -EFAULT;

		return kvm_vm_has_attr(kvm, &attr);
	}
	case KVM_SET_DEVICE_ATTR: {
		if (copy_from_user(&attr, argp, sizeof(attr)))
			return -EFAULT;

		return kvm_vm_set_attr(kvm, &attr);
	}
	default:
		return -EINVAL;
	}
Loading