diff mbox

[2/5] s390: Virtual channel subsystem support.

Message ID 1344351173-2716-3-git-send-email-cornelia.huck@de.ibm.com
State New
Headers show

Commit Message

Cornelia Huck Aug. 7, 2012, 2:52 p.m. UTC
Provide a mechanism for qemu to provide fully virtual subchannels to
the guest. In the KVM case, this relies on the kernel's css support.
The !KVM case is not yet supported.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
 hw/s390x/Makefile.objs     |   1 +
 hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
 hw/s390x/css.h             |  62 +++++++
 target-s390x/Makefile.objs |   2 +-
 target-s390x/cpu.h         | 108 +++++++++++
 target-s390x/ioinst.c      |  38 ++++
 target-s390x/ioinst.h      | 173 ++++++++++++++++++
 target-s390x/kvm.c         | 101 +++++++++++
 8 files changed, 924 insertions(+), 1 deletion(-)
 create mode 100644 hw/s390x/css.c
 create mode 100644 hw/s390x/css.h
 create mode 100644 target-s390x/ioinst.c
 create mode 100644 target-s390x/ioinst.h

Comments

Blue Swirl Aug. 7, 2012, 9 p.m. UTC | #1
On Tue, Aug 7, 2012 at 2:52 PM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> Provide a mechanism for qemu to provide fully virtual subchannels to
> the guest. In the KVM case, this relies on the kernel's css support.
> The !KVM case is not yet supported.
>
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> ---
>  hw/s390x/Makefile.objs     |   1 +
>  hw/s390x/css.c             | 440 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/s390x/css.h             |  62 +++++++
>  target-s390x/Makefile.objs |   2 +-
>  target-s390x/cpu.h         | 108 +++++++++++
>  target-s390x/ioinst.c      |  38 ++++
>  target-s390x/ioinst.h      | 173 ++++++++++++++++++
>  target-s390x/kvm.c         | 101 +++++++++++
>  8 files changed, 924 insertions(+), 1 deletion(-)
>  create mode 100644 hw/s390x/css.c
>  create mode 100644 hw/s390x/css.h
>  create mode 100644 target-s390x/ioinst.c
>  create mode 100644 target-s390x/ioinst.h
>
> diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
> index dcdcac8..93b41fb 100644
> --- a/hw/s390x/Makefile.objs
> +++ b/hw/s390x/Makefile.objs
> @@ -1,3 +1,4 @@
>  obj-y = s390-virtio-bus.o s390-virtio.o
>
>  obj-y := $(addprefix ../,$(obj-y))
> +obj-y += css.o
> diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> new file mode 100644
> index 0000000..7941c44
> --- /dev/null
> +++ b/hw/s390x/css.c
> @@ -0,0 +1,440 @@
> +/*
> + * Channel subsystem base support.
> + *
> + * Copyright 2012 IBM Corp.
> + * Author(s): Cornelia Huck <cornelia.huck@de.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-thread.h"
> +#include "qemu-queue.h"
> +#include <hw/qdev.h>
> +#include "kvm.h"
> +#include "cpu.h"
> +#include "ioinst.h"
> +#include "css.h"
> +
> +struct chp_info {

CamelCase, please.

> +    uint8_t in_use;
> +    uint8_t type;
> +};
> +
> +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> +
> +static css_subch_cb_func css_subch_cb;

Probably these can be put to a container structure which can be passed around.

> +
> +int css_set_subch_cb(css_subch_cb_func func)
> +{
> +    if (func && css_subch_cb) {
> +        return -EBUSY;
> +    }
> +    css_subch_cb = func;
> +    return 0;
> +}
> +
> +static void css_inject_io_interrupt(SubchDev *sch, uint8_t func)
> +{
> +    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
> +                      &sch->curr_status.pmcw, &sch->sense_data, 0,
> +                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm,
> +                      func);
> +}
> +
> +void css_conditional_io_interrupt(SubchDev *sch)
> +{
> +    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
> +                      &sch->curr_status.pmcw, &sch->sense_data, 1,
> +                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm, 0);
> +}
> +
> +static void sch_handle_clear_func(SubchDev *sch)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int path;
> +
> +    /* Path management: In our simple css, we always choose the only path. */
> +    path = 0x80;
> +
> +    /* Reset values prior to 'issueing the clear signal'. */
> +    p->lpum = 0;
> +    p->pom = 0xff;
> +    s->pno = 0;
> +
> +    /* We always 'attempt to issue the clear signal', and we always succeed. */
> +    sch->orb = NULL;
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    s->actl &= ~SCSW_ACTL_CLEAR_PEND;
> +    s->stctl |= SCSW_STCTL_STATUS_PEND;
> +
> +    s->dstat = 0;
> +    s->cstat = 0;
> +    p->lpum = path;
> +
> +}
> +
> +static void sch_handle_halt_func(SubchDev *sch)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int path;
> +
> +    /* Path management: In our simple css, we always choose the only path. */
> +    path = 0x80;
> +
> +    /* We always 'attempt to issue the halt signal', and we always succeed. */
> +    sch->orb = NULL;
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    s->actl &= ~SCSW_ACTL_HALT_PEND;
> +    s->stctl |= SCSW_STCTL_STATUS_PEND;
> +
> +    if ((s->actl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) ||
> +        !((s->actl & SCSW_ACTL_START_PEND) ||
> +          (s->actl & SCSW_ACTL_SUSP))) {
> +        s->dstat = SCSW_DSTAT_DEVICE_END;
> +    }
> +    s->cstat = 0;
> +    p->lpum = path;
> +
> +}
> +
> +static int css_interpret_ccw(SubchDev *sch, struct ccw1 *ccw)
> +{
> +    int ret;
> +    bool check_len;
> +    int len;
> +    int i;
> +
> +    if (!ccw) {
> +        return -EIO;
> +    }
> +
> +    /* Check for invalid command codes. */
> +    if ((ccw->cmd_code & 0x0f) == 0) {
> +        return -EINVAL;
> +    }
> +    if (((ccw->cmd_code & 0x0f) == CCW_CMD_TIC) &&
> +        ((ccw->cmd_code & 0xf0) != 0)) {
> +        return -EINVAL;
> +    }
> +
> +    if (ccw->flags & CCW_FLAG_SUSPEND) {
> +        return -ERESTART;
> +    }
> +
> +    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
> +
> +    /* Look at the command. */
> +    switch (ccw->cmd_code) {
> +    case CCW_CMD_NOOP:
> +        /* Nothing to do. */
> +        ret = 0;
> +        break;
> +    case CCW_CMD_BASIC_SENSE:
> +        if (check_len) {
> +            if (ccw->count != sizeof(sch->sense_data)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, sizeof(sch->sense_data));
> +        cpu_physical_memory_write(ccw->cda, sch->sense_data, len);
> +        sch->curr_status.scsw.count = ccw->count - len;
> +        memset(sch->sense_data, 0, sizeof(sch->sense_data));
> +        ret = 0;
> +        break;
> +    case CCW_CMD_SENSE_ID:
> +    {
> +        uint8_t sense_bytes[256];
> +
> +        /* Sense ID information is device specific. */
> +        memcpy(sense_bytes, &sch->id, sizeof(sense_bytes));
> +        if (check_len) {
> +            if (ccw->count != sizeof(sense_bytes)) {
> +                ret = -EINVAL;
> +                break;
> +            }
> +        }
> +        len = MIN(ccw->count, sizeof(sense_bytes));
> +        /*
> +         * Only indicate 0xff in the first sense byte if we actually
> +         * have enough place to store at least bytes 0-3.
> +         */
> +        if (len >= 4) {
> +            stb_phys(ccw->cda, 0xff);
> +        } else {
> +            stb_phys(ccw->cda, 0);
> +        }
> +        i = 1;
> +        for (i = 1; i < len - 1; i++) {
> +            stb_phys(ccw->cda + i, sense_bytes[i]);
> +        }

cpu_physical_memory_write()

> +        sch->curr_status.scsw.count = ccw->count - len;
> +        ret = 0;
> +        break;
> +    }
> +    case CCW_CMD_TIC:
> +        if (sch->last_cmd->cmd_code == CCW_CMD_TIC) {
> +            ret = -EINVAL;
> +            break;
> +        }
> +        if (ccw->flags & (CCW_FLAG_CC | CCW_FLAG_DC)) {
> +            ret = -EINVAL;
> +            break;
> +        }
> +        sch->channel_prog = qemu_get_ram_ptr(ccw->cda);
> +        ret = sch->channel_prog ? -EAGAIN : -EFAULT;
> +        break;
> +    default:
> +        if (sch->ccw_cb) {
> +            /* Handle device specific commands. */
> +            ret = sch->ccw_cb(sch, ccw);
> +        } else {
> +            ret = -EOPNOTSUPP;
> +        }
> +        break;
> +    }
> +    sch->last_cmd = ccw;
> +    if (ret == 0) {
> +        if (ccw->flags & CCW_FLAG_CC) {
> +            sch->channel_prog += 8;
> +            ret = -EAGAIN;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +static void sch_handle_start_func(SubchDev *sch)
> +{
> +
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    struct orb *orb = sch->orb;
> +    int path;
> +    int ret;
> +
> +    /* Path management: In our simple css, we always choose the only path. */
> +    path = 0x80;
> +
> +    if (!s->actl & SCSW_ACTL_SUSP) {
> +        /* Look at the orb and try to execute the channel program. */
> +        p->intparm = orb->intparm;
> +        if (!(orb->lpm & path)) {
> +            /* Generate a deferred cc 3 condition. */
> +            s->cc = 3;
> +            s->stctl = (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND);
> +            return;
> +        }
> +    } else {
> +        s->actl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
> +    }
> +    sch->last_cmd = NULL;
> +    do {
> +        ret = css_interpret_ccw(sch, sch->channel_prog);
> +        switch (ret) {
> +        case -EAGAIN:
> +            /* ccw chain, continue processing */
> +            break;
> +        case 0:
> +            /* success */
> +            s->actl &= ~SCSW_ACTL_START_PEND;
> +            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
> +                SCSW_STCTL_STATUS_PEND;
> +            s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END;
> +            break;
> +        case -EOPNOTSUPP:
> +            /* unsupported command, generate unit check (command reject) */
> +            s->actl &= ~SCSW_ACTL_START_PEND;
> +            s->dstat = SCSW_DSTAT_UNIT_CHECK;
> +            /* Set sense bit 0 in ecw0. */
> +            sch->sense_data[0] = 0x80;
> +            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
> +                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
> +            break;
> +        case -EFAULT:
> +            /* memory problem, generate channel data check */
> +            s->actl &= ~SCSW_ACTL_START_PEND;
> +            s->cstat = SCSW_CSTAT_DATA_CHECK;
> +            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
> +                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
> +            break;
> +        case -EBUSY:
> +            /* subchannel busy, generate deferred cc 1 */
> +            s->cc = 1;
> +            s->stctl = SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
> +            break;
> +        case -ERESTART:
> +            /* channel program has been suspended */
> +            s->actl &= ~SCSW_ACTL_START_PEND;
> +            s->actl |= SCSW_ACTL_SUSP;
> +            break;
> +        default:
> +            /* error, generate channel program check */
> +            s->actl &= ~SCSW_ACTL_START_PEND;
> +            s->cstat = SCSW_CSTAT_PROG_CHECK;
> +            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
> +                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
> +            break;
> +        }
> +    } while (ret == -EAGAIN);
> +
> +}
> +
> +int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
> +                      void *pmcw)
> +{
> +    SubchDev *sch;
> +    int cssid;
> +    int ssid;
> +    int schid;
> +    int m;
> +    int ret;
> +    int notify = 0;
> +
> +    ret = ioinst_disassemble_sch_ident(sch_id, &m, &cssid, &ssid, &schid);
> +    if (ret) {
> +        return ret;
> +    }
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (!sch) {
> +        return -ENODEV;
> +    }
> +    qemu_mutex_lock(&sch->mutex);
> +    memcpy(&sch->curr_status.pmcw, pmcw, sizeof(struct pmcw));
> +    switch (func) {
> +    case CSS_DO_CSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
> +        /* fallthrough */
> +    case CSS_DO_CSCH_SIMPLE:
> +        sch_handle_clear_func(sch);
> +        notify = 1;
> +        break;
> +    case CSS_DO_HSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
> +        /* fallthrough */
> +    case CSS_DO_HSCH_SIMPLE:
> +        sch_handle_halt_func(sch);
> +        notify = 1;
> +        break;
> +    case CSS_DO_SSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
> +        sch->orb = qemu_get_ram_ptr(orb);
> +        sch->channel_prog = qemu_get_ram_ptr(sch->orb->cpa);
> +        /* fallthrough */
> +    case CSS_DO_SSCH_SIMPLE:
> +        sch_handle_start_func(sch);
> +        notify = 1;
> +        break;
> +    case CSS_DO_RSCH:
> +        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
> +        sch_handle_start_func(sch);
> +        notify = 1;
> +        break;
> +    case CSS_DO_XSCH:
> +        sch->orb = NULL;
> +        sch->channel_prog = NULL;
> +        sch->last_cmd = NULL;
> +        break;
> +    }
> +    if (notify) {
> +        css_inject_io_interrupt(sch, func);
> +    }
> +    qemu_mutex_unlock(&sch->mutex);
> +    return 0;
> +}
> +
> +static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
> +{
> +    if (cssid > MAX_CSSID) {
> +        return -EINVAL;
> +    }
> +    if (chpids[cssid][chpid].in_use) {
> +        return -EEXIST;
> +    }
> +    chpids[cssid][chpid].in_use = 1;
> +    chpids[cssid][chpid].type = type;
> +
> +    s390_chp_hotplug(cssid, chpid, type, 1, 1);
> +
> +    return 0;
> +}
> +
> +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int i;
> +
> +    memset(p, 0, sizeof(struct pmcw));
> +    p->dnv = 1;
> +    p->dev = sch->devno;
> +    /* single path */
> +    p->pim = 0x80;
> +    p->pom = 0xff;
> +    p->pam = 0x80;
> +    p->chpid[0] = chpid;
> +    if (!chpids[sch->cssid][chpid].in_use) {
> +        css_add_virtual_chpid(sch->cssid, chpid, type);
> +    }
> +
> +    memset(s, 0, sizeof(struct scsw));
> +    sch->curr_status.mba = 0;
> +    for (i = 0; i < 4; i++) {
> +        sch->curr_status.mda[i] = 0;
> +    }
> +}
> +
> +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
> +{
> +    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
> +}
> +
> +static void css_init(void)
> +{
> +    css_subch_cb = NULL;
> +}
> +machine_init(css_init);
> +
> +void css_reset_sch(SubchDev *sch)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +
> +    p->intparm = 0;
> +    p->isc = 0;
> +    p->ena = 0;
> +    p->lm = 0;
> +    p->mme = 0;
> +    p->mp = 0;
> +    p->tf = 0;
> +    p->dnv = 1;
> +    p->dev = sch->devno;
> +    p->pim = 0x80;
> +    p->lpm = p->pim;
> +    p->pnom = 0;
> +    p->lpum = 0;
> +    p->mbi = 0;
> +    p->pom = 0xff;
> +    p->pam = 0x80;
> +    p->mbfc = 0;
> +    p->xmwme = 0;
> +    p->csense = 0;
> +
> +    memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw));
> +    sch->curr_status.mba = 0;
> +
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    sch->orb = NULL;
> +}
> +
> +void css_reset(void)
> +{
> +    /* Nothing for now. */
> +}
> diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> new file mode 100644
> index 0000000..b8a95cc
> --- /dev/null
> +++ b/hw/s390x/css.h
> @@ -0,0 +1,62 @@
> +/*
> + * Channel subsystem structures and definitions.
> + *
> + * Copyright 2012 IBM Corp.
> + * Author(s): Cornelia Huck <cornelia.huck@de.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.
> + */
> +
> +#ifndef CSS_H
> +#define CSS_H
> +
> +#include "ioinst.h"
> +
> +/* Channel subsystem constants. */
> +#define MAX_SCHID 65535
> +#define MAX_SSID 3
> +#define MAX_CSSID 254 /* 255 is reserved */
> +#define MAX_CHPID 255
> +
> +#define MAX_CIWS 8
> +
> +struct senseid {

SenseID

> +    /* common part */
> +    uint32_t  reserved:8;    /* always 0x'FF' */

The standard syntax calls for 'unsigned' instead of uint32_t for bit
fields. But bit fields are not very well defined, it's better to avoid
them.

> +    uint32_t  cu_type:16;    /* control unit type */
> +    uint32_t  cu_model:8;    /* control unit model */
> +    uint32_t  dev_type:16;   /* device type */
> +    uint32_t  dev_model:8;   /* device model */
> +    uint32_t  unused:8;      /* padding byte */
> +    /* extended part */
> +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> +};
> +
> +struct SubchDev {
> +    /* channel-subsystem related things: */
> +    uint8_t cssid;
> +    uint8_t ssid;
> +    uint16_t schid;
> +    uint16_t devno;
> +    struct schib curr_status;
> +    uint8_t sense_data[32];
> +    struct ccw1 *channel_prog;
> +    struct ccw1 *last_cmd;
> +    struct orb *orb;
> +    QemuMutex mutex;
> +    /* transport-provided data: */
> +    int (*ccw_cb) (SubchDev *, struct ccw1 *);
> +    struct senseid id;
> +    void *driver_data;
> +};
> +
> +typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid);
> +int css_set_subch_cb(css_subch_cb_func func);
> +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type);
> +void css_reset(void);
> +void css_reset_sch(SubchDev *sch);
> +
> +#endif
> diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs
> index 262747f..cb05241 100644
> --- a/target-s390x/Makefile.objs
> +++ b/target-s390x/Makefile.objs
> @@ -1,5 +1,5 @@
>  obj-y += translate.o op_helper.o helper.o cpu.o
> -obj-$(CONFIG_SOFTMMU) += machine.o
> +obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o
>  obj-$(CONFIG_KVM) += kvm.o
>
>  $(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
> index c30ac3a..c09fa61 100644
> --- a/target-s390x/cpu.h
> +++ b/target-s390x/cpu.h
> @@ -336,6 +336,23 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>  void cpu_lock(void);
>  void cpu_unlock(void);
>
> +typedef struct SubchDev SubchDev;
> +struct orb;
> +
> +#ifndef CONFIG_USER_ONLY
> +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid);
> +void css_conditional_io_interrupt(SubchDev *sch);
> +#else
> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid)
> +{
> +    return NULL;
> +}
> +static inline void css_conditional_io_interrupt(SubchDev *sch)
> +{
> +}
> +#endif
> +
>  static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>  {
>      env->aregs[0] = newtls >> 32;
> @@ -996,4 +1013,95 @@ static inline void cpu_pc_from_tb(CPUS390XState *env, TranslationBlock* tb)
>      env->psw.addr = tb->pc;
>  }
>
> +int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
> +                      void *pmcw);
> +#ifdef CONFIG_KVM
> +int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                         uint16_t devno, void *data, int hotplugged, int add,
> +                         int virtual);
> +int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
> +                         int virtual);
> +int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                          void *scsw, void *pmcw, void *sense,
> +                          int unsolicited, uint8_t func);
> +void kvm_s390_enable_css_support(CPUS390XState *env);
> +#else
> +static inline int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid, uint16_t devno,
> +                                       void *data, int hotplugged, int add,
> +                                       int virtual)
> +{
> +    return -EOPNOTSUPP;
> +}
> +static inline int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid,
> +                                       uint8_t type, int add, int virtual)
> +{
> +    return -EOPNOTSUPP;
> +}
> +static inline int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid,
> +                                        uint16_t schid, void *scsw, void *pmcw,
> +                                        void *sense, int unsolicited, uint8_t func)
> +{
> +    return -EOPNOTSUPP;
> +}
> +static inline void kvm_s390_enable_css_support(CPUS390XState *env)
> +{
> +}
> +#endif
> +
> +static inline void s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                                    uint16_t devno, void *data, int hotplugged,
> +                                    int add, int virtual)
> +{
> +    int ret;
> +
> +    ret = kvm_s390_sch_hotplug(cssid, ssid, schid, devno, data, hotplugged,
> +                               add, virtual);
> +    if (ret == -EOPNOTSUPP) {
> +        fprintf(stderr, "Hotplugging subchannels not supported\n");
> +    }
> +}
> +
> +static inline void s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type,
> +                                    int add, int virtual)
> +{
> +    int ret;
> +
> +    ret = kvm_s390_chp_hotplug(cssid, chpid, type, add, virtual);
> +    if (ret == -EOPNOTSUPP) {
> +        fprintf(stderr, "Hotplugging chpids not supported\n");
> +    }
> +}
> +
> +static inline void s390_io_interrupt(uint8_t cssid, uint8_t ssid,
> +                                     uint16_t schid, void *scsw, void *pmcw,
> +                                     void *sense, int unsolicited,
> +                                     uint8_t isc, uint32_t intparm, uint8_t func)
> +{
> +    int ret;
> +
> +    ret = kvm_s390_io_interrupt(cssid, ssid, schid, scsw, pmcw, sense,
> +                                unsolicited, func);
> +    if (ret == -EOPNOTSUPP) {
> +        fprintf(stderr, "Injecting I/O interrupts not supported\n");
> +    }
> +}
> +
> +#ifdef CONFIG_KVM
> +#define CSS_DO_CSCH SCH_DO_CSCH
> +#define CSS_DO_HSCH SCH_DO_HSCH
> +#define CSS_DO_SSCH SCH_DO_SSCH
> +#define CSS_DO_RSCH SCH_DO_RSCH
> +#define CSS_DO_XSCH SCH_DO_XSCH
> +#else
> +#define CSS_DO_CSCH 0
> +#define CSS_DO_HSCH 1
> +#define CSS_DO_SSCH 2
> +#define CSS_DO_RSCH 3
> +#define CSS_DO_XSCH 4
> +#endif
> +#define CSS_DO_CSCH_SIMPLE 0xf0
> +#define CSS_DO_HSCH_SIMPLE 0xf1
> +#define CSS_DO_SSCH_SIMPLE 0xf2
> +
>  #endif
> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
> new file mode 100644
> index 0000000..8f358d5
> --- /dev/null
> +++ b/target-s390x/ioinst.c
> @@ -0,0 +1,38 @@
> +/*
> + * I/O instructions for S/390
> + *
> + * Copyright 2012 IBM Corp.
> + * Author(s): Cornelia Huck <cornelia.huck@de.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 <sys/types.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +
> +#include "cpu.h"
> +#include "ioinst.h"
> +
> +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
> +                                 int *schid)
> +{
> +    if (!(value & 0x00010000)) {
> +        return -EINVAL;
> +    }
> +    if (!(value & 0x00080000)) {
> +        if (value & 0xff000000) {
> +            return -EINVAL;
> +        }
> +        *cssid = 0;
> +        *m = 0;
> +    } else {
> +        *cssid = (value & 0xff000000) >> 24;
> +        *m = 1;
> +    }
> +    *ssid = (value & 0x00060000) >> 17;
> +    *schid = value & 0x0000ffff;
> +    return 0;
> +}
> diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
> new file mode 100644
> index 0000000..79628b4
> --- /dev/null
> +++ b/target-s390x/ioinst.h
> @@ -0,0 +1,173 @@
> +/*
> + * S/390 channel I/O instructions
> + *
> + * Copyright 2012 IBM Corp.
> + * Author(s): Cornelia Huck <cornelia.huck@de.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.
> +*/
> +
> +#ifndef IOINST_S390X_H
> +#define IOINST_S390X_H
> +
> +/*
> + * Channel I/O related definitions, as defined in the Principles
> + * Of Operation (and taken from the Linux implementation).

Is this a copy and if so, is the license of original Linux file also GPLv2+?

> + */
> +
> +/* subchannel status word (command mode only) */
> +struct scsw {

Please use more descriptive names instead of acronyms, for example SubChStatus.

> +    uint32_t key:4;
> +    uint32_t sctl:1;
> +    uint32_t eswf:1;
> +    uint32_t cc:2;
> +    uint32_t fmt:1;
> +    uint32_t pfch:1;
> +    uint32_t isic:1;
> +    uint32_t alcc:1;
> +    uint32_t ssi:1;
> +    uint32_t zcc:1;
> +    uint32_t ectl:1;
> +    uint32_t pno:1;
> +    uint32_t res:1;
> +    uint32_t fctl:3;
> +    uint32_t actl:7;
> +    uint32_t stctl:5;
> +    uint32_t cpa;
> +    uint32_t dstat:8;
> +    uint32_t cstat:8;
> +    uint32_t count:16;
> +};
> +
> +/* path management control word */
> +struct pmcw {
> +    uint32_t intparm;
> +    uint32_t qf:1;
> +    uint32_t w:1;
> +    uint32_t isc:3;
> +    uint32_t zeroes0:3;
> +    uint32_t ena:1;
> +    uint32_t lm:2;
> +    uint32_t mme:2;
> +    uint32_t mp:1;
> +    uint32_t tf:1;
> +    uint32_t dnv:1;
> +    uint32_t dev:16;
> +    uint8_t  lpm;
> +    uint8_t  pnom;
> +    uint8_t  lpum;
> +    uint8_t  pim;
> +    uint16_t mbi;
> +    uint8_t  pom;
> +    uint8_t  pam;
> +    uint8_t  chpid[8];
> +    uint32_t zeroes1:8;
> +    uint32_t st:3;
> +    uint32_t zeroes2:18;
> +    uint32_t mbfc:1;
> +    uint32_t xmwme:1;
> +    uint32_t csense:1;
> +};
> +
> +/* subchannel information block */
> +struct schib {
> +    struct pmcw pmcw;
> +    struct scsw scsw;
> +    uint64_t mba;
> +    uint8_t mda[4];
> +};
> +
> +/* interruption response block */
> +struct irb {
> +    struct scsw scsw;
> +    uint32_t esw[5];
> +    uint32_t ecw[8];
> +    uint32_t emw[8];
> +};
> +
> +/* operation request block */
> +struct orb {
> +    uint32_t intparm;
> +    uint32_t key:4;
> +    uint32_t spnd:1;
> +    uint32_t str:1;
> +    uint32_t mod:1;
> +    uint32_t sync:1;
> +    uint32_t fmt:1;
> +    uint32_t pfch:1;
> +    uint32_t isic:1;
> +    uint32_t alcc:1;
> +    uint32_t ssic:1;
> +    uint32_t zero0:1;
> +    uint32_t c64:1;
> +    uint32_t i2k:1;
> +    uint32_t lpm:8;
> +    uint32_t ils:1;
> +    uint32_t midaw:1;
> +    uint32_t zero1:5;
> +    uint32_t orbx:1;
> +    uint32_t cpa;
> +};
> +
> +/* channel command word (type 1) */
> +struct ccw1 {
> +    uint8_t cmd_code;
> +    uint8_t flags;
> +    uint16_t count;
> +    uint32_t cda;
> +};
> +
> +#define CCW_FLAG_DC              0x80
> +#define CCW_FLAG_CC              0x40
> +#define CCW_FLAG_SLI             0x20
> +#define CCW_FLAG_SKIP            0x10
> +#define CCW_FLAG_PCI             0x08
> +#define CCW_FLAG_IDA             0x04
> +#define CCW_FLAG_SUSPEND         0x02
> +
> +#define CCW_CMD_NOOP             0x03
> +#define CCW_CMD_BASIC_SENSE      0x04
> +#define CCW_CMD_TIC              0x08
> +#define CCW_CMD_SENSE_ID         0xe4
> +
> +#define SCSW_FCTL_CLEAR_FUNC     0x1
> +#define SCSW_FCTL_HALT_FUNC      0x2
> +#define SCSW_FCTL_START_FUNC     0x4
> +
> +#define SCSW_ACTL_SUSP           0x1
> +#define SCSW_ACTL_DEVICE_ACTIVE  0x2
> +#define SCSW_ACTL_SUBCH_ACTIVE   0x4
> +#define SCSW_ACTL_CLEAR_PEND     0x8
> +#define SCSW_ACTL_HALT_PEND      0x10
> +#define SCSW_ACTL_START_PEND     0x20
> +#define SCSW_ACTL_RESUME_PEND    0x40
> +
> +#define SCSW_STCTL_STATUS_PEND   0x1
> +#define SCSW_STCTL_SECONDARY     0x2
> +#define SCSW_STCTL_PRIMARY       0x4
> +#define SCSW_STCTL_INTERMEDIATE  0x8
> +#define SCSW_STCTL_ALERT         0x10
> +
> +#define SCSW_DSTAT_ATTENTION     0x80
> +#define SCSW_DSTAT_STAT_MOD      0x40
> +#define SCSW_DSTAT_CU_END        0x20
> +#define SCSW_DSTAT_BUSY          0x10
> +#define SCSW_DSTAT_CHANNEL_END   0x08
> +#define SCSW_DSTAT_DEVICE_END    0x04
> +#define SCSW_DSTAT_UNIT_CHECK    0x02
> +#define SCSW_DSTAT_UNIT_EXCEP    0x01
> +
> +#define SCSW_CSTAT_PCI           0x80
> +#define SCSW_CSTAT_INCORR_LEN    0x40
> +#define SCSW_CSTAT_PROG_CHECK    0x20
> +#define SCSW_CSTAT_PROT_CHECK    0x10
> +#define SCSW_CSTAT_DATA_CHECK    0x08
> +#define SCSW_CSTAT_CHN_CTRL_CHK  0x04
> +#define SCSW_CSTAT_INTF_CTRL_CHK 0x02
> +#define SCSW_CSTAT_CHAIN_CHECK   0x01
> +
> +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
> +                                 int *schid);
> +#endif
> diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
> index 47008c2..e4cde14 100644
> --- a/target-s390x/kvm.c
> +++ b/target-s390x/kvm.c
> @@ -26,6 +26,7 @@
>
>  #include "qemu-common.h"
>  #include "qemu-timer.h"
> +#include "qemu-thread.h"
>  #include "sysemu.h"
>  #include "kvm.h"
>  #include "cpu.h"
> @@ -110,6 +111,7 @@ int kvm_arch_put_registers(CPUS390XState *env, int level)
>
>      env->kvm_run->psw_addr = env->psw.addr;
>      env->kvm_run->psw_mask = env->psw.mask;
> +    env->kvm_run->s.regs.prefix = env->psa;
>
>      return ret;
>  }
> @@ -131,6 +133,7 @@ int kvm_arch_get_registers(CPUS390XState *env)
>
>      env->psw.addr = env->kvm_run->psw_addr;
>      env->psw.mask = env->kvm_run->psw_mask;
> +    env->psa = env->kvm_run->s.regs.prefix;
>
>      return 0;
>  }
> @@ -506,6 +509,13 @@ int kvm_arch_handle_exit(CPUS390XState *env, struct kvm_run *run)
>          case KVM_EXIT_S390_RESET:
>              qemu_system_reset_request();
>              break;
> +        case KVM_EXIT_S390_SCH_IO:
> +            ret = css_handle_sch_io(run->s390_sch_io.sch_id,
> +                                    run->s390_sch_io.func,
> +                                    run->s390_sch_io.orb,
> +                                    run->s390_sch_io.scsw,
> +                                    run->s390_sch_io.pmcw);
> +            break;
>          default:
>              fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
>              break;
> @@ -531,3 +541,94 @@ int kvm_arch_on_sigbus(int code, void *addr)
>  {
>      return 1;
>  }
> +
> +int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                         uint16_t devno, void *data, int hotplugged, int add,
> +                         int virtual)
> +{
> +    struct kvm_s390_sch_info sch_info;
> +    S390CPU *cpu = s390_cpu_addr2state(0);
> +    int ret;
> +
> +    if (!kvm_enabled()) {
> +        return -EOPNOTSUPP;
> +    }
> +
> +    /* Notify the kernel. */
> +    sch_info.cssid = cssid;
> +    sch_info.ssid = ssid;
> +    sch_info.schid = schid;
> +    sch_info.devno = devno;
> +    memcpy(&sch_info.schib, data, sizeof(sch_info.schib));
> +    sch_info.hotplugged = hotplugged;
> +    sch_info.add = add;
> +    sch_info.virtual = virtual;
> +    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CCW_HOTPLUG, &sch_info);
> +    assert(ret == 0);
> +    return ret;
> +}
> +
> +int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
> +                         int virtual)
> +{
> +    S390CPU *cpu = s390_cpu_addr2state(0);
> +    struct kvm_s390_chp_info chpid_info;
> +    int ret;
> +
> +    if (!kvm_enabled()) {
> +        return -EOPNOTSUPP;
> +    }
> +
> +    /* Notify the kernel. */
> +    chpid_info.cssid = cssid;
> +    chpid_info.chpid = chpid;
> +    chpid_info.type = type;
> +    chpid_info.add = 1;
> +    chpid_info.virtual = 1;
> +    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CHP_HOTPLUG, &chpid_info);
> +    assert(ret == 0);
> +    return ret;
> +}
> +
> +int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
> +                          void *scsw, void *pmcw, void *sense,
> +                          int unsolicited, uint8_t func)
> +{
> +    S390CPU *cpu = s390_cpu_addr2state(0);
> +    struct kvm_css_notify notify;
> +    int ret;
> +
> +    if (!kvm_enabled()) {
> +        return -EOPNOTSUPP;
> +    }
> +
> +    notify.cssid = cssid;
> +    notify.ssid = ssid;
> +    notify.schid = schid;
> +    if (!unsolicited) {
> +        memcpy(&notify.scsw, scsw, sizeof(notify.scsw));
> +        memcpy(&notify.pmcw, pmcw, sizeof(notify.pmcw));
> +        memcpy(&notify.sense_data, sense, sizeof(notify.sense_data));
> +        notify.func = func;
> +    }
> +    notify.unsolicited = unsolicited;
> +    ret = kvm_vcpu_ioctl(&cpu->env, KVM_S390_CSS_NOTIFY, &notify);
> +    assert(ret == 0);
> +    return ret;
> +}
> +
> +void kvm_s390_enable_css_support(CPUS390XState *env)
> +{
> +    struct kvm_enable_cap cap = {};
> +    int r;
> +
> +    /* Activate host kernel channel subsystem support. */
> +    if (kvm_enabled()) {
> +        /* One CPU has to run */
> +        s390_add_running_cpu(env);
> +
> +        cap.cap = KVM_CAP_S390_CSS_SUPPORT;
> +        r = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap);
> +        assert(r == 0);
> +    }
> +}
> --
> 1.7.11.4
>
>
Cornelia Huck Aug. 8, 2012, 8:17 a.m. UTC | #2
On Tue, 7 Aug 2012 21:00:59 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:


> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
> > new file mode 100644
> > index 0000000..7941c44
> > --- /dev/null
> > +++ b/hw/s390x/css.c
> > @@ -0,0 +1,440 @@
> > +/*
> > + * Channel subsystem base support.
> > + *
> > + * Copyright 2012 IBM Corp.
> > + * Author(s): Cornelia Huck <cornelia.huck@de.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-thread.h"
> > +#include "qemu-queue.h"
> > +#include <hw/qdev.h>
> > +#include "kvm.h"
> > +#include "cpu.h"
> > +#include "ioinst.h"
> > +#include "css.h"
> > +
> > +struct chp_info {
> 
> CamelCase, please.

OK.
> 
> > +    uint8_t in_use;
> > +    uint8_t type;
> > +};
> > +
> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
> > +
> > +static css_subch_cb_func css_subch_cb;
> 
> Probably these can be put to a container structure which can be passed around.

Still trying to come up with a good model for that.

> 

> > +    case CCW_CMD_SENSE_ID:
> > +    {
> > +        uint8_t sense_bytes[256];
> > +
> > +        /* Sense ID information is device specific. */
> > +        memcpy(sense_bytes, &sch->id, sizeof(sense_bytes));
> > +        if (check_len) {
> > +            if (ccw->count != sizeof(sense_bytes)) {
> > +                ret = -EINVAL;
> > +                break;
> > +            }
> > +        }
> > +        len = MIN(ccw->count, sizeof(sense_bytes));
> > +        /*
> > +         * Only indicate 0xff in the first sense byte if we actually
> > +         * have enough place to store at least bytes 0-3.
> > +         */
> > +        if (len >= 4) {
> > +            stb_phys(ccw->cda, 0xff);
> > +        } else {
> > +            stb_phys(ccw->cda, 0);
> > +        }
> > +        i = 1;
> > +        for (i = 1; i < len - 1; i++) {
> > +            stb_phys(ccw->cda + i, sense_bytes[i]);
> > +        }
> 
> cpu_physical_memory_write()

