1 /* 2 * Emulated ccw-attached 3270 implementation 3 * 4 * Copyright 2017 IBM Corp. 5 * Author(s): Yang Chen <bjcyang@linux.vnet.ibm.com> 6 * Jing Liu <liujbjl@linux.vnet.ibm.com> 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or (at 9 * your option) any later version. See the COPYING file in the top-level 10 * directory. 11 */ 12 #include "qemu/osdep.h" 13 #include "qapi/error.h" 14 #include "qemu/module.h" 15 #include "cpu.h" 16 #include "hw/s390x/css.h" 17 #include "hw/s390x/css-bridge.h" 18 #include "hw/s390x/3270-ccw.h" 19 20 /* Handle READ ccw commands from guest */ 21 static int handle_payload_3270_read(EmulatedCcw3270Device *dev, CCW1 *ccw) 22 { 23 EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); 24 CcwDevice *ccw_dev = CCW_DEVICE(dev); 25 int len; 26 27 if (!ccw->cda) { 28 return -EFAULT; 29 } 30 31 len = ck->read_payload_3270(dev, ccw->cda, ccw->count); 32 ccw_dev->sch->curr_status.scsw.count = ccw->count - len; 33 34 return 0; 35 } 36 37 /* Handle WRITE ccw commands to write data to client */ 38 static int handle_payload_3270_write(EmulatedCcw3270Device *dev, CCW1 *ccw) 39 { 40 EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); 41 CcwDevice *ccw_dev = CCW_DEVICE(dev); 42 int len; 43 44 if (!ccw->cda) { 45 return -EFAULT; 46 } 47 48 len = ck->write_payload_3270(dev, ccw->cmd_code, ccw->cda, ccw->count); 49 50 if (len <= 0) { 51 return -EIO; 52 } 53 54 ccw_dev->sch->curr_status.scsw.count = ccw->count - len; 55 return 0; 56 } 57 58 static int emulated_ccw_3270_cb(SubchDev *sch, CCW1 ccw) 59 { 60 int rc = 0; 61 EmulatedCcw3270Device *dev = sch->driver_data; 62 63 switch (ccw.cmd_code) { 64 case TC_WRITESF: 65 case TC_WRITE: 66 case TC_EWRITE: 67 case TC_EWRITEA: 68 rc = handle_payload_3270_write(dev, &ccw); 69 break; 70 case TC_RDBUF: 71 case TC_READMOD: 72 rc = handle_payload_3270_read(dev, &ccw); 73 break; 74 default: 75 rc = -ENOSYS; 76 break; 77 } 78 79 if (rc == -EIO) { 80 /* I/O error, specific devices generate specific conditions */ 81 SCSW *s = &sch->curr_status.scsw; 82 83 sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_CHECK; 84 sch->sense_data[0] = 0x40; /* intervention-req */ 85 s->ctrl &= ~SCSW_ACTL_START_PEND; 86 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 87 s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | 88 SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 89 } 90 91 return rc; 92 } 93 94 static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) 95 { 96 uint16_t chpid; 97 EmulatedCcw3270Device *dev = EMULATED_CCW_3270(ds); 98 EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); 99 CcwDevice *cdev = CCW_DEVICE(ds); 100 CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev); 101 DeviceState *parent = DEVICE(cdev); 102 BusState *qbus = qdev_get_parent_bus(parent); 103 VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); 104 SubchDev *sch; 105 Error *err = NULL; 106 107 sch = css_create_sch(cdev->devno, true, cbus->squash_mcss, errp); 108 if (!sch) { 109 return; 110 } 111 112 if (!ck->init) { 113 goto out_err; 114 } 115 116 sch->driver_data = dev; 117 cdev->sch = sch; 118 chpid = css_find_free_chpid(sch->cssid); 119 120 if (chpid > MAX_CHPID) { 121 error_setg(&err, "No available chpid to use."); 122 goto out_err; 123 } 124 125 sch->id.reserved = 0xff; 126 sch->id.cu_type = EMULATED_CCW_3270_CU_TYPE; 127 css_sch_build_virtual_schib(sch, (uint8_t)chpid, 128 EMULATED_CCW_3270_CHPID_TYPE); 129 sch->do_subchannel_work = do_subchannel_work_virtual; 130 sch->ccw_cb = emulated_ccw_3270_cb; 131 132 ck->init(dev, &err); 133 if (err) { 134 goto out_err; 135 } 136 137 cdk->realize(cdev, &err); 138 if (err) { 139 goto out_err; 140 } 141 142 return; 143 144 out_err: 145 error_propagate(errp, err); 146 css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); 147 cdev->sch = NULL; 148 g_free(sch); 149 } 150 151 static Property emulated_ccw_3270_properties[] = { 152 DEFINE_PROP_END_OF_LIST(), 153 }; 154 155 static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) 156 { 157 DeviceClass *dc = DEVICE_CLASS(klass); 158 159 dc->props = emulated_ccw_3270_properties; 160 dc->bus_type = TYPE_VIRTUAL_CSS_BUS; 161 dc->realize = emulated_ccw_3270_realize; 162 dc->hotpluggable = false; 163 } 164 165 static const TypeInfo emulated_ccw_3270_info = { 166 .name = TYPE_EMULATED_CCW_3270, 167 .parent = TYPE_CCW_DEVICE, 168 .instance_size = sizeof(EmulatedCcw3270Device), 169 .class_init = emulated_ccw_3270_class_init, 170 .class_size = sizeof(EmulatedCcw3270Class), 171 .abstract = true, 172 }; 173 174 static void emulated_ccw_register(void) 175 { 176 type_register_static(&emulated_ccw_3270_info); 177 } 178 179 type_init(emulated_ccw_register) 180