xref: /kvm-unit-tests/lib/s390x/css_lib.c (revision 6c9f99df2fa51f58bd6a8b6775810b7f249bd0d7)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Channel Subsystem tests library
4  *
5  * Copyright (c) 2020 IBM Corp
6  *
7  * Authors:
8  *  Pierre Morel <pmorel@linux.ibm.com>
9  */
10 #include <libcflat.h>
11 #include <alloc_phys.h>
12 #include <asm/page.h>
13 #include <string.h>
14 #include <interrupt.h>
15 #include <asm/arch_def.h>
16 #include <asm/time.h>
17 #include <asm/arch_def.h>
18 
19 #include <css.h>
20 
21 static struct schib schib;
22 
23 /*
24  * css_enumerate:
25  * On success return the first subchannel ID found.
26  * On error return an invalid subchannel ID containing cc
27  */
28 int css_enumerate(void)
29 {
30 	struct pmcw *pmcw = &schib.pmcw;
31 	int scn_found = 0;
32 	int dev_found = 0;
33 	int schid = 0;
34 	int cc;
35 	int scn;
36 
37 	for (scn = 0; scn < 0xffff; scn++) {
38 		cc = stsch(scn | SCHID_ONE, &schib);
39 		switch (cc) {
40 		case 0:		/* 0 means SCHIB stored */
41 			break;
42 		case 3:		/* 3 means no more channels */
43 			goto out;
44 		default:	/* 1 or 2 should never happen for STSCH */
45 			report_abort("Unexpected error %d on subchannel %08x",
46 				     cc, scn | SCHID_ONE);
47 			return cc;
48 		}
49 
50 		/* We currently only support type 0, a.k.a. I/O channels */
51 		if (PMCW_CHANNEL_TYPE(pmcw) != 0)
52 			continue;
53 
54 		/* We ignore I/O channels without valid devices */
55 		scn_found++;
56 		if (!(pmcw->flags & PMCW_DNV))
57 			continue;
58 
59 		/* We keep track of the first device as our test device */
60 		if (!schid)
61 			schid = scn | SCHID_ONE;
62 		report_info("Found subchannel %08x", scn | SCHID_ONE);
63 		dev_found++;
64 	}
65 
66 out:
67 	report_info("Tested subchannels: %d, I/O subchannels: %d, I/O devices: %d",
68 		    scn, scn_found, dev_found);
69 	return schid;
70 }
71 
72 /*
73  * css_enable: enable the subchannel with the specified ISC
74  * @schid: Subchannel Identifier
75  * @isc  : number of the interruption subclass to use
76  * Return value:
77  *   On success: 0
78  *   On error the CC of the faulty instruction
79  *      or -1 if the retry count is exceeded.
80  */
81 int css_enable(int schid, int isc)
82 {
83 	struct pmcw *pmcw = &schib.pmcw;
84 	int retry_count = 0;
85 	uint16_t flags;
86 	int cc;
87 
88 	/* Read the SCHIB for this subchannel */
89 	cc = stsch(schid, &schib);
90 	if (cc) {
91 		report_info("stsch: sch %08x failed with cc=%d", schid, cc);
92 		return cc;
93 	}
94 
95 	flags = PMCW_ENABLE | (isc << PMCW_ISC_SHIFT);
96 	if ((pmcw->flags & (PMCW_ISC_MASK | PMCW_ENABLE)) == flags) {
97 		report_info("stsch: sch %08x already enabled", schid);
98 		return 0;
99 	}
100 
101 retry:
102 	/* Update the SCHIB to enable the channel and set the ISC */
103 	pmcw->flags &= ~PMCW_ISC_MASK;
104 	pmcw->flags |= flags;
105 
106 	/* Tell the CSS we want to modify the subchannel */
107 	cc = msch(schid, &schib);
108 	if (cc) {
109 		/*
110 		 * If the subchannel is status pending or
111 		 * if a function is in progress,
112 		 * we consider both cases as errors.
113 		 */
114 		report_info("msch: sch %08x failed with cc=%d", schid, cc);
115 		return cc;
116 	}
117 
118 	/*
119 	 * Read the SCHIB again to verify the enablement
120 	 */
121 	cc = stsch(schid, &schib);
122 	if (cc) {
123 		report_info("stsch: updating sch %08x failed with cc=%d",
124 			    schid, cc);
125 		return cc;
126 	}
127 
128 	if ((pmcw->flags & flags) == flags) {
129 		report_info("stsch: sch %08x successfully modified after %d retries",
130 			    schid, retry_count);
131 		return 0;
132 	}
133 
134 	if (retry_count++ < MAX_ENABLE_RETRIES) {
135 		mdelay(10); /* the hardware was not ready, give it some time */
136 		goto retry;
137 	}
138 
139 	report_info("msch: modifying sch %08x failed after %d retries. pmcw flags: %04x",
140 		    schid, retry_count, pmcw->flags);
141 	return -1;
142 }
143 
144 static struct irb irb;
145 
146 void css_irq_io(void)
147 {
148 	int ret = 0;
149 	char *flags;
150 	int sid;
151 
152 	report_prefix_push("Interrupt");
153 	sid = lowcore_ptr->subsys_id_word;
154 	/* Lowlevel set the SID as interrupt parameter. */
155 	if (lowcore_ptr->io_int_param != sid) {
156 		report(0,
157 		       "io_int_param: %x differs from subsys_id_word: %x",
158 		       lowcore_ptr->io_int_param, sid);
159 		goto pop;
160 	}
161 	report_info("subsys_id_word: %08x io_int_param %08x io_int_word %08x",
162 			lowcore_ptr->subsys_id_word,
163 			lowcore_ptr->io_int_param,
164 			lowcore_ptr->io_int_word);
165 	report_prefix_pop();
166 
167 	report_prefix_push("tsch");
168 	ret = tsch(sid, &irb);
169 	switch (ret) {
170 	case 1:
171 		dump_irb(&irb);
172 		flags = dump_scsw_flags(irb.scsw.ctrl);
173 		report(0,
174 		       "I/O interrupt, but tsch returns CC 1 for subchannel %08x. SCSW flags: %s",
175 		       sid, flags);
176 		break;
177 	case 2:
178 		report(0, "tsch returns unexpected CC 2");
179 		break;
180 	case 3:
181 		report(0, "tsch reporting sch %08x as not operational", sid);
182 		break;
183 	case 0:
184 		/* Stay humble on success */
185 		break;
186 	}
187 pop:
188 	report_prefix_pop();
189 	lowcore_ptr->io_old_psw.mask &= ~PSW_MASK_WAIT;
190 }
191 
192 int start_ccw1_chain(unsigned int sid, struct ccw1 *ccw)
193 {
194 	struct orb orb = {
195 		.intparm = sid,
196 		.ctrl = ORB_CTRL_ISIC|ORB_CTRL_FMT|ORB_LPM_DFLT,
197 		.cpa = (unsigned int) (unsigned long)ccw,
198 	};
199 
200 	return ssch(sid, &orb);
201 }
202 
203 /*
204  * In the future, we want to implement support for CCW chains;
205  * for that, we will need to work with ccw1 pointers.
206  */
207 static struct ccw1 unique_ccw;
208 
209 int start_single_ccw(unsigned int sid, int code, void *data, int count,
210 		     unsigned char flags)
211 {
212 	int cc;
213 	struct ccw1 *ccw = &unique_ccw;
214 
215 	report_prefix_push("start_subchannel");
216 	/* Build the CCW chain with a single CCW */
217 	ccw->code = code;
218 	ccw->flags = flags;
219 	ccw->count = count;
220 	ccw->data_address = (int)(unsigned long)data;
221 
222 	cc = start_ccw1_chain(sid, ccw);
223 	if (cc) {
224 		report(0, "cc = %d", cc);
225 		report_prefix_pop();
226 		return cc;
227 	}
228 	report_prefix_pop();
229 	return 0;
230 }
231 
232 /* wait_and_check_io_completion:
233  * @schid: the subchannel ID
234  *
235  * Makes the most common check to validate a successful I/O
236  * completion.
237  * Only report failures.
238  */
239 int wait_and_check_io_completion(int schid)
240 {
241 	int ret = 0;
242 
243 	wait_for_interrupt(PSW_MASK_IO);
244 
245 	report_prefix_push("check I/O completion");
246 
247 	if (lowcore_ptr->io_int_param != schid) {
248 		report(0, "interrupt parameter: expected %08x got %08x",
249 		       schid, lowcore_ptr->io_int_param);
250 		ret = -1;
251 		goto end;
252 	}
253 
254 	/* Verify that device status is valid */
255 	if (!(irb.scsw.ctrl & SCSW_SC_PENDING)) {
256 		report(0, "No status pending after interrupt. Subch Ctrl: %08x",
257 		       irb.scsw.ctrl);
258 		ret = -1;
259 		goto end;
260 	}
261 
262 	if (!(irb.scsw.ctrl & (SCSW_SC_SECONDARY | SCSW_SC_PRIMARY))) {
263 		report(0, "Primary or secondary status missing. Subch Ctrl: %08x",
264 		       irb.scsw.ctrl);
265 		ret = -1;
266 		goto end;
267 	}
268 
269 	if (!(irb.scsw.dev_stat & (SCSW_DEVS_DEV_END | SCSW_DEVS_SCH_END))) {
270 		report(0, "No device end or sch end. Dev. status: %02x",
271 		       irb.scsw.dev_stat);
272 		ret = -1;
273 		goto end;
274 	}
275 
276 	if (irb.scsw.sch_stat & ~SCSW_SCHS_IL) {
277 		report_info("Unexpected Subch. status %02x", irb.scsw.sch_stat);
278 		ret = -1;
279 		goto end;
280 	}
281 
282 end:
283 	report_prefix_pop();
284 	return ret;
285 }
286 
287 /*
288  * css_residual_count
289  * Return the residual count, if it is valid.
290  *
291  * Return value:
292  * Success: the residual count
293  * Not meaningful: -1 (-1 can not be a valid count)
294  */
295 int css_residual_count(unsigned int schid)
296 {
297 
298 	if (!(irb.scsw.ctrl & (SCSW_SC_PENDING | SCSW_SC_PRIMARY)))
299 		return -1;
300 
301 	if (irb.scsw.dev_stat)
302 		if (irb.scsw.sch_stat & ~(SCSW_SCHS_PCI | SCSW_SCHS_IL))
303 			return -1;
304 
305 	return irb.scsw.count;
306 }
307 
308 /*
309  * enable_io_isc: setup ISC in Control Register 6
310  * @isc: The interruption Sub Class as a bitfield
311  */
312 void enable_io_isc(uint8_t isc)
313 {
314 	uint64_t value;
315 
316 	value = (uint64_t)isc << 24;
317 	lctlg(6, value);
318 }
319