Hm, what's wrong with storing byte-by-byte?

> 
> > +        sch->curr_status.scsw.count = ccw->count - len;
> > +        ret = 0;
> > +        break;
> > +    }
> > +    case CCW_CMD_TIC:
> > +        if (sch->last_cmd->cmd_code == CCW_CMD_TIC) {
> > +            ret = -EINVAL;
> > +            break;
> > +        }
> > +        if (ccw->flags & (CCW_FLAG_CC | CCW_FLAG_DC)) {
> > +            ret = -EINVAL;
> > +            break;
> > +        }
> > +        sch->channel_prog = qemu_get_ram_ptr(ccw->cda);
> > +        ret = sch->channel_prog ? -EAGAIN : -EFAULT;
> > +        break;
> > +    default:
> > +        if (sch->ccw_cb) {
> > +            /* Handle device specific commands. */
> > +            ret = sch->ccw_cb(sch, ccw);
> > +        } else {
> > +            ret = -EOPNOTSUPP;
> > +        }
> > +        break;
> > +    }
> > +    sch->last_cmd = ccw;
> > +    if (ret == 0) {
> > +        if (ccw->flags & CCW_FLAG_CC) {
> > +            sch->channel_prog += 8;
> > +            ret = -EAGAIN;
> > +        }
> > +    }
> > +
> > +    return ret;

> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
> > new file mode 100644
> > index 0000000..b8a95cc
> > --- /dev/null
> > +++ b/hw/s390x/css.h
> > @@ -0,0 +1,62 @@
> > +/*
> > + * Channel subsystem structures and definitions.
> > + *
> > + * Copyright 2012 IBM Corp.
> > + * Author(s): Cornelia Huck <cornelia.huck@de.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.
> > + */
> > +
> > +#ifndef CSS_H
> > +#define CSS_H
> > +
> > +#include "ioinst.h"
> > +
> > +/* Channel subsystem constants. */
> > +#define MAX_SCHID 65535
> > +#define MAX_SSID 3
> > +#define MAX_CSSID 254 /* 255 is reserved */
> > +#define MAX_CHPID 255
> > +
> > +#define MAX_CIWS 8
> > +
> > +struct senseid {
> 
> SenseID

OK.
> 
> > +    /* common part */
> > +    uint32_t  reserved:8;    /* always 0x'FF' */
> 
> The standard syntax calls for 'unsigned' instead of uint32_t for bit
> fields. But bit fields are not very well defined, it's better to avoid
> them.

Well, the equivalent Linux structure also looks like that :) But I can
switch this to a uint8_t/uint16_t structure.

> 
> > +    uint32_t  cu_type:16;    /* control unit type */
> > +    uint32_t  cu_model:8;    /* control unit model */
> > +    uint32_t  dev_type:16;   /* device type */
> > +    uint32_t  dev_model:8;   /* device model */
> > +    uint32_t  unused:8;      /* padding byte */
> > +    /* extended part */
> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
> > +};
> > +

> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
> > new file mode 100644
> > index 0000000..79628b4
> > --- /dev/null
> > +++ b/target-s390x/ioinst.h
> > @@ -0,0 +1,173 @@
> > +/*
> > + * S/390 channel I/O instructions
> > + *
> > + * Copyright 2012 IBM Corp.
> > + * Author(s): Cornelia Huck <cornelia.huck@de.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.
> > +*/
> > +
> > +#ifndef IOINST_S390X_H
> > +#define IOINST_S390X_H
> > +
> > +/*
> > + * Channel I/O related definitions, as defined in the Principles
> > + * Of Operation (and taken from the Linux implementation).
> 
> Is this a copy and if so, is the license of original Linux file also GPLv2+?

It's not a verbatim copy.

> 
> > + */
> > +
> > +/* subchannel status word (command mode only) */
> > +struct scsw {
> 
> Please use more descriptive names instead of acronyms, for example SubChStatus.

I'd rather leave these at the well-known scsw, pmcw, etc. names. These
have been around for decades, and somebody familiar with channel I/O
will instantly know what a struct scsw is, but will need to look hard
at the code to figure out the meaning of SubChStatus.

> 
> > +    uint32_t key:4;
> > +    uint32_t sctl:1;
> > +    uint32_t eswf:1;
> > +    uint32_t cc:2;
> > +    uint32_t fmt:1;
> > +    uint32_t pfch:1;
> > +    uint32_t isic:1;
> > +    uint32_t alcc:1;
> > +    uint32_t ssi:1;
> > +    uint32_t zcc:1;
> > +    uint32_t ectl:1;
> > +    uint32_t pno:1;
> > +    uint32_t res:1;
> > +    uint32_t fctl:3;
> > +    uint32_t actl:7;
> > +    uint32_t stctl:5;
> > +    uint32_t cpa;
> > +    uint32_t dstat:8;
> > +    uint32_t cstat:8;
> > +    uint32_t count:16;
> > +};
Peter Maydell Aug. 8, 2012, 8:27 a.m. UTC | #3
On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> +static void sch_handle_clear_func(SubchDev *sch)
> +{
> +    struct pmcw *p = &sch->curr_status.pmcw;
> +    struct scsw *s = &sch->curr_status.scsw;
> +    int path;
> +
> +    /* Path management: In our simple css, we always choose the only path. */
> +    path = 0x80;
> +
> +    /* Reset values prior to 'issueing the clear signal'. */
> +    p->lpum = 0;

This is unnecessary since we're going to set p->lpum to something else about
ten lines later, right?

> +    p->pom = 0xff;
> +    s->pno = 0;
> +
> +    /* We always 'attempt to issue the clear signal', and we always succeed. */
> +    sch->orb = NULL;
> +    sch->channel_prog = NULL;
> +    sch->last_cmd = NULL;
> +    s->actl &= ~SCSW_ACTL_CLEAR_PEND;
> +    s->stctl |= SCSW_STCTL_STATUS_PEND;
> +
> +    s->dstat = 0;
> +    s->cstat = 0;
> +    p->lpum = path;
> +
> +}

