Message ID | 1520942503-6163-6-git-send-email-frankja@linux.vnet.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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@linux.vnet.ibm.com> > --- > 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@linux.vnet.ibm.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/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
On 14.03.2018 05:59, Thomas Huth wrote: > 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@linux.vnet.ibm.com> >> --- >> 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@linux.vnet.ibm.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/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? Yes, it should be 2. Well it seems, I never tested 2G and it came to haunt me. > >> +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. Good catch > >> + 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. Ugh, yes. > >> + 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. Sure > >> + 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 >
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@linux.vnet.ibm.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/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 + +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" + : [addr] "+a" (addr) : [r1] "d" (r1)); + 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; + + 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]; + 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(); +} diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg index 5531590..3b975ca 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -44,3 +44,6 @@ file = skey.elf [diag10] file = diag10.elf + +[pfmf] +file = pfmf.elf
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@linux.vnet.ibm.com> --- s390x/Makefile | 1 + s390x/pfmf.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg | 3 ++ 3 files changed, 147 insertions(+) create mode 100644 s390x/pfmf.c