On 13.03.2018 13:01, Janosch Frank wrote: > Perform Frame Management Functions (pfmf) is an instruction that sets > storage keys or clears memory for frames in the size of 4k, 1m and > 2g. Hence it's quite complex and deserves some testing. > > Signed-off-by: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx> > --- > s390x/Makefile | 1 + > s390x/pfmf.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > s390x/unittests.cfg | 3 ++ > 3 files changed, 147 insertions(+) > create mode 100644 s390x/pfmf.c > > diff --git a/s390x/Makefile b/s390x/Makefile > index bc5dd91..438c6b2 100644 > --- a/s390x/Makefile > +++ b/s390x/Makefile > @@ -5,6 +5,7 @@ tests += $(TEST_DIR)/sieve.elf > tests += $(TEST_DIR)/sthyi.elf > tests += $(TEST_DIR)/skey.elf > tests += $(TEST_DIR)/diag10.elf > +tests += $(TEST_DIR)/pfmf.elf > > all: directories test_cases > > diff --git a/s390x/pfmf.c b/s390x/pfmf.c > new file mode 100644 > index 0000000..fe31a82 > --- /dev/null > +++ b/s390x/pfmf.c > @@ -0,0 +1,143 @@ > +/* > + * Perform Frame Management Function (pfmf) tests > + * > + * Copyright (c) 2017 IBM > + * > + * Authors: > + * Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx> > + * > + * 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/asm-offsets.h> > +#include <asm/interrupt.h> > +#include <asm/page.h> > +#include <asm/facility.h> > +#include <asm/mem.h> > + > +#define FSC_4K 0 > +#define FSC_1M 1 > +#define FSC_2G 3 According to my version of the PoP, FSC_2G should be 2 instead? > +union r1 { > + struct { > + unsigned long pad0 : 32; > + unsigned long pad1 : 12; > + unsigned long pad_fmfi : 2; > + unsigned long sk : 1; /* set key*/ > + unsigned long cf : 1; /* clear frame */ > + unsigned long ui : 1; /* usage indication */ > + unsigned long fsc : 3; > + unsigned long pad2 : 1; > + unsigned long mr : 1; > + unsigned long mc : 1; > + unsigned long pad3 : 1; > + unsigned long key : 8; /* storage keys */ > + } reg; > + unsigned long val; > +}; > + > +static uint8_t pagebuf[PAGE_SIZE * 256] __attribute__((aligned(PAGE_SIZE * 256))); > + > +static inline unsigned long pfmf(unsigned long r1, unsigned long paddr) > +{ > + register uint64_t addr asm("1") = paddr; > + > + asm volatile(".insn rrf,0xb9af0000,%[r1],%[addr],0,0" PFMF is only "rre", not "rrf". Then you can also omit the ",0,0" here. > + : [addr] "+a" (addr) : [r1] "d" (r1)); Since pfmf can be used to clear memory, you should add "memory" to the clobber list. > + return addr; > +} > + > +static void test_priv(void) > +{ > + expect_pgm_int(); > + enter_pstate(); > + pfmf(0, (unsigned long) pagebuf); > + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); > +} > + > +static void test_4k_key(void) > +{ > + union r1 r1; > + union skey skey; > + > + r1.val = 0; > + r1.reg.sk = 1; > + r1.reg.fsc = FSC_4K; > + r1.reg.key = 0x30; > + pfmf(r1.val, (unsigned long) pagebuf); > + skey.val = get_storage_key((unsigned long) pagebuf); > + report("set 4k", skey.val == 0x30); > +} > + > +static void test_1m_key(void) > +{ > + int i; > + union r1 r1; > + > + if (!test_facility(8)) > + return; You already checked that in the main() function, no need to check it here again. > + r1.val = 0; > + r1.reg.sk = 1; > + r1.reg.fsc = FSC_1M; > + r1.reg.key = 0x30; > + pfmf(r1.val, (unsigned long) pagebuf); > + for (i = 0; i < 256; i++) { > + if (get_storage_key((unsigned long) pagebuf + i * PAGE_SIZE) != 0x30) { > + report("set 1M", false); > + return; > + } > + } > + report("set 1M", true); > +} > + > +static void test_4k_clear(void) > +{ > + union r1 r1; > + > + r1.val = 0; > + r1.reg.cf = 1; > + r1.reg.fsc = FSC_4K; > + > + memset(pagebuf, 42, PAGE_SIZE); > + pfmf(r1.val, (unsigned long) pagebuf); > + report("clear 4k", !memcmp(pagebuf, pagebuf + PAGE_SIZE, PAGE_SIZE)); > +} > + > +static void test_1m_clear(void) > +{ > + int i; > + union r1 r1; > + unsigned long sum = 0; > + > + r1.val = 0; > + r1.reg.cf = 1; > + r1.reg.fsc = FSC_1M; > + > + memset(pagebuf, 42, PAGE_SIZE * 256); > + pfmf(r1.val, (unsigned long) pagebuf); > + for (i = 0; i < PAGE_SIZE * 256; i++) > + sum += pagebuf[i]; Maybe better use "|=" instead of "+=" to avoid that the sum wraps around to 0 again by accident. > + report("clear 1m", !sum); > +} > + > +int main(void) > +{ > + if (!test_facility(8)) { > + printf("EDAT facility not available, hence pfmf is not available."); > + return 0; > + } > + > + report_prefix_push("pfmf"); > + test_priv(); > + /* Force the buffer pages in */ > + memset(pagebuf, 0, PAGE_SIZE * 256); > + > + test_4k_key(); > + test_1m_key(); > + test_4k_clear(); > + test_1m_clear(); > + return report_summary(); > +} Thomas