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(run->exit_reason == KVM_EXIT_S390_SIEIC,
39 		    "DIAGNOSE 0x0318 instruction was not intercepted");
40 	TEST_ASSERT(run->s390_sieic.icptcode == ICPT_INSTRUCTION,
41 		    "Unexpected intercept code: 0x%x", run->s390_sieic.icptcode);
42 	TEST_ASSERT((run->s390_sieic.ipa & 0xff00) == IPA0_DIAG,
43 		    "Unexpected IPA0 code: 0x%x", (run->s390_sieic.ipa & 0xff00));
44 
45 	reg = (run->s390_sieic.ipa & 0x00f0) >> 4;
46 	diag318_info = run->s.regs.gprs[reg];
47 
48 	TEST_ASSERT(diag318_info != 0, "DIAGNOSE 0x0318 info not set");
49 
50 	kvm_vm_free(vm);
51 
52 	return diag318_info;
53 }
54 
55 uint64_t get_diag318_info(void)
56 {
57 	static uint64_t diag318_info;
58 	static bool printed_skip;
59 
60 	/*
61 	 * If KVM does not support diag318, then return 0 to
62 	 * ensure tests do not break.
63 	 */
64 	if (!kvm_has_cap(KVM_CAP_S390_DIAG318)) {
65 		if (!printed_skip) {
66 			fprintf(stdout, "KVM_CAP_S390_DIAG318 not supported. "
67 				"Skipping diag318 test.\n");
68 			printed_skip = true;
69 		}
70 		return 0;
71 	}
72 
73 	/*
74 	 * If a test has previously requested the diag318 info,
75 	 * then don't bother spinning up a temporary VM again.
76 	 */
77 	if (!diag318_info)
78 		diag318_info = diag318_handler();
79 
80 	return diag318_info;
81 }
82