xref: /openbmc/qemu/hw/s390x/3270-ccw.c (revision bf7942e406cb5e96d2490909d2cb31c7625b087b)
1ff20b0a3SYang Chen /*
2ff20b0a3SYang Chen  * Emulated ccw-attached 3270 implementation
3ff20b0a3SYang Chen  *
4ff20b0a3SYang Chen  * Copyright 2017 IBM Corp.
5ff20b0a3SYang Chen  * Author(s): Yang Chen <bjcyang@linux.vnet.ibm.com>
6ff20b0a3SYang Chen  *            Jing Liu <liujbjl@linux.vnet.ibm.com>
7ff20b0a3SYang Chen  *
8ff20b0a3SYang Chen  * This work is licensed under the terms of the GNU GPL, version 2 or (at
9ff20b0a3SYang Chen  * your option) any later version. See the COPYING file in the top-level
10ff20b0a3SYang Chen  * directory.
11ff20b0a3SYang Chen  */
12a27bd6c7SMarkus Armbruster 
13ff20b0a3SYang Chen #include "qemu/osdep.h"
14ff20b0a3SYang Chen #include "qapi/error.h"
15ff20b0a3SYang Chen #include "qemu/module.h"
16ff20b0a3SYang Chen #include "hw/s390x/css.h"
17ff20b0a3SYang Chen #include "hw/s390x/css-bridge.h"
18a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
19ff20b0a3SYang Chen #include "hw/s390x/3270-ccw.h"
20ff20b0a3SYang Chen 
212dc95b4cSJing Liu /* Handle READ ccw commands from guest */
handle_payload_3270_read(EmulatedCcw3270Device * dev,CCW1 * ccw)222dc95b4cSJing Liu static int handle_payload_3270_read(EmulatedCcw3270Device *dev, CCW1 *ccw)
232dc95b4cSJing Liu {
242dc95b4cSJing Liu     EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev);
252dc95b4cSJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
262dc95b4cSJing Liu     int len;
272dc95b4cSJing Liu 
282dc95b4cSJing Liu     if (!ccw->cda) {
292dc95b4cSJing Liu         return -EFAULT;
302dc95b4cSJing Liu     }
312dc95b4cSJing Liu 
321baa2eb0SHalil Pasic     len = ck->read_payload_3270(dev);
33d895d25aSPierre Morel     if (len < 0) {
34d895d25aSPierre Morel         return len;
35d895d25aSPierre Morel     }
362dc95b4cSJing Liu     ccw_dev->sch->curr_status.scsw.count = ccw->count - len;
372dc95b4cSJing Liu 
382dc95b4cSJing Liu     return 0;
392dc95b4cSJing Liu }
402dc95b4cSJing Liu 
412dc95b4cSJing Liu /* Handle WRITE ccw commands to write data to client */
handle_payload_3270_write(EmulatedCcw3270Device * dev,CCW1 * ccw)422dc95b4cSJing Liu static int handle_payload_3270_write(EmulatedCcw3270Device *dev, CCW1 *ccw)
432dc95b4cSJing Liu {
442dc95b4cSJing Liu     EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev);
452dc95b4cSJing Liu     CcwDevice *ccw_dev = CCW_DEVICE(dev);
462dc95b4cSJing Liu     int len;
472dc95b4cSJing Liu 
482dc95b4cSJing Liu     if (!ccw->cda) {
492dc95b4cSJing Liu         return -EFAULT;
502dc95b4cSJing Liu     }
512dc95b4cSJing Liu 
521baa2eb0SHalil Pasic     len = ck->write_payload_3270(dev, ccw->cmd_code);
532dc95b4cSJing Liu 
542dc95b4cSJing Liu     if (len <= 0) {
55d895d25aSPierre Morel         return len ? len : -EIO;
562dc95b4cSJing Liu     }
572dc95b4cSJing Liu 
582dc95b4cSJing Liu     ccw_dev->sch->curr_status.scsw.count = ccw->count - len;
592dc95b4cSJing Liu     return 0;
602dc95b4cSJing Liu }
612dc95b4cSJing Liu 
emulated_ccw_3270_cb(SubchDev * sch,CCW1 ccw)622dc95b4cSJing Liu static int emulated_ccw_3270_cb(SubchDev *sch, CCW1 ccw)
632dc95b4cSJing Liu {
642dc95b4cSJing Liu     int rc = 0;
652dc95b4cSJing Liu     EmulatedCcw3270Device *dev = sch->driver_data;
662dc95b4cSJing Liu 
672dc95b4cSJing Liu     switch (ccw.cmd_code) {
682dc95b4cSJing Liu     case TC_WRITESF:
692dc95b4cSJing Liu     case TC_WRITE:
702dc95b4cSJing Liu     case TC_EWRITE:
712dc95b4cSJing Liu     case TC_EWRITEA:
722dc95b4cSJing Liu         rc = handle_payload_3270_write(dev, &ccw);
732dc95b4cSJing Liu         break;
742dc95b4cSJing Liu     case TC_RDBUF:
752dc95b4cSJing Liu     case TC_READMOD:
762dc95b4cSJing Liu         rc = handle_payload_3270_read(dev, &ccw);
772dc95b4cSJing Liu         break;
782dc95b4cSJing Liu     default:
792dc95b4cSJing Liu         rc = -ENOSYS;
802dc95b4cSJing Liu         break;
812dc95b4cSJing Liu     }
822dc95b4cSJing Liu 
832dc95b4cSJing Liu     if (rc == -EIO) {
842dc95b4cSJing Liu         /* I/O error, specific devices generate specific conditions */
857357b221SDaniel P. Berrangé         SCHIB *schib = &sch->curr_status;
862dc95b4cSJing Liu 
872dc95b4cSJing Liu         sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_CHECK;
882dc95b4cSJing Liu         sch->sense_data[0] = 0x40;    /* intervention-req */
897357b221SDaniel P. Berrangé         schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND;
907357b221SDaniel P. Berrangé         schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL;
917357b221SDaniel P. Berrangé         schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
922dc95b4cSJing Liu                    SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
932dc95b4cSJing Liu     }
942dc95b4cSJing Liu 
952dc95b4cSJing Liu     return rc;
962dc95b4cSJing Liu }
972dc95b4cSJing Liu 
emulated_ccw_3270_realize(DeviceState * ds,Error ** errp)98ff20b0a3SYang Chen static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp)
99ff20b0a3SYang Chen {
100ff20b0a3SYang Chen     uint16_t chpid;
101ff20b0a3SYang Chen     EmulatedCcw3270Device *dev = EMULATED_CCW_3270(ds);
102ff20b0a3SYang Chen     EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev);
103ff20b0a3SYang Chen     CcwDevice *cdev = CCW_DEVICE(ds);
104ff20b0a3SYang Chen     CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev);
105817d4a6bSDong Jia Shi     SubchDev *sch;
106ff20b0a3SYang Chen     Error *err = NULL;
107ff20b0a3SYang Chen 
10836699ab4SCornelia Huck     sch = css_create_sch(cdev->devno, errp);
109ff20b0a3SYang Chen     if (!sch) {
110ff20b0a3SYang Chen         return;
111ff20b0a3SYang Chen     }
112ff20b0a3SYang Chen 
113ff20b0a3SYang Chen     if (!ck->init) {
114ff20b0a3SYang Chen         goto out_err;
115ff20b0a3SYang Chen     }
116ff20b0a3SYang Chen 
117ff20b0a3SYang Chen     sch->driver_data = dev;
118ff20b0a3SYang Chen     cdev->sch = sch;
119ff20b0a3SYang Chen     chpid = css_find_free_chpid(sch->cssid);
120ff20b0a3SYang Chen 
121ff20b0a3SYang Chen     if (chpid > MAX_CHPID) {
122ff20b0a3SYang Chen         error_setg(&err, "No available chpid to use.");
123ff20b0a3SYang Chen         goto out_err;
124ff20b0a3SYang Chen     }
125ff20b0a3SYang Chen 
126ff20b0a3SYang Chen     sch->id.reserved = 0xff;
127ff20b0a3SYang Chen     sch->id.cu_type = EMULATED_CCW_3270_CU_TYPE;
128ff20b0a3SYang Chen     css_sch_build_virtual_schib(sch, (uint8_t)chpid,
129ff20b0a3SYang Chen                                 EMULATED_CCW_3270_CHPID_TYPE);
1301728cff2SDong Jia Shi     sch->do_subchannel_work = do_subchannel_work_virtual;
1312dc95b4cSJing Liu     sch->ccw_cb = emulated_ccw_3270_cb;
132*0599a046SEric Farman     sch->irb_cb = build_irb_virtual;
133ff20b0a3SYang Chen 
134ff20b0a3SYang Chen     ck->init(dev, &err);
135ff20b0a3SYang Chen     if (err) {
136ff20b0a3SYang Chen         goto out_err;
137ff20b0a3SYang Chen     }
138ff20b0a3SYang Chen 
139ff20b0a3SYang Chen     cdk->realize(cdev, &err);
140ff20b0a3SYang Chen     if (err) {
141ff20b0a3SYang Chen         goto out_err;
142ff20b0a3SYang Chen     }
143ff20b0a3SYang Chen 
144ff20b0a3SYang Chen     return;
145ff20b0a3SYang Chen 
146ff20b0a3SYang Chen out_err:
147ff20b0a3SYang Chen     error_propagate(errp, err);
148ff20b0a3SYang Chen     css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
149ff20b0a3SYang Chen     cdev->sch = NULL;
150ff20b0a3SYang Chen     g_free(sch);
151ff20b0a3SYang Chen }
152ff20b0a3SYang Chen 
153ff20b0a3SYang Chen static Property emulated_ccw_3270_properties[] = {
154ff20b0a3SYang Chen     DEFINE_PROP_END_OF_LIST(),
155ff20b0a3SYang Chen };
156ff20b0a3SYang Chen 
emulated_ccw_3270_class_init(ObjectClass * klass,void * data)157ff20b0a3SYang Chen static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data)
158ff20b0a3SYang Chen {
159ff20b0a3SYang Chen     DeviceClass *dc = DEVICE_CLASS(klass);
160ff20b0a3SYang Chen 
1614f67d30bSMarc-André Lureau     device_class_set_props(dc, emulated_ccw_3270_properties);
162ff20b0a3SYang Chen     dc->realize = emulated_ccw_3270_realize;
163ff20b0a3SYang Chen     dc->hotpluggable = false;
164bd2aef10SCornelia Huck     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
165ff20b0a3SYang Chen }
166ff20b0a3SYang Chen 
167ff20b0a3SYang Chen static const TypeInfo emulated_ccw_3270_info = {
168ff20b0a3SYang Chen     .name = TYPE_EMULATED_CCW_3270,
169ff20b0a3SYang Chen     .parent = TYPE_CCW_DEVICE,
170ff20b0a3SYang Chen     .instance_size = sizeof(EmulatedCcw3270Device),
171ff20b0a3SYang Chen     .class_init = emulated_ccw_3270_class_init,
172ff20b0a3SYang Chen     .class_size = sizeof(EmulatedCcw3270Class),
173ff20b0a3SYang Chen     .abstract = true,
174ff20b0a3SYang Chen };
175ff20b0a3SYang Chen 
emulated_ccw_register(void)176ff20b0a3SYang Chen static void emulated_ccw_register(void)
177ff20b0a3SYang Chen {
178ff20b0a3SYang Chen     type_register_static(&emulated_ccw_3270_info);
179ff20b0a3SYang Chen }
180ff20b0a3SYang Chen 
181ff20b0a3SYang Chen type_init(emulated_ccw_register)
182