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