Skip to contentSkip to conversation

s390x: add a pgm irq handler and a way to expect them

PATCH In review David Hildenbrand 3 participants 6 messages May 29, 2017 – May 30, 2017 KVM

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 s390x: add a pgm irq handler and a way to expect them

The pgm irq handler will detect unexpected pgm irqs and allows to expect pgm irqs + verify that the pgm irq was triggered.

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 exception.

---
Note: This will require TCG patch "target/s390x: addressing exceptions are suppressing" in order to pass.

Patch "[kvm-unit-tests PATCH v1] s390x: generate asm offsets for the lowcore" is requires as a prerequisite.

 lib/s390x/asm-offsets.c  |  1 +
 lib/s390x/asm/arch_def.h | 59 +++++++++++++++++++++++++++++++-
 lib/s390x/asm/irq.h      | 18 ++++++++++
 lib/s390x/irq.c          | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 s390x/Makefile           |  2 ++
 s390x/cstart64.S         | 16 +++++++++
 s390x/selftest.c         | 14 ++++++++
 7 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 lib/s390x/asm/irq.h
 create mode 100644 lib/s390x/irq.c
lib/s390x/asm-offsets.c +1 -0
index 28915e5..cc357e9 100644
@@ -54,6 +54,7 @@ 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_IRQ_AREA, lowcore, sw_irq_area);
 	OFFSET(GEN_LC_FPRS_SA, lowcore, fprs_sa);
 	OFFSET(GEN_LC_GRS_SA, lowcore, grs_sa);
 	OFFSET(GEN_LC_PSW_SA, lowcore, psw_sa);
lib/s390x/asm/arch_def.h +58 -1

69 lines not on this page · raw message

lib/s390x/asm/irq.h new file +18 -0
new file mode 100644
index 0000000..8f6013b
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ *  David Hildenbrand <[email protected]>
+ *
+ * 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_irq(void);
+void expect_pgm_irq(void);
+uint16_t received_pgm_irq(void);
+
+#endif
lib/s390x/irq.c new file +89 -0

89 lines not on this page · raw message

s390x/Makefile +2 -0
index 4e4b94a..712ef49 100644
@@ -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/irq.o
 
 OBJDIRS += lib/s390x
 
s390x/cstart64.S +16 -0
index 28cd59d..6bed38f 100644
@@ -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,12 @@ start:
 	larl	%r1, initital_psw
 	lpswe	0(%r1)
 init_psw_cont:
+	/* setup pgm irq handler */
+	larl	%r1, pgm_irq_mask
+	stg	%r1, GEN_LC_PGM_NEW_PSW
+	larl	%r1, pgm_irq
+	stg	%r1, GEN_LC_PGM_NEW_PSW + 8
+	/* setup the initital PSW and enable 64bit addressing */
 	larl	%r1, initital_cr0
 	lctlg	%c0, %c0, 0(%r1)
 	/* call setup() */
@@ -36,9 +44,17 @@ init_psw_cont:
 	/* call exit() */
 	j exit
 
+pgm_irq:
+	stmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	brasl	%r14, handle_pgm_irq
+	lmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	lpswe GEN_LC_PGM_OLD_PSW
+
 	.align	8
 initital_psw:
 	.quad	0x0000000180000000, init_psw_cont
+pgm_irq_mask:
+	.quad	0x0000000180000000
 initital_cr0:
 	/* enable AFP-register control, so FP regs (+BFP instr) can be used */
 	.quad	0x0000000000040000
s390x/selftest.c +14 -0
index 4558e47..a67b87a 100644
@@ -10,6 +10,7 @@
  */
 #include <libcflat.h>
 #include <util.h>
+#include <asm/irq.h>
 
 static void test_fp(void)
 {
@@ -25,6 +26,18 @@ static void test_fp(void)
 	report("3.0/2.0 == 1.5", c == 1.5);
 }
 
+static void test_pgm_irq(void)
+{
+	expect_pgm_irq();
+	asm volatile(
+		"	.insn e,0x0001");
+	report("OPERATION pgm irq", received_pgm_irq() == PGM_CODE_OPERATION);
+
+	expect_pgm_irq();
+	*((unsigned int*)-1) = 1;
+	report("ADDRESSING pgm irq", received_pgm_irq() == PGM_CODE_ADDRESSING);
+}
+
 int main(int argc, char**argv)
 {
 	report_prefix_push("selftest");
@@ -36,6 +49,7 @@ int main(int argc, char**argv)
 	report("argv[2] == 123", !strcmp(argv[2], "123"));
 
 	test_fp();
+	test_pgm_irq();
 
 	return report_summary();
 }
Signature · 1 line
2.9.3
#1 lore raw
Quoted 267 lines · David Hildenbrand

The pgm irq handler will detect unexpected pgm irqs and allows to expect pgm irqs + verify that the pgm irq was triggered.

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 exception.

---
Note: This will require TCG patch "target/s390x: addressing exceptions are suppressing" in order to pass.