-- PMM
Cornelia Huck Aug. 8, 2012, 8:53 a.m. UTC | #4
On Wed, 8 Aug 2012 09:27:32 +0100
Peter Maydell <peter.maydell@linaro.org> wrote:

> On 7 August 2012 15:52, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> > +static void sch_handle_clear_func(SubchDev *sch)
> > +{
> > +    struct pmcw *p = &sch->curr_status.pmcw;
> > +    struct scsw *s = &sch->curr_status.scsw;
> > +    int path;
> > +
> > +    /* Path management: In our simple css, we always choose the only path. */
> > +    path = 0x80;
> > +
> > +    /* Reset values prior to 'issueing the clear signal'. */
> > +    p->lpum = 0;
> 
> This is unnecessary since we're going to set p->lpum to something else about
> ten lines later, right?

My intention here was following the description in the architecture
closely. In our case, 'attempting to issue the clear signal' will
always succeed, and so we will always set the lpum later on - on real
hardware, this may be different.

> 
> > +    p->pom = 0xff;
> > +    s->pno = 0;
> > +
> > +    /* We always 'attempt to issue the clear signal', and we always succeed. */
> > +    sch->orb = NULL;
> > +    sch->channel_prog = NULL;
> > +    sch->last_cmd = NULL;
> > +    s->actl &= ~SCSW_ACTL_CLEAR_PEND;
> > +    s->stctl |= SCSW_STCTL_STATUS_PEND;
> > +
> > +    s->dstat = 0;
> > +    s->cstat = 0;
> > +    p->lpum = path;
> > +
> > +}
> 
> -- PMM
>
Blue Swirl Aug. 8, 2012, 7:16 p.m. UTC | #5
On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> On Tue, 7 Aug 2012 21:00:59 +0000
> Blue Swirl <blauwirbel@gmail.com> wrote:
>
>
>> > diff --git a/hw/s390x/css.c b/hw/s390x/css.c
>> > new file mode 100644
>> > index 0000000..7941c44
>> > --- /dev/null
>> > +++ b/hw/s390x/css.c
>> > @@ -0,0 +1,440 @@
>> > +/*
>> > + * Channel subsystem base support.
>> > + *
>> > + * Copyright 2012 IBM Corp.
>> > + * Author(s): Cornelia Huck <cornelia.huck@de.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-thread.h"
>> > +#include "qemu-queue.h"
>> > +#include <hw/qdev.h>
>> > +#include "kvm.h"
>> > +#include "cpu.h"
>> > +#include "ioinst.h"
>> > +#include "css.h"
>> > +
>> > +struct chp_info {
>>
>> CamelCase, please.
>
> OK.
>>
>> > +    uint8_t in_use;
>> > +    uint8_t type;
>> > +};
>> > +
>> > +static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
>> > +
>> > +static css_subch_cb_func css_subch_cb;
>>
>> Probably these can be put to a container structure which can be passed around.
>
> Still trying to come up with a good model for that.
>
>>
>
>> > +    case CCW_CMD_SENSE_ID:
>> > +    {
>> > +        uint8_t sense_bytes[256];
>> > +
>> > +        /* Sense ID information is device specific. */
>> > +        memcpy(sense_bytes, &sch->id, sizeof(sense_bytes));
>> > +        if (check_len) {
>> > +            if (ccw->count != sizeof(sense_bytes)) {
>> > +                ret = -EINVAL;
>> > +                break;
>> > +            }
>> > +        }
>> > +        len = MIN(ccw->count, sizeof(sense_bytes));
>> > +        /*
>> > +         * Only indicate 0xff in the first sense byte if we actually
>> > +         * have enough place to store at least bytes 0-3.
>> > +         */
>> > +        if (len >= 4) {
>> > +            stb_phys(ccw->cda, 0xff);
>> > +        } else {
>> > +            stb_phys(ccw->cda, 0);
>> > +        }
>> > +        i = 1;
>> > +        for (i = 1; i < len - 1; i++) {
>> > +            stb_phys(ccw->cda + i, sense_bytes[i]);
>> > +        }
>>
>> cpu_physical_memory_write()
>
> Hm, what's wrong with storing byte-by-byte?

cpu_physical_memory_write() could be more optimal, for example resolve
guest addresses only once per page.

>
>>
>> > +        sch->curr_status.scsw.count = ccw->count - len;
>> > +        ret = 0;
>> > +        break;
>> > +    }
>> > +    case CCW_CMD_TIC:
>> > +        if (sch->last_cmd->cmd_code == CCW_CMD_TIC) {
>> > +            ret = -EINVAL;
>> > +            break;
>> > +        }
>> > +        if (ccw->flags & (CCW_FLAG_CC | CCW_FLAG_DC)) {
>> > +            ret = -EINVAL;
>> > +            break;
>> > +        }
>> > +        sch->channel_prog = qemu_get_ram_ptr(ccw->cda);
>> > +        ret = sch->channel_prog ? -EAGAIN : -EFAULT;
>> > +        break;
>> > +    default:
>> > +        if (sch->ccw_cb) {
>> > +            /* Handle device specific commands. */
>> > +            ret = sch->ccw_cb(sch, ccw);
>> > +        } else {
>> > +            ret = -EOPNOTSUPP;
>> > +        }
>> > +        break;
>> > +    }
>> > +    sch->last_cmd = ccw;
>> > +    if (ret == 0) {
>> > +        if (ccw->flags & CCW_FLAG_CC) {
>> > +            sch->channel_prog += 8;
>> > +            ret = -EAGAIN;
>> > +        }
>> > +    }
>> > +
>> > +    return ret;
>
>> > diff --git a/hw/s390x/css.h b/hw/s390x/css.h
>> > new file mode 100644
>> > index 0000000..b8a95cc
>> > --- /dev/null
>> > +++ b/hw/s390x/css.h
>> > @@ -0,0 +1,62 @@
>> > +/*
>> > + * Channel subsystem structures and definitions.
>> > + *
>> > + * Copyright 2012 IBM Corp.
>> > + * Author(s): Cornelia Huck <cornelia.huck@de.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.
>> > + */
>> > +
>> > +#ifndef CSS_H
>> > +#define CSS_H
>> > +
>> > +#include "ioinst.h"
>> > +
>> > +/* Channel subsystem constants. */
>> > +#define MAX_SCHID 65535
>> > +#define MAX_SSID 3
>> > +#define MAX_CSSID 254 /* 255 is reserved */
>> > +#define MAX_CHPID 255
>> > +
>> > +#define MAX_CIWS 8
>> > +
>> > +struct senseid {
>>
>> SenseID
>
> OK.
>>
>> > +    /* common part */
>> > +    uint32_t  reserved:8;    /* always 0x'FF' */
>>
>> The standard syntax calls for 'unsigned' instead of uint32_t for bit
>> fields. But bit fields are not very well defined, it's better to avoid
>> them.
>
> Well, the equivalent Linux structure also looks like that :) But I can
> switch this to a uint8_t/uint16_t structure.
>
>>
>> > +    uint32_t  cu_type:16;    /* control unit type */
>> > +    uint32_t  cu_model:8;    /* control unit model */
>> > +    uint32_t  dev_type:16;   /* device type */
>> > +    uint32_t  dev_model:8;   /* device model */
>> > +    uint32_t  unused:8;      /* padding byte */
>> > +    /* extended part */
>> > +    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
>> > +};
>> > +
>
>> > diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
>> > new file mode 100644
>> > index 0000000..79628b4
>> > --- /dev/null
>> > +++ b/target-s390x/ioinst.h
>> > @@ -0,0 +1,173 @@
>> > +/*
>> > + * S/390 channel I/O instructions
>> > + *
>> > + * Copyright 2012 IBM Corp.
>> > + * Author(s): Cornelia Huck <cornelia.huck@de.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.
>> > +*/
>> > +
>> > +#ifndef IOINST_S390X_H
>> > +#define IOINST_S390X_H
>> > +
>> > +/*
>> > + * Channel I/O related definitions, as defined in the Principles
>> > + * Of Operation (and taken from the Linux implementation).
>>
>> Is this a copy and if so, is the license of original Linux file also GPLv2+?
>
> It's not a verbatim copy.

But a copy of some sort? Can we use the original instead?

>
>>
>> > + */
>> > +
>> > +/* subchannel status word (command mode only) */
>> > +struct scsw {
>>
>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>
> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> have been around for decades, and somebody familiar with channel I/O
> will instantly know what a struct scsw is, but will need to look hard
> at the code to figure out the meaning of SubChStatus.

If they are well-known and have been around for so long time, are
there any suitable header files (with compatible licenses) where they
are defined which could be reused?

Otherwise, please follow CODING_STYLE.

>
>>
>> > +    uint32_t key:4;
>> > +    uint32_t sctl:1;
>> > +    uint32_t eswf:1;
>> > +    uint32_t cc:2;
>> > +    uint32_t fmt:1;
>> > +    uint32_t pfch:1;
>> > +    uint32_t isic:1;
>> > +    uint32_t alcc:1;
>> > +    uint32_t ssi:1;
>> > +    uint32_t zcc:1;
>> > +    uint32_t ectl:1;
>> > +    uint32_t pno:1;
>> > +    uint32_t res:1;
>> > +    uint32_t fctl:3;
>> > +    uint32_t actl:7;
>> > +    uint32_t stctl:5;
>> > +    uint32_t cpa;
>> > +    uint32_t dstat:8;
>> > +    uint32_t cstat:8;
>> > +    uint32_t count:16;
>> > +};
>
Peter Maydell Aug. 8, 2012, 7:34 p.m. UTC | #6
On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>> On Tue, 7 Aug 2012 21:00:59 +0000
>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>
>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>> have been around for decades, and somebody familiar with channel I/O
>> will instantly know what a struct scsw is, but will need to look hard
>> at the code to figure out the meaning of SubChStatus.
>
> If they are well-known and have been around for so long time, are
> there any suitable header files (with compatible licenses) where they
> are defined which could be reused?
>
> Otherwise, please follow CODING_STYLE.

I think we should follow CODING_STYLE for capitalisation issues
but generally if the device's documentation has standard abbreviations
for register names, structures, etc, etc we should use them. Often
this code has to be maintained later by somebody else who might not
be familiar with the general operation of the hardware and who is trying
to match up the code with whatever the data sheet says. Following the
naming used in the h/w docs makes that job easier.

(for instance I took the opportunity of making a bunch of structure
member names in target-arm line up with the ARM ARM names
as part of the refactoring that went on a while back.)

