@@ -54,6 +54,9 @@ int main(void)
OFFSET(GEN_LC_PGM_NEW_PSW, lowcore, pgm_new_psw);
OFFSET(GEN_LC_MCCK_NEW_PSW, lowcore, mcck_new_psw);
OFFSET(GEN_LC_IO_NEW_PSW, lowcore, io_new_psw);
+ OFFSET(GEN_LC_SW_INT_GRS, lowcore, sw_int_grs);
+ OFFSET(GEN_LC_SW_INT_FPRS, lowcore, sw_int_fprs);
+ OFFSET(GEN_LC_SW_INT_FPC, lowcore, sw_int_fpc);
OFFSET(GEN_LC_MCCK_EXT_SA_ADDR, lowcore, mcck_ext_sa_addr);
OFFSET(GEN_LC_FPRS_SA, lowcore, fprs_sa);
OFFSET(GEN_LC_GRS_SA, lowcore, grs_sa);
@@ -64,7 +64,11 @@ struct lowcore {
struct psw pgm_new_psw; /* 0x01d0 */
struct psw mcck_new_psw; /* 0x01e0 */
struct psw io_new_psw; /* 0x01f0 */
- uint8_t pad_0x0200[0x11b0 - 0x0200]; /* 0x0200 */
+ /* sw definition: save area for registers in interrupt handlers */
+ uint64_t sw_int_grs[16]; /* 0x0200 */
+ uint64_t sw_int_fprs[16]; /* 0x0280 */
+ uint32_t sw_int_fpc; /* 0x0300 */
+ uint8_t pad_0x0304[0x11b0 - 0x0304]; /* 0x0304 */
uint64_t mcck_ext_sa_addr; /* 0x11b0 */
uint8_t pad_0x11b8[0x1200 - 0x11b8]; /* 0x11b8 */
uint64_t fprs_sa[16]; /* 0x1200 */
@@ -84,4 +88,59 @@ struct lowcore {
uint8_t pgm_int_tdb[0x1900 - 0x1800]; /* 0x1800 */
} __attribute__ ((__packed__));
+#define PGM_INT_CODE_OPERATION 0x01
+#define PGM_INT_CODE_PRIVILEGED_OPERATION 0x02
+#define PGM_INT_CODE_EXECUTE 0x03
+#define PGM_INT_CODE_PROTECTION 0x04
+#define PGM_INT_CODE_ADDRESSING 0x05
+#define PGM_INT_CODE_SPECIFICATION 0x06
+#define PGM_INT_CODE_DATA 0x07
+#define PGM_INT_CODE_FIXED_POINT_OVERFLOW 0x08
+#define PGM_INT_CODE_FIXED_POINT_DIVIDE 0x09
+#define PGM_INT_CODE_DECIMAL_OVERFLOW 0x0a
+#define PGM_INT_CODE_DECIMAL_DIVIDE 0x0b
+#define PGM_INT_CODE_HFP_EXPONENT_OVERFLOW 0x0c
+#define PGM_INT_CODE_HFP_EXPONENT_UNDERFLOW 0x0d
+#define PGM_INT_CODE_HFP_SIGNIFICANCE 0x0e
+#define PGM_INT_CODE_HFP_DIVIDE 0x0f
+#define PGM_INT_CODE_SEGMENT_TRANSLATION 0x10
+#define PGM_INT_CODE_PAGE_TRANSLATION 0x11
+#define PGM_INT_CODE_TRANSLATION_SPEC 0x12
+#define PGM_INT_CODE_SPECIAL_OPERATION 0x13
+#define PGM_INT_CODE_OPERAND 0x15
+#define PGM_INT_CODE_TRACE_TABLE 0x16
+#define PGM_INT_CODE_VECTOR_PROCESSING 0x1b
+#define PGM_INT_CODE_SPACE_SWITCH_EVENT 0x1c
+#define PGM_INT_CODE_HFP_SQUARE_ROOT 0x1d
+#define PGM_INT_CODE_PC_TRANSLATION_SPEC 0x1f
+#define PGM_INT_CODE_AFX_TRANSLATION 0x20
+#define PGM_INT_CODE_ASX_TRANSLATION 0x21
+#define PGM_INT_CODE_LX_TRANSLATION 0x22
+#define PGM_INT_CODE_EX_TRANSLATION 0x23
+#define PGM_INT_CODE_PRIMARY_AUTHORITY 0x24
+#define PGM_INT_CODE_SECONDARY_AUTHORITY 0x25
+#define PGM_INT_CODE_LFX_TRANSLATION 0x26
+#define PGM_INT_CODE_LSX_TRANSLATION 0x27
+#define PGM_INT_CODE_ALET_SPECIFICATION 0x28
+#define PGM_INT_CODE_ALEN_TRANSLATION 0x29
+#define PGM_INT_CODE_ALE_SEQUENCE 0x2a
+#define PGM_INT_CODE_ASTE_VALIDITY 0x2b
+#define PGM_INT_CODE_ASTE_SEQUENCE 0x2c
+#define PGM_INT_CODE_EXTENDED_AUTHORITY 0x2d
+#define PGM_INT_CODE_LSTE_SEQUENCE 0x2e
+#define PGM_INT_CODE_ASTE_INSTANCE 0x2f
+#define PGM_INT_CODE_STACK_FULL 0x30
+#define PGM_INT_CODE_STACK_EMPTY 0x31
+#define PGM_INT_CODE_STACK_SPECIFICATION 0x32
+#define PGM_INT_CODE_STACK_TYPE 0x33
+#define PGM_INT_CODE_STACK_OPERATION 0x34
+#define PGM_INT_CODE_ASCE_TYPE 0x38
+#define PGM_INT_CODE_REGION_FIRST_TRANS 0x39
+#define PGM_INT_CODE_REGION_SECOND_TRANS 0x3a
+#define PGM_INT_CODE_REGION_THIRD_TRANS 0x3b
+#define PGM_INT_CODE_MONITOR_EVENT 0x40
+#define PGM_INT_CODE_PER 0x80
+#define PGM_INT_CODE_CRYPTO_OPERATION 0x119
+#define PGM_INT_CODE_TX_ABORTED_EVENT 0x200
+
#endif
new file mode 100644
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ * David Hildenbrand <david@redhat.com>
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License version 2.
+ */
+#ifndef _ASMS390X_IRQ_H_
+#define _ASMS390X_IRQ_H_
+#include <asm/arch_def.h>
+
+void handle_pgm_int(void);
+void expect_pgm_int(void);
+void check_pgm_int_code(uint16_t code);
+
+#endif
new file mode 100644
@@ -0,0 +1,82 @@
+/*
+ * s390x interrupt handling
+ *
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ * David Hildenbrand <david@redhat.com>
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License version 2.
+ */
+#include <libcflat.h>
+#include <asm/interrupt.h>
+#include <asm/barrier.h>
+
+static bool pgm_int_expected;
+static struct lowcore *lc;
+
+void expect_pgm_int(void)
+{
+ pgm_int_expected = true;
+ lc->pgm_int_code = 0;
+ mb();
+}
+
+void check_pgm_int_code(uint16_t code)
+{
+ mb();
+ report("Program interrupt: expected(%d) == received(%d)",
+ code == lc->pgm_int_code, code, lc->pgm_int_code);
+}
+
+static void fixup_pgm_int(void)
+{
+ switch (lc->pgm_int_code) {
+ case PGM_INT_CODE_SEGMENT_TRANSLATION:
+ case PGM_INT_CODE_PAGE_TRANSLATION:
+ case PGM_INT_CODE_TRACE_TABLE:
+ case PGM_INT_CODE_AFX_TRANSLATION:
+ case PGM_INT_CODE_ASX_TRANSLATION:
+ case PGM_INT_CODE_LX_TRANSLATION:
+ case PGM_INT_CODE_EX_TRANSLATION:
+ case PGM_INT_CODE_PRIMARY_AUTHORITY:
+ case PGM_INT_CODE_SECONDARY_AUTHORITY:
+ case PGM_INT_CODE_LFX_TRANSLATION:
+ case PGM_INT_CODE_LSX_TRANSLATION:
+ case PGM_INT_CODE_ALEN_TRANSLATION:
+ case PGM_INT_CODE_ALE_SEQUENCE:
+ case PGM_INT_CODE_ASTE_VALIDITY:
+ case PGM_INT_CODE_ASTE_SEQUENCE:
+ case PGM_INT_CODE_EXTENDED_AUTHORITY:
+ case PGM_INT_CODE_LSTE_SEQUENCE:
+ case PGM_INT_CODE_ASTE_INSTANCE:
+ case PGM_INT_CODE_STACK_FULL:
+ case PGM_INT_CODE_STACK_EMPTY:
+ case PGM_INT_CODE_STACK_SPECIFICATION:
+ case PGM_INT_CODE_STACK_TYPE:
+ case PGM_INT_CODE_STACK_OPERATION:
+ case PGM_INT_CODE_ASCE_TYPE:
+ case PGM_INT_CODE_REGION_FIRST_TRANS:
+ case PGM_INT_CODE_REGION_SECOND_TRANS:
+ case PGM_INT_CODE_REGION_THIRD_TRANS:
+ case PGM_INT_CODE_PER:
+ case PGM_INT_CODE_CRYPTO_OPERATION:
+ /* The interrupt was nullified, the old PSW points at the
+ * responsible instruction. Forward the PSW so we don't loop.
+ */
+ lc->pgm_old_psw.addr += lc->pgm_int_id;
+ }
+ /* suppressed/terminated/completed point already at the next address */
+}
+
+void handle_pgm_int(void)
+{
+ if (!pgm_int_expected)
+ report_abort("Unexpected program interrupt: %d at %#lx, ilen %d\n",
+ lc->pgm_int_code, lc->pgm_old_psw.addr,
+ lc->pgm_int_id);
+
+ pgm_int_expected = false;
+ fixup_pgm_int();
+}
@@ -10,6 +10,7 @@ CFLAGS += -Wextra
CFLAGS += -I $(SRCDIR)/lib
CFLAGS += -O2
CFLAGS += -march=z900
+CFLAGS += -fno-delete-null-pointer-checks
LDFLAGS += -nostdlib
# We want to keep intermediate files
@@ -23,6 +24,7 @@ cflatobjs += lib/alloc.o
cflatobjs += lib/s390x/io.o
cflatobjs += lib/s390x/stack.o
cflatobjs += lib/s390x/sclp-ascii.o
+cflatobjs += lib/s390x/interrupt.o
OBJDIRS += lib/s390x
@@ -10,6 +10,8 @@
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License version 2.
*/
+#include <asm/asm-offsets.h>
+
.section .init
/* entry point - for KVM + TCG we directly start in 64 bit mode */
@@ -21,6 +23,10 @@ start:
larl %r1, initital_psw
lpswe 0(%r1)
init_psw_cont:
+ /* setup pgm interrupt handler */
+ larl %r1, pgm_int_psw
+ mvc GEN_LC_PGM_NEW_PSW(16), 0(%r1)
+ /* setup cr0, enabling e.g. AFP-register control */
larl %r1, initital_cr0
lctlg %c0, %c0, 0(%r1)
/* call setup() */
@@ -36,9 +42,58 @@ init_psw_cont:
/* call exit() */
j exit
+pgm_int:
+ /* save grs 0-15 */
+ stmg %r0, %r15, GEN_LC_SW_INT_GRS
+ /* save fprs 0-15 + fpc */
+ larl %r1, GEN_LC_SW_INT_FPRS
+ std %f0, 0(%r1)
+ std %f1, 8(%r1)
+ std %f2, 16(%r1)
+ std %f3, 24(%r1)
+ std %f4, 32(%r1)
+ std %f5, 40(%r1)
+ std %f6, 48(%r1)
+ std %f7, 56(%r1)
+ std %f8, 64(%r1)
+ std %f9, 72(%r1)
+ std %f10, 80(%r1)
+ std %f11, 88(%r1)
+ std %f12, 96(%r1)
+ std %f13, 104(%r1)
+ std %f14, 112(%r1)
+ std %f15, 120(%r1)
+ stfpc GEN_LC_SW_INT_FPC
+ /* call our c handler */
+ brasl %r14, handle_pgm_int
+ /* restore fprs 0-15 + fpc */
+ larl %r1, GEN_LC_SW_INT_FPRS
+ ld %f0, 0(%r1)
+ ld %f1, 8(%r1)
+ ld %f2, 16(%r1)
+ ld %f3, 24(%r1)
+ ld %f4, 32(%r1)
+ ld %f5, 40(%r1)
+ ld %f6, 48(%r1)
+ ld %f7, 56(%r1)
+ ld %f8, 64(%r1)
+ ld %f9, 72(%r1)
+ ld %f10, 80(%r1)
+ ld %f11, 88(%r1)
+ ld %f12, 96(%r1)
+ ld %f13, 104(%r1)
+ ld %f14, 112(%r1)
+ ld %f15, 120(%r1)
+ lfpc GEN_LC_SW_INT_FPC
+ /* restore grs 0-15 */
+ lmg %r0, %r15, GEN_LC_SW_INT_GRS
+ lpswe GEN_LC_PGM_OLD_PSW
+
.align 8
initital_psw:
.quad 0x0000000180000000, init_psw_cont
+pgm_int_psw:
+ .quad 0x0000000180000000, pgm_int
initital_cr0:
/* enable AFP-register control, so FP regs (+BFP instr) can be used */
.quad 0x0000000000040000
@@ -10,6 +10,7 @@
*/
#include <libcflat.h>
#include <util.h>
+#include <asm/interrupt.h>
static void test_fp(void)
{
@@ -25,6 +26,17 @@ static void test_fp(void)
report("3.0/2.0 == 1.5", c == 1.5);
}
+static void test_pgm_int(void)
+{
+ expect_pgm_int();
+ asm volatile(" .insn e,0x0001"); /* used for SW breakpoints in QEMU */
+ check_pgm_int_code(PGM_INT_CODE_OPERATION);
+
+ expect_pgm_int();
+ *((unsigned int*)-1) = 1;
+ check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
+}
+
int main(int argc, char**argv)
{
report_prefix_push("selftest");
@@ -36,6 +48,7 @@ int main(int argc, char**argv)
report("argv[2] == 123", !strcmp(argv[2], "123"));
test_fp();
+ test_pgm_int();
return report_summary();
}
The program interrupt handler will detect unexpected program interrupts and allow to expect + verify program interrupts for testing purposes. We need "-fno-delete-null-pointer-checks", otherwise trying to access the lowcore at address 0 makes GCC generate very weird code. Add two tests to test for simple operation and addressing exceptions. Signed-off-by: David Hildenbrand <david@redhat.com> --- lib/s390x/asm-offsets.c | 3 ++ lib/s390x/asm/arch_def.h | 61 ++++++++++++++++++++++++++++++++++- lib/s390x/asm/interrupt.h | 18 +++++++++++ lib/s390x/interrupt.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ s390x/Makefile | 2 ++ s390x/cstart64.S | 55 +++++++++++++++++++++++++++++++ s390x/selftest.c | 13 ++++++++ 7 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 lib/s390x/asm/interrupt.h create mode 100644 lib/s390x/interrupt.c