Message ID | 20220720142526.29634-2-scgl@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add specification exception tests | expand |
On 7/20/22 16:25, Janis Schoetterl-Glausch wrote: > Generate specification exceptions and check that they occur. > > Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> > --- > s390x/Makefile | 1 + > lib/s390x/asm/arch_def.h | 5 ++ > s390x/spec_ex.c | 180 +++++++++++++++++++++++++++++++++++++++ > s390x/unittests.cfg | 3 + > 4 files changed, 189 insertions(+) > create mode 100644 s390x/spec_ex.c > > diff --git a/s390x/Makefile b/s390x/Makefile > index efd5e0c1..58b1bf54 100644 > --- a/s390x/Makefile > +++ b/s390x/Makefile > @@ -27,6 +27,7 @@ tests += $(TEST_DIR)/uv-host.elf > tests += $(TEST_DIR)/edat.elf > tests += $(TEST_DIR)/mvpg-sie.elf > tests += $(TEST_DIR)/spec_ex-sie.elf > +tests += $(TEST_DIR)/spec_ex.elf > tests += $(TEST_DIR)/firq.elf > tests += $(TEST_DIR)/epsw.elf > tests += $(TEST_DIR)/adtl-status.elf > diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h > index 78b257b7..8fbc451c 100644 > --- a/lib/s390x/asm/arch_def.h > +++ b/lib/s390x/asm/arch_def.h > @@ -41,6 +41,11 @@ struct psw { > uint64_t addr; > }; > > +struct short_psw { > + uint32_t mask; > + uint32_t addr; > +}; > + > #define AS_PRIM 0 > #define AS_ACCR 1 > #define AS_SECN 2 > diff --git a/s390x/spec_ex.c b/s390x/spec_ex.c > new file mode 100644 > index 00000000..77fc6246 > --- /dev/null > +++ b/s390x/spec_ex.c > @@ -0,0 +1,180 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright IBM Corp. 2021, 2022 > + * > + * Specification exception test. > + * Tests that specification exceptions occur when expected. > + * > + * Can be extended by adding triggers to spec_ex_triggers, see comments below. > + */ > +#include <stdlib.h> Which things are you hoping to include from stdlib.h? As we normally use libcflat including external files can be pretty dangerous. > +#include <libcflat.h> > +#include <asm/interrupt.h> > + > +static bool invalid_psw_expected; > +static struct psw expected_psw; > +static struct psw invalid_psw; > +static struct psw fixup_psw; > + > +/* > + * The standard program exception handler cannot deal with invalid old PSWs, > + * especially not invalid instruction addresses, as in that case one cannot > + * find the instruction following the faulting one from the old PSW. > + * The PSW to return to is set by load_psw. > + */ > +static void fixup_invalid_psw(void) > +{ > + /* signal occurrence of invalid psw fixup */ > + invalid_psw_expected = false; > + invalid_psw = lowcore.pgm_old_psw; > + lowcore.pgm_old_psw = fixup_psw; > +} > + > +/* > + * Load possibly invalid psw, but setup fixup_psw before, > + * so that fixup_invalid_psw() can bring us back onto the right track. > + * Also acts as compiler barrier, -> none required in expect/check_invalid_psw > + */ > +static void load_psw(struct psw psw) > +{ > + uint64_t scratch; > + /* Store a valid mask and the address of the nop into the fixup PSW. Then load the possibly invalid PSW. */ > + fixup_psw.mask = extract_psw_mask(); > + asm volatile ( "larl %[scratch],0f\n" > + " stg %[scratch],%[addr]\n" > + " lpswe %[psw]\n" > + "0: nop\n" > + : [scratch] "=&d"(scratch), > + [addr] "=&T"(fixup_psw.addr) s/addr/psw_addr/ ? > + : [psw] "Q"(psw) > + : "cc", "memory" > + ); > +} > + > +static void load_short_psw(struct short_psw psw) > +{ > + uint64_t scratch; > + > + fixup_psw.mask = extract_psw_mask(); > + asm volatile ( "larl %[scratch],0f\n" > + " stg %[scratch],%[addr]\n" > + " lpsw %[psw]\n" > + "0: nop\n" > + : [scratch] "=&d"(scratch), > + [addr] "=&T"(fixup_psw.addr) > + : [psw] "Q"(psw) > + : "cc", "memory" > + ); Same story. > +} > + > +static void expect_invalid_psw(struct psw psw) > +{ > + expected_psw = psw; > + invalid_psw_expected = true; > +} > + > +static int check_invalid_psw(void) > +{ > + /* toggled to signal occurrence of invalid psw fixup */ That comment's location is a bit weird. Move it to the declaration of the variable. > + if (!invalid_psw_expected) { > + if (expected_psw.mask == invalid_psw.mask && > + expected_psw.addr == invalid_psw.addr) > + return 0; > + report_fail("Wrong invalid PSW"); > + } else { > + report_fail("Expected exception due to invalid PSW"); > + } > + return 1; > +} > + /* For normal PSWs bit 12 has to be 0 to be a valid PSW*/ > +static int psw_bit_12_is_1(void) > +{ > + struct psw invalid = { .mask = 0x0008000000000000, .addr = 0x00000000deadbeee}; You could use BIT(63-12) for the mask. I usually but struct initializations on new lines, it's easier to read. > + > + expect_invalid_psw(invalid); > + load_psw(invalid); > + return check_invalid_psw(); > +} > + /* A short PSW needs to have bit 12 set to be valid. */ > +static int short_psw_bit_12_is_0(void) > +{ > + struct short_psw short_invalid = { .mask = 0x00000000, .addr = 0xdeadbeee}; I don't see a reason to specify more than one 0 if the whole value is 0. > + > + /* > + * lpsw may optionally check bit 12 before loading the new psw > + * -> cannot check the expected invalid psw like with lpswe > + */ > + load_short_psw(short_invalid); > + return 0; > +} > + > +static int bad_alignment(void) > +{ > + uint32_t words[5] __attribute__((aligned(16))); > + uint32_t (*bad_aligned)[4] = (uint32_t (*)[4])&words[1]; > + /* lpq loads a quad word into a register pair and requires quad word alignment */ > + asm volatile ("lpq %%r6,%[bad]" Of course there's an instruction for that... > + : : [bad] "T"(*bad_aligned) > + : "%r6", "%r7" > + ); > + return 0; > +} > + > +static int not_even(void) > +{ > + uint64_t quad[2] __attribute__((aligned(16))) = {0}; > + > + asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" /* lpq %%r7,%[quad] */ > + : : [quad] "T"(quad) Is there a reason you never put a space after the constraint? > + : "%r7", "%r8" > + ); > + return 0; > +} > + > +/* > + * Harness for specification exception testing. > + * func only triggers exception, reporting is taken care of automatically. > + */ > +struct spec_ex_trigger { > + const char *name; > + int (*func)(void); > + void (*fixup)(void); > +}; > + > +/* List of all tests to execute */ > +static const struct spec_ex_trigger spec_ex_triggers[] = { > + { "psw_bit_12_is_1", &psw_bit_12_is_1, &fixup_invalid_psw }, > + { "short_psw_bit_12_is_0", &short_psw_bit_12_is_0, &fixup_invalid_psw }, > + { "bad_alignment", &bad_alignment, NULL }, > + { "not_even", ¬_even, NULL }, > + { NULL, NULL, NULL }, > +}; > + > +static void test_spec_ex(const struct spec_ex_trigger *trigger) > +{ > + int rc; > + > + expect_pgm_int(); > + register_pgm_cleanup_func(trigger->fixup); > + rc = trigger->func(); > + register_pgm_cleanup_func(NULL); > + /* test failed, nothing to be done, reporting responsibility of trigger */ > + if (rc) > + return; > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > +} > + > +int main(int argc, char **argv) > +{ > + unsigned int i; > + > + report_prefix_push("specification exception"); > + for (i = 0; spec_ex_triggers[i].name; i++) { > + report_prefix_push(spec_ex_triggers[i].name); > + test_spec_ex(&spec_ex_triggers[i]); > + report_prefix_pop(); > + } > + report_prefix_pop(); > + > + return report_summary(); > +} > diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg > index 8e52f560..d2740a40 100644 > --- a/s390x/unittests.cfg > +++ b/s390x/unittests.cfg > @@ -113,6 +113,9 @@ file = mvpg-sie.elf > [spec_ex-sie] > file = spec_ex-sie.elf > > +[spec_ex] > +file = spec_ex.elf > + > [firq-linear-cpu-ids-kvm] > file = firq.elf > timeout = 20
On 8/24/22 11:35, Janosch Frank wrote: > On 7/20/22 16:25, Janis Schoetterl-Glausch wrote: >> Generate specification exceptions and check that they occur. >> >> Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> >> --- >> s390x/Makefile | 1 + >> lib/s390x/asm/arch_def.h | 5 ++ >> s390x/spec_ex.c | 180 +++++++++++++++++++++++++++++++++++++++ >> s390x/unittests.cfg | 3 + >> 4 files changed, 189 insertions(+) >> create mode 100644 s390x/spec_ex.c >> >> diff --git a/s390x/Makefile b/s390x/Makefile >> index efd5e0c1..58b1bf54 100644 >> --- a/s390x/Makefile >> +++ b/s390x/Makefile >> @@ -27,6 +27,7 @@ tests += $(TEST_DIR)/uv-host.elf >> tests += $(TEST_DIR)/edat.elf >> tests += $(TEST_DIR)/mvpg-sie.elf >> tests += $(TEST_DIR)/spec_ex-sie.elf >> +tests += $(TEST_DIR)/spec_ex.elf >> tests += $(TEST_DIR)/firq.elf >> tests += $(TEST_DIR)/epsw.elf >> tests += $(TEST_DIR)/adtl-status.elf >> diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h >> index 78b257b7..8fbc451c 100644 >> --- a/lib/s390x/asm/arch_def.h >> +++ b/lib/s390x/asm/arch_def.h >> @@ -41,6 +41,11 @@ struct psw { >> uint64_t addr; >> }; >> >> +struct short_psw { >> + uint32_t mask; >> + uint32_t addr; >> +}; >> + >> #define AS_PRIM 0 >> #define AS_ACCR 1 >> #define AS_SECN 2 >> diff --git a/s390x/spec_ex.c b/s390x/spec_ex.c >> new file mode 100644 >> index 00000000..77fc6246 >> --- /dev/null >> +++ b/s390x/spec_ex.c >> @@ -0,0 +1,180 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* >> + * Copyright IBM Corp. 2021, 2022 >> + * >> + * Specification exception test. >> + * Tests that specification exceptions occur when expected. >> + * >> + * Can be extended by adding triggers to spec_ex_triggers, see comments below. >> + */ >> +#include <stdlib.h> > > Which things are you hoping to include from stdlib.h? > As we normally use libcflat including external files can be pretty > dangerous. Ignore that comment, we have stdlib in the lib...
On Wed, 2022-08-24 at 11:35 +0200, Janosch Frank wrote: > On 7/20/22 16:25, Janis Schoetterl-Glausch wrote: > > Generate specification exceptions and check that they occur. > > > > Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> > > --- > > s390x/Makefile | 1 + > > lib/s390x/asm/arch_def.h | 5 ++ > > s390x/spec_ex.c | 180 +++++++++++++++++++++++++++++++++++++++ > > s390x/unittests.cfg | 3 + > > 4 files changed, 189 insertions(+) > > create mode 100644 s390x/spec_ex.c > > > > > > + > > +/* > > + * Load possibly invalid psw, but setup fixup_psw before, > > + * so that fixup_invalid_psw() can bring us back onto the right track. > > + * Also acts as compiler barrier, -> none required in expect/check_invalid_psw > > + */ > > +static void load_psw(struct psw psw) > > +{ > > + uint64_t scratch; > > + [...] > /* > Store a valid mask and the address of the nop into the fixup PSW. > Then load the possibly invalid PSW. > */ This seems a bit redundant given the function comment, but I can drop a comment in here describing how the fixup psw is computed. > > > + fixup_psw.mask = extract_psw_mask(); > > + asm volatile ( "larl %[scratch],0f\n" > > + " stg %[scratch],%[addr]\n" > > + " lpswe %[psw]\n" > > + "0: nop\n" > > + : [scratch] "=&d"(scratch), > > + [addr] "=&T"(fixup_psw.addr) > > s/addr/psw_addr/ ? > > > + : [psw] "Q"(psw) > > + : "cc", "memory" > > + ); > > +} > > + > > +static void load_short_psw(struct short_psw psw) > > +{ > > + uint64_t scratch; > > + > > + fixup_psw.mask = extract_psw_mask(); > > + asm volatile ( "larl %[scratch],0f\n" > > + " stg %[scratch],%[addr]\n" > > + " lpsw %[psw]\n" > > + "0: nop\n" > > + : [scratch] "=&d"(scratch), > > + [addr] "=&T"(fixup_psw.addr) > > + : [psw] "Q"(psw) > > + : "cc", "memory" > > + ); > > Same story. Do you want me to repeat the comments here or just rename addr? [...] > > +static int not_even(void) > > +{ > > + uint64_t quad[2] __attribute__((aligned(16))) = {0}; > > + > > + asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" /* lpq %%r7,%[quad] */ > > + : : [quad] "T"(quad) > > Is there a reason you never put a space after the constraint? TBH I never noticed I'm unusual in that regard. I guess I tend to think of the operand and constraint as one entity. I'll add the spaces. > > > + : "%r7", "%r8" > > + ); > > + return 0; > > +} > > + [...]
On 8/26/22 13:23, Janis Schoetterl-Glausch wrote: > On Wed, 2022-08-24 at 11:35 +0200, Janosch Frank wrote: >> On 7/20/22 16:25, Janis Schoetterl-Glausch wrote: >>> Generate specification exceptions and check that they occur. >>> >>> Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> >>> --- >>> s390x/Makefile | 1 + >>> lib/s390x/asm/arch_def.h | 5 ++ >>> s390x/spec_ex.c | 180 +++++++++++++++++++++++++++++++++++++++ >>> s390x/unittests.cfg | 3 + >>> 4 files changed, 189 insertions(+) >>> create mode 100644 s390x/spec_ex.c >>> >>> >>> + >>> +/* >>> + * Load possibly invalid psw, but setup fixup_psw before, >>> + * so that fixup_invalid_psw() can bring us back onto the right track. Not sure if the second line is needed as fixup_psw is a descriptive name already. >>> + * Also acts as compiler barrier, -> none required in expect/check_invalid_psw >>> + */ >>> +static void load_psw(struct psw psw) >>> +{ >>> + uint64_t scratch; >>> + > > [...] > >> /* >> Store a valid mask and the address of the nop into the fixup PSW. >> Then load the possibly invalid PSW. >> */ > > This seems a bit redundant given the function comment, but I can > drop a comment in here describing how the fixup psw is computed. Well, I skipped the function comment, got confused by the addr asm variable and then decided to propose the comment. It's a bit confusing since you have the invalid PSW and the global fixup PSW in one function. Maybe something like: /* From here to the lpswe we're computing and setting the fixup PSW */ > >> >>> + fixup_psw.mask = extract_psw_mask(); >>> + asm volatile ( "larl %[scratch],0f\n" >>> + " stg %[scratch],%[addr]\n" >>> + " lpswe %[psw]\n" >>> + "0: nop\n" >>> + : [scratch] "=&d"(scratch), >>> + [addr] "=&T"(fixup_psw.addr) >> >> s/addr/psw_addr/ ? >> >>> + : [psw] "Q"(psw) >>> + : "cc", "memory" >>> + ); >>> +} >>> + >>> +static void load_short_psw(struct short_psw psw) >>> +{ >>> + uint64_t scratch; >>> + >>> + fixup_psw.mask = extract_psw_mask(); >>> + asm volatile ( "larl %[scratch],0f\n" >>> + " stg %[scratch],%[addr]\n" >>> + " lpsw %[psw]\n" >>> + "0: nop\n" >>> + : [scratch] "=&d"(scratch), >>> + [addr] "=&T"(fixup_psw.addr) >>> + : [psw] "Q"(psw) >>> + : "cc", "memory" >>> + ); >> >> Same story. > > Do you want me to repeat the comments here or just rename addr? Just rename addr > > [...] > >>> +static int not_even(void) >>> +{ >>> + uint64_t quad[2] __attribute__((aligned(16))) = {0}; >>> + >>> + asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" /* lpq %%r7,%[quad] */ >>> + : : [quad] "T"(quad) >> >> Is there a reason you never put a space after the constraint? > > TBH I never noticed I'm unusual in that regard. I guess I tend to think > of the operand and constraint as one entity. > I'll add the spaces. > >> >>> + : "%r7", "%r8" >>> + ); >>> + return 0; >>> +} >>> + > > [...]
diff --git a/s390x/Makefile b/s390x/Makefile index efd5e0c1..58b1bf54 100644 --- a/s390x/Makefile +++ b/s390x/Makefile @@ -27,6 +27,7 @@ tests += $(TEST_DIR)/uv-host.elf tests += $(TEST_DIR)/edat.elf tests += $(TEST_DIR)/mvpg-sie.elf tests += $(TEST_DIR)/spec_ex-sie.elf +tests += $(TEST_DIR)/spec_ex.elf tests += $(TEST_DIR)/firq.elf tests += $(TEST_DIR)/epsw.elf tests += $(TEST_DIR)/adtl-status.elf diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h index 78b257b7..8fbc451c 100644 --- a/lib/s390x/asm/arch_def.h +++ b/lib/s390x/asm/arch_def.h @@ -41,6 +41,11 @@ struct psw { uint64_t addr; }; +struct short_psw { + uint32_t mask; + uint32_t addr; +}; + #define AS_PRIM 0 #define AS_ACCR 1 #define AS_SECN 2 diff --git a/s390x/spec_ex.c b/s390x/spec_ex.c new file mode 100644 index 00000000..77fc6246 --- /dev/null +++ b/s390x/spec_ex.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright IBM Corp. 2021, 2022 + * + * Specification exception test. + * Tests that specification exceptions occur when expected. + * + * Can be extended by adding triggers to spec_ex_triggers, see comments below. + */ +#include <stdlib.h> +#include <libcflat.h> +#include <asm/interrupt.h> + +static bool invalid_psw_expected; +static struct psw expected_psw; +static struct psw invalid_psw; +static struct psw fixup_psw; + +/* + * The standard program exception handler cannot deal with invalid old PSWs, + * especially not invalid instruction addresses, as in that case one cannot + * find the instruction following the faulting one from the old PSW. + * The PSW to return to is set by load_psw. + */ +static void fixup_invalid_psw(void) +{ + /* signal occurrence of invalid psw fixup */ + invalid_psw_expected = false; + invalid_psw = lowcore.pgm_old_psw; + lowcore.pgm_old_psw = fixup_psw; +} + +/* + * Load possibly invalid psw, but setup fixup_psw before, + * so that fixup_invalid_psw() can bring us back onto the right track. + * Also acts as compiler barrier, -> none required in expect/check_invalid_psw + */ +static void load_psw(struct psw psw) +{ + uint64_t scratch; + + fixup_psw.mask = extract_psw_mask(); + asm volatile ( "larl %[scratch],0f\n" + " stg %[scratch],%[addr]\n" + " lpswe %[psw]\n" + "0: nop\n" + : [scratch] "=&d"(scratch), + [addr] "=&T"(fixup_psw.addr) + : [psw] "Q"(psw) + : "cc", "memory" + ); +} + +static void load_short_psw(struct short_psw psw) +{ + uint64_t scratch; + + fixup_psw.mask = extract_psw_mask(); + asm volatile ( "larl %[scratch],0f\n" + " stg %[scratch],%[addr]\n" + " lpsw %[psw]\n" + "0: nop\n" + : [scratch] "=&d"(scratch), + [addr] "=&T"(fixup_psw.addr) + : [psw] "Q"(psw) + : "cc", "memory" + ); +} + +static void expect_invalid_psw(struct psw psw) +{ + expected_psw = psw; + invalid_psw_expected = true; +} + +static int check_invalid_psw(void) +{ + /* toggled to signal occurrence of invalid psw fixup */ + if (!invalid_psw_expected) { + if (expected_psw.mask == invalid_psw.mask && + expected_psw.addr == invalid_psw.addr) + return 0; + report_fail("Wrong invalid PSW"); + } else { + report_fail("Expected exception due to invalid PSW"); + } + return 1; +} + +static int psw_bit_12_is_1(void) +{ + struct psw invalid = { .mask = 0x0008000000000000, .addr = 0x00000000deadbeee}; + + expect_invalid_psw(invalid); + load_psw(invalid); + return check_invalid_psw(); +} + +static int short_psw_bit_12_is_0(void) +{ + struct short_psw short_invalid = { .mask = 0x00000000, .addr = 0xdeadbeee}; + + /* + * lpsw may optionally check bit 12 before loading the new psw + * -> cannot check the expected invalid psw like with lpswe + */ + load_short_psw(short_invalid); + return 0; +} + +static int bad_alignment(void) +{ + uint32_t words[5] __attribute__((aligned(16))); + uint32_t (*bad_aligned)[4] = (uint32_t (*)[4])&words[1]; + + asm volatile ("lpq %%r6,%[bad]" + : : [bad] "T"(*bad_aligned) + : "%r6", "%r7" + ); + return 0; +} + +static int not_even(void) +{ + uint64_t quad[2] __attribute__((aligned(16))) = {0}; + + asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" /* lpq %%r7,%[quad] */ + : : [quad] "T"(quad) + : "%r7", "%r8" + ); + return 0; +} + +/* + * Harness for specification exception testing. + * func only triggers exception, reporting is taken care of automatically. + */ +struct spec_ex_trigger { + const char *name; + int (*func)(void); + void (*fixup)(void); +}; + +/* List of all tests to execute */ +static const struct spec_ex_trigger spec_ex_triggers[] = { + { "psw_bit_12_is_1", &psw_bit_12_is_1, &fixup_invalid_psw }, + { "short_psw_bit_12_is_0", &short_psw_bit_12_is_0, &fixup_invalid_psw }, + { "bad_alignment", &bad_alignment, NULL }, + { "not_even", ¬_even, NULL }, + { NULL, NULL, NULL }, +}; + +static void test_spec_ex(const struct spec_ex_trigger *trigger) +{ + int rc; + + expect_pgm_int(); + register_pgm_cleanup_func(trigger->fixup); + rc = trigger->func(); + register_pgm_cleanup_func(NULL); + /* test failed, nothing to be done, reporting responsibility of trigger */ + if (rc) + return; + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); +} + +int main(int argc, char **argv) +{ + unsigned int i; + + report_prefix_push("specification exception"); + for (i = 0; spec_ex_triggers[i].name; i++) { + report_prefix_push(spec_ex_triggers[i].name); + test_spec_ex(&spec_ex_triggers[i]); + report_prefix_pop(); + } + report_prefix_pop(); + + return report_summary(); +} diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg index 8e52f560..d2740a40 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -113,6 +113,9 @@ file = mvpg-sie.elf [spec_ex-sie] file = spec_ex-sie.elf +[spec_ex] +file = spec_ex.elf + [firq-linear-cpu-ids-kvm] file = firq.elf timeout = 20
Generate specification exceptions and check that they occur. Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com> --- s390x/Makefile | 1 + lib/s390x/asm/arch_def.h | 5 ++ s390x/spec_ex.c | 180 +++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg | 3 + 4 files changed, 189 insertions(+) create mode 100644 s390x/spec_ex.c