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