Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+169 -1
+16 -1
Documentation/virt/kvm/api.rst
··· 7820 7820 :Architectures: s390 7821 7821 :Parameters: none 7822 7822 7823 - With this capability enabled, all illegal instructions 0x0000 (2 bytes) will 7823 + With this capability enabled, the illegal instruction 0x0000 (2 bytes) will 7824 7824 be intercepted and forwarded to user space. User space can use this 7825 7825 mechanism e.g. to realize 2-byte software breakpoints. The kernel will 7826 7826 not inject an operating exception for these instructions, user space has ··· 8702 8702 This capability indicate to the userspace whether a PFNMAP memory region 8703 8703 can be safely mapped as cacheable. This relies on the presence of 8704 8704 force write back (FWB) feature support on the hardware. 8705 + 8706 + 7.45 KVM_CAP_S390_USER_OPEREXEC 8707 + ------------------------------- 8708 + 8709 + :Architectures: s390 8710 + :Parameters: none 8711 + 8712 + When this capability is enabled KVM forwards all operation exceptions 8713 + that it doesn't handle itself to user space. This also includes the 8714 + 0x0000 instructions managed by KVM_CAP_S390_USER_INSTR0. This is 8715 + helpful if user space wants to emulate instructions which are not 8716 + (yet) implemented in hardware. 8717 + 8718 + This capability can be enabled dynamically even if VCPUs were already 8719 + created and are running. 8705 8720 8706 8721 8. Other capabilities. 8707 8722 ======================
+1
arch/s390/include/asm/kvm_host.h
··· 648 648 int user_sigp; 649 649 int user_stsi; 650 650 int user_instr0; 651 + int user_operexec; 651 652 struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; 652 653 wait_queue_head_t ipte_wq; 653 654 int ipte_lock_count;
+3
arch/s390/kvm/intercept.c
··· 471 471 if (vcpu->arch.sie_block->ipa == 0xb256) 472 472 return handle_sthyi(vcpu); 473 473 474 + if (vcpu->kvm->arch.user_operexec) 475 + return -EOPNOTSUPP; 476 + 474 477 if (vcpu->arch.sie_block->ipa == 0 && vcpu->kvm->arch.user_instr0) 475 478 return -EOPNOTSUPP; 476 479 rc = read_guest_lc(vcpu, __LC_PGM_NEW_PSW, &newpsw, sizeof(psw_t));
+7
arch/s390/kvm/kvm-s390.c
··· 606 606 case KVM_CAP_SET_GUEST_DEBUG: 607 607 case KVM_CAP_S390_DIAG318: 608 608 case KVM_CAP_IRQFD_RESAMPLE: 609 + case KVM_CAP_S390_USER_OPEREXEC: 609 610 r = 1; 610 611 break; 611 612 case KVM_CAP_SET_GUEST_DEBUG2: ··· 921 920 mutex_unlock(&kvm->lock); 922 921 VM_EVENT(kvm, 3, "ENABLE: CAP_S390_CPU_TOPOLOGY %s", 923 922 r ? "(not available)" : "(success)"); 923 + break; 924 + case KVM_CAP_S390_USER_OPEREXEC: 925 + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_OPEREXEC"); 926 + kvm->arch.user_operexec = 1; 927 + icpt_operexc_on_all_vcpus(kvm); 928 + r = 0; 924 929 break; 925 930 default: 926 931 r = -EINVAL;
+1
include/uapi/linux/kvm.h
··· 963 963 #define KVM_CAP_RISCV_MP_STATE_RESET 242 964 964 #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 965 965 #define KVM_CAP_GUEST_MEMFD_FLAGS 244 966 + #define KVM_CAP_S390_USER_OPEREXEC 245 966 967 967 968 struct kvm_irq_routing_irqchip { 968 969 __u32 irqchip;
+1
tools/testing/selftests/kvm/Makefile.kvm
··· 194 194 TEST_GEN_PROGS_s390 += s390/cpumodel_subfuncs_test 195 195 TEST_GEN_PROGS_s390 += s390/shared_zeropage_test 196 196 TEST_GEN_PROGS_s390 += s390/ucontrol_test 197 + TEST_GEN_PROGS_s390 += s390/user_operexec 197 198 TEST_GEN_PROGS_s390 += rseq_test 198 199 199 200 TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
+140
tools/testing/selftests/kvm/s390/user_operexec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Test operation exception forwarding. 3 + * 4 + * Copyright IBM Corp. 2025 5 + * 6 + * Authors: 7 + * Janosch Frank <frankja@linux.ibm.com> 8 + */ 9 + #include "kselftest.h" 10 + #include "kvm_util.h" 11 + #include "test_util.h" 12 + #include "sie.h" 13 + 14 + #include <linux/kvm.h> 15 + 16 + static void guest_code_instr0(void) 17 + { 18 + asm(".word 0x0000"); 19 + } 20 + 21 + static void test_user_instr0(void) 22 + { 23 + struct kvm_vcpu *vcpu; 24 + struct kvm_vm *vm; 25 + int rc; 26 + 27 + vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); 28 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 29 + TEST_ASSERT_EQ(0, rc); 30 + 31 + vcpu_run(vcpu); 32 + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 33 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 34 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); 35 + 36 + kvm_vm_free(vm); 37 + } 38 + 39 + static void guest_code_user_operexec(void) 40 + { 41 + asm(".word 0x0807"); 42 + } 43 + 44 + static void test_user_operexec(void) 45 + { 46 + struct kvm_vcpu *vcpu; 47 + struct kvm_vm *vm; 48 + int rc; 49 + 50 + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 51 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 52 + TEST_ASSERT_EQ(0, rc); 53 + 54 + vcpu_run(vcpu); 55 + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 56 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 57 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 58 + 59 + kvm_vm_free(vm); 60 + 61 + /* 62 + * Since user_operexec is the superset it can be used for the 63 + * 0 instruction. 64 + */ 65 + vm = vm_create_with_one_vcpu(&vcpu, guest_code_instr0); 66 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 67 + TEST_ASSERT_EQ(0, rc); 68 + 69 + vcpu_run(vcpu); 70 + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 71 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 72 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0); 73 + 74 + kvm_vm_free(vm); 75 + } 76 + 77 + /* combine user_instr0 and user_operexec */ 78 + static void test_user_operexec_combined(void) 79 + { 80 + struct kvm_vcpu *vcpu; 81 + struct kvm_vm *vm; 82 + int rc; 83 + 84 + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 85 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 86 + TEST_ASSERT_EQ(0, rc); 87 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 88 + TEST_ASSERT_EQ(0, rc); 89 + 90 + vcpu_run(vcpu); 91 + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 92 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 93 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 94 + 95 + kvm_vm_free(vm); 96 + 97 + /* Reverse enablement order */ 98 + vm = vm_create_with_one_vcpu(&vcpu, guest_code_user_operexec); 99 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_OPEREXEC, 0); 100 + TEST_ASSERT_EQ(0, rc); 101 + rc = __vm_enable_cap(vm, KVM_CAP_S390_USER_INSTR0, 0); 102 + TEST_ASSERT_EQ(0, rc); 103 + 104 + vcpu_run(vcpu); 105 + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); 106 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_OPEREXC); 107 + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x0807); 108 + 109 + kvm_vm_free(vm); 110 + } 111 + 112 + /* 113 + * Run all tests above. 114 + * 115 + * Enablement after VCPU has been added is automatically tested since 116 + * we enable the capability after VCPU creation. 117 + */ 118 + static struct testdef { 119 + const char *name; 120 + void (*test)(void); 121 + } testlist[] = { 122 + { "instr0", test_user_instr0 }, 123 + { "operexec", test_user_operexec }, 124 + { "operexec_combined", test_user_operexec_combined}, 125 + }; 126 + 127 + int main(int argc, char *argv[]) 128 + { 129 + int idx; 130 + 131 + TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_USER_INSTR0)); 132 + 133 + ksft_print_header(); 134 + ksft_set_plan(ARRAY_SIZE(testlist)); 135 + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { 136 + testlist[idx].test(); 137 + ksft_test_result_pass("%s\n", testlist[idx].name); 138 + } 139 + ksft_finished(); 140 + }