@@ -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
*/
@@ -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;
+}
@@ -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 }
};