Commit 4da93626 authored by David Hildenbrand's avatar David Hildenbrand Committed by Paolo Bonzini
Browse files

s390x: pgm interrupt handler and a way to test them



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: default avatarDavid Hildenbrand <david@redhat.com>
Message-Id: <20170531123925.4547-3-david@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent cfb204f9
......@@ -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
/*
* 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
/*
* 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();
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment