xref: /qemu/pc-bios/s390-ccw/cio.c (revision ab9056ff)
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"
16 #include "s390-arch.h"
17 #include "helper.h"
18 #include "cio.h"
19 
20 static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
21 
22 static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb);
23 
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 }
49 
50 uint16_t cu_type(SubChannelId schid)
51 {
52     Ccw1 sense_id_ccw;
53     SenseId sense_data;
54 
55     sense_id_ccw.cmd_code = CCW_CMD_SENSE_ID;
56     sense_id_ccw.cda = ptr2u32(&sense_data);
57     sense_id_ccw.count = sizeof(sense_data);
58     sense_id_ccw.flags |= CCW_FLAG_SLI;
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 {
70     Ccw1 senseCcw;
71     Irb irb;
72 
73     senseCcw.cmd_code = CCW_CMD_BASIC_SENSE;
74     senseCcw.cda = ptr2u32(sense_data);
75     senseCcw.count = data_size;
76 
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 
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 
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 {
317     CmdOrb orb = {};
318     int rc;
319 
320     IPL_assert(fmt == 0 || fmt == 1, "Invalid ccw format");
321 
322     /* ccw_addr must be <= 24 bits and point to at least one whole ccw. */
323     if (fmt == 0) {
324         IPL_assert(ccw_addr <= 0xFFFFFF - 8, "Invalid ccw address");
325     }
326 
327     orb.fmt = fmt;
328     orb.pfch = 1;  /* QEMU's cio implementation requires prefetch */
329     orb.c64 = 1;   /* QEMU's cio implementation requires 64-bit idaws */
330     orb.lpm = 0xFF; /* All paths allowed */
331     orb.cpa = ccw_addr;
332 
333     rc = ssch(schid, &orb);
334     if (rc == 1 || rc == 2) {
335         /* Subchannel status pending or busy. Eat status and ask for retry. */
336         tsch(schid, irb);
337         return -1;
338     }
339     if (rc) {
340         print_int("ssch failed with cc=", rc);
341         return rc;
342     }
343 
344     consume_io_int();
345 
346     /* collect status */
347     rc = tsch(schid, irb);
348     if (rc) {
349         print_int("tsch failed with cc=", rc);
350     }
351 
352     return rc;
353 }
354 
355 /*
356  * Executes a channel program at a given subchannel. The request to run the
357  * channel program is sent to the subchannel, we then wait for the interrupt
358  * signaling completion of the I/O operation(s) performed by the channel
359  * program. Lastly we verify that the i/o operation completed without error and
360  * that the interrupt we received was for the subchannel used to run the
361  * channel program.
362  *
363  * Note: This function assumes it is running in an environment where no other
364  * cpus are generating or receiving I/O interrupts. So either run it in a
365  * single-cpu environment or make sure all other cpus are not doing I/O and
366  * have I/O interrupts masked off. We also assume that only one device is
367  * active (generating i/o interrupts).
368  *
369  * Returns non-zero on error.
370  */
371 int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt)
372 {
373     Irb irb = {};
374     SenseDataEckdDasd sd;
375     int rc, retries = 0;
376 
377     while (true) {
378         rc = __do_cio(schid, ccw_addr, fmt, &irb);
379 
380         if (rc == -1) {
381             retries++;
382             continue;
383         }
384         if (rc) {
385             /* ssch/tsch error. Message already reported by __do_cio */
386             break;
387         }
388 
389         if (!irb_error(&irb)) {
390             break;
391         }
392 
393         /*
394          * Unexpected unit check, or interface-control-check. Use sense to
395          * clear (unit check only) then retry.
396          */
397         if ((unit_check(&irb) || iface_ctrl_check(&irb)) && retries <= 2) {
398             if (unit_check(&irb)) {
399                 basic_sense(schid, cutype, &sd, sizeof(sd));
400             }
401             retries++;
402             continue;
403         }
404 
405         sclp_print("cio device error\n");
406         print_int("  ssid  ", schid.ssid);
407         print_int("  cssid ", schid.cssid);
408         print_int("  sch_no", schid.sch_no);
409         print_int("  ctrl-unit type", cutype);
410         sclp_print("\n");
411         print_irb_err(&irb);
412         if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 ||
413             cutype == CU_TYPE_UNKNOWN) {
414             if (!basic_sense(schid, cutype, &sd, sizeof(sd))) {
415                 print_eckd_dasd_sense_data(&sd);
416             }
417         }
418         rc = -1;
419         break;
420     }
421 
422     return rc;
423 }
424