diff options
Diffstat (limited to 'hw/s390x')
-rw-r--r-- | hw/s390x/3270-ccw.c | 174 | ||||
-rw-r--r-- | hw/s390x/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/s390x/css.c | 24 | ||||
-rw-r--r-- | hw/s390x/ipl.c | 39 | ||||
-rw-r--r-- | hw/s390x/ipl.h | 3 | ||||
-rw-r--r-- | hw/s390x/s390-virtio-ccw.c | 37 | ||||
-rw-r--r-- | hw/s390x/sclp.c | 9 |
7 files changed, 284 insertions, 3 deletions
diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c new file mode 100644 index 0000000000..a7a5b412e4 --- /dev/null +++ b/hw/s390x/3270-ccw.c @@ -0,0 +1,174 @@ +/* + * Emulated ccw-attached 3270 implementation + * + * Copyright 2017 IBM Corp. + * Author(s): Yang Chen <bjcyang@linux.vnet.ibm.com> + * Jing Liu <liujbjl@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "cpu.h" +#include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" +#include "hw/s390x/3270-ccw.h" + +/* Handle READ ccw commands from guest */ +static int handle_payload_3270_read(EmulatedCcw3270Device *dev, CCW1 *ccw) +{ + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + int len; + + if (!ccw->cda) { + return -EFAULT; + } + + len = ck->read_payload_3270(dev, ccw->cda, ccw->count); + ccw_dev->sch->curr_status.scsw.count = ccw->count - len; + + return 0; +} + +/* Handle WRITE ccw commands to write data to client */ +static int handle_payload_3270_write(EmulatedCcw3270Device *dev, CCW1 *ccw) +{ + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *ccw_dev = CCW_DEVICE(dev); + int len; + + if (!ccw->cda) { + return -EFAULT; + } + + len = ck->write_payload_3270(dev, ccw->cmd_code, ccw->cda, ccw->count); + + if (len <= 0) { + return -EIO; + } + + ccw_dev->sch->curr_status.scsw.count = ccw->count - len; + return 0; +} + +static int emulated_ccw_3270_cb(SubchDev *sch, CCW1 ccw) +{ + int rc = 0; + EmulatedCcw3270Device *dev = sch->driver_data; + + switch (ccw.cmd_code) { + case TC_WRITESF: + case TC_WRITE: + case TC_EWRITE: + case TC_EWRITEA: + rc = handle_payload_3270_write(dev, &ccw); + break; + case TC_RDBUF: + case TC_READMOD: + rc = handle_payload_3270_read(dev, &ccw); + break; + default: + rc = -ENOSYS; + break; + } + + if (rc == -EIO) { + /* I/O error, specific devices generate specific conditions */ + SCSW *s = &sch->curr_status.scsw; + + sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_CHECK; + sch->sense_data[0] = 0x40; /* intervention-req */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + } + + return rc; +} + +static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) +{ + uint16_t chpid; + EmulatedCcw3270Device *dev = EMULATED_CCW_3270(ds); + EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); + CcwDevice *cdev = CCW_DEVICE(ds); + CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev); + SubchDev *sch = css_create_virtual_sch(cdev->devno, errp); + Error *err = NULL; + + if (!sch) { + return; + } + + if (!ck->init) { + goto out_err; + } + + sch->driver_data = dev; + cdev->sch = sch; + chpid = css_find_free_chpid(sch->cssid); + + if (chpid > MAX_CHPID) { + error_setg(&err, "No available chpid to use."); + goto out_err; + } + + sch->id.reserved = 0xff; + sch->id.cu_type = EMULATED_CCW_3270_CU_TYPE; + css_sch_build_virtual_schib(sch, (uint8_t)chpid, + EMULATED_CCW_3270_CHPID_TYPE); + sch->ccw_cb = emulated_ccw_3270_cb; + + ck->init(dev, &err); + if (err) { + goto out_err; + } + + cdk->realize(cdev, &err); + if (err) { + goto out_err; + } + + return; + +out_err: + error_propagate(errp, err); + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + cdev->sch = NULL; + g_free(sch); +} + +static Property emulated_ccw_3270_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = emulated_ccw_3270_properties; + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + dc->realize = emulated_ccw_3270_realize; + dc->hotpluggable = false; +} + +static const TypeInfo emulated_ccw_3270_info = { + .name = TYPE_EMULATED_CCW_3270, + .parent = TYPE_CCW_DEVICE, + .instance_size = sizeof(EmulatedCcw3270Device), + .class_init = emulated_ccw_3270_class_init, + .class_size = sizeof(EmulatedCcw3270Class), + .abstract = true, +}; + +static void emulated_ccw_register(void) +{ + type_register_static(&emulated_ccw_3270_info); +} + +type_init(emulated_ccw_register) diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 41ac4ec325..36bd4b1645 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -7,6 +7,7 @@ obj-y += sclpcpu.o obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o +obj-y += 3270-ccw.o obj-y += virtio-ccw.o obj-y += css-bridge.o obj-y += ccw-device.o diff --git a/hw/s390x/css.c b/hw/s390x/css.c index c03bb20bc9..15c4f4b249 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -576,6 +576,9 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb) s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; s->cpa = sch->channel_prog + 8; break; + case -EIO: + /* I/O errors, status depends on specific devices */ + break; case -ENOSYS: /* unsupported command, generate unit check (command reject) */ s->ctrl &= ~SCSW_ACTL_START_PEND; @@ -1302,6 +1305,27 @@ bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid) (MAX_SCHID + 1) / sizeof(unsigned long)); } +unsigned int css_find_free_chpid(uint8_t cssid) +{ + CssImage *css = channel_subsys.css[cssid]; + unsigned int chpid; + + if (!css) { + return MAX_CHPID + 1; + } + + for (chpid = 0; chpid <= MAX_CHPID; chpid++) { + /* skip reserved chpid */ + if (chpid == VIRTIO_CCW_CHPID) { + continue; + } + if (!css->chpids[chpid].in_use) { + return chpid; + } + } + return MAX_CHPID + 1; +} + static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) { CssImage *css; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 7978c7d52a..75d3c681a4 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -17,8 +17,10 @@ #include "cpu.h" #include "elf.h" #include "hw/loader.h" +#include "hw/boards.h" #include "hw/s390x/virtio-ccw.h" #include "hw/s390x/css.h" +#include "hw/s390x/ebcdic.h" #include "ipl.h" #include "qemu/error-report.h" @@ -243,12 +245,17 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.pbt = S390_IPL_TYPE_CCW; ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; - return true; } else if (sd) { SCSIBus *bus = scsi_bus_from_device(sd); VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus); VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev); - CcwDevice *ccw_dev = CCW_DEVICE(scsi_ccw); + CcwDevice *ccw_dev; + + ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw), + TYPE_CCW_DEVICE); + if (!ccw_dev) { /* It might be a PCI device instead */ + return false; + } ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); ipl->iplb.blk0_len = @@ -259,13 +266,39 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl) ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; - return true; + } else { + return false; /* unknown device */ } + + if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { + ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; + } + return true; } return false; } +int s390_ipl_set_loadparm(uint8_t *loadparm) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL); + + if (lp) { + int i; + + /* lp is an uppercase string without leading/embedded spaces */ + for (i = 0; i < 8 && lp[i]; i++) { + loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; + } + + g_free(lp); + return 0; + } + + return -1; +} + static int load_netboot_image(Error **errp) { S390IPLState *ipl = get_ipl_device(); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 46930e4c64..8a705e0428 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -57,6 +57,8 @@ struct IplBlockQemuScsi { } QEMU_PACKED; typedef struct IplBlockQemuScsi IplBlockQemuScsi; +#define DIAG308_FLAGS_LP_VALID 0x80 + union IplParameterBlock { struct { uint32_t len; @@ -82,6 +84,7 @@ union IplParameterBlock { } QEMU_PACKED; typedef union IplParameterBlock IplParameterBlock; +int s390_ipl_set_loadparm(uint8_t *loadparm); void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 04bd0ebe40..fdd4384ff0 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -274,6 +274,36 @@ bool cpu_model_allowed(void) return true; } +static char *machine_get_loadparm(Object *obj, Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + + return g_memdup(ms->loadparm, sizeof(ms->loadparm)); +} + +static void machine_set_loadparm(Object *obj, const char *val, Error **errp) +{ + S390CcwMachineState *ms = S390_CCW_MACHINE(obj); + int i; + + for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) { + uint8_t c = toupper(val[i]); /* mimic HMC */ + + if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') || + (c == ' ')) { + ms->loadparm[i] = c; + } else { + error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", + c, c); + return; + } + } + + for (; i < sizeof(ms->loadparm); i++) { + ms->loadparm[i] = ' '; /* pad right with spaces */ + } +} + static inline void s390_machine_initfn(Object *obj) { object_property_add_bool(obj, "aes-key-wrap", @@ -291,6 +321,13 @@ static inline void s390_machine_initfn(Object *obj) "enable/disable DEA key wrapping using the CPACF wrapping key", NULL); object_property_set_bool(obj, true, "dea-key-wrap", NULL); + object_property_add_str(obj, "loadparm", + machine_get_loadparm, machine_set_loadparm, NULL); + object_property_set_description(obj, "loadparm", + "Up to 8 chars in set of [A-Za-z0-9. ] (lower case chars converted" + " to upper case) to pass to machine loader, boot manager," + " and guest kernel", + NULL); } static const TypeInfo ccw_machine_info = { diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index e741da1141..b4f6dd58dd 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -23,6 +23,7 @@ #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" +#include "hw/s390x/ipl.h" static inline SCLPDevice *get_sclp_device(void) { @@ -57,6 +58,7 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) int cpu_count = 0; int rnsize, rnmax; int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state)); + IplParameterBlock *ipib = s390_ipl_get_iplb(); CPU_FOREACH(cpu) { cpu_count++; @@ -129,6 +131,13 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnmax2 = cpu_to_be64(rnmax); } + if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) { + memcpy(&read_info->loadparm, &ipib->loadparm, + sizeof(read_info->loadparm)); + } else { + s390_ipl_set_loadparm(read_info->loadparm); + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } |