]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: s390: Add capability that forwards operation exceptions
authorJanosch Frank <frankja@linux.ibm.com>
Tue, 8 Jul 2025 12:57:57 +0000 (12:57 +0000)
committerJanosch Frank <frankja@linux.ibm.com>
Fri, 21 Nov 2025 09:26:03 +0000 (10:26 +0100)
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>
Documentation/virt/kvm/api.rst
arch/s390/include/asm/kvm_host.h
arch/s390/kvm/intercept.c
arch/s390/kvm/kvm-s390.c
include/uapi/linux/kvm.h
tools/testing/selftests/kvm/Makefile.kvm
tools/testing/selftests/kvm/s390/user_operexec.c [new file with mode: 0644]

index 72b2fae99a835a87aff68de4cbfe3d633d30e9a4..1bc2a84c59ee71ace07290348dc3848b9a6344d1 100644 (file)
@@ -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.
 ======================
 
index 22cedcaea4756be50dcd65bdd85b83cdb0386dbb..1e4829c70216c8a7f2ef97f854254e647f76d455 100644 (file)
@@ -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;
index c7908950c1f44dd76ad5ace223e06afeed74608d..420ae62977e2ec5b4af269f494b0667b2c0a9937 100644 (file)
@@ -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));
index 70ebc54b1bb1133ed46ada1e33a6aebbbce9467b..56d4730b7c41e2f87a572b36241bdabd2715fc18 100644 (file)
@@ -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;
index 52f6000ab020840ef9b38e545a148ee2e99fd1cb..8ab07396ce3b02ae641d786c8784a498fc21b365 100644 (file)
@@ -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;
index 148d427ff24befa7e144d9214feb3e34018b7d10..87e429206bb874bd01ad6623e318c93fccf0b8b9 100644 (file)
@@ -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)
diff --git a/tools/testing/selftests/kvm/s390/user_operexec.c b/tools/testing/selftests/kvm/s390/user_operexec.c
new file mode 100644 (file)
index 0000000..714906c
--- /dev/null
@@ -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();
+}