Patch "[kvm-unit-tests PATCH v1] s390x: generate asm offsets for the lowcore" is requires as a prerequisite.

 lib/s390x/asm-offsets.c  |  1 +
 lib/s390x/asm/arch_def.h | 59 +++++++++++++++++++++++++++++++-
 lib/s390x/asm/irq.h      | 18 ++++++++++
 lib/s390x/irq.c          | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 s390x/Makefile           |  2 ++
 s390x/cstart64.S         | 16 +++++++++
 s390x/selftest.c         | 14 ++++++++
 7 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 lib/s390x/asm/irq.h
 create mode 100644 lib/s390x/irq.c
lib/s390x/asm-offsets.c +1 -0
index 28915e5..cc357e9 100644
@@ -54,6 +54,7 @@ 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_IRQ_AREA, lowcore, sw_irq_area);
 	OFFSET(GEN_LC_FPRS_SA, lowcore, fprs_sa);
 	OFFSET(GEN_LC_GRS_SA, lowcore, grs_sa);
 	OFFSET(GEN_LC_PSW_SA, lowcore, psw_sa);
lib/s390x/asm/arch_def.h +58 -1
index 25c9516..c49261a 100644
@@ -64,7 +64,9 @@ struct lowcore {
 	struct psw	pgm_new_psw;			/* 0x01d0 */
 	struct psw	mcck_new_psw;			/* 0x01e0 */
 	struct psw	io_new_psw;			/* 0x01f0 */
-	uint8_t		pad_0x0200[0x1200 - 0x0200];	/* 0x0200 */
+	/* sw definition: large enough to save all grs in irq handlers */
+	uint8_t		sw_irq_area[0x280 - 0x0200];	/* 0x0200 */
+	uint8_t		pad_0x0280[0x1200 - 0x0280];	/* 0x0280 */
 	uint64_t	fprs_sa[16];			/* 0x1200 */
 	uint64_t	grs_sa[16];			/* 0x1280 */
 	struct psw	psw_sa;				/* 0x1300 */
@@ -82,4 +84,59 @@ struct lowcore {
 	uint8_t		pgm_int_tdb[0x1900 - 0x1800];	/* 0x1800 */
 } __attribute__ ((__packed__));
 
+#define PGM_CODE_OPERATION		0x01
+#define PGM_CODE_PRIVILEGED_OPERATION	0x02
+#define PGM_CODE_EXECUTE		0x03
+#define PGM_CODE_PROTECTION		0x04
+#define PGM_CODE_ADDRESSING		0x05
+#define PGM_CODE_SPECIFICATION		0x06
+#define PGM_CODE_DATA			0x07
+#define PGM_CODE_FIXED_POINT_OVERFLOW	0x08
+#define PGM_CODE_FIXED_POINT_DIVIDE	0x09
+#define PGM_CODE_DECIMAL_OVERFLOW	0x0a
+#define PGM_CODE_DECIMAL_DIVIDE		0x0b
+#define PGM_CODE_HFP_EXPONENT_OVERFLOW	0x0c
+#define PGM_CODE_HFP_EXPONENT_UNDERFLOW	0x0d
+#define PGM_CODE_HFP_SIGNIFICANCE	0x0e
+#define PGM_CODE_HFP_DIVIDE		0x0f
+#define PGM_CODE_SEGMENT_TRANSLATION	0x10
+#define PGM_CODE_PAGE_TRANSLATION	0x11
+#define PGM_CODE_TRANSLATION_SPEC	0x12
+#define PGM_CODE_SPECIAL_OPERATION	0x13
+#define PGM_CODE_OPERAND		0x15
+#define PGM_CODE_TRACE_TABLE		0x16
+#define PGM_CODE_VECTOR_PROCESSING	0x1b
+#define PGM_CODE_SPACE_SWITCH_EVENT	0x1c
+#define PGM_CODE_HFP_SQUARE_ROOT	0x1d
+#define PGM_CODE_PC_TRANSLATION_SPEC	0x1f
+#define PGM_CODE_AFX_TRANSLATION	0x20
+#define PGM_CODE_ASX_TRANSLATION	0x21
+#define PGM_CODE_LX_TRANSLATION		0x22
+#define PGM_CODE_EX_TRANSLATION		0x23
+#define PGM_CODE_PRIMARY_AUTHORITY	0x24
+#define PGM_CODE_SECONDARY_AUTHORITY	0x25
+#define PGM_CODE_LFX_TRANSLATION	0x26
+#define PGM_CODE_LSX_TRANSLATION	0x27
+#define PGM_CODE_ALET_SPECIFICATION	0x28
+#define PGM_CODE_ALEN_TRANSLATION	0x29
+#define PGM_CODE_ALE_SEQUENCE		0x2a
+#define PGM_CODE_ASTE_VALIDITY		0x2b
+#define PGM_CODE_ASTE_SEQUENCE		0x2c
+#define PGM_CODE_EXTENDED_AUTHORITY	0x2d
+#define PGM_CODE_LSTE_SEQUENCE		0x2e
+#define PGM_CODE_ASTE_INSTANCE		0x2f
+#define PGM_CODE_STACK_FULL		0x30
+#define PGM_CODE_STACK_EMPTY		0x31
+#define PGM_CODE_STACK_SPECIFICATION	0x32
+#define PGM_CODE_STACK_TYPE		0x33
+#define PGM_CODE_STACK_OPERATION	0x34
+#define PGM_CODE_ASCE_TYPE		0x38
+#define PGM_CODE_REGION_FIRST_TRANS	0x39
+#define PGM_CODE_REGION_SECOND_TRANS	0x3a
+#define PGM_CODE_REGION_THIRD_TRANS	0x3b
+#define PGM_CODE_MONITOR_EVENT		0x40
+#define PGM_CODE_PER			0x80
+#define PGM_CODE_CRYPTO_OPERATION	0x119
+#define PGM_CODE_TX_ABORTED_EVENT	0x200
+
 #endif
lib/s390x/asm/irq.h new file +18 -0
new file mode 100644
index 0000000..8f6013b
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ *  David Hildenbrand <[email protected]>
+ *
+ * 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_irq(void);
+void expect_pgm_irq(void);
+uint16_t received_pgm_irq(void);
+
+#endif
lib/s390x/irq.c new file +89 -0
new file mode 100644
index 0000000..a54c01a
@@ -0,0 +1,89 @@
+/*
+ * s390x irq handling
+ *
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ *  Thomas Huth <[email protected]>
+ *  David Hildenbrand <[email protected]>
+ *
+ * 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/irq.h>
+#include <asm/barrier.h>
+
+static bool pgm_irq_expected;
+
+void expect_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	pgm_irq_expected = true;
+	lc->pgm_int_code = 0;
+	mb();
+}
+
+uint16_t received_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	mb();
+	return lc->pgm_int_code;
+}
+
+static void fixup_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	switch (lc->pgm_int_code) {
+	case PGM_CODE_SEGMENT_TRANSLATION:
+	case PGM_CODE_PAGE_TRANSLATION:
+	case PGM_CODE_TRACE_TABLE:
+	case PGM_CODE_AFX_TRANSLATION:
+	case PGM_CODE_ASX_TRANSLATION:
+	case PGM_CODE_LX_TRANSLATION:
+	case PGM_CODE_EX_TRANSLATION:
+	case PGM_CODE_PRIMARY_AUTHORITY:
+	case PGM_CODE_SECONDARY_AUTHORITY:
+	case PGM_CODE_LFX_TRANSLATION:
+	case PGM_CODE_LSX_TRANSLATION:
+	case PGM_CODE_ALEN_TRANSLATION:
+	case PGM_CODE_ALE_SEQUENCE:
+	case PGM_CODE_ASTE_VALIDITY:
+	case PGM_CODE_ASTE_SEQUENCE:
+	case PGM_CODE_EXTENDED_AUTHORITY:
+	case PGM_CODE_LSTE_SEQUENCE:
+	case PGM_CODE_ASTE_INSTANCE:
+	case PGM_CODE_STACK_FULL:
+	case PGM_CODE_STACK_EMPTY:
+	case PGM_CODE_STACK_SPECIFICATION:
+	case PGM_CODE_STACK_TYPE:
+	case PGM_CODE_STACK_OPERATION:
+	case PGM_CODE_ASCE_TYPE:
+	case PGM_CODE_REGION_FIRST_TRANS:
+	case PGM_CODE_REGION_SECOND_TRANS:
+	case PGM_CODE_REGION_THIRD_TRANS:
+	case PGM_CODE_PER:
+	case PGM_CODE_CRYPTO_OPERATION:
+		/* The irq 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_irq(void)
+{
+	struct lowcore *lc = (void *) 0;
+
+	if (!pgm_irq_expected)
+		report_abort("Unexpected pgm irq %d at %#lx, ilen %d\n",
+			     lc->pgm_int_code, lc->pgm_old_psw.addr,
+			     lc->pgm_int_id);
+
+	pgm_irq_expected = false;
+	fixup_pgm_irq();
+}
s390x/Makefile +2 -0
index 4e4b94a..712ef49 100644
@@ -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/irq.o
 
 OBJDIRS += lib/s390x
 
s390x/cstart64.S +2 -0
index 28cd59d..6bed38f 100644
@@ -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 */

s390x/cstart64.S start: · line 27

Quoted 5 lines
s390x/cstart64.S +2 -0
@@ -21,6 +23,12 @@ start:
 	larl	%r1, initital_psw
 	lpswe	0(%r1)
 init_psw_cont:
+	/* setup pgm irq handler */
+	larl	%r1, pgm_irq_mask

So, apparently, TCG does not check for specification exceptions.

Here, a
lg %r1, 0(%r1)

is missing.

Quoted 65 lines
s390x/cstart64.S +12 -0
+	stg	%r1, GEN_LC_PGM_NEW_PSW
+	larl	%r1, pgm_irq
+	stg	%r1, GEN_LC_PGM_NEW_PSW + 8
+	/* setup the initital PSW and enable 64bit addressing */
 	larl	%r1, initital_cr0
 	lctlg	%c0, %c0, 0(%r1)
 	/* call setup() */
@@ -36,9 +44,17 @@ init_psw_cont:
 	/* call exit() */
 	j exit
 
+pgm_irq:
+	stmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	brasl	%r14, handle_pgm_irq
+	lmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	lpswe GEN_LC_PGM_OLD_PSW
+
 	.align	8
 initital_psw:
 	.quad	0x0000000180000000, init_psw_cont
+pgm_irq_mask:
+	.quad	0x0000000180000000
 initital_cr0:
 	/* enable AFP-register control, so FP regs (+BFP instr) can be used */
 	.quad	0x0000000000040000
s390x/selftest.c +14 -0
index 4558e47..a67b87a 100644
@@ -10,6 +10,7 @@
  */
 #include <libcflat.h>
 #include <util.h>
+#include <asm/irq.h>
 
 static void test_fp(void)
 {
@@ -25,6 +26,18 @@ static void test_fp(void)
 	report("3.0/2.0 == 1.5", c == 1.5);
 }
 
+static void test_pgm_irq(void)
+{
+	expect_pgm_irq();
+	asm volatile(
+		"	.insn e,0x0001");
+	report("OPERATION pgm irq", received_pgm_irq() == PGM_CODE_OPERATION);
+
+	expect_pgm_irq();
+	*((unsigned int*)-1) = 1;
+	report("ADDRESSING pgm irq", received_pgm_irq() == PGM_CODE_ADDRESSING);
+}
+
 int main(int argc, char**argv)
 {
 	report_prefix_push("selftest");
@@ -36,6 +49,7 @@ int main(int argc, char**argv)
 	report("argv[2] == 123", !strcmp(argv[2], "123"));
 
 	test_fp();
+	test_pgm_irq();
 
 	return report_summary();
 }
Signature · 3 lines
Thanks,

David
#2 lore raw
In reply to David Hildenbrand
Quoted 5 lines · David Hildenbrand

The pgm irq handler will detect unexpected pgm irqs and allows to expect pgm irqs + verify that the pgm irq was triggered.

We need "-fno-delete-null-pointer-checks", otherwise trying to access the lowcore at address 0 makes GCC generate very weird code.

I wonder whether you could get rid of that by using a global variable for lc instead?

Quoted 9 lines

Add two tests to test for simple operation and addressing exception.

---
Note: This will require TCG patch "target/s390x: addressing exceptions are suppressing" in order to pass.

Patch "[kvm-unit-tests PATCH v1] s390x: generate asm offsets for the lowcore" is requires as a prerequisite.

Maybe send them together as a series next time?

Quoted 28 lines
 lib/s390x/asm-offsets.c  |  1 +
 lib/s390x/asm/arch_def.h | 59 +++++++++++++++++++++++++++++++-
 lib/s390x/asm/irq.h      | 18 ++++++++++
 lib/s390x/irq.c          | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 s390x/Makefile           |  2 ++
 s390x/cstart64.S         | 16 +++++++++
 s390x/selftest.c         | 14 ++++++++
 7 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 lib/s390x/asm/irq.h
 create mode 100644 lib/s390x/irq.c
lib/s390x/asm-offsets.c +1 -0
index 28915e5..cc357e9 100644
@@ -54,6 +54,7 @@ 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_IRQ_AREA, lowcore, sw_irq_area);
 	OFFSET(GEN_LC_FPRS_SA, lowcore, fprs_sa);
 	OFFSET(GEN_LC_GRS_SA, lowcore, grs_sa);
 	OFFSET(GEN_LC_PSW_SA, lowcore, psw_sa);