-- PMM
Blue Swirl Aug. 8, 2012, 7:39 p.m. UTC | #7
On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
>> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
>>> On Tue, 7 Aug 2012 21:00:59 +0000
>>> Blue Swirl <blauwirbel@gmail.com> wrote:
>>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
>>>
>>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
>>> have been around for decades, and somebody familiar with channel I/O
>>> will instantly know what a struct scsw is, but will need to look hard
>>> at the code to figure out the meaning of SubChStatus.
>>
>> If they are well-known and have been around for so long time, are
>> there any suitable header files (with compatible licenses) where they
>> are defined which could be reused?
>>
>> Otherwise, please follow CODING_STYLE.
>
> I think we should follow CODING_STYLE for capitalisation issues
> but generally if the device's documentation has standard abbreviations
> for register names, structures, etc, etc we should use them. Often
> this code has to be maintained later by somebody else who might not
> be familiar with the general operation of the hardware and who is trying
> to match up the code with whatever the data sheet says. Following the
> naming used in the h/w docs makes that job easier.

Yes. typedef struct SCSW {} SCSW; should be OK too.

>
> (for instance I took the opportunity of making a bunch of structure
> member names in target-arm line up with the ARM ARM names
> as part of the refactoring that went on a while back.)
>
> -- PMM
Cornelia Huck Aug. 9, 2012, 7:19 a.m. UTC | #8
On Wed, 8 Aug 2012 19:39:33 +0000
Blue Swirl <blauwirbel@gmail.com> wrote:

> On Wed, Aug 8, 2012 at 7:34 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> > On 8 August 2012 20:16, Blue Swirl <blauwirbel@gmail.com> wrote:
> >> On Wed, Aug 8, 2012 at 8:17 AM, Cornelia Huck <cornelia.huck@de.ibm.com> wrote:
> >>> On Tue, 7 Aug 2012 21:00:59 +0000
> >>> Blue Swirl <blauwirbel@gmail.com> wrote:
> >>>> Please use more descriptive names instead of acronyms, for example SubChStatus.
> >>>
> >>> I'd rather leave these at the well-known scsw, pmcw, etc. names. These
> >>> have been around for decades, and somebody familiar with channel I/O
> >>> will instantly know what a struct scsw is, but will need to look hard
> >>> at the code to figure out the meaning of SubChStatus.
> >>
> >> If they are well-known and have been around for so long time, are
> >> there any suitable header files (with compatible licenses) where they
> >> are defined which could be reused?

There's the Linux headers, but they are not exported as this is not a
user space interface (on the guest side).

Otherwise, most code dealing with channel I/O is probably not written
in C ;)

> >>
> >> Otherwise, please follow CODING_STYLE.
> >
> > I think we should follow CODING_STYLE for capitalisation issues
> > but generally if the device's documentation has standard abbreviations
> > for register names, structures, etc, etc we should use them. Often
> > this code has to be maintained later by somebody else who might not
> > be familiar with the general operation of the hardware and who is trying
> > to match up the code with whatever the data sheet says. Following the
> > naming used in the h/w docs makes that job easier.
> 
> Yes. typedef struct SCSW {} SCSW; should be OK too.

Then I'll use something like that.
diff mbox

Patch

diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index dcdcac8..93b41fb 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -1,3 +1,4 @@ 
 obj-y = s390-virtio-bus.o s390-virtio.o
 
 obj-y := $(addprefix ../,$(obj-y))
+obj-y += css.o
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
new file mode 100644
index 0000000..7941c44
--- /dev/null
+++ b/hw/s390x/css.c
@@ -0,0 +1,440 @@ 
+/*
+ * Channel subsystem base support.
+ *
+ * Copyright 2012 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.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-thread.h"
+#include "qemu-queue.h"
+#include <hw/qdev.h>
+#include "kvm.h"
+#include "cpu.h"
+#include "ioinst.h"
+#include "css.h"
+
+struct chp_info {
+    uint8_t in_use;
+    uint8_t type;
+};
+
+static struct chp_info chpids[MAX_CSSID + 1][MAX_CHPID + 1];
+
+static css_subch_cb_func css_subch_cb;
+
+int css_set_subch_cb(css_subch_cb_func func)
+{
+    if (func && css_subch_cb) {
+        return -EBUSY;
+    }
+    css_subch_cb = func;
+    return 0;
+}
+
+static void css_inject_io_interrupt(SubchDev *sch, uint8_t func)
+{
+    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
+                      &sch->curr_status.pmcw, &sch->sense_data, 0,
+                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm,
+                      func);
+}
+
+void css_conditional_io_interrupt(SubchDev *sch)
+{
+    s390_io_interrupt(sch->cssid, sch->ssid, sch->schid, &sch->curr_status.scsw,
+                      &sch->curr_status.pmcw, &sch->sense_data, 1,
+                      sch->curr_status.pmcw.isc, sch->curr_status.pmcw.intparm, 0);
+}
+
+static void sch_handle_clear_func(SubchDev *sch)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    int path;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    /* Reset values prior to 'issueing the clear signal'. */
+    p->lpum = 0;
+    p->pom = 0xff;
+    s->pno = 0;
+
+    /* We always 'attempt to issue the clear signal', and we always succeed. */
+    sch->orb = NULL;
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    s->actl &= ~SCSW_ACTL_CLEAR_PEND;
+    s->stctl |= SCSW_STCTL_STATUS_PEND;
+
+    s->dstat = 0;
+    s->cstat = 0;
+    p->lpum = path;
+
+}
+
+static void sch_handle_halt_func(SubchDev *sch)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    int path;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    /* We always 'attempt to issue the halt signal', and we always succeed. */
+    sch->orb = NULL;
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    s->actl &= ~SCSW_ACTL_HALT_PEND;
+    s->stctl |= SCSW_STCTL_STATUS_PEND;
+
+    if ((s->actl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) ||
+        !((s->actl & SCSW_ACTL_START_PEND) ||
+          (s->actl & SCSW_ACTL_SUSP))) {
+        s->dstat = SCSW_DSTAT_DEVICE_END;
+    }
+    s->cstat = 0;
+    p->lpum = path;
+
+}
+
+static int css_interpret_ccw(SubchDev *sch, struct ccw1 *ccw)
+{
+    int ret;
+    bool check_len;
+    int len;
+    int i;
+
+    if (!ccw) {
+        return -EIO;
+    }
+
+    /* Check for invalid command codes. */
+    if ((ccw->cmd_code & 0x0f) == 0) {
+        return -EINVAL;
+    }
+    if (((ccw->cmd_code & 0x0f) == CCW_CMD_TIC) &&
+        ((ccw->cmd_code & 0xf0) != 0)) {
+        return -EINVAL;
+    }
+
+    if (ccw->flags & CCW_FLAG_SUSPEND) {
+        return -ERESTART;
+    }
+
+    check_len = !((ccw->flags & CCW_FLAG_SLI) && !(ccw->flags & CCW_FLAG_DC));
+
+    /* Look at the command. */
+    switch (ccw->cmd_code) {
+    case CCW_CMD_NOOP:
+        /* Nothing to do. */
+        ret = 0;
+        break;
+    case CCW_CMD_BASIC_SENSE:
+        if (check_len) {
+            if (ccw->count != sizeof(sch->sense_data)) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, sizeof(sch->sense_data));
+        cpu_physical_memory_write(ccw->cda, sch->sense_data, len);
+        sch->curr_status.scsw.count = ccw->count - len;
+        memset(sch->sense_data, 0, sizeof(sch->sense_data));
+        ret = 0;
+        break;
+    case CCW_CMD_SENSE_ID:
+    {
+        uint8_t sense_bytes[256];
+
+        /* Sense ID information is device specific. */
+        memcpy(sense_bytes, &sch->id, sizeof(sense_bytes));
+        if (check_len) {
+            if (ccw->count != sizeof(sense_bytes)) {
+                ret = -EINVAL;
+                break;
+            }
+        }
+        len = MIN(ccw->count, sizeof(sense_bytes));
+        /*
+         * Only indicate 0xff in the first sense byte if we actually
+         * have enough place to store at least bytes 0-3.
+         */
+        if (len >= 4) {
+            stb_phys(ccw->cda, 0xff);
+        } else {
+            stb_phys(ccw->cda, 0);
+        }
+        i = 1;
+        for (i = 1; i < len - 1; i++) {
+            stb_phys(ccw->cda + i, sense_bytes[i]);
+        }
+        sch->curr_status.scsw.count = ccw->count - len;
+        ret = 0;
+        break;
+    }
+    case CCW_CMD_TIC:
+        if (sch->last_cmd->cmd_code == CCW_CMD_TIC) {
+            ret = -EINVAL;
+            break;
+        }
+        if (ccw->flags & (CCW_FLAG_CC | CCW_FLAG_DC)) {
+            ret = -EINVAL;
+            break;
+        }
+        sch->channel_prog = qemu_get_ram_ptr(ccw->cda);
+        ret = sch->channel_prog ? -EAGAIN : -EFAULT;
+        break;
+    default:
+        if (sch->ccw_cb) {
+            /* Handle device specific commands. */
+            ret = sch->ccw_cb(sch, ccw);
+        } else {
+            ret = -EOPNOTSUPP;
+        }
+        break;
+    }
+    sch->last_cmd = ccw;
+    if (ret == 0) {
+        if (ccw->flags & CCW_FLAG_CC) {
+            sch->channel_prog += 8;
+            ret = -EAGAIN;
+        }
+    }
+
+    return ret;
+}
+
+static void sch_handle_start_func(SubchDev *sch)
+{
+
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    struct orb *orb = sch->orb;
+    int path;
+    int ret;
+
+    /* Path management: In our simple css, we always choose the only path. */
+    path = 0x80;
+
+    if (!s->actl & SCSW_ACTL_SUSP) {
+        /* Look at the orb and try to execute the channel program. */
+        p->intparm = orb->intparm;
+        if (!(orb->lpm & path)) {
+            /* Generate a deferred cc 3 condition. */
+            s->cc = 3;
+            s->stctl = (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND);
+            return;
+        }
+    } else {
+        s->actl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND);
+    }
+    sch->last_cmd = NULL;
+    do {
+        ret = css_interpret_ccw(sch, sch->channel_prog);
+        switch (ret) {
+        case -EAGAIN:
+            /* ccw chain, continue processing */
+            break;
+        case 0:
+            /* success */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_STATUS_PEND;
+            s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END;
+            break;
+        case -EOPNOTSUPP:
+            /* unsupported command, generate unit check (command reject) */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->dstat = SCSW_DSTAT_UNIT_CHECK;
+            /* Set sense bit 0 in ecw0. */
+            sch->sense_data[0] = 0x80;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -EFAULT:
+            /* memory problem, generate channel data check */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->cstat = SCSW_CSTAT_DATA_CHECK;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -EBUSY:
+            /* subchannel busy, generate deferred cc 1 */
+            s->cc = 1;
+            s->stctl = SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        case -ERESTART:
+            /* channel program has been suspended */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->actl |= SCSW_ACTL_SUSP;
+            break;
+        default:
+            /* error, generate channel program check */
+            s->actl &= ~SCSW_ACTL_START_PEND;
+            s->cstat = SCSW_CSTAT_PROG_CHECK;
+            s->stctl = SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
+                SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
+            break;
+        }
+    } while (ret == -EAGAIN);
+
+}
+
+int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
+                      void *pmcw)
+{
+    SubchDev *sch;
+    int cssid;
+    int ssid;
+    int schid;
+    int m;
+    int ret;
+    int notify = 0;
+
+    ret = ioinst_disassemble_sch_ident(sch_id, &m, &cssid, &ssid, &schid);
+    if (ret) {
+        return ret;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (!sch) {
+        return -ENODEV;
+    }
+    qemu_mutex_lock(&sch->mutex);
+    memcpy(&sch->curr_status.pmcw, pmcw, sizeof(struct pmcw));
+    switch (func) {
+    case CSS_DO_CSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
+        /* fallthrough */
+    case CSS_DO_CSCH_SIMPLE:
+        sch_handle_clear_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_HSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
+        /* fallthrough */
+    case CSS_DO_HSCH_SIMPLE:
+        sch_handle_halt_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_SSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
+        sch->orb = qemu_get_ram_ptr(orb);
+        sch->channel_prog = qemu_get_ram_ptr(sch->orb->cpa);
+        /* fallthrough */
+    case CSS_DO_SSCH_SIMPLE:
+        sch_handle_start_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_RSCH:
+        memcpy(&sch->curr_status.scsw, scsw, sizeof(struct scsw));
+        sch_handle_start_func(sch);
+        notify = 1;
+        break;
+    case CSS_DO_XSCH:
+        sch->orb = NULL;
+        sch->channel_prog = NULL;
+        sch->last_cmd = NULL;
+        break;
+    }
+    if (notify) {
+        css_inject_io_interrupt(sch, func);
+    }
+    qemu_mutex_unlock(&sch->mutex);
+    return 0;
+}
+
+static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type)
+{
+    if (cssid > MAX_CSSID) {
+        return -EINVAL;
+    }
+    if (chpids[cssid][chpid].in_use) {
+        return -EEXIST;
+    }
+    chpids[cssid][chpid].in_use = 1;
+    chpids[cssid][chpid].type = type;
+
+    s390_chp_hotplug(cssid, chpid, type, 1, 1);
+
+    return 0;
+}
+
+void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+    struct scsw *s = &sch->curr_status.scsw;
+    int i;
+
+    memset(p, 0, sizeof(struct pmcw));
+    p->dnv = 1;
+    p->dev = sch->devno;
+    /* single path */
+    p->pim = 0x80;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->chpid[0] = chpid;
+    if (!chpids[sch->cssid][chpid].in_use) {
+        css_add_virtual_chpid(sch->cssid, chpid, type);
+    }
+
+    memset(s, 0, sizeof(struct scsw));
+    sch->curr_status.mba = 0;
+    for (i = 0; i < 4; i++) {
+        sch->curr_status.mda[i] = 0;
+    }
+}
+
+SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return css_subch_cb ? css_subch_cb(m, cssid, ssid, schid) : NULL;
+}
+
+static void css_init(void)
+{
+    css_subch_cb = NULL;
+}
+machine_init(css_init);
+
+void css_reset_sch(SubchDev *sch)
+{
+    struct pmcw *p = &sch->curr_status.pmcw;
+
+    p->intparm = 0;
+    p->isc = 0;
+    p->ena = 0;
+    p->lm = 0;
+    p->mme = 0;
+    p->mp = 0;
+    p->tf = 0;
+    p->dnv = 1;
+    p->dev = sch->devno;
+    p->pim = 0x80;
+    p->lpm = p->pim;
+    p->pnom = 0;
+    p->lpum = 0;
+    p->mbi = 0;
+    p->pom = 0xff;
+    p->pam = 0x80;
+    p->mbfc = 0;
+    p->xmwme = 0;
+    p->csense = 0;
+
+    memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw));
+    sch->curr_status.mba = 0;
+
+    sch->channel_prog = NULL;
+    sch->last_cmd = NULL;
+    sch->orb = NULL;
+}
+
+void css_reset(void)
+{
+    /* Nothing for now. */
+}
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
new file mode 100644
index 0000000..b8a95cc
--- /dev/null
+++ b/hw/s390x/css.h
@@ -0,0 +1,62 @@ 
+/*
+ * Channel subsystem structures and definitions.
+ *
+ * Copyright 2012 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.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.
+ */
+
+#ifndef CSS_H
+#define CSS_H
+
+#include "ioinst.h"
+
+/* Channel subsystem constants. */
+#define MAX_SCHID 65535
+#define MAX_SSID 3
+#define MAX_CSSID 254 /* 255 is reserved */
+#define MAX_CHPID 255
+
+#define MAX_CIWS 8
+
+struct senseid {
+    /* common part */
+    uint32_t  reserved:8;    /* always 0x'FF' */
+    uint32_t  cu_type:16;    /* control unit type */
+    uint32_t  cu_model:8;    /* control unit model */
+    uint32_t  dev_type:16;   /* device type */
+    uint32_t  dev_model:8;   /* device model */
+    uint32_t  unused:8;      /* padding byte */
+    /* extended part */
+    uint32_t ciw[MAX_CIWS];  /* variable # of CIWs */
+};
+
+struct SubchDev {
+    /* channel-subsystem related things: */
+    uint8_t cssid;
+    uint8_t ssid;
+    uint16_t schid;
+    uint16_t devno;
+    struct schib curr_status;
+    uint8_t sense_data[32];
+    struct ccw1 *channel_prog;
+    struct ccw1 *last_cmd;
+    struct orb *orb;
+    QemuMutex mutex;
+    /* transport-provided data: */
+    int (*ccw_cb) (SubchDev *, struct ccw1 *);
+    struct senseid id;
+    void *driver_data;
+};
+
+typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid);
+int css_set_subch_cb(css_subch_cb_func func);
+void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type);
+void css_reset(void);
+void css_reset_sch(SubchDev *sch);
+
+#endif
diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs
index 262747f..cb05241 100644
--- a/target-s390x/Makefile.objs
+++ b/target-s390x/Makefile.objs
@@ -1,5 +1,5 @@ 
 obj-y += translate.o op_helper.o helper.o cpu.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o
 obj-$(CONFIG_KVM) += kvm.o
 
 $(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index c30ac3a..c09fa61 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -336,6 +336,23 @@  static inline unsigned s390_del_running_cpu(CPUS390XState *env)
 void cpu_lock(void);
 void cpu_unlock(void);
 
+typedef struct SubchDev SubchDev;
+struct orb;
+
+#ifndef CONFIG_USER_ONLY
+SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid);
+void css_conditional_io_interrupt(SubchDev *sch);
+#else
+static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid)
+{
+    return NULL;
+}
+static inline void css_conditional_io_interrupt(SubchDev *sch)
+{
+}
+#endif
+
 static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
 {
     env->aregs[0] = newtls >> 32;
@@ -996,4 +1013,95 @@  static inline void cpu_pc_from_tb(CPUS390XState *env, TranslationBlock* tb)
     env->psw.addr = tb->pc;
 }
 
