mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
KVM: s390: Add capability that forwards operation exceptions
Setting KVM_CAP_S390_USER_OPEREXEC will forward all operation exceptions to user space. This also includes the 0x0000 instructions managed by KVM_CAP_S390_USER_INSTR0. It's helpful if user space wants to emulate instructions which do not (yet) have an opcode. While we're at it refine the documentation for KVM_CAP_S390_USER_INSTR0. Signed-off-by: Janosch Frank <frankja@linux.ibm.com> Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Acked-by: Christian Borntraeger <borntraeger@linux.ibm.com> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
This commit is contained in:
@@ -7820,7 +7820,7 @@ where 0xff represents CPUs 0-7 in cluster 0.
|
||||
:Architectures: s390
|
||||
:Parameters: none
|
||||
|
||||
With this capability enabled, all illegal instructions 0x0000 (2 bytes) will
|
||||
With this capability enabled, the illegal instruction 0x0000 (2 bytes) will
|
||||
be intercepted and forwarded to user space. User space can use this
|
||||
mechanism e.g. to realize 2-byte software breakpoints. The kernel will
|
||||
not inject an operating exception for these instructions, user space has
|
||||
@@ -8703,6 +8703,21 @@ This capability indicate to the userspace whether a PFNMAP memory region
|
||||
can be safely mapped as cacheable. This relies on the presence of
|
||||
force write back (FWB) feature support on the hardware.
|
||||
|
||||
7.45 KVM_CAP_S390_USER_OPEREXEC
|
||||
-------------------------------
|
||||
|
||||
:Architectures: s390
|
||||
:Parameters: none
|
||||
|
||||
When this capability is enabled KVM forwards all operation exceptions
|
||||
that it doesn't handle itself to user space. This also includes the
|
||||
0x0000 instructions managed by KVM_CAP_S390_USER_INSTR0. This is
|
||||
helpful if user space wants to emulate instructions which are not
|
||||
(yet) implemented in hardware.
|
||||
|
||||
This capability can be enabled dynamically even if VCPUs were already
|
||||
created and are running.
|
||||
|
||||
8. Other capabilities.
|
||||
======================
|
||||
|
||||
|
||||
@@ -648,6 +648,7 @@ struct kvm_arch {
|
||||
int user_sigp;
|
||||
int user_stsi;
|
||||
int user_instr0;
|
||||
int user_operexec;
|
||||
struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS];
|
||||
wait_queue_head_t ipte_wq;
|
||||
int ipte_lock_count;
|
||||
|
||||
@@ -471,6 +471,9 @@ static int handle_operexc(struct kvm_vcpu *vcpu)
|
||||
if (vcpu->arch.sie_block->ipa == 0xb256)
|
||||
return handle_sthyi(vcpu);
|
||||
|
||||
if (vcpu->kvm->arch.user_operexec)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (vcpu->arch.sie_block->ipa == 0 && vcpu->kvm->arch.user_instr0)
|
||||
return -EOPNOTSUPP;
|
||||
rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &newpsw, sizeof(psw_t));
|
||||
|
||||
@@ -606,6 +606,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_SET_GUEST_DEBUG:
|
||||
case KVM_CAP_S390_DIAG318:
|
||||
case KVM_CAP_IRQFD_RESAMPLE:
|
||||
case KVM_CAP_S390_USER_OPEREXEC:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_SET_GUEST_DEBUG2:
|
||||
@@ -921,6 +922,12 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
|
||||
VM_EVENT(kvm, 3, "ENABLE: CAP_S390_CPU_TOPOLOGY %s",
|
||||
r ? "(not available)" : "(success)");
|
||||
break;
|
||||
case KVM_CAP_S390_USER_OPEREXEC:
|
||||
VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_OPEREXEC");
|
||||
kvm->arch.user_operexec = 1;
|
||||
icpt_operexc_on_all_vcpus(kvm);
|
||||
r = 0;
|
||||
break;
|
||||
default:
|
||||
r = -EINVAL;
|
||||
break;
|
||||
|
||||
@@ -963,6 +963,7 @@ struct kvm_enable_cap {
|
||||
#define KVM_CAP_RISCV_MP_STATE_RESET 242
|
||||
#define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243
|
||||
#define KVM_CAP_GUEST_MEMFD_FLAGS 244
|
||||
#define KVM_CAP_S390_USER_OPEREXEC 245
|
||||
|
||||
struct kvm_irq_routing_irqchip {
|
||||
__u32 irqchip;
|
||||
|
||||
@@ -194,6 +194,7 @@ TEST_GEN_PROGS_s390 += s390/debug_test
|
||||
TEST_GEN_PROGS_s390 += s390/cpumodel_subfuncs_test
|
||||
TEST_GEN_PROGS_s390 += s390/shared_zeropage_test
|
||||
TEST_GEN_PROGS_s390 += s390/ucontrol_test
|
||||
TEST_GEN_PROGS_s390 += s390/user_operexec
|
||||
TEST_GEN_PROGS_s390 += rseq_test
|
||||
|
||||
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
|
||||
|
||||
140
tools/testing/selftests/kvm/s390/user_operexec.c
Normal file
140
tools/testing/selftests/kvm/s390/user_operexec.c
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Test operation exception forwarding.
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* Authors:
|
||||
* Janosch Frank <frankja@linux.ibm.com>
|
||||
*/
|
||||
#include "kselftest.h"
|
||||
#include "kvm_util.h"
|
||||
#include "test_util.h"
|
||||
#include "sie.h"
|
||||
|
||||
#include <linux/kvm.h>
|
||||
|
||||
static void guest_code_instr0(void)
|
||||
{
|
||||
asm(".word 0x0000");
|
||||
}
|
||||
|
||||
static void test_user_instr0(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
int rc;
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
static void guest_code_user_operexec(void)
|
||||
{
|
||||
asm(".word 0x0807");
|
||||
}
|
||||
|
||||
static void test_user_operexec(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
int rc;
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
|
||||
/*
|
||||
* Since user_operexec is the superset it can be used for the
|
||||
* 0 instruction.
|
||||
*/
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
/* combine user_instr0 and user_operexec */
|
||||
static void test_user_operexec_combined(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
int rc;
|
||||
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
|
||||
/* Reverse enablement order */
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0);
|
||||
TEST_ASSERT_EQ(0, rc);
|
||||
|
||||
vcpu_run(vcpu);
|
||||
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC);
|
||||
TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807);
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run all tests above.
|
||||
*
|
||||
* Enablement after VCPU has been added is automatically tested since
|
||||
* we enable the capability after VCPU creation.
|
||||
*/
|
||||
static struct testdef {
|
||||
const char *name;
|
||||
void (*test)(void);
|
||||
} testlist[] = {
|
||||
{ "instr0", test_user_instr0 },
|
||||
{ "operexec", test_user_operexec },
|
||||
{ "operexec_combined", test_user_operexec_combined},
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int idx;
|
||||
|
||||
TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0));
|
||||
|
||||
ksft_print_header();
|
||||
ksft_set_plan(ARRAY_SIZE(testlist));
|
||||
for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) {
|
||||
testlist[idx].test();
|
||||
ksft_test_result_pass("%s\n", testlist[idx].name);
|
||||
}
|
||||
ksft_finished();
|
||||
}
|
||||
Reference in New Issue
Block a user