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