lib/s390x/asm/arch_def.h +0 -0
index 25c9516..c49261a 100644

lib/s390x/asm/arch_def.h struct lowcore { · line 68

Quoted 6 lines
lib/s390x/asm/arch_def.h +2 -1
@@ -64,7 +64,9 @@ struct lowcore {
 	struct psw	pgm_new_psw;			/* 0x01d0 */
 	struct psw	mcck_new_psw;			/* 0x01e0 */
 	struct psw	io_new_psw;			/* 0x01f0 */
-	uint8_t		pad_0x0200[0x1200 - 0x0200];	/* 0x0200 */
+	/* sw definition: large enough to save all grs in irq handlers */
+	uint8_t		sw_irq_area[0x280 - 0x0200];	/* 0x0200 */

Since you're saving 64-bit registers into this array, I'd suggest to make it an array of uint64_t values instead of uint8_t.

Quoted 118 lines
lib/s390x/asm/arch_def.h +56 -0
+	uint8_t		pad_0x0280[0x1200 - 0x0280];	/* 0x0280 */
 	uint64_t	fprs_sa[16];			/* 0x1200 */
 	uint64_t	grs_sa[16];			/* 0x1280 */
 	struct psw	psw_sa;				/* 0x1300 */
@@ -82,4 +84,59 @@ struct lowcore {
 	uint8_t		pgm_int_tdb[0x1900 - 0x1800];	/* 0x1800 */
 } __attribute__ ((__packed__));
 
+#define PGM_CODE_OPERATION		0x01
+#define PGM_CODE_PRIVILEGED_OPERATION	0x02
+#define PGM_CODE_EXECUTE		0x03
+#define PGM_CODE_PROTECTION		0x04
+#define PGM_CODE_ADDRESSING		0x05
+#define PGM_CODE_SPECIFICATION		0x06
+#define PGM_CODE_DATA			0x07
+#define PGM_CODE_FIXED_POINT_OVERFLOW	0x08
+#define PGM_CODE_FIXED_POINT_DIVIDE	0x09
+#define PGM_CODE_DECIMAL_OVERFLOW	0x0a
+#define PGM_CODE_DECIMAL_DIVIDE		0x0b
+#define PGM_CODE_HFP_EXPONENT_OVERFLOW	0x0c
+#define PGM_CODE_HFP_EXPONENT_UNDERFLOW	0x0d
+#define PGM_CODE_HFP_SIGNIFICANCE	0x0e
+#define PGM_CODE_HFP_DIVIDE		0x0f
+#define PGM_CODE_SEGMENT_TRANSLATION	0x10
+#define PGM_CODE_PAGE_TRANSLATION	0x11
+#define PGM_CODE_TRANSLATION_SPEC	0x12
+#define PGM_CODE_SPECIAL_OPERATION	0x13
+#define PGM_CODE_OPERAND		0x15
+#define PGM_CODE_TRACE_TABLE		0x16
+#define PGM_CODE_VECTOR_PROCESSING	0x1b
+#define PGM_CODE_SPACE_SWITCH_EVENT	0x1c
+#define PGM_CODE_HFP_SQUARE_ROOT	0x1d
+#define PGM_CODE_PC_TRANSLATION_SPEC	0x1f
+#define PGM_CODE_AFX_TRANSLATION	0x20
+#define PGM_CODE_ASX_TRANSLATION	0x21
+#define PGM_CODE_LX_TRANSLATION		0x22
+#define PGM_CODE_EX_TRANSLATION		0x23
+#define PGM_CODE_PRIMARY_AUTHORITY	0x24
+#define PGM_CODE_SECONDARY_AUTHORITY	0x25
+#define PGM_CODE_LFX_TRANSLATION	0x26
+#define PGM_CODE_LSX_TRANSLATION	0x27
+#define PGM_CODE_ALET_SPECIFICATION	0x28
+#define PGM_CODE_ALEN_TRANSLATION	0x29
+#define PGM_CODE_ALE_SEQUENCE		0x2a
+#define PGM_CODE_ASTE_VALIDITY		0x2b
+#define PGM_CODE_ASTE_SEQUENCE		0x2c
+#define PGM_CODE_EXTENDED_AUTHORITY	0x2d
+#define PGM_CODE_LSTE_SEQUENCE		0x2e
+#define PGM_CODE_ASTE_INSTANCE		0x2f
+#define PGM_CODE_STACK_FULL		0x30
+#define PGM_CODE_STACK_EMPTY		0x31
+#define PGM_CODE_STACK_SPECIFICATION	0x32
+#define PGM_CODE_STACK_TYPE		0x33
+#define PGM_CODE_STACK_OPERATION	0x34
+#define PGM_CODE_ASCE_TYPE		0x38
+#define PGM_CODE_REGION_FIRST_TRANS	0x39
+#define PGM_CODE_REGION_SECOND_TRANS	0x3a
+#define PGM_CODE_REGION_THIRD_TRANS	0x3b
+#define PGM_CODE_MONITOR_EVENT		0x40
+#define PGM_CODE_PER			0x80
+#define PGM_CODE_CRYPTO_OPERATION	0x119
+#define PGM_CODE_TX_ABORTED_EVENT	0x200
+
 #endif
lib/s390x/asm/irq.h new file +18 -0
new file mode 100644
index 0000000..8f6013b
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ *  David Hildenbrand <[email protected]>
+ *
+ * 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_irq(void);
+void expect_pgm_irq(void);
+uint16_t received_pgm_irq(void);
+
+#endif
lib/s390x/irq.c new file +24 -0
new file mode 100644
index 0000000..a54c01a
@@ -0,0 +1,89 @@
+/*
+ * s390x irq handling
+ *
+ * Copyright (c) 2017 Red Hat Inc
+ *
+ * Authors:
+ *  Thomas Huth <[email protected]>
+ *  David Hildenbrand <[email protected]>
+ *
+ * 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/irq.h>
+#include <asm/barrier.h>
+
+static bool pgm_irq_expected;
+
+void expect_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	pgm_irq_expected = true;
+	lc->pgm_int_code = 0;

lib/s390x/irq.c line 34

Quoted 10 lines
lib/s390x/irq.c new file +10 -0
+	mb();
+}
+
+uint16_t received_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	mb();
+	return lc->pgm_int_code;
+}

I'd maybe name this "get_pgm_int_code()" instead (but that's just a matter of taste)

Quoted 88 lines
lib/s390x/irq.c new file +54 -0
+static void fixup_pgm_irq(void)
+{
+	struct lowcore *lc = (void * ) 0;
+
+	switch (lc->pgm_int_code) {
+	case PGM_CODE_SEGMENT_TRANSLATION:
+	case PGM_CODE_PAGE_TRANSLATION:
+	case PGM_CODE_TRACE_TABLE:
+	case PGM_CODE_AFX_TRANSLATION:
+	case PGM_CODE_ASX_TRANSLATION:
+	case PGM_CODE_LX_TRANSLATION:
+	case PGM_CODE_EX_TRANSLATION:
+	case PGM_CODE_PRIMARY_AUTHORITY:
+	case PGM_CODE_SECONDARY_AUTHORITY:
+	case PGM_CODE_LFX_TRANSLATION:
+	case PGM_CODE_LSX_TRANSLATION:
+	case PGM_CODE_ALEN_TRANSLATION:
+	case PGM_CODE_ALE_SEQUENCE:
+	case PGM_CODE_ASTE_VALIDITY:
+	case PGM_CODE_ASTE_SEQUENCE:
+	case PGM_CODE_EXTENDED_AUTHORITY:
+	case PGM_CODE_LSTE_SEQUENCE:
+	case PGM_CODE_ASTE_INSTANCE:
+	case PGM_CODE_STACK_FULL:
+	case PGM_CODE_STACK_EMPTY:
+	case PGM_CODE_STACK_SPECIFICATION:
+	case PGM_CODE_STACK_TYPE:
+	case PGM_CODE_STACK_OPERATION:
+	case PGM_CODE_ASCE_TYPE:
+	case PGM_CODE_REGION_FIRST_TRANS:
+	case PGM_CODE_REGION_SECOND_TRANS:
+	case PGM_CODE_REGION_THIRD_TRANS:
+	case PGM_CODE_PER:
+	case PGM_CODE_CRYPTO_OPERATION:
+		/* The irq 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_irq(void)
+{
+	struct lowcore *lc = (void *) 0;
+
+	if (!pgm_irq_expected)
+		report_abort("Unexpected pgm irq %d at %#lx, ilen %d\n",
+			     lc->pgm_int_code, lc->pgm_old_psw.addr,
+			     lc->pgm_int_id);
+
+	pgm_irq_expected = false;
+	fixup_pgm_irq();
+}
s390x/Makefile +2 -0
index 4e4b94a..712ef49 100644
@@ -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/irq.o
 
 OBJDIRS += lib/s390x
 
s390x/cstart64.S +2 -0
index 28cd59d..6bed38f 100644
@@ -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 */

s390x/cstart64.S start: · line 30

Quoted 8 lines
s390x/cstart64.S +5 -0
@@ -21,6 +23,12 @@ start:
 	larl	%r1, initital_psw
 	lpswe	0(%r1)
 init_psw_cont:
+	/* setup pgm irq handler */
+	larl	%r1, pgm_irq_mask
+	stg	%r1, GEN_LC_PGM_NEW_PSW
+	larl	%r1, pgm_irq
+	stg	%r1, GEN_LC_PGM_NEW_PSW + 8

Not sure whether it's better/nicer, but I think you could also do:

	lmg	%r0, %r1, pgm_irq_psw
	stmg	%r0, %r1, GEN_LC_PGM_NEW_PSW
	...
pgm_irq_psw:
	.quad	0x0000000180000000, pgm_irq

s390x/cstart64.S start: · line 31

Quoted 1 line
s390x/cstart64.S +1 -0
+	/* setup the initital PSW and enable 64bit addressing */

That comment does not really match the code below?

s390x/cstart64.S init_psw_cont: · line 51

Quoted 12 lines
s390x/cstart64.S +5 -0
 	larl	%r1, initital_cr0
 	lctlg	%c0, %c0, 0(%r1)
 	/* call setup() */
@@ -36,9 +44,17 @@ init_psw_cont:
 	/* call exit() */
 	j exit
 
+pgm_irq:
+	stmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	brasl	%r14, handle_pgm_irq
+	lmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	lpswe GEN_LC_PGM_OLD_PSW

Use a TAB instead of space after lpswe ?

I also wonder whether you've got to save the floating point registers here, too, in case the compiler emits some floating point code in the interrupt handlers (since we're not using -msoftfloat anymore)?

Quoted 21 lines
s390x/cstart64.S +2 -0
 	.align	8
 initital_psw:
 	.quad	0x0000000180000000, init_psw_cont
+pgm_irq_mask:
+	.quad	0x0000000180000000
 initital_cr0:
 	/* enable AFP-register control, so FP regs (+BFP instr) can be used */
 	.quad	0x0000000000040000
s390x/selftest.c +1 -0
index 4558e47..a67b87a 100644
@@ -10,6 +10,7 @@
  */
 #include <libcflat.h>
 #include <util.h>
+#include <asm/irq.h>
 
 static void test_fp(void)
 {

s390x/selftest.c static void test_fp(void) · line 33

Quoted 8 lines
s390x/selftest.c +5 -0
@@ -25,6 +26,18 @@ static void test_fp(void)
 	report("3.0/2.0 == 1.5", c == 1.5);
 }
 
+static void test_pgm_irq(void)
+{
+	expect_pgm_irq();
+	asm volatile(
+		"	.insn e,0x0001");

I'd put that into one line. And please add a comment - not everybody knows what opcode 0x0001 is good for.

Quoted 12 lines
s390x/selftest.c +7 -0
+	report("OPERATION pgm irq", received_pgm_irq() == PGM_CODE_OPERATION);
+
+	expect_pgm_irq();
+	*((unsigned int*)-1) = 1;
+	report("ADDRESSING pgm irq", received_pgm_irq() == PGM_CODE_ADDRESSING);
+}
+
 int main(int argc, char**argv)
 {
 	report_prefix_push("selftest");

s390x/selftest.c int main(int argc, char**argv) · line 55

Quoted 7 lines
s390x/selftest.c +1 -0
@@ -36,6 +49,7 @@ int main(int argc, char**argv)
 	report("argv[2] == 123", !strcmp(argv[2], "123"));
 
 	test_fp();
+	test_pgm_irq();
 
 	return report_summary();
 }

Thomas

#3 lore raw
Quoted 9 lines · Thomas Huth
Quoted 5 lines · David Hildenbrand

The pgm irq handler will detect unexpected pgm irqs and allows to expect pgm irqs + verify that the pgm irq was triggered.

We need "-fno-delete-null-pointer-checks", otherwise trying to access the lowcore at address 0 makes GCC generate very weird code.

I wonder whether you could get rid of that by using a global variable for lc instead?

As far as I remember, I tried that and it didn't change a thing. Will do a quick test.

Quoted 8 lines
Quoted 8 lines

Add two tests to test for simple operation and addressing exception.

---
Note: This will require TCG patch "target/s390x: addressing exceptions are suppressing" in order to pass.

Quoted 4 lines
Quoted 2 lines

Patch "[kvm-unit-tests PATCH v1] s390x: generate asm offsets for the lowcore" is requires as a prerequisite.

Maybe send them together as a series next time?

Yes, will send them out in one series for the next round.

Quoted 30 lines
Quoted 30 lines
 lib/s390x/asm-offsets.c  |  1 +
 lib/s390x/asm/arch_def.h | 59 +++++++++++++++++++++++++++++++-
 lib/s390x/asm/irq.h      | 18 ++++++++++
 lib/s390x/irq.c          | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 s390x/Makefile           |  2 ++
 s390x/cstart64.S         | 16 +++++++++
 s390x/selftest.c         | 14 ++++++++
 7 files changed, 198 insertions(+), 1 deletion(-)
 create mode 100644 lib/s390x/asm/irq.h
 create mode 100644 lib/s390x/irq.c
lib/s390x/asm-offsets.c +1 -0
index 28915e5..cc357e9 100644
@@ -54,6 +54,7 @@ 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_IRQ_AREA, lowcore, sw_irq_area);
 	OFFSET(GEN_LC_FPRS_SA, lowcore, fprs_sa);
 	OFFSET(GEN_LC_GRS_SA, lowcore, grs_sa);
 	OFFSET(GEN_LC_PSW_SA, lowcore, psw_sa);
