mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
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>
This commit is contained in:
commit
6dcf7316e0
@ -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 */
|
||||
|
@ -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
|
||||
=========== ===============
|
||||
|
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ static void kvm_handle_guest_serror(struct kvm_vcpu *vcpu, u64 esr)
|
||||
|
||||
static int handle_hvc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_kvm_hvc_arm64(*vcpu_pc(vcpu), vcpu_get_reg(vcpu, 0),
|
||||
kvm_vcpu_hvc_get_imm(vcpu));
|
||||
vcpu->stat.hvc_exit_stat++;
|
||||
@ -52,33 +50,29 @@ static int handle_hvc(struct kvm_vcpu *vcpu)
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = kvm_hvc_call_handler(vcpu);
|
||||
if (ret < 0) {
|
||||
vcpu_set_reg(vcpu, 0, ~0UL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return kvm_smccc_call_handler(vcpu);
|
||||
}
|
||||
|
||||
static int handle_smc(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* "If an SMC instruction executed at Non-secure EL1 is
|
||||
* trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
|
||||
* Trap exception, not a Secure Monitor Call exception [...]"
|
||||
*
|
||||
* We need to advance the PC after the trap, as it would
|
||||
* otherwise return to the same address...
|
||||
*
|
||||
* Only handle SMCs from the virtual EL2 with an immediate of zero and
|
||||
* skip it otherwise.
|
||||
* otherwise return to the same address. Furthermore, pre-incrementing
|
||||
* the PC before potentially exiting to userspace maintains the same
|
||||
* abstraction for both SMCs and HVCs.
|
||||
*/
|
||||
if (!vcpu_is_el2(vcpu) || kvm_vcpu_hvc_get_imm(vcpu)) {
|
||||
kvm_incr_pc(vcpu);
|
||||
|
||||
/*
|
||||
* SMCs with a nonzero immediate are reserved according to DEN0028E 2.9
|
||||
* "SMC and HVC immediate value".
|
||||
*/
|
||||
if (kvm_vcpu_hvc_get_imm(vcpu)) {
|
||||
vcpu_set_reg(vcpu, 0, ~0UL);
|
||||
kvm_incr_pc(vcpu);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -89,13 +83,7 @@ static int handle_smc(struct kvm_vcpu *vcpu)
|
||||
* at Non-secure EL1 is trapped to EL2 if HCR_EL2.TSC==1, rather than
|
||||
* being treated as UNDEFINED.
|
||||
*/
|
||||
ret = kvm_hvc_call_handler(vcpu);
|
||||
if (ret < 0)
|
||||
vcpu_set_reg(vcpu, 0, ~0UL);
|
||||
|
||||
kvm_incr_pc(vcpu);
|
||||
|
||||
return ret;
|
||||
return kvm_smccc_call_handler(vcpu);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -65,7 +65,7 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
|
||||
val[3] = lower_32_bits(cycles);
|
||||
}
|
||||
|
||||
static bool kvm_hvc_call_default_allowed(u32 func_id)
|
||||
static bool kvm_smccc_default_allowed(u32 func_id)
|
||||
{
|
||||
switch (func_id) {
|
||||
/*
|
||||
@ -93,7 +93,7 @@ static bool kvm_hvc_call_default_allowed(u32 func_id)
|
||||
}
|
||||
}
|
||||
|
||||
static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
|
||||
static bool kvm_smccc_test_fw_bmap(struct kvm_vcpu *vcpu, u32 func_id)
|
||||
{
|
||||
struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
|
||||
|
||||
@ -117,20 +117,161 @@ static bool kvm_hvc_call_allowed(struct kvm_vcpu *vcpu, u32 func_id)
|
||||
return test_bit(KVM_REG_ARM_VENDOR_HYP_BIT_PTP,
|
||||
&smccc_feat->vendor_hyp_bmap);
|
||||
default:
|
||||
return kvm_hvc_call_default_allowed(func_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
#define SMC32_ARCH_RANGE_BEGIN ARM_SMCCC_VERSION_FUNC_ID
|
||||
#define SMC32_ARCH_RANGE_END ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
0, ARM_SMCCC_FUNC_MASK)
|
||||
|
||||
#define SMC64_ARCH_RANGE_BEGIN ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_64, \
|
||||
0, 0)
|
||||
#define SMC64_ARCH_RANGE_END ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_64, \
|
||||
0, ARM_SMCCC_FUNC_MASK)
|
||||
|
||||
static void init_smccc_filter(struct kvm *kvm)
|
||||
{
|
||||
int r;
|
||||
|
||||
mt_init(&kvm->arch.smccc_filter);
|
||||
|
||||
/*
|
||||
* Prevent userspace from handling any SMCCC calls in the architecture
|
||||
* range, avoiding the risk of misrepresenting Spectre mitigation status
|
||||
* to the guest.
|
||||
*/
|
||||
r = mtree_insert_range(&kvm->arch.smccc_filter,
|
||||
SMC32_ARCH_RANGE_BEGIN, SMC32_ARCH_RANGE_END,
|
||||
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
WARN_ON_ONCE(r);
|
||||
|
||||
r = mtree_insert_range(&kvm->arch.smccc_filter,
|
||||
SMC64_ARCH_RANGE_BEGIN, SMC64_ARCH_RANGE_END,
|
||||
xa_mk_value(KVM_SMCCC_FILTER_HANDLE),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
WARN_ON_ONCE(r);
|
||||
|
||||
}
|
||||
|
||||
static int kvm_smccc_set_filter(struct kvm *kvm, struct kvm_smccc_filter __user *uaddr)
|
||||
{
|
||||
const void *zero_page = page_to_virt(ZERO_PAGE(0));
|
||||
struct kvm_smccc_filter filter;
|
||||
u32 start, end;
|
||||
int r;
|
||||
|
||||
if (copy_from_user(&filter, uaddr, sizeof(filter)))
|
||||
return -EFAULT;
|
||||
|
||||
if (memcmp(filter.pad, zero_page, sizeof(filter.pad)))
|
||||
return -EINVAL;
|
||||
|
||||
start = filter.base;
|
||||
end = start + filter.nr_functions - 1;
|
||||
|
||||
if (end < start || filter.action >= NR_SMCCC_FILTER_ACTIONS)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
|
||||
if (kvm_vm_has_ran_once(kvm)) {
|
||||
r = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
r = mtree_insert_range(&kvm->arch.smccc_filter, start, end,
|
||||
xa_mk_value(filter.action), GFP_KERNEL_ACCOUNT);
|
||||
if (r)
|
||||
goto out_unlock;
|
||||
|
||||
set_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static u8 kvm_smccc_filter_get_action(struct kvm *kvm, u32 func_id)
|
||||
{
|
||||
unsigned long idx = func_id;
|
||||
void *val;
|
||||
|
||||
if (!test_bit(KVM_ARCH_FLAG_SMCCC_FILTER_CONFIGURED, &kvm->arch.flags))
|
||||
return KVM_SMCCC_FILTER_HANDLE;
|
||||
|
||||
/*
|
||||
* But where's the error handling, you say?
|
||||
*
|
||||
* mt_find() returns NULL if no entry was found, which just so happens
|
||||
* to match KVM_SMCCC_FILTER_HANDLE.
|
||||
*/
|
||||
val = mt_find(&kvm->arch.smccc_filter, &idx, idx);
|
||||
return xa_to_value(val);
|
||||
}
|
||||
|
||||
static u8 kvm_smccc_get_action(struct kvm_vcpu *vcpu, u32 func_id)
|
||||
{
|
||||
/*
|
||||
* Intervening actions in the SMCCC filter take precedence over the
|
||||
* pseudo-firmware register bitmaps.
|
||||
*/
|
||||
u8 action = kvm_smccc_filter_get_action(vcpu->kvm, func_id);
|
||||
if (action != KVM_SMCCC_FILTER_HANDLE)
|
||||
return action;
|
||||
|
||||
if (kvm_smccc_test_fw_bmap(vcpu, func_id) ||
|
||||
kvm_smccc_default_allowed(func_id))
|
||||
return KVM_SMCCC_FILTER_HANDLE;
|
||||
|
||||
return KVM_SMCCC_FILTER_DENY;
|
||||
}
|
||||
|
||||
static void kvm_prepare_hypercall_exit(struct kvm_vcpu *vcpu, u32 func_id)
|
||||
{
|
||||
u8 ec = ESR_ELx_EC(kvm_vcpu_get_esr(vcpu));
|
||||
struct kvm_run *run = vcpu->run;
|
||||
u64 flags = 0;
|
||||
|
||||
if (ec == ESR_ELx_EC_SMC32 || ec == ESR_ELx_EC_SMC64)
|
||||
flags |= KVM_HYPERCALL_EXIT_SMC;
|
||||
|
||||
if (!kvm_vcpu_trap_il_is32bit(vcpu))
|
||||
flags |= KVM_HYPERCALL_EXIT_16BIT;
|
||||
|
||||
run->exit_reason = KVM_EXIT_HYPERCALL;
|
||||
run->hypercall = (typeof(run->hypercall)) {
|
||||
.nr = func_id,
|
||||
.flags = flags,
|
||||
};
|
||||
}
|
||||
|
||||
int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct kvm_smccc_features *smccc_feat = &vcpu->kvm->arch.smccc_feat;
|
||||
u32 func_id = smccc_get_function(vcpu);
|
||||
u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
|
||||
u32 feature;
|
||||
u8 action;
|
||||
gpa_t gpa;
|
||||
|
||||
if (!kvm_hvc_call_allowed(vcpu, func_id))
|
||||
action = kvm_smccc_get_action(vcpu, func_id);
|
||||
switch (action) {
|
||||
case KVM_SMCCC_FILTER_HANDLE:
|
||||
break;
|
||||
case KVM_SMCCC_FILTER_DENY:
|
||||
goto out;
|
||||
case KVM_SMCCC_FILTER_FWD_TO_USER:
|
||||
kvm_prepare_hypercall_exit(vcpu, func_id);
|
||||
return 0;
|
||||
default:
|
||||
WARN_RATELIMIT(1, "Unhandled SMCCC filter action: %d\n", action);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (func_id) {
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
@ -245,6 +386,13 @@ void kvm_arm_init_hypercalls(struct kvm *kvm)
|
||||
smccc_feat->std_bmap = KVM_ARM_SMCCC_STD_FEATURES;
|
||||
smccc_feat->std_hyp_bmap = KVM_ARM_SMCCC_STD_HYP_FEATURES;
|
||||
smccc_feat->vendor_hyp_bmap = KVM_ARM_SMCCC_VENDOR_HYP_FEATURES;
|
||||
|
||||
init_smccc_filter(kvm);
|
||||
}
|
||||
|
||||
void kvm_arm_teardown_hypercalls(struct kvm *kvm)
|
||||
{
|
||||
mtree_destroy(&kvm->arch.smccc_filter);
|
||||
}
|
||||
|
||||
int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu)
|
||||
@ -379,8 +527,7 @@ static int kvm_arm_set_fw_reg_bmap(struct kvm_vcpu *vcpu, u64 reg_id, u64 val)
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
|
||||
if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags) &&
|
||||
val != *fw_reg_bmap) {
|
||||
if (kvm_vm_has_ran_once(kvm) && val != *fw_reg_bmap) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
@ -479,3 +626,25 @@ int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int kvm_vm_smccc_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
|
||||
{
|
||||
switch (attr->attr) {
|
||||
case KVM_ARM_VM_SMCCC_FILTER:
|
||||
return 0;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_vm_smccc_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
|
||||
{
|
||||
void __user *uaddr = (void __user *)attr->addr;
|
||||
|
||||
switch (attr->attr) {
|
||||
case KVM_ARM_VM_SMCCC_FILTER:
|
||||
return kvm_smccc_set_filter(kvm, uaddr);
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
@ -880,7 +880,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id)
|
||||
list_for_each_entry(entry, &arm_pmus, entry) {
|
||||
arm_pmu = entry->arm_pmu;
|
||||
if (arm_pmu->pmu.type == pmu_id) {
|
||||
if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags) ||
|
||||
if (kvm_vm_has_ran_once(kvm) ||
|
||||
(kvm->arch.pmu_filter && kvm->arch.arm_pmu != arm_pmu)) {
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
@ -958,7 +958,7 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr)
|
||||
filter.action != KVM_PMU_EVENT_DENY))
|
||||
return -EINVAL;
|
||||
|
||||
if (test_bit(KVM_ARCH_FLAG_HAS_RAN_ONCE, &kvm->arch.flags))
|
||||
if (kvm_vm_has_ran_once(kvm))
|
||||
return -EBUSY;
|
||||
|
||||
if (!kvm->arch.pmu_filter) {
|
||||
|
@ -439,6 +439,7 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
|
||||
int kvm_psci_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 psci_fn = smccc_get_function(vcpu);
|
||||
int version = kvm_psci_version(vcpu);
|
||||
unsigned long val;
|
||||
|
||||
val = kvm_psci_check_allowed_function(vcpu, psci_fn);
|
||||
@ -447,7 +448,7 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (kvm_psci_version(vcpu)) {
|
||||
switch (version) {
|
||||
case KVM_ARM_PSCI_1_1:
|
||||
return kvm_psci_1_x_call(vcpu, 1);
|
||||
case KVM_ARM_PSCI_1_0:
|
||||
@ -457,6 +458,8 @@ int kvm_psci_call(struct kvm_vcpu *vcpu)
|
||||
case KVM_ARM_PSCI_0_1:
|
||||
return kvm_psci_0_1_call(vcpu);
|
||||
default:
|
||||
return -EINVAL;
|
||||
WARN_ONCE(1, "Unknown PSCI version %d", version);
|
||||
smccc_set_retval(vcpu, SMCCC_RET_NOT_SUPPORTED, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -2204,4 +2204,11 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages);
|
||||
KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \
|
||||
KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS)
|
||||
|
||||
/*
|
||||
* KVM previously used a u32 field in kvm_run to indicate the hypercall was
|
||||
* initiated from long mode. KVM now sets bit 0 to indicate long mode, but the
|
||||
* remaining 31 lower bits must be 0 to preserve ABI.
|
||||
*/
|
||||
#define KVM_EXIT_HYPERCALL_MBZ GENMASK_ULL(31, 1)
|
||||
|
||||
#endif /* _ASM_X86_KVM_HOST_H */
|
||||
|
@ -559,4 +559,7 @@ struct kvm_pmu_event_filter {
|
||||
#define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */
|
||||
#define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */
|
||||
|
||||
/* x86-specific KVM_EXIT_HYPERCALL flags. */
|
||||
#define KVM_EXIT_HYPERCALL_LONG_MODE BIT(0)
|
||||
|
||||
#endif /* _ASM_X86_KVM_H */
|
||||
|
@ -9803,7 +9803,11 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
|
||||
vcpu->run->hypercall.args[0] = gpa;
|
||||
vcpu->run->hypercall.args[1] = npages;
|
||||
vcpu->run->hypercall.args[2] = attrs;
|
||||
vcpu->run->hypercall.longmode = op_64_bit;
|
||||
vcpu->run->hypercall.flags = 0;
|
||||
if (op_64_bit)
|
||||
vcpu->run->hypercall.flags |= KVM_EXIT_HYPERCALL_LONG_MODE;
|
||||
|
||||
WARN_ON_ONCE(vcpu->run->hypercall.flags & KVM_EXIT_HYPERCALL_MBZ);
|
||||
vcpu->arch.complete_userspace_io = complete_hypercall_exit;
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <asm/kvm_emulate.h>
|
||||
|
||||
int kvm_hvc_call_handler(struct kvm_vcpu *vcpu);
|
||||
int kvm_smccc_call_handler(struct kvm_vcpu *vcpu);
|
||||
|
||||
static inline u32 smccc_get_function(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
@ -43,9 +43,13 @@ static inline void smccc_set_retval(struct kvm_vcpu *vcpu,
|
||||
struct kvm_one_reg;
|
||||
|
||||
void kvm_arm_init_hypercalls(struct kvm *kvm);
|
||||
void kvm_arm_teardown_hypercalls(struct kvm *kvm);
|
||||
int kvm_arm_get_fw_num_regs(struct kvm_vcpu *vcpu);
|
||||
int kvm_arm_copy_fw_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices);
|
||||
int kvm_arm_get_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
|
||||
int kvm_arm_set_fw_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
|
||||
|
||||
int kvm_vm_smccc_has_attr(struct kvm *kvm, struct kvm_device_attr *attr);
|
||||
int kvm_vm_smccc_set_attr(struct kvm *kvm, struct kvm_device_attr *attr);
|
||||
|
||||
#endif
|
||||
|
@ -341,8 +341,13 @@ struct kvm_run {
|
||||
__u64 nr;
|
||||
__u64 args[6];
|
||||
__u64 ret;
|
||||
__u32 longmode;
|
||||
__u32 pad;
|
||||
|
||||
union {
|
||||
#ifndef __KERNEL__
|
||||
__u32 longmode;
|
||||
#endif
|
||||
__u64 flags;
|
||||
};
|
||||
} hypercall;
|
||||
/* KVM_EXIT_TPR_ACCESS */
|
||||
struct {
|
||||
|
@ -141,6 +141,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/psci_test
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/smccc_filter
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
|
||||
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
|
||||
|
268
tools/testing/selftests/kvm/aarch64/smccc_filter.c
Normal file
268
tools/testing/selftests/kvm/aarch64/smccc_filter.c
Normal file
@ -0,0 +1,268 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* smccc_filter - Tests for the SMCCC filter UAPI.
|
||||
*
|
||||
* Copyright (c) 2023 Google LLC
|
||||
*
|
||||
* This test includes:
|
||||
* - Tests that the UAPI constraints are upheld by KVM. For example, userspace
|
||||
* is prevented from filtering the architecture range of SMCCC calls.
|
||||
* - Test that the filter actions (DENIED, FWD_TO_USER) work as intended.
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/psci.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "processor.h"
|
||||
#include "test_util.h"
|
||||
|
||||
enum smccc_conduit {
|
||||
HVC_INSN,
|
||||
SMC_INSN,
|
||||
};
|
||||
|
||||
#define for_each_conduit(conduit) \
|
||||
for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++)
|
||||
|
||||
static void guest_main(uint32_t func_id, enum smccc_conduit conduit)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
if (conduit == SMC_INSN)
|
||||
smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
else
|
||||
smccc_hvc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
GUEST_SYNC(res.a0);
|
||||
}
|
||||
|
||||
static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
|
||||
enum kvm_smccc_filter_action action)
|
||||
{
|
||||
struct kvm_smccc_filter filter = {
|
||||
.base = start,
|
||||
.nr_functions = nr_functions,
|
||||
.action = action,
|
||||
};
|
||||
|
||||
return __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
|
||||
KVM_ARM_VM_SMCCC_FILTER, &filter);
|
||||
}
|
||||
|
||||
static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
|
||||
enum kvm_smccc_filter_action action)
|
||||
{
|
||||
int ret = __set_smccc_filter(vm, start, nr_functions, action);
|
||||
|
||||
TEST_ASSERT(!ret, "failed to configure SMCCC filter: %d", ret);
|
||||
}
|
||||
|
||||
static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu)
|
||||
{
|
||||
struct kvm_vcpu_init init;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
vm = vm_create(1);
|
||||
vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
|
||||
|
||||
/*
|
||||
* Enable in-kernel emulation of PSCI to ensure that calls are denied
|
||||
* due to the SMCCC filter, not because of KVM.
|
||||
*/
|
||||
init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
|
||||
|
||||
*vcpu = aarch64_vcpu_add(vm, 0, &init, guest_main);
|
||||
return vm;
|
||||
}
|
||||
|
||||
static void test_pad_must_be_zero(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
struct kvm_smccc_filter filter = {
|
||||
.base = PSCI_0_2_FN_PSCI_VERSION,
|
||||
.nr_functions = 1,
|
||||
.action = KVM_SMCCC_FILTER_DENY,
|
||||
.pad = { -1 },
|
||||
};
|
||||
int r;
|
||||
|
||||
r = __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
|
||||
KVM_ARM_VM_SMCCC_FILTER, &filter);
|
||||
TEST_ASSERT(r < 0 && errno == EINVAL,
|
||||
"Setting filter with nonzero padding should return EINVAL");
|
||||
}
|
||||
|
||||
/* Ensure that userspace cannot filter the Arm Architecture SMCCC range */
|
||||
static void test_filter_reserved_range(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
uint32_t smc64_fn;
|
||||
int r;
|
||||
|
||||
r = __set_smccc_filter(vm, ARM_SMCCC_ARCH_WORKAROUND_1,
|
||||
1, KVM_SMCCC_FILTER_DENY);
|
||||
TEST_ASSERT(r < 0 && errno == EEXIST,
|
||||
"Attempt to filter reserved range should return EEXIST");
|
||||
|
||||
smc64_fn = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
|
||||
0, 0);
|
||||
|
||||
r = __set_smccc_filter(vm, smc64_fn, 1, KVM_SMCCC_FILTER_DENY);
|
||||
TEST_ASSERT(r < 0 && errno == EEXIST,
|
||||
"Attempt to filter reserved range should return EEXIST");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void test_invalid_nr_functions(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
int r;
|
||||
|
||||
r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 0, KVM_SMCCC_FILTER_DENY);
|
||||
TEST_ASSERT(r < 0 && errno == EINVAL,
|
||||
"Attempt to filter 0 functions should return EINVAL");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void test_overflow_nr_functions(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
int r;
|
||||
|
||||
r = __set_smccc_filter(vm, ~0, ~0, KVM_SMCCC_FILTER_DENY);
|
||||
TEST_ASSERT(r < 0 && errno == EINVAL,
|
||||
"Attempt to overflow filter range should return EINVAL");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void test_reserved_action(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
int r;
|
||||
|
||||
r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, -1);
|
||||
TEST_ASSERT(r < 0 && errno == EINVAL,
|
||||
"Attempt to use reserved filter action should return EINVAL");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
|
||||
/* Test that overlapping configurations of the SMCCC filter are rejected */
|
||||
static void test_filter_overlap(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm = setup_vm(&vcpu);
|
||||
int r;
|
||||
|
||||
set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
|
||||
|
||||
r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
|
||||
TEST_ASSERT(r < 0 && errno == EEXIST,
|
||||
"Attempt to filter already configured range should return EEXIST");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void expect_call_denied(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct ucall uc;
|
||||
|
||||
if (get_ucall(vcpu, &uc) != UCALL_SYNC)
|
||||
TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd);
|
||||
|
||||
TEST_ASSERT(uc.args[1] == SMCCC_RET_NOT_SUPPORTED,
|
||||
"Unexpected SMCCC return code: %lu", uc.args[1]);
|
||||
}
|
||||
|
||||
/* Denied SMCCC calls have a return code of SMCCC_RET_NOT_SUPPORTED */
|
||||
static void test_filter_denied(void)
|
||||
{
|
||||
enum smccc_conduit conduit;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
for_each_conduit(conduit) {
|
||||
vm = setup_vm(&vcpu);
|
||||
|
||||
set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_DENY);
|
||||
vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
expect_call_denied(vcpu);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
}
|
||||
|
||||
static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t func_id,
|
||||
enum smccc_conduit conduit)
|
||||
{
|
||||
struct kvm_run *run = vcpu->run;
|
||||
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_HYPERCALL,
|
||||
"Unexpected exit reason: %u", run->exit_reason);
|
||||
TEST_ASSERT(run->hypercall.nr == func_id,
|
||||
"Unexpected SMCCC function: %llu", run->hypercall.nr);
|
||||
|
||||
if (conduit == SMC_INSN)
|
||||
TEST_ASSERT(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC,
|
||||
"KVM_HYPERCALL_EXIT_SMC is not set");
|
||||
else
|
||||
TEST_ASSERT(!(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC),
|
||||
"KVM_HYPERCALL_EXIT_SMC is set");
|
||||
}
|
||||
|
||||
/* SMCCC calls forwarded to userspace cause KVM_EXIT_HYPERCALL exits */
|
||||
static void test_filter_fwd_to_user(void)
|
||||
{
|
||||
enum smccc_conduit conduit;
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
|
||||
for_each_conduit(conduit) {
|
||||
vm = setup_vm(&vcpu);
|
||||
|
||||
set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_FWD_TO_USER);
|
||||
vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
expect_call_fwd_to_user(vcpu, PSCI_0_2_FN_PSCI_VERSION, conduit);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
}
|
||||
|
||||
static bool kvm_supports_smccc_filter(void)
|
||||
{
|
||||
struct kvm_vm *vm = vm_create_barebones();
|
||||
int r;
|
||||
|
||||
r = __kvm_has_device_attr(vm->fd, KVM_ARM_VM_SMCCC_CTRL, KVM_ARM_VM_SMCCC_FILTER);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
return !r;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TEST_REQUIRE(kvm_supports_smccc_filter());
|
||||
|
||||
test_pad_must_be_zero();
|
||||
test_invalid_nr_functions();
|
||||
test_overflow_nr_functions();
|
||||
test_reserved_action();
|
||||
test_filter_reserved_range();
|
||||
test_filter_overlap();
|
||||
test_filter_denied();
|
||||
test_filter_fwd_to_user();
|
||||
}
|
@ -214,6 +214,19 @@ void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
|
||||
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
|
||||
uint64_t arg6, struct arm_smccc_res *res);
|
||||
|
||||
/**
|
||||
* smccc_smc - Invoke a SMCCC function using the smc conduit
|
||||
* @function_id: the SMCCC function to be called
|
||||
* @arg0-arg6: SMCCC function arguments, corresponding to registers x1-x7
|
||||
* @res: pointer to write the return values from registers x0-x3
|
||||
*
|
||||
*/
|
||||
void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
|
||||
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
|
||||
uint64_t arg6, struct arm_smccc_res *res);
|
||||
|
||||
|
||||
|
||||
uint32_t guest_get_vcpuid(void);
|
||||
|
||||
#endif /* SELFTEST_KVM_PROCESSOR_H */
|
||||
|
@ -527,29 +527,43 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
|
||||
close(kvm_fd);
|
||||
}
|
||||
|
||||
#define __smccc_call(insn, function_id, arg0, arg1, arg2, arg3, arg4, arg5, \
|
||||
arg6, res) \
|
||||
asm volatile("mov w0, %w[function_id]\n" \
|
||||
"mov x1, %[arg0]\n" \
|
||||
"mov x2, %[arg1]\n" \
|
||||
"mov x3, %[arg2]\n" \
|
||||
"mov x4, %[arg3]\n" \
|
||||
"mov x5, %[arg4]\n" \
|
||||
"mov x6, %[arg5]\n" \
|
||||
"mov x7, %[arg6]\n" \
|
||||
#insn "#0\n" \
|
||||
"mov %[res0], x0\n" \
|
||||
"mov %[res1], x1\n" \
|
||||
"mov %[res2], x2\n" \
|
||||
"mov %[res3], x3\n" \
|
||||
: [res0] "=r"(res->a0), [res1] "=r"(res->a1), \
|
||||
[res2] "=r"(res->a2), [res3] "=r"(res->a3) \
|
||||
: [function_id] "r"(function_id), [arg0] "r"(arg0), \
|
||||
[arg1] "r"(arg1), [arg2] "r"(arg2), [arg3] "r"(arg3), \
|
||||
[arg4] "r"(arg4), [arg5] "r"(arg5), [arg6] "r"(arg6) \
|
||||
: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7")
|
||||
|
||||
|
||||
void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
|
||||
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
|
||||
uint64_t arg6, struct arm_smccc_res *res)
|
||||
{
|
||||
asm volatile("mov w0, %w[function_id]\n"
|
||||
"mov x1, %[arg0]\n"
|
||||
"mov x2, %[arg1]\n"
|
||||
"mov x3, %[arg2]\n"
|
||||
"mov x4, %[arg3]\n"
|
||||
"mov x5, %[arg4]\n"
|
||||
"mov x6, %[arg5]\n"
|
||||
"mov x7, %[arg6]\n"
|
||||
"hvc #0\n"
|
||||
"mov %[res0], x0\n"
|
||||
"mov %[res1], x1\n"
|
||||
"mov %[res2], x2\n"
|
||||
"mov %[res3], x3\n"
|
||||
: [res0] "=r"(res->a0), [res1] "=r"(res->a1),
|
||||
[res2] "=r"(res->a2), [res3] "=r"(res->a3)
|
||||
: [function_id] "r"(function_id), [arg0] "r"(arg0),
|
||||
[arg1] "r"(arg1), [arg2] "r"(arg2), [arg3] "r"(arg3),
|
||||
[arg4] "r"(arg4), [arg5] "r"(arg5), [arg6] "r"(arg6)
|
||||
: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7");
|
||||
__smccc_call(hvc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
|
||||
arg6, res);
|
||||
}
|
||||
|
||||
void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
|
||||
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
|
||||
uint64_t arg6, struct arm_smccc_res *res)
|
||||
{
|
||||
__smccc_call(smc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
|
||||
arg6, res);
|
||||
}
|
||||
|
||||
void kvm_selftest_arch_init(void)
|
||||
|
Loading…
Reference in New Issue
Block a user