+int css_handle_sch_io(uint32_t sch_id, uint8_t func, uint64_t orb, void *scsw,
+                      void *pmcw);
+#ifdef CONFIG_KVM
+int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                         uint16_t devno, void *data, int hotplugged, int add,
+                         int virtual);
+int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
+                         int virtual);
+int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                          void *scsw, void *pmcw, void *sense,
+                          int unsolicited, uint8_t func);
+void kvm_s390_enable_css_support(CPUS390XState *env);
+#else
+static inline int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid, uint16_t devno,
+                                       void *data, int hotplugged, int add,
+                                       int virtual)
+{
+    return -EOPNOTSUPP;
+}
+static inline int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid,
+                                       uint8_t type, int add, int virtual)
+{
+    return -EOPNOTSUPP;
+}
+static inline int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid,
+                                        uint16_t schid, void *scsw, void *pmcw,
+                                        void *sense, int unsolicited, uint8_t func)
+{
+    return -EOPNOTSUPP;
+}
+static inline void kvm_s390_enable_css_support(CPUS390XState *env)
+{
+}
+#endif
+
+static inline void s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                                    uint16_t devno, void *data, int hotplugged,
+                                    int add, int virtual)
+{
+    int ret;
+
+    ret = kvm_s390_sch_hotplug(cssid, ssid, schid, devno, data, hotplugged,
+                               add, virtual);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Hotplugging subchannels not supported\n");
+    }
+}
+
+static inline void s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type,
+                                    int add, int virtual)
+{
+    int ret;
+
+    ret = kvm_s390_chp_hotplug(cssid, chpid, type, add, virtual);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Hotplugging chpids not supported\n");
+    }
+}
+
+static inline void s390_io_interrupt(uint8_t cssid, uint8_t ssid,
+                                     uint16_t schid, void *scsw, void *pmcw,
+                                     void *sense, int unsolicited,
+                                     uint8_t isc, uint32_t intparm, uint8_t func)
+{
+    int ret;
+
+    ret = kvm_s390_io_interrupt(cssid, ssid, schid, scsw, pmcw, sense,
+                                unsolicited, func);
+    if (ret == -EOPNOTSUPP) {
+        fprintf(stderr, "Injecting I/O interrupts not supported\n");
+    }
+}
+
+#ifdef CONFIG_KVM
+#define CSS_DO_CSCH SCH_DO_CSCH
+#define CSS_DO_HSCH SCH_DO_HSCH
+#define CSS_DO_SSCH SCH_DO_SSCH
+#define CSS_DO_RSCH SCH_DO_RSCH
+#define CSS_DO_XSCH SCH_DO_XSCH
+#else
+#define CSS_DO_CSCH 0
+#define CSS_DO_HSCH 1
+#define CSS_DO_SSCH 2
+#define CSS_DO_RSCH 3
+#define CSS_DO_XSCH 4
+#endif
+#define CSS_DO_CSCH_SIMPLE 0xf0
+#define CSS_DO_HSCH_SIMPLE 0xf1
+#define CSS_DO_SSCH_SIMPLE 0xf2
+
 #endif
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
new file mode 100644
index 0000000..8f358d5
--- /dev/null
+++ b/target-s390x/ioinst.c
@@ -0,0 +1,38 @@ 
+/*
+ * I/O instructions for S/390
+ *
+ * Copyright 2012 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "cpu.h"
+#include "ioinst.h"
+
+int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
+                                 int *schid)
+{
+    if (!(value & 0x00010000)) {
+        return -EINVAL;
+    }
+    if (!(value & 0x00080000)) {
+        if (value & 0xff000000) {
+            return -EINVAL;
+        }
+        *cssid = 0;
+        *m = 0;
+    } else {
+        *cssid = (value & 0xff000000) >> 24;
+        *m = 1;
+    }
+    *ssid = (value & 0x00060000) >> 17;
+    *schid = value & 0x0000ffff;
+    return 0;
+}
diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
new file mode 100644
index 0000000..79628b4
--- /dev/null
+++ b/target-s390x/ioinst.h
@@ -0,0 +1,173 @@ 
+/*
+ * S/390 channel I/O instructions
+ *
+ * Copyright 2012 IBM Corp.
+ * Author(s): Cornelia Huck <cornelia.huck@de.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.
+*/
+
+#ifndef IOINST_S390X_H
+#define IOINST_S390X_H
+
+/*
+ * Channel I/O related definitions, as defined in the Principles
+ * Of Operation (and taken from the Linux implementation).
+ */
+
+/* subchannel status word (command mode only) */
+struct scsw {
+    uint32_t key:4;
+    uint32_t sctl:1;
+    uint32_t eswf:1;
+    uint32_t cc:2;
+    uint32_t fmt:1;
+    uint32_t pfch:1;
+    uint32_t isic:1;
+    uint32_t alcc:1;
+    uint32_t ssi:1;
+    uint32_t zcc:1;
+    uint32_t ectl:1;
+    uint32_t pno:1;
+    uint32_t res:1;
+    uint32_t fctl:3;
+    uint32_t actl:7;
+    uint32_t stctl:5;
+    uint32_t cpa;
+    uint32_t dstat:8;
+    uint32_t cstat:8;
+    uint32_t count:16;
+};
+
+/* path management control word */
+struct pmcw {
+    uint32_t intparm;
+    uint32_t qf:1;
+    uint32_t w:1;
+    uint32_t isc:3;
+    uint32_t zeroes0:3;
+    uint32_t ena:1;
+    uint32_t lm:2;
+    uint32_t mme:2;
+    uint32_t mp:1;
+    uint32_t tf:1;
+    uint32_t dnv:1;
+    uint32_t dev:16;
+    uint8_t  lpm;
+    uint8_t  pnom;
+    uint8_t  lpum;
+    uint8_t  pim;
+    uint16_t mbi;
+    uint8_t  pom;
+    uint8_t  pam;
+    uint8_t  chpid[8];
+    uint32_t zeroes1:8;
+    uint32_t st:3;
+    uint32_t zeroes2:18;
+    uint32_t mbfc:1;
+    uint32_t xmwme:1;
+    uint32_t csense:1;
+};
+
+/* subchannel information block */
+struct schib {
+    struct pmcw pmcw;
+    struct scsw scsw;
+    uint64_t mba;
+    uint8_t mda[4];
+};
+
+/* interruption response block */
+struct irb {
+    struct scsw scsw;
+    uint32_t esw[5];
+    uint32_t ecw[8];
+    uint32_t emw[8];
+};
+
+/* operation request block */
+struct orb {
+    uint32_t intparm;
+    uint32_t key:4;
+    uint32_t spnd:1;
+    uint32_t str:1;
+    uint32_t mod:1;
+    uint32_t sync:1;
+    uint32_t fmt:1;
+    uint32_t pfch:1;
+    uint32_t isic:1;
+    uint32_t alcc:1;
+    uint32_t ssic:1;
+    uint32_t zero0:1;
+    uint32_t c64:1;
+    uint32_t i2k:1;
+    uint32_t lpm:8;
+    uint32_t ils:1;
+    uint32_t midaw:1;
+    uint32_t zero1:5;
+    uint32_t orbx:1;
+    uint32_t cpa;
+};
+
+/* channel command word (type 1) */
+struct ccw1 {
+    uint8_t cmd_code;
+    uint8_t flags;
+    uint16_t count;
+    uint32_t cda;
+};
+
+#define CCW_FLAG_DC              0x80
+#define CCW_FLAG_CC              0x40
+#define CCW_FLAG_SLI             0x20
+#define CCW_FLAG_SKIP            0x10
+#define CCW_FLAG_PCI             0x08
+#define CCW_FLAG_IDA             0x04
+#define CCW_FLAG_SUSPEND         0x02
+
+#define CCW_CMD_NOOP             0x03
+#define CCW_CMD_BASIC_SENSE      0x04
+#define CCW_CMD_TIC              0x08
+#define CCW_CMD_SENSE_ID         0xe4
+
+#define SCSW_FCTL_CLEAR_FUNC     0x1
+#define SCSW_FCTL_HALT_FUNC      0x2
+#define SCSW_FCTL_START_FUNC     0x4
+
+#define SCSW_ACTL_SUSP           0x1
+#define SCSW_ACTL_DEVICE_ACTIVE  0x2
+#define SCSW_ACTL_SUBCH_ACTIVE   0x4
+#define SCSW_ACTL_CLEAR_PEND     0x8
+#define SCSW_ACTL_HALT_PEND      0x10
+#define SCSW_ACTL_START_PEND     0x20
+#define SCSW_ACTL_RESUME_PEND    0x40
+
+#define SCSW_STCTL_STATUS_PEND   0x1
+#define SCSW_STCTL_SECONDARY     0x2
+#define SCSW_STCTL_PRIMARY       0x4
+#define SCSW_STCTL_INTERMEDIATE  0x8
+#define SCSW_STCTL_ALERT         0x10
+
+#define SCSW_DSTAT_ATTENTION     0x80
+#define SCSW_DSTAT_STAT_MOD      0x40
+#define SCSW_DSTAT_CU_END        0x20
+#define SCSW_DSTAT_BUSY          0x10
+#define SCSW_DSTAT_CHANNEL_END   0x08
+#define SCSW_DSTAT_DEVICE_END    0x04
+#define SCSW_DSTAT_UNIT_CHECK    0x02
+#define SCSW_DSTAT_UNIT_EXCEP    0x01
+
+#define SCSW_CSTAT_PCI           0x80
+#define SCSW_CSTAT_INCORR_LEN    0x40
+#define SCSW_CSTAT_PROG_CHECK    0x20
+#define SCSW_CSTAT_PROT_CHECK    0x10
+#define SCSW_CSTAT_DATA_CHECK    0x08
+#define SCSW_CSTAT_CHN_CTRL_CHK  0x04
+#define SCSW_CSTAT_INTF_CTRL_CHK 0x02
+#define SCSW_CSTAT_CHAIN_CHECK   0x01
+
+int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
+                                 int *schid);
+#endif
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 47008c2..e4cde14 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -26,6 +26,7 @@ 
 
 #include "qemu-common.h"
 #include "qemu-timer.h"