lib/s390x/asm/arch_def.h +0 -0
index 25c9516..c49261a 100644
@@ -64,7 +64,9 @@ struct lowcore {
 	struct psw	pgm_new_psw;			/* 0x01d0 */
Quoted 8 lines

lib/s390x/asm/arch_def.h struct lowcore { · line 68

Quoted 5 lines
lib/s390x/asm/arch_def.h +2 -1
 	struct psw	mcck_new_psw;			/* 0x01e0 */
 	struct psw	io_new_psw;			/* 0x01f0 */
-	uint8_t		pad_0x0200[0x1200 - 0x0200];	/* 0x0200 */
+	/* sw definition: large enough to save all grs in irq handlers */
+	uint8_t		sw_irq_area[0x280 - 0x0200];	/* 0x0200 */

Since you're saving 64-bit registers into this array, I'd suggest to make it an array of uint64_t values instead of uint8_t.

Ack. Also added save areas for fpc + fprs.

[...]>> /* entry point - for KVM + TCG we directly start in 64 bit mode */

Quoted 11 lines

lib/s390x/asm/arch_def.h start: · line 30

Quoted 9 lines
lib/s390x/asm/arch_def.h +5 -0
@@ -21,6 +23,12 @@ start:
 	larl	%r1, initital_psw
 	lpswe	0(%r1)
 init_psw_cont:
+	/* setup pgm irq handler */
+	larl	%r1, pgm_irq_mask
+	stg	%r1, GEN_LC_PGM_NEW_PSW
+	larl	%r1, pgm_irq
+	stg	%r1, GEN_LC_PGM_NEW_PSW + 8

Not sure whether it's better/nicer, but I think you could also do:

I now do

larl %r1, pgm_irq_psw
mvc GEN_LC_PGM_NEW_PSW(16), 0(%r1)

Quoted 10 lines
	lmg	%r0, %r1, pgm_irq_psw
	stmg	%r0, %r1, GEN_LC_PGM_NEW_PSW
	...
pgm_irq_psw:
	.quad	0x0000000180000000, pgm_irq

lib/s390x/asm/arch_def.h start: · line 31

Quoted 1 line
lib/s390x/asm/arch_def.h +1 -0
+	/* setup the initital PSW and enable 64bit addressing */

That comment does not really match the code below?

Yes, wonder how that slipped in.

/* setup cr0, enabling e.g. AFP-register control */

Quoted 8 lines
Quoted 8 lines
lib/s390x/asm/arch_def.h +0 -0
 	larl	%r1, initital_cr0
 	lctlg	%c0, %c0, 0(%r1)
 	/* call setup() */
@@ -36,9 +44,17 @@ init_psw_cont:
 	/* call exit() */
 	j exit
 
Quoted 7 lines

lib/s390x/asm/arch_def.h init_psw_cont: · line 51

Quoted 5 lines
lib/s390x/asm/arch_def.h +5 -0
+pgm_irq:
+	stmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	brasl	%r14, handle_pgm_irq
+	lmg	%r0, %r15, GEN_LC_SW_IRQ_AREA
+	lpswe GEN_LC_PGM_OLD_PSW

Use a TAB instead of space after lpswe ?

Ack.

Quoted 4 lines

I also wonder whether you've got to save the floating point registers here, too, in case the compiler emits some floating point code in the interrupt handlers (since we're not using -msoftfloat anymore)?

Yes you're right. Saving/restoring fprs + fpc now. Unfortunately these can't be saved/loaded in one shot like grs. For vector registers, there is again an instruction to save/restore them in one shot.

Quoted 25 lines
Quoted 25 lines
lib/s390x/asm/arch_def.h +2 -0
 	.align	8
 initital_psw:
 	.quad	0x0000000180000000, init_psw_cont
+pgm_irq_mask:
+	.quad	0x0000000180000000
 initital_cr0:
 	/* enable AFP-register control, so FP regs (+BFP instr) can be used */
 	.quad	0x0000000000040000
s390x/selftest.c +1 -0
index 4558e47..a67b87a 100644
@@ -10,6 +10,7 @@
  */
 #include <libcflat.h>
 #include <util.h>
+#include <asm/irq.h>
 
 static void test_fp(void)
 {
@@ -25,6 +26,18 @@ static void test_fp(void)
 	report("3.0/2.0 == 1.5", c == 1.5);
 }
 
Quoted 8 lines

s390x/selftest.c static void test_fp(void) · line 33

Quoted 5 lines
s390x/selftest.c +5 -0
+static void test_pgm_irq(void)
+{
+	expect_pgm_irq();
+	asm volatile(
+		"	.insn e,0x0001");

I'd put that into one line. And please add a comment - not everybody knows what opcode 0x0001 is good for.

Makes sense!

Quoted 14 lines
Quoted 14 lines
s390x/selftest.c +7 -0
+	report("OPERATION pgm irq", received_pgm_irq() == PGM_CODE_OPERATION);
+
+	expect_pgm_irq();
+	*((unsigned int*)-1) = 1;
+	report("ADDRESSING pgm irq", received_pgm_irq() == PGM_CODE_ADDRESSING);
+}
+
 int main(int argc, char**argv)
 {
 	report_prefix_push("selftest");
@@ -36,6 +49,7 @@ int main(int argc, char**argv)
 	report("argv[2] == 123", !strcmp(argv[2], "123"));
 
Quoted 9 lines

s390x/selftest.c int main(int argc, char**argv) · line 55

Quoted 6 lines
s390x/selftest.c +1 -0
 	test_fp();
+	test_pgm_irq();
 
 	return report_summary();
 }

Thomas

Thanks a lot Thomas!

Signature · 3 lines
Thanks,

David
#4 lore raw
Quoted 5 lines · David Hildenbrand
Quoted 4 lines · Thomas Huth
Quoted 3 lines · David Hildenbrand

The pgm irq handler will detect unexpected pgm irqs and allows to expect pgm irqs + verify that the pgm irq was triggered.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

We need "-fno-delete-null-pointer-checks", otherwise trying to access the lowcore at address 0 makes GCC generate very weird code.

I wonder whether you could get rid of that by using a global variable for lc instead?

As far as I remember, I tried that and it didn't change a thing. Will do a quick test.

It works as long as that global variable is not static, because then GCC must assume that any other code could set it to !0.

Signature · 3 lines
Thanks,

David
maintainer
#5 lore raw
In reply to David Hildenbrand
Quoted 7 lines · David Hildenbrand
Quoted 4 lines
Quoted 2 lines

We need "-fno-delete-null-pointer-checks", otherwise trying to access the lowcore at address 0 makes GCC generate very weird code.

I wonder whether you could get rid of that by using a global variable for lc instead?

As far as I remember, I tried that and it didn't change a thing. Will do a quick test.

-fno-delete-null-pointer-checks is the right thing to do, trying to hide undefined behavior from the compiler leads to misery. :)

Paolo