xref: /kvm-unit-tests/lib/s390x/sie-icpt.c (revision 1f08a91a41402b0e032ecce8ed1b5952cbfca0ea)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Functionality for SIE interception handling.
4  *
5  * Copyright IBM Corp. 2024
6  */
7 
8 #include <sie-icpt.h>
9 
sblk_ip_as_diag(struct kvm_s390_sie_block * sblk)10 struct diag_itext sblk_ip_as_diag(struct kvm_s390_sie_block *sblk)
11 {
12 	union {
13 		struct {
14 			uint64_t ipa : 16;
15 			uint64_t ipb : 32;
16 			uint64_t     : 16;
17 		};
18 		struct diag_itext diag;
19 	} instr = { .ipa = sblk->ipa, .ipb = sblk->ipb };
20 
21 	return instr.diag;
22 }
23 
sie_is_diag_icpt(struct vm * vm,unsigned int diag)24 bool sie_is_diag_icpt(struct vm *vm, unsigned int diag)
25 {
26 	struct diag_itext instr = sblk_ip_as_diag(vm->sblk);
27 	uint8_t icptcode;
28 	uint64_t code;
29 
30 	switch (diag) {
31 	case 0x44:
32 	case 0x9c:
33 	case 0x288:
34 	case 0x308:
35 		icptcode = ICPT_PV_NOTIFY;
36 		break;
37 	case 0x500:
38 		icptcode = ICPT_PV_INSTR;
39 		break;
40 	default:
41 		/* If a new diag is introduced add it to the cases above! */
42 		assert_msg(false, "unknown diag 0x%x", diag);
43 	}
44 
45 	if (sie_is_pv(vm)) {
46 		if (instr.r_1 != 0 || instr.r_2 != 2 || instr.r_base != 5)
47 			return false;
48 		if (instr.displace)
49 			return false;
50 	} else {
51 		icptcode = ICPT_INST;
52 	}
53 	if (vm->sblk->icptcode != icptcode)
54 		return false;
55 	if (instr.opcode != 0x83 || instr.zero)
56 		return false;
57 	code = instr.r_base ? vm->save_area.guest.grs[instr.r_base] : 0;
58 	code = (code + instr.displace) & 0xffff;
59 	return code == diag;
60 }
61