xref: /kvm-unit-tests/lib/s390x/interrupt.c (revision 8ead801e5a97ba47b7c1691e56867061b94bd57b)
14da93626SDavid Hildenbrand /*
24da93626SDavid Hildenbrand  * s390x interrupt handling
34da93626SDavid Hildenbrand  *
44da93626SDavid Hildenbrand  * Copyright (c) 2017 Red Hat Inc
54da93626SDavid Hildenbrand  *
64da93626SDavid Hildenbrand  * Authors:
74da93626SDavid Hildenbrand  *  David Hildenbrand <david@redhat.com>
84da93626SDavid Hildenbrand  *
94da93626SDavid Hildenbrand  * This code is free software; you can redistribute it and/or modify it
104da93626SDavid Hildenbrand  * under the terms of the GNU Library General Public License version 2.
114da93626SDavid Hildenbrand  */
124da93626SDavid Hildenbrand #include <libcflat.h>
134da93626SDavid Hildenbrand #include <asm/interrupt.h>
144da93626SDavid Hildenbrand #include <asm/barrier.h>
15*8ead801eSJanosch Frank #include <sclp.h>
164da93626SDavid Hildenbrand 
174da93626SDavid Hildenbrand static bool pgm_int_expected;
184da93626SDavid Hildenbrand static struct lowcore *lc;
194da93626SDavid Hildenbrand 
204da93626SDavid Hildenbrand void expect_pgm_int(void)
214da93626SDavid Hildenbrand {
224da93626SDavid Hildenbrand 	pgm_int_expected = true;
234da93626SDavid Hildenbrand 	lc->pgm_int_code = 0;
244da93626SDavid Hildenbrand 	mb();
254da93626SDavid Hildenbrand }
264da93626SDavid Hildenbrand 
273db880b6SDavid Hildenbrand uint16_t clear_pgm_int(void)
283db880b6SDavid Hildenbrand {
293db880b6SDavid Hildenbrand 	uint16_t code;
303db880b6SDavid Hildenbrand 
313db880b6SDavid Hildenbrand 	mb();
323db880b6SDavid Hildenbrand 	code = lc->pgm_int_code;
333db880b6SDavid Hildenbrand 	lc->pgm_int_code = 0;
343db880b6SDavid Hildenbrand 	pgm_int_expected = false;
353db880b6SDavid Hildenbrand 	return code;
363db880b6SDavid Hildenbrand }
373db880b6SDavid Hildenbrand 
384da93626SDavid Hildenbrand void check_pgm_int_code(uint16_t code)
394da93626SDavid Hildenbrand {
404da93626SDavid Hildenbrand 	mb();
414da93626SDavid Hildenbrand 	report("Program interrupt: expected(%d) == received(%d)",
424da93626SDavid Hildenbrand 	       code == lc->pgm_int_code, code, lc->pgm_int_code);
434da93626SDavid Hildenbrand }
444da93626SDavid Hildenbrand 
454da93626SDavid Hildenbrand static void fixup_pgm_int(void)
464da93626SDavid Hildenbrand {
474da93626SDavid Hildenbrand 	switch (lc->pgm_int_code) {
484ef6f57dSJanosch Frank 	case PGM_INT_CODE_PRIVILEGED_OPERATION:
494ef6f57dSJanosch Frank 		/* Normal operation is in supervisor state, so this exception
504ef6f57dSJanosch Frank 		 * was produced intentionally and we should return to the
514ef6f57dSJanosch Frank 		 * supervisor state.
524ef6f57dSJanosch Frank 		 */
534ef6f57dSJanosch Frank 		lc->pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
544ef6f57dSJanosch Frank 		break;
5549a732c7SJanosch Frank 	case PGM_INT_CODE_PROTECTION:
5649a732c7SJanosch Frank 		/* Handling for iep.c test case. */
5749a732c7SJanosch Frank 		if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL &&
5849a732c7SJanosch Frank 		    !(lc->trans_exc_id & 0x08UL))
5949a732c7SJanosch Frank 			lc->pgm_old_psw.addr = lc->sw_int_grs[14];
6049a732c7SJanosch Frank 		break;
614da93626SDavid Hildenbrand 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
624da93626SDavid Hildenbrand 	case PGM_INT_CODE_PAGE_TRANSLATION:
634da93626SDavid Hildenbrand 	case PGM_INT_CODE_TRACE_TABLE:
644da93626SDavid Hildenbrand 	case PGM_INT_CODE_AFX_TRANSLATION:
654da93626SDavid Hildenbrand 	case PGM_INT_CODE_ASX_TRANSLATION:
664da93626SDavid Hildenbrand 	case PGM_INT_CODE_LX_TRANSLATION:
674da93626SDavid Hildenbrand 	case PGM_INT_CODE_EX_TRANSLATION:
684da93626SDavid Hildenbrand 	case PGM_INT_CODE_PRIMARY_AUTHORITY:
694da93626SDavid Hildenbrand 	case PGM_INT_CODE_SECONDARY_AUTHORITY:
704da93626SDavid Hildenbrand 	case PGM_INT_CODE_LFX_TRANSLATION:
714da93626SDavid Hildenbrand 	case PGM_INT_CODE_LSX_TRANSLATION:
724da93626SDavid Hildenbrand 	case PGM_INT_CODE_ALEN_TRANSLATION:
734da93626SDavid Hildenbrand 	case PGM_INT_CODE_ALE_SEQUENCE:
744da93626SDavid Hildenbrand 	case PGM_INT_CODE_ASTE_VALIDITY:
754da93626SDavid Hildenbrand 	case PGM_INT_CODE_ASTE_SEQUENCE:
764da93626SDavid Hildenbrand 	case PGM_INT_CODE_EXTENDED_AUTHORITY:
774da93626SDavid Hildenbrand 	case PGM_INT_CODE_LSTE_SEQUENCE:
784da93626SDavid Hildenbrand 	case PGM_INT_CODE_ASTE_INSTANCE:
794da93626SDavid Hildenbrand 	case PGM_INT_CODE_STACK_FULL:
804da93626SDavid Hildenbrand 	case PGM_INT_CODE_STACK_EMPTY:
814da93626SDavid Hildenbrand 	case PGM_INT_CODE_STACK_SPECIFICATION:
824da93626SDavid Hildenbrand 	case PGM_INT_CODE_STACK_TYPE:
834da93626SDavid Hildenbrand 	case PGM_INT_CODE_STACK_OPERATION:
844da93626SDavid Hildenbrand 	case PGM_INT_CODE_ASCE_TYPE:
854da93626SDavid Hildenbrand 	case PGM_INT_CODE_REGION_FIRST_TRANS:
864da93626SDavid Hildenbrand 	case PGM_INT_CODE_REGION_SECOND_TRANS:
874da93626SDavid Hildenbrand 	case PGM_INT_CODE_REGION_THIRD_TRANS:
884da93626SDavid Hildenbrand 	case PGM_INT_CODE_PER:
894da93626SDavid Hildenbrand 	case PGM_INT_CODE_CRYPTO_OPERATION:
904da93626SDavid Hildenbrand 		/* The interrupt was nullified, the old PSW points at the
914da93626SDavid Hildenbrand 		 * responsible instruction. Forward the PSW so we don't loop.
924da93626SDavid Hildenbrand 		 */
934da93626SDavid Hildenbrand 		lc->pgm_old_psw.addr += lc->pgm_int_id;
944da93626SDavid Hildenbrand 	}
954da93626SDavid Hildenbrand 	/* suppressed/terminated/completed point already at the next address */
964da93626SDavid Hildenbrand }
974da93626SDavid Hildenbrand 
984da93626SDavid Hildenbrand void handle_pgm_int(void)
994da93626SDavid Hildenbrand {
1004da93626SDavid Hildenbrand 	if (!pgm_int_expected)
1014da93626SDavid Hildenbrand 		report_abort("Unexpected program interrupt: %d at %#lx, ilen %d\n",
1024da93626SDavid Hildenbrand 			     lc->pgm_int_code, lc->pgm_old_psw.addr,
1034da93626SDavid Hildenbrand 			     lc->pgm_int_id);
1044da93626SDavid Hildenbrand 
1054da93626SDavid Hildenbrand 	pgm_int_expected = false;
1064da93626SDavid Hildenbrand 	fixup_pgm_int();
1074da93626SDavid Hildenbrand }
1080f87a91aSJanosch Frank 
1090f87a91aSJanosch Frank void handle_ext_int(void)
1100f87a91aSJanosch Frank {
111*8ead801eSJanosch Frank 	if (lc->ext_int_code != EXT_IRQ_SERVICE_SIG) {
1120f87a91aSJanosch Frank 		report_abort("Unexpected external call interrupt: at %#lx",
1130f87a91aSJanosch Frank 			     lc->ext_old_psw.addr);
114*8ead801eSJanosch Frank 	} else {
115*8ead801eSJanosch Frank 		lc->ext_old_psw.mask &= ~PSW_MASK_EXT;
116*8ead801eSJanosch Frank 		lc->sw_int_cr0 &= ~(1UL << 9);
117*8ead801eSJanosch Frank 		sclp_handle_ext();
118*8ead801eSJanosch Frank 		lc->ext_int_code = 0;
119*8ead801eSJanosch Frank 	}
1200f87a91aSJanosch Frank }
1210f87a91aSJanosch Frank 
1220f87a91aSJanosch Frank void handle_mcck_int(void)
1230f87a91aSJanosch Frank {
1240f87a91aSJanosch Frank 	report_abort("Unexpected machine check interrupt: at %#lx",
1250f87a91aSJanosch Frank 		     lc->mcck_old_psw.addr);
1260f87a91aSJanosch Frank }
1270f87a91aSJanosch Frank 
1280f87a91aSJanosch Frank void handle_io_int(void)
1290f87a91aSJanosch Frank {
1300f87a91aSJanosch Frank 	report_abort("Unexpected io interrupt: at %#lx",
1310f87a91aSJanosch Frank 		     lc->io_old_psw.addr);
1320f87a91aSJanosch Frank }
1330f87a91aSJanosch Frank 
1340f87a91aSJanosch Frank void handle_svc_int(void)
1350f87a91aSJanosch Frank {
136d57da8bdSJanosch Frank 	report_abort("Unexpected supervisor call interrupt: at %#lx",
1370f87a91aSJanosch Frank 		     lc->svc_old_psw.addr);
1380f87a91aSJanosch Frank }
139