1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Test handler for the s390x DIAGNOSE 0x0318 instruction.
4  *
5  * Copyright (C) 2020, IBM
6  */
7 
8 #include "test_util.h"
9 #include "kvm_util.h"
10 
11 #define ICPT_INSTRUCTION	0x04
12 #define IPA0_DIAG		0x8300
13 
14 static void guest_code(void)
15 {
16 	uint64_t diag318_info = 0x12345678;
17 
18 	asm volatile ("diag %0,0,0x318\n" : : "d" (diag318_info));
19 }
20 
21 /*
22  * The DIAGNOSE 0x0318 instruction call must be handled via userspace. As such,
23  * we create an ad-hoc VM here to handle the instruction then extract the
24  * necessary data. It is up to the caller to decide what to do with that data.
25  */
26 static uint64_t diag318_handler(void)
27 {
28 	struct kvm_vcpu *vcpu;
29 	struct kvm_vm *vm;
30 	struct kvm_run *run;
31 	uint64_t reg;
32 	uint64_t diag318_info;
33 
34 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
35 	vcpu_run(vcpu);
36 	run = vcpu->run;
37 
38 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
39 	TEST_ASSERT(run->s390_sieic.icptcode == ICPT_INSTRUCTION,
40 		    "Unexpected intercept code: 0x%x", run->s390_sieic.icptcode);
41 	TEST_ASSERT((run->s390_sieic.ipa & 0xff00) == IPA0_DIAG,
42 		    "Unexpected IPA0 code: 0x%x", (run->s390_sieic.ipa & 0xff00));
43 
44 	reg = (run->s390_sieic.ipa & 0x00f0) >> 4;
45 	diag318_info = run->s.regs.gprs[reg];
46 
47 	TEST_ASSERT(diag318_info != 0, "DIAGNOSE 0x0318 info not set");
48 
49 	kvm_vm_free(vm);
50 
51 	return diag318_info;
52 }
53 
54 uint64_t get_diag318_info(void)
55 {
56 	static uint64_t diag318_info;
57 	static bool printed_skip;
58 
59 	/*
60 	 * If KVM does not support diag318, then return 0 to
61 	 * ensure tests do not break.
62 	 */
63 	if (!kvm_has_cap(KVM_CAP_S390_DIAG318)) {
64 		if (!printed_skip) {
65 			fprintf(stdout, "KVM_CAP_S390_DIAG318 not supported. "
66 				"Skipping diag318 test.\n");
67 			printed_skip = true;
68 		}
69 		return 0;
70 	}
71 
72 	/*
73 	 * If a test has previously requested the diag318 info,
74 	 * then don't bother spinning up a temporary VM again.
75 	 */
76 	if (!diag318_info)
77 		diag318_info = diag318_handler();
78 
79 	return diag318_info;
80 }
81