]>
Commit | Line | Data |
---|---|---|
120d0410 JH |
1 | /* |
2 | * S390 Channel I/O | |
3 | * | |
4 | * Copyright (c) 2013 Alexander Graf <agraf@suse.de> | |
5 | * Copyright (c) 2019 IBM Corp. | |
6 | * | |
7 | * Author(s): Jason J. Herne <jjherne@us.ibm.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
10 | * your option) any later version. See the COPYING file in the top-level | |
11 | * directory. | |
12 | */ | |
13 | ||
14 | #include "libc.h" | |
15 | #include "s390-ccw.h" | |
3083a1bb JH |
16 | #include "s390-arch.h" |
17 | #include "helper.h" | |
120d0410 JH |
18 | #include "cio.h" |
19 | ||
20 | static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); | |
21 | ||
3083a1bb JH |
22 | static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb); |
23 | ||
120d0410 JH |
24 | int enable_mss_facility(void) |
25 | { | |
26 | int ret; | |
27 | ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page; | |
28 | ||
29 | memset(sda_area, 0, PAGE_SIZE); | |
30 | sda_area->request.length = 0x0400; | |
31 | sda_area->request.code = 0x0031; | |
32 | sda_area->operation_code = 0x2; | |
33 | ||
34 | ret = chsc(sda_area); | |
35 | if ((ret == 0) && (sda_area->response.code == 0x0001)) { | |
36 | return 0; | |
37 | } | |
38 | return -EIO; | |
39 | } | |
40 | ||
41 | void enable_subchannel(SubChannelId schid) | |
42 | { | |
43 | Schib schib; | |
44 | ||
45 | stsch_err(schid, &schib); | |
46 | schib.pmcw.ena = 1; | |
47 | msch(schid, &schib); | |
48 | } | |
3083a1bb JH |
49 | |
50 | uint16_t cu_type(SubChannelId schid) | |
51 | { | |
3083a1bb | 52 | SenseId sense_data; |
8c6cc7b9 JF |
53 | Ccw1 sense_id_ccw = { |
54 | .cmd_code = CCW_CMD_SENSE_ID, | |
55 | .flags = CCW_FLAG_SLI, | |
56 | .count = sizeof(sense_data), | |
57 | .cda = ptr2u32(&sense_data), | |
58 | }; | |
3083a1bb JH |
59 | |
60 | if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) { | |
61 | panic("Failed to run SenseID CCw\n"); | |
62 | } | |
63 | ||
64 | return sense_data.cu_type; | |
65 | } | |
66 | ||
67 | int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data, | |
68 | uint16_t data_size) | |
69 | { | |
8c6cc7b9 JF |
70 | Ccw1 senseCcw = { |
71 | .cmd_code = CCW_CMD_BASIC_SENSE, | |
72 | .count = data_size, | |
73 | .cda = ptr2u32(sense_data), | |
74 | }; | |
3083a1bb JH |
75 | Irb irb; |
76 | ||
3083a1bb JH |
77 | return __do_cio(schid, ptr2u32(&senseCcw), CCW_FMT1, &irb); |
78 | } | |
79 | ||
80 | static bool irb_error(Irb *irb) | |
81 | { | |
82 | if (irb->scsw.cstat) { | |
83 | return true; | |
84 | } | |
85 | return irb->scsw.dstat != (SCSW_DSTAT_DEVEND | SCSW_DSTAT_CHEND); | |
86 | } | |
87 | ||
86c58705 JH |
88 | static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd) |
89 | { | |
90 | char msgline[512]; | |
91 | ||
92 | if (sd->config_info & 0x8000) { | |
93 | sclp_print("Eckd Dasd Sense Data (fmt 24-bytes):\n"); | |
94 | } else { | |
95 | sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n"); | |
96 | } | |
97 | ||
98 | strcat(msgline, " Sense Condition Flags :"); | |
99 | if (sd->common_status & SNS_STAT0_CMD_REJECT) { | |
100 | strcat(msgline, " [Cmd-Reject]"); | |
101 | } | |
102 | if (sd->common_status & SNS_STAT0_INTERVENTION_REQ) { | |
103 | strcat(msgline, " [Intervention-Required]"); | |
104 | } | |
105 | if (sd->common_status & SNS_STAT0_BUS_OUT_CHECK) { | |
106 | strcat(msgline, " [Bus-Out-Parity-Check]"); | |
107 | } | |
108 | if (sd->common_status & SNS_STAT0_EQUIPMENT_CHECK) { | |
109 | strcat(msgline, " [Equipment-Check]"); | |
110 | } | |
111 | if (sd->common_status & SNS_STAT0_DATA_CHECK) { | |
112 | strcat(msgline, " [Data-Check]"); | |
113 | } | |
114 | if (sd->common_status & SNS_STAT0_OVERRUN) { | |
115 | strcat(msgline, " [Overrun]"); | |
116 | } | |
117 | if (sd->common_status & SNS_STAT0_INCOMPL_DOMAIN) { | |
118 | strcat(msgline, " [Incomplete-Domain]"); | |
119 | } | |
120 | ||
121 | if (sd->status[0] & SNS_STAT1_PERM_ERR) { | |
122 | strcat(msgline, " [Permanent-Error]"); | |
123 | } | |
124 | if (sd->status[0] & SNS_STAT1_INV_TRACK_FORMAT) { | |
125 | strcat(msgline, " [Invalid-Track-Fmt]"); | |
126 | } | |
127 | if (sd->status[0] & SNS_STAT1_EOC) { | |
128 | strcat(msgline, " [End-of-Cyl]"); | |
129 | } | |
130 | if (sd->status[0] & SNS_STAT1_MESSAGE_TO_OPER) { | |
131 | strcat(msgline, " [Operator-Msg]"); | |
132 | } | |
133 | if (sd->status[0] & SNS_STAT1_NO_REC_FOUND) { | |
134 | strcat(msgline, " [No-Record-Found]"); | |
135 | } | |
136 | if (sd->status[0] & SNS_STAT1_FILE_PROTECTED) { | |
137 | strcat(msgline, " [File-Protected]"); | |
138 | } | |
139 | if (sd->status[0] & SNS_STAT1_WRITE_INHIBITED) { | |
140 | strcat(msgline, " [Write-Inhibited]"); | |
141 | } | |
142 | if (sd->status[0] & SNS_STAT1_IMPRECISE_END) { | |
143 | strcat(msgline, " [Imprecise-Ending]"); | |
144 | } | |
145 | ||
146 | if (sd->status[1] & SNS_STAT2_REQ_INH_WRITE) { | |
147 | strcat(msgline, " [Req-Inhibit-Write]"); | |
148 | } | |
149 | if (sd->status[1] & SNS_STAT2_CORRECTABLE) { | |
150 | strcat(msgline, " [Correctable-Data-Check]"); | |
151 | } | |
152 | if (sd->status[1] & SNS_STAT2_FIRST_LOG_ERR) { | |
153 | strcat(msgline, " [First-Error-Log]"); | |
154 | } | |
155 | if (sd->status[1] & SNS_STAT2_ENV_DATA_PRESENT) { | |
156 | strcat(msgline, " [Env-Data-Present]"); | |
157 | } | |
158 | if (sd->status[1] & SNS_STAT2_IMPRECISE_END) { | |
159 | strcat(msgline, " [Imprecise-End]"); | |
160 | } | |
161 | strcat(msgline, "\n"); | |
162 | sclp_print(msgline); | |
163 | ||
164 | print_int(" Residual Count =", sd->res_count); | |
165 | print_int(" Phys Drive ID =", sd->phys_drive_id); | |
166 | print_int(" low cyl address =", sd->low_cyl_addr); | |
167 | print_int(" head addr & hi cyl =", sd->head_high_cyl_addr); | |
168 | print_int(" format/message =", sd->fmt_msg); | |
169 | print_int(" fmt-dependent[0-7] =", sd->fmt_dependent_info[0]); | |
170 | print_int(" fmt-dependent[8-15]=", sd->fmt_dependent_info[1]); | |
171 | print_int(" prog action code =", sd->program_action_code); | |
172 | print_int(" Configuration info =", sd->config_info); | |
173 | print_int(" mcode / hi-cyl =", sd->mcode_hicyl); | |
174 | print_int(" cyl & head addr [0]=", sd->cyl_head_addr[0]); | |
175 | print_int(" cyl & head addr [1]=", sd->cyl_head_addr[1]); | |
176 | print_int(" cyl & head addr [2]=", sd->cyl_head_addr[2]); | |
177 | } | |
178 | ||
179 | static void print_irb_err(Irb *irb) | |
180 | { | |
181 | uint64_t this_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa); | |
182 | uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8); | |
183 | char msgline[256]; | |
184 | ||
185 | sclp_print("Interrupt Response Block Data:\n"); | |
186 | ||
187 | strcat(msgline, " Function Ctrl :"); | |
188 | if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) { | |
189 | strcat(msgline, " [Start]"); | |
190 | } | |
191 | if (irb->scsw.ctrl & SCSW_FCTL_HALT_FUNC) { | |
192 | strcat(msgline, " [Halt]"); | |
193 | } | |
194 | if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { | |
195 | strcat(msgline, " [Clear]"); | |
196 | } | |
197 | strcat(msgline, "\n"); | |
198 | sclp_print(msgline); | |
199 | ||
200 | msgline[0] = '\0'; | |
201 | strcat(msgline, " Activity Ctrl :"); | |
202 | if (irb->scsw.ctrl & SCSW_ACTL_RESUME_PEND) { | |
203 | strcat(msgline, " [Resume-Pending]"); | |
204 | } | |
205 | if (irb->scsw.ctrl & SCSW_ACTL_START_PEND) { | |
206 | strcat(msgline, " [Start-Pending]"); | |
207 | } | |
208 | if (irb->scsw.ctrl & SCSW_ACTL_HALT_PEND) { | |
209 | strcat(msgline, " [Halt-Pending]"); | |
210 | } | |
211 | if (irb->scsw.ctrl & SCSW_ACTL_CLEAR_PEND) { | |
212 | strcat(msgline, " [Clear-Pending]"); | |
213 | } | |
214 | if (irb->scsw.ctrl & SCSW_ACTL_CH_ACTIVE) { | |
215 | strcat(msgline, " [Channel-Active]"); | |
216 | } | |
217 | if (irb->scsw.ctrl & SCSW_ACTL_DEV_ACTIVE) { | |
218 | strcat(msgline, " [Device-Active]"); | |
219 | } | |
220 | if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) { | |
221 | strcat(msgline, " [Suspended]"); | |
222 | } | |
223 | strcat(msgline, "\n"); | |
224 | sclp_print(msgline); | |
225 | ||
226 | msgline[0] = '\0'; | |
227 | strcat(msgline, " Status Ctrl :"); | |
228 | if (irb->scsw.ctrl & SCSW_SCTL_ALERT) { | |
229 | strcat(msgline, " [Alert]"); | |
230 | } | |
231 | if (irb->scsw.ctrl & SCSW_SCTL_INTERMED) { | |
232 | strcat(msgline, " [Intermediate]"); | |
233 | } | |
234 | if (irb->scsw.ctrl & SCSW_SCTL_PRIMARY) { | |
235 | strcat(msgline, " [Primary]"); | |
236 | } | |
237 | if (irb->scsw.ctrl & SCSW_SCTL_SECONDARY) { | |
238 | strcat(msgline, " [Secondary]"); | |
239 | } | |
240 | if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) { | |
241 | strcat(msgline, " [Status-Pending]"); | |
242 | } | |
243 | ||
244 | strcat(msgline, "\n"); | |
245 | sclp_print(msgline); | |
246 | ||
247 | msgline[0] = '\0'; | |
248 | strcat(msgline, " Device Status :"); | |
249 | if (irb->scsw.dstat & SCSW_DSTAT_ATTN) { | |
250 | strcat(msgline, " [Attention]"); | |
251 | } | |
252 | if (irb->scsw.dstat & SCSW_DSTAT_STATMOD) { | |
253 | strcat(msgline, " [Status-Modifier]"); | |
254 | } | |
255 | if (irb->scsw.dstat & SCSW_DSTAT_CUEND) { | |
256 | strcat(msgline, " [Ctrl-Unit-End]"); | |
257 | } | |
258 | if (irb->scsw.dstat & SCSW_DSTAT_BUSY) { | |
259 | strcat(msgline, " [Busy]"); | |
260 | } | |
261 | if (irb->scsw.dstat & SCSW_DSTAT_CHEND) { | |
262 | strcat(msgline, " [Channel-End]"); | |
263 | } | |
264 | if (irb->scsw.dstat & SCSW_DSTAT_DEVEND) { | |
265 | strcat(msgline, " [Device-End]"); | |
266 | } | |
267 | if (irb->scsw.dstat & SCSW_DSTAT_UCHK) { | |
268 | strcat(msgline, " [Unit-Check]"); | |
269 | } | |
270 | if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) { | |
271 | strcat(msgline, " [Unit-Exception]"); | |
272 | } | |
273 | strcat(msgline, "\n"); | |
274 | sclp_print(msgline); | |
275 | ||
276 | msgline[0] = '\0'; | |
277 | strcat(msgline, " Channel Status :"); | |
278 | if (irb->scsw.cstat & SCSW_CSTAT_PCINT) { | |
279 | strcat(msgline, " [Program-Ctrl-Interruption]"); | |
280 | } | |
281 | if (irb->scsw.cstat & SCSW_CSTAT_BADLEN) { | |
282 | strcat(msgline, " [Incorrect-Length]"); | |
283 | } | |
284 | if (irb->scsw.cstat & SCSW_CSTAT_PROGCHK) { | |
285 | strcat(msgline, " [Program-Check]"); | |
286 | } | |
287 | if (irb->scsw.cstat & SCSW_CSTAT_PROTCHK) { | |
288 | strcat(msgline, " [Protection-Check]"); | |
289 | } | |
290 | if (irb->scsw.cstat & SCSW_CSTAT_CHDCHK) { | |
291 | strcat(msgline, " [Channel-Data-Check]"); | |
292 | } | |
293 | if (irb->scsw.cstat & SCSW_CSTAT_CHCCHK) { | |
294 | strcat(msgline, " [Channel-Ctrl-Check]"); | |
295 | } | |
296 | if (irb->scsw.cstat & SCSW_CSTAT_ICCHK) { | |
297 | strcat(msgline, " [Interface-Ctrl-Check]"); | |
298 | } | |
299 | if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) { | |
300 | strcat(msgline, " [Chaining-Check]"); | |
301 | } | |
302 | strcat(msgline, "\n"); | |
303 | sclp_print(msgline); | |
304 | ||
305 | print_int(" cpa=", irb->scsw.cpa); | |
306 | print_int(" prev_ccw=", prev_ccw); | |
307 | print_int(" this_ccw=", this_ccw); | |
308 | } | |
309 | ||
3083a1bb JH |
310 | /* |
311 | * Handles executing ssch, tsch and returns the irb obtained from tsch. | |
312 | * Returns 0 on success, -1 if unexpected status pending and we need to retry, | |
313 | * otherwise returns condition code from ssch/tsch for error cases. | |
314 | */ | |
315 | static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) | |
316 | { | |
8c6cc7b9 JF |
317 | /* |
318 | * QEMU's CIO implementation requires prefetch and 64-bit idaws. We | |
319 | * allow all paths. | |
320 | */ | |
321 | CmdOrb orb = { | |
322 | .fmt = fmt, | |
323 | .pfch = 1, | |
324 | .c64 = 1, | |
325 | .lpm = 0xFF, | |
326 | .cpa = ccw_addr, | |
327 | }; | |
3083a1bb JH |
328 | int rc; |
329 | ||
330 | IPL_assert(fmt == 0 || fmt == 1, "Invalid ccw format"); | |
331 | ||
332 | /* ccw_addr must be <= 24 bits and point to at least one whole ccw. */ | |
333 | if (fmt == 0) { | |
334 | IPL_assert(ccw_addr <= 0xFFFFFF - 8, "Invalid ccw address"); | |
335 | } | |
336 | ||
3083a1bb JH |
337 | rc = ssch(schid, &orb); |
338 | if (rc == 1 || rc == 2) { | |
339 | /* Subchannel status pending or busy. Eat status and ask for retry. */ | |
340 | tsch(schid, irb); | |
341 | return -1; | |
342 | } | |
343 | if (rc) { | |
344 | print_int("ssch failed with cc=", rc); | |
345 | return rc; | |
346 | } | |
347 | ||
348 | consume_io_int(); | |
349 | ||
350 | /* collect status */ | |
351 | rc = tsch(schid, irb); | |
352 | if (rc) { | |
353 | print_int("tsch failed with cc=", rc); | |
354 | } | |
355 | ||
356 | return rc; | |
357 | } | |
358 | ||
359 | /* | |
360 | * Executes a channel program at a given subchannel. The request to run the | |
361 | * channel program is sent to the subchannel, we then wait for the interrupt | |
362 | * signaling completion of the I/O operation(s) performed by the channel | |
363 | * program. Lastly we verify that the i/o operation completed without error and | |
364 | * that the interrupt we received was for the subchannel used to run the | |
365 | * channel program. | |
366 | * | |
367 | * Note: This function assumes it is running in an environment where no other | |
368 | * cpus are generating or receiving I/O interrupts. So either run it in a | |
369 | * single-cpu environment or make sure all other cpus are not doing I/O and | |
370 | * have I/O interrupts masked off. We also assume that only one device is | |
371 | * active (generating i/o interrupts). | |
372 | * | |
373 | * Returns non-zero on error. | |
374 | */ | |
375 | int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt) | |
376 | { | |
377 | Irb irb = {}; | |
378 | SenseDataEckdDasd sd; | |
379 | int rc, retries = 0; | |
380 | ||
381 | while (true) { | |
382 | rc = __do_cio(schid, ccw_addr, fmt, &irb); | |
383 | ||
384 | if (rc == -1) { | |
385 | retries++; | |
386 | continue; | |
387 | } | |
388 | if (rc) { | |
389 | /* ssch/tsch error. Message already reported by __do_cio */ | |
390 | break; | |
391 | } | |
392 | ||
393 | if (!irb_error(&irb)) { | |
394 | break; | |
395 | } | |
396 | ||
397 | /* | |
398 | * Unexpected unit check, or interface-control-check. Use sense to | |
399 | * clear (unit check only) then retry. | |
400 | */ | |
401 | if ((unit_check(&irb) || iface_ctrl_check(&irb)) && retries <= 2) { | |
402 | if (unit_check(&irb)) { | |
403 | basic_sense(schid, cutype, &sd, sizeof(sd)); | |
404 | } | |
405 | retries++; | |
406 | continue; | |
407 | } | |
408 | ||
86c58705 JH |
409 | sclp_print("cio device error\n"); |
410 | print_int(" ssid ", schid.ssid); | |
411 | print_int(" cssid ", schid.cssid); | |
412 | print_int(" sch_no", schid.sch_no); | |
413 | print_int(" ctrl-unit type", cutype); | |
414 | sclp_print("\n"); | |
415 | print_irb_err(&irb); | |
416 | if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 || | |
417 | cutype == CU_TYPE_UNKNOWN) { | |
418 | if (!basic_sense(schid, cutype, &sd, sizeof(sd))) { | |
419 | print_eckd_dasd_sense_data(&sd); | |
420 | } | |
421 | } | |
3083a1bb JH |
422 | rc = -1; |
423 | break; | |
424 | } | |
425 | ||
426 | return rc; | |
427 | } |