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