+#include "qemu-thread.h"
 #include "sysemu.h"
 #include "kvm.h"
 #include "cpu.h"
@@ -110,6 +111,7 @@  int kvm_arch_put_registers(CPUS390XState *env, int level)
 
     env->kvm_run->psw_addr = env->psw.addr;
     env->kvm_run->psw_mask = env->psw.mask;
+    env->kvm_run->s.regs.prefix = env->psa;
 
     return ret;
 }
@@ -131,6 +133,7 @@  int kvm_arch_get_registers(CPUS390XState *env)
 
     env->psw.addr = env->kvm_run->psw_addr;
     env->psw.mask = env->kvm_run->psw_mask;
+    env->psa = env->kvm_run->s.regs.prefix;
 
     return 0;
 }
@@ -506,6 +509,13 @@  int kvm_arch_handle_exit(CPUS390XState *env, struct kvm_run *run)
         case KVM_EXIT_S390_RESET:
             qemu_system_reset_request();
             break;
+        case KVM_EXIT_S390_SCH_IO:
+            ret = css_handle_sch_io(run->s390_sch_io.sch_id,
+                                    run->s390_sch_io.func,
+                                    run->s390_sch_io.orb,
+                                    run->s390_sch_io.scsw,
+                                    run->s390_sch_io.pmcw);
+            break;
         default:
             fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
             break;
@@ -531,3 +541,94 @@  int kvm_arch_on_sigbus(int code, void *addr)
 {
     return 1;
 }
+
+int kvm_s390_sch_hotplug(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                         uint16_t devno, void *data, int hotplugged, int add,
+                         int virtual)
+{
+    struct kvm_s390_sch_info sch_info;
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    /* Notify the kernel. */
+    sch_info.cssid = cssid;
+    sch_info.ssid = ssid;
+    sch_info.schid = schid;
+    sch_info.devno = devno;
+    memcpy(&sch_info.schib, data, sizeof(sch_info.schib));
+    sch_info.hotplugged = hotplugged;
+    sch_info.add = add;
+    sch_info.virtual = virtual;
+    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CCW_HOTPLUG, &sch_info);
+    assert(ret == 0);
+    return ret;
+}
+
+int kvm_s390_chp_hotplug(uint8_t cssid, uint8_t chpid, uint8_t type, int add,
+                         int virtual)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    struct kvm_s390_chp_info chpid_info;
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    /* Notify the kernel. */
+    chpid_info.cssid = cssid;
+    chpid_info.chpid = chpid;
+    chpid_info.type = type;
+    chpid_info.add = 1;
+    chpid_info.virtual = 1;
+    ret = kvm_vm_ioctl(cpu->env.kvm_state, KVM_S390_CHP_HOTPLUG, &chpid_info);
+    assert(ret == 0);
+    return ret;
+}
+
+int kvm_s390_io_interrupt(uint8_t cssid, uint8_t ssid, uint16_t schid,
+                          void *scsw, void *pmcw, void *sense,
+                          int unsolicited, uint8_t func)
+{
+    S390CPU *cpu = s390_cpu_addr2state(0);
+    struct kvm_css_notify notify;
+    int ret;
+
+    if (!kvm_enabled()) {
+        return -EOPNOTSUPP;
+    }
+
+    notify.cssid = cssid;
+    notify.ssid = ssid;
+    notify.schid = schid;
+    if (!unsolicited) {
+        memcpy(&notify.scsw, scsw, sizeof(notify.scsw));
+        memcpy(&notify.pmcw, pmcw, sizeof(notify.pmcw));
+        memcpy(&notify.sense_data, sense, sizeof(notify.sense_data));
+        notify.func = func;
+    }
+    notify.unsolicited = unsolicited;
+    ret = kvm_vcpu_ioctl(&cpu->env, KVM_S390_CSS_NOTIFY, &notify);
+    assert(ret == 0);
+    return ret;
+}
+
+void kvm_s390_enable_css_support(CPUS390XState *env)
+{
+    struct kvm_enable_cap cap = {};
+    int r;
+
+    /* Activate host kernel channel subsystem support. */
+    if (kvm_enabled()) {
+        /* One CPU has to run */
+        s390_add_running_cpu(env);
+
+        cap.cap = KVM_CAP_S390_CSS_SUPPORT;
+        r = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap);
+        assert(r == 0);
+    }
+}