[kvm-unit-tests PATCH v4 4/8] s390x: Add tests for STCRW

Nico Boehr
2022-02-24T15:44:01Z

The test consists of two parts: First a simple check to ensure we
enforce an aligned address. We test misalignment by 1 and 2 bytes.

The second part tests the handling of pending Channel Reports (CR). We
first assume no CR is initally pending and check STCRW returns
accordingly. Then, we generate a CR by resetting a Channel Path using
RCHP and make sure this results in exactly one CRW being generated which
has a Reporting-Source Code (RSC) corresponding to the Channel Path
facility.

Signed-off-by: Nico Boehr <nrb@linux.ibm.com>
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
---
lib/s390x/css.h | 17 ++++++++++
lib/s390x/css_lib.c | 60 +++++++++++++++++++++++++++++++++
s390x/css.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 159 insertions(+)

diff --git a/lib/s390x/css.h b/lib/s390x/css.h
index 0db8a28166f6..a6a68577248b 100644
--- a/lib/s390x/css.h
+++ b/lib/s390x/css.h
@@ -266,6 +266,20 @@ static inline int rchp(unsigned long chpid)
return cc;
}

+static inline int stcrw(uint32_t *crw)
+{
+ int cc;
+
+ asm volatile(
+ " stcrw %[crw]\n"
+ " ipm %[cc]\n"
+ " srl %[cc],28"
+ : [cc] "=d" (cc)
+ : [crw] "Q" (*crw)
+ : "cc", "memory");
+ return cc;
+}
+
/* Debug functions */
char *dump_pmcw_flags(uint16_t f);
char *dump_scsw_flags(uint32_t f);
@@ -294,6 +308,9 @@ int css_residual_count(unsigned int schid);
void enable_io_isc(uint8_t isc);
int wait_and_check_io_completion(int schid);

+int css_find_installed_chpid(int sid, uint8_t *chpid_out);
+int css_generate_crw(int sid);
+
/*
* CHSC definitions
*/
diff --git a/lib/s390x/css_lib.c b/lib/s390x/css_lib.c
index 80e9e078b0f6..a9f5097f3486 100644
--- a/lib/s390x/css_lib.c
+++ b/lib/s390x/css_lib.c
@@ -504,3 +504,63 @@ void enable_io_isc(uint8_t isc)
value = (uint64_t)isc << 24;
lctlg(6, value);
}
+
+static int is_path_installed(struct schib *schib, int chp_idx)
+{
+ return schib->pmcw.pim & BIT(7 - chp_idx);
+}
+
+/*
+ * css_find_installed_chpid: find any installed CHPID
+ * @sid: subsystem-identification word
+ * @chpid_out: store the found chpid here, left alone if none found
+ *
+ * returns 0 on success, -1 if no chpid found any other value
+ * indicates the condition code of a failing STSCH instruction
+ */
+int css_find_installed_chpid(int sid, uint8_t *chpid_out)
+{
+ int cc;
+
+ cc = stsch(sid, &schib);
+ if (cc) {
+ report_fail("%s: sch %08x failed with cc=%d", __func__, sid, cc);
+ return cc;
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(schib.pmcw.chpid); i++) {
+ if (is_path_installed(&schib, i)) {
+ *chpid_out = schib.pmcw.chpid[i];
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * css_generate_crw: Generate a CRW by issuing RCHP on any channel path
+ * @sid: subsystem-identification word
+ *
+ * returns 0 when a CRW was generated, -1 if no chpid found.
+ */
+int css_generate_crw(int sid)
+{
+ int ret, cc;
+ uint8_t chpid;
+
+ report_prefix_push("Generate CRW");
+
+ ret = css_find_installed_chpid(sid, &chpid);
+ if (ret) {
+ report_fail("No CHPID found: ret=%d", ret);
+ return -1;
+ }
+
+ cc = rchp(chpid);
+ report(!cc, "rhcp cc != 0");
+
+ report_prefix_pop();
+
+ return 0;
+}
diff --git a/s390x/css.c b/s390x/css.c
index 396007ed0074..fcc264eecd4b 100644
--- a/s390x/css.c
+++ b/s390x/css.c
@@ -388,6 +388,87 @@ static void test_msch(void)
schib.pmcw.flags = old_pmcw_flags;
}

+static void check_stcrw_no_crw_available(void)
+{
+ uint32_t crw = 0xfeedc0fe;
+ int cc;
+
+ report_prefix_push("No CRW available");
+ cc = stcrw(&crw);
+ report(cc == 1, "cc == 1");
+ report(!crw, "stored zeroes in crw");
+ report_prefix_pop();
+}
+
+static int check_stcrw_crw_available(void)
+{
+ const uint32_t magic = 0xfeedc0fe;
+ uint32_t crw = magic;
+ int cc;
+
+ report_prefix_push("CRW available");
+ cc = stcrw(&crw);
+ report(!cc, "cc is zero");
+ report(crw != magic, "stored crw");
+ report_prefix_pop();
+
+ return crw;
+}
+
+static uint32_t crw_get_rsc(uint32_t crw)
+{
+ const int rsc_begin = 4;
+ const int rsc_end = 8;
+
+ return (crw & GENMASK(31 - rsc_begin, 31 - rsc_end)) >> 24;
+}
+
+#define CRW_RSC_CHP 4
+static void test_stcrw(void)
+{
+ const int align_to = 4;
+ int res;
+ uint32_t crw;
+
+ if (!test_device_sid) {
+ report_skip("No device");
+ return;
+ }
+
+ report_prefix_push("Unaligned");
+ for (int i = 1; i < align_to; i *= 2) {
+ report_prefix_pushf("%d", i);
+
+ expect_pgm_int();
+ stcrw((uint32_t *)(alignment_test_page + i));
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+
+ report_prefix_pop();
+ }
+ report_prefix_pop();
+
+ report_prefix_push("No CRW available initally");
+ check_stcrw_no_crw_available();
+ report_prefix_pop();
+
+ res = css_generate_crw(test_device_sid);
+ if (res) {
+ report_skip("Couldn't generate CRW");
+ report_prefix_pop();
+ return;
+ }
+
+ crw = check_stcrw_crw_available();
+
+ report_prefix_push("CRW available");
+ report(crw_get_rsc(crw) == CRW_RSC_CHP, "CRW has Channel Path RSC");
+ report_prefix_pop();
+
+ report_prefix_push("No more CRWs pending");
+ check_stcrw_no_crw_available();
+ report_prefix_pop();
+}
+
static struct {
const char *name;
void (*func)(void);
@@ -401,6 +482,7 @@ static struct {
{ "measurement block format0", test_schm_fmt0 },
{ "measurement block format1", test_schm_fmt1 },
{ "msch", test_msch },
+ { "stcrw", test_stcrw },
{ NULL, NULL }
};

--
2.31.1