Patchwork [06/15] s390: Add channel I/O instructions.

login
register
mail settings
Submitter Alexander Graf
Date Jan. 25, 2013, 12:48 p.m.
Message ID <1359118138-9367-7-git-send-email-agraf@suse.de>
Download mbox | patch
Permalink /patch/215680/
State New
Headers show

Comments

Alexander Graf - Jan. 25, 2013, 12:48 p.m.
From: Cornelia Huck <cornelia.huck@de.ibm.com>

Provide handlers for (most) channel I/O instructions.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
---
 target-s390x/cpu.h    |  100 +++++++
 target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
 target-s390x/ioinst.h |   16 ++
 trace-events          |    6 +
 4 files changed, 838 insertions(+), 0 deletions(-)
Blue Swirl - Jan. 25, 2013, 7:20 p.m.
On Fri, Jan 25, 2013 at 12:48 PM, Alexander Graf <agraf@suse.de> wrote:
> From: Cornelia Huck <cornelia.huck@de.ibm.com>
>
> Provide handlers for (most) channel I/O instructions.
>
> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  target-s390x/cpu.h    |  100 +++++++
>  target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
>  target-s390x/ioinst.h |   16 ++
>  trace-events          |    6 +
>  4 files changed, 838 insertions(+), 0 deletions(-)
>
> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
> index 3e00d38..76a822c 100644
> --- a/target-s390x/cpu.h
> +++ b/target-s390x/cpu.h
> @@ -147,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
>  }
>  #endif
>
> +/* distinguish between 24 bit and 31 bit addressing */
> +#define HIGH_ORDER_BIT 0x80000000
> +
>  /* Interrupt Codes */
>  /* Program Interrupts */
>  #define PGM_OPERATION                   0x0001
> @@ -331,6 +334,20 @@ void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
>                                     int is_write);
>  void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
>                                      int is_write);
> +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
> +{
> +    hwaddr addr = 0;
> +    uint8_t reg;
> +
> +    reg = ipb >> 28;
> +    if (reg > 0) {
> +        addr = env->regs[reg];
> +    }
> +    addr += (ipb >> 16) & 0xfff;
> +
> +    return addr;
> +}
> +
>  void s390x_tod_timer(void *opaque);
>  void s390x_cpu_timer(void *opaque);
>
> @@ -380,6 +397,89 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>  void cpu_lock(void);
>  void cpu_unlock(void);
>
> +typedef struct SubchDev SubchDev;
> +
> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
> +                                       uint16_t schid)
> +{
> +    return NULL;
> +}
> +static inline bool css_subch_visible(SubchDev *sch)
> +{
> +    return false;
> +}
> +static inline void css_conditional_io_interrupt(SubchDev *sch)
> +{
> +}
> +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
> +{
> +    return -ENODEV;
> +}
> +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
> +{
> +    return true;
> +}
> +static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_xsch(SubchDev *sch)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_csch(SubchDev *sch)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_hsch(SubchDev *sch)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_ssch(SubchDev *sch, ORB *orb)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_tsch(SubchDev *sch, IRB *irb)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_stcrw(CRW *crw)
> +{
> +    return 1;
> +}
> +static inline int css_do_tpi(uint64_t addr, int lowcore)
> +{
> +    return 0;
> +}
> +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
> +                                       int rfmt, uint8_t l_chpid, void *buf)
> +{
> +    return 0;
> +}
> +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
> +{
> +}
> +static inline int css_enable_mss(void)
> +{
> +    return -EINVAL;
> +}
> +static inline int css_enable_mcsse(void)
> +{
> +    return -EINVAL;
> +}
> +static inline int css_do_rsch(SubchDev *sch)
> +{
> +    return -ENODEV;
> +}
> +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
> +{
> +    return -ENODEV;
> +}
> +static inline bool css_present(uint8_t cssid)
> +{
> +    return false;
> +}
> +
>  static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>  {
>      env->aregs[0] = newtls >> 32;
> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
> index 06a16ee..4ef2d73 100644
> --- a/target-s390x/ioinst.c
> +++ b/target-s390x/ioinst.c
> @@ -13,6 +13,7 @@
>
>  #include "cpu.h"
>  #include "ioinst.h"
> +#include "trace.h"
>
>  int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>                                   int *schid)
> @@ -34,3 +35,718 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>      *schid = IOINST_SCHID_NR(value);
>      return 0;
>  }
> +
> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    int ret = -ENODEV;
> +    int cc;
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_xsch(sch);
> +    }
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EBUSY:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        cc = 1;
> +        break;
> +    }
> +
> +    return cc;
> +}
> +
> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    int ret = -ENODEV;
> +    int cc;
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("csch", cssid, ssid, schid);
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_csch(sch);
> +    }
> +    if (ret == -ENODEV) {
> +        cc = 3;
> +    } else {
> +        cc = 0;
> +    }
> +    return cc;
> +}
> +
> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    int ret = -ENODEV;
> +    int cc;
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_hsch(sch);
> +    }
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EBUSY:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        cc = 1;
> +        break;
> +    }
> +
> +    return cc;
> +}
> +
> +static int ioinst_schib_valid(SCHIB *schib)
> +{
> +    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
> +        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
> +        return 0;
> +    }
> +    /* Disallow extended measurements for now. */
> +    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
> +        return 0;
> +    }
> +    return 1;
> +}
> +
> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    SCHIB *schib;
> +    uint64_t addr;
> +    int ret = -ENODEV;
> +    int cc;
> +    hwaddr len = sizeof(*schib);
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("msch", cssid, ssid, schid);
> +    addr = decode_basedisp_s(env, ipb);
> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
> +    if (!schib || len != sizeof(*schib)) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    if (!ioinst_schib_valid(schib)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_msch(sch, schib);
> +    }
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EBUSY:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        cc = 1;
> +        break;
> +    }
> +out:
> +    s390_cpu_physical_memory_unmap(env, schib, len, 0);
> +    return cc;
> +}
> +
> +static void copy_orb_from_guest(ORB *dest, const ORB *src)
> +{
> +    dest->intparm = be32_to_cpu(src->intparm);
> +    dest->ctrl0 = be16_to_cpu(src->ctrl0);
> +    dest->lpm = src->lpm;
> +    dest->ctrl1 = src->ctrl1;
> +    dest->cpa = be32_to_cpu(src->cpa);
> +}
> +
> +static int ioinst_orb_valid(ORB *orb)
> +{
> +    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
> +        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
> +        return 0;
> +    }
> +    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
> +        return 0;
> +    }
> +    return 1;
> +}
> +
> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    ORB *orig_orb, orb;
> +    uint64_t addr;
> +    int ret = -ENODEV;
> +    int cc;
> +    hwaddr len = sizeof(*orig_orb);
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
> +    addr = decode_basedisp_s(env, ipb);
> +    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
> +    if (!orig_orb || len != sizeof(*orig_orb)) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    copy_orb_from_guest(&orb, orig_orb);
> +    if (!ioinst_orb_valid(&orb)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_ssch(sch, &orb);
> +    }
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EBUSY:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        cc = 1;
> +        break;
> +    }
> +
> +out:
> +    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
> +    return cc;
> +}
> +
> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
> +{
> +    CRW *crw;
> +    uint64_t addr;
> +    int cc;
> +    hwaddr len = sizeof(*crw);
> +
> +    addr = decode_basedisp_s(env, ipb);
> +    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
> +    if (!crw || len != sizeof(*crw)) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    cc = css_do_stcrw(crw);
> +    /* 0 - crw stored, 1 - zeroes stored */
> +out:
> +    s390_cpu_physical_memory_unmap(env, crw, len, 1);
> +    return cc;
> +}
> +
> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    uint64_t addr;
> +    int cc;
> +    SCHIB *schib;
> +    hwaddr len = sizeof(*schib);
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
> +    addr = decode_basedisp_s(env, ipb);
> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
> +    if (!schib || len != sizeof(*schib)) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch) {
> +        if (css_subch_visible(sch)) {
> +            css_do_stsch(sch, schib);
> +            cc = 0;
> +        } else {
> +            /* Indicate no more subchannels in this css/ss */
> +            cc = 3;
> +        }
> +    } else {
> +        if (css_schid_final(cssid, ssid, schid)) {
> +            cc = 3; /* No more subchannels in this css/ss */
> +        } else {
> +            /* Store an empty schib. */
> +            memset(schib, 0, sizeof(*schib));
> +            cc = 0;
> +        }
> +    }
> +out:
> +    s390_cpu_physical_memory_unmap(env, schib, len, 1);
> +    return cc;
> +}
> +
> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    IRB *irb;
> +    uint64_t addr;
> +    int ret = -ENODEV;
> +    int cc;
> +    hwaddr len = sizeof(*irb);
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
> +    addr = decode_basedisp_s(env, ipb);
> +    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
> +    if (!irb || len != sizeof(*irb)) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        cc = -EIO;
> +        goto out;
> +    }
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_tsch(sch, irb);
> +        /* 0 - status pending, 1 - not status pending */
> +        cc = ret;
> +    } else {
> +        cc = 3;
> +    }
> +out:
> +    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
> +    return cc;
> +}
> +
> +typedef struct ChscReq {
> +    uint16_t len;
> +    uint16_t command;
> +    uint32_t param0;
> +    uint32_t param1;
> +    uint32_t param2;
> +} QEMU_PACKED ChscReq;
> +
> +typedef struct ChscResp {
> +    uint16_t len;
> +    uint16_t code;
> +    uint32_t param;
> +    char data[0];
> +} QEMU_PACKED ChscResp;
> +
> +#define CHSC_MIN_RESP_LEN 0x0008
> +
> +#define CHSC_SCPD 0x0002
> +#define CHSC_SCSC 0x0010
> +#define CHSC_SDA  0x0031
> +
> +#define CHSC_SCPD_0_M 0x20000000
> +#define CHSC_SCPD_0_C 0x10000000
> +#define CHSC_SCPD_0_FMT 0x0f000000
> +#define CHSC_SCPD_0_CSSID 0x00ff0000
> +#define CHSC_SCPD_0_RFMT 0x00000f00
> +#define CHSC_SCPD_0_RES 0xc000f000
> +#define CHSC_SCPD_1_RES 0xffffff00
> +#define CHSC_SCPD_01_CHPID 0x000000ff
> +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
> +{
> +    uint16_t len = be16_to_cpu(req->len);
> +    uint32_t param0 = be32_to_cpu(req->param0);
> +    uint32_t param1 = be32_to_cpu(req->param1);
> +    uint16_t resp_code;
> +    int rfmt;
> +    uint16_t cssid;
> +    uint8_t f_chpid, l_chpid;
> +    int desc_size;
> +    int m;
> +
> +    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
> +    if ((rfmt == 0) ||  (rfmt == 1)) {
> +        rfmt = !!(param0 & CHSC_SCPD_0_C);
> +    }
> +    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
> +        (param1 & CHSC_SCPD_1_RES) || req->param2) {
> +        resp_code = 0x0003;
> +        goto out_err;
> +    }
> +    if (param0 & CHSC_SCPD_0_FMT) {
> +        resp_code = 0x0007;
> +        goto out_err;
> +    }
> +    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
> +    m = param0 & CHSC_SCPD_0_M;
> +    if (cssid != 0) {
> +        if (!m || !css_present(cssid)) {
> +            resp_code = 0x0008;
> +            goto out_err;
> +        }
> +    }
> +    f_chpid = param0 & CHSC_SCPD_01_CHPID;
> +    l_chpid = param1 & CHSC_SCPD_01_CHPID;
> +    if (l_chpid < f_chpid) {
> +        resp_code = 0x0003;
> +        goto out_err;
> +    }
> +    /* css_collect_chp_desc() is endian-aware */
> +    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
> +                                     &res->data);
> +    res->code = cpu_to_be16(0x0001);
> +    res->len = cpu_to_be16(8 + desc_size);
> +    res->param = cpu_to_be32(rfmt);
> +    return;
> +
> +  out_err:
> +    res->code = cpu_to_be16(resp_code);
> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
> +    res->param = cpu_to_be32(rfmt);
> +}
> +
> +#define CHSC_SCSC_0_M 0x20000000
> +#define CHSC_SCSC_0_FMT 0x000f0000
> +#define CHSC_SCSC_0_CSSID 0x0000ff00
> +#define CHSC_SCSC_0_RES 0xdff000ff
> +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
> +{
> +    uint16_t len = be16_to_cpu(req->len);
> +    uint32_t param0 = be32_to_cpu(req->param0);
> +    uint8_t cssid;
> +    uint16_t resp_code;
> +    uint32_t general_chars[510];
> +    uint32_t chsc_chars[508];
> +
> +    if (len != 0x0010) {
> +        resp_code = 0x0003;
> +        goto out_err;
> +    }
> +
> +    if (param0 & CHSC_SCSC_0_FMT) {
> +        resp_code = 0x0007;
> +        goto out_err;
> +    }
> +    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
> +    if (cssid != 0) {
> +        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
> +            resp_code = 0x0008;
> +            goto out_err;
> +        }
> +    }
> +    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
> +        resp_code = 0x0003;
> +        goto out_err;
> +    }
> +    res->code = cpu_to_be16(0x0001);
> +    res->len = cpu_to_be16(4080);
> +    res->param = 0;
> +
> +    memset(general_chars, 0, sizeof(general_chars));
> +    memset(chsc_chars, 0, sizeof(chsc_chars));
> +
> +    general_chars[0] = cpu_to_be32(0x03000000);
> +    general_chars[1] = cpu_to_be32(0x00059000);
> +
> +    chsc_chars[0] = cpu_to_be32(0x40000000);
> +    chsc_chars[3] = cpu_to_be32(0x00040000);
> +
> +    memcpy(res->data, general_chars, sizeof(general_chars));
> +    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
> +    return;
> +
> +  out_err:
> +    res->code = cpu_to_be16(resp_code);
> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
> +    res->param = 0;
> +}
> +
> +#define CHSC_SDA_0_FMT 0x0f000000
> +#define CHSC_SDA_0_OC 0x0000ffff
> +#define CHSC_SDA_0_RES 0xf0ff0000
> +#define CHSC_SDA_OC_MCSSE 0x0
> +#define CHSC_SDA_OC_MSS 0x2
> +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
> +{
> +    uint16_t resp_code = 0x0001;
> +    uint16_t len = be16_to_cpu(req->len);
> +    uint32_t param0 = be32_to_cpu(req->param0);
> +    uint16_t oc;
> +    int ret;
> +
> +    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
> +        resp_code = 0x0003;
> +        goto out;
> +    }
> +
> +    if (param0 & CHSC_SDA_0_FMT) {
> +        resp_code = 0x0007;
> +        goto out;
> +    }
> +
> +    oc = param0 & CHSC_SDA_0_OC;
> +    switch (oc) {
> +    case CHSC_SDA_OC_MCSSE:
> +        ret = css_enable_mcsse();
> +        if (ret == -EINVAL) {
> +            resp_code = 0x0101;
> +            goto out;
> +        }
> +        break;
> +    case CHSC_SDA_OC_MSS:
> +        ret = css_enable_mss();
> +        if (ret == -EINVAL) {
> +            resp_code = 0x0101;
> +            goto out;
> +        }
> +        break;
> +    default:
> +        resp_code = 0x0003;
> +        goto out;
> +    }
> +
> +out:
> +    res->code = cpu_to_be16(resp_code);
> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
> +    res->param = 0;
> +}
> +
> +static void ioinst_handle_chsc_unimplemented(ChscResp *res)
> +{
> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
> +    res->code = cpu_to_be16(0x0004);
> +    res->param = 0;
> +}
> +
> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
> +{
> +    ChscReq *req;
> +    ChscResp *res;
> +    uint64_t addr;
> +    int reg;
> +    uint16_t len;
> +    uint16_t command;
> +    hwaddr map_size = TARGET_PAGE_SIZE;
> +    int ret = 0;
> +
> +    trace_ioinst("chsc");
> +    reg = (ipb >> 20) & 0x00f;
> +    addr = env->regs[reg];
> +    /* Page boundary? */
> +    if (addr & 0xfff) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        return -EIO;
> +    }
> +    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
> +    if (!req || map_size != TARGET_PAGE_SIZE) {
> +        program_interrupt(env, PGM_SPECIFICATION, 2);
> +        ret = -EIO;
> +        goto out;
> +    }
> +    len = be16_to_cpu(req->len);
> +    /* Length field valid? */
> +    if ((len < 16) || (len > 4088) || (len & 7)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        ret = -EIO;
> +        goto out;
> +    }
> +    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
> +    res = (void *)((char *)req + len);
> +    command = be16_to_cpu(req->command);
> +    trace_ioinst_chsc_cmd(command, len);
> +    switch (command) {
> +    case CHSC_SCSC:
> +        ioinst_handle_chsc_scsc(req, res);
> +        break;
> +    case CHSC_SCPD:
> +        ioinst_handle_chsc_scpd(req, res);
> +        break;
> +    case CHSC_SDA:
> +        ioinst_handle_chsc_sda(req, res);
> +        break;
> +    default:
> +        ioinst_handle_chsc_unimplemented(res);
> +        break;
> +    }
> +
> +out:
> +    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
> +    return ret;
> +}
> +
> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
> +{
> +    uint64_t addr;
> +    int lowcore;
> +
> +    trace_ioinst("tpi");
> +    addr = decode_basedisp_s(env, ipb);
> +    lowcore = addr ? 0 : 1;
> +    if (addr < 8192) {
> +        addr += env->psa;
> +    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
> +        addr -= env->psa;
> +    }

Doesn't this replicate the logic in s390_cpu_physical_memory_map()?
Perhaps the memory API should be used instead to create a new address
space which the devices can use for their memory accesses.

> +    return css_do_tpi(addr, lowcore);
> +}
> +
> +#define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc)
> +#define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28)
> +#define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1)
> +#define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001)
> +
> +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
> +                       uint32_t ipb)
> +{
> +    uint8_t mbk;
> +    int update;
> +    int dct;
> +
> +    trace_ioinst("schm");
> +
> +    if (SCHM_REG1_RES(reg1)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +
> +    mbk = SCHM_REG1_MBK(reg1);
> +    update = SCHM_REG1_UPD(reg1);
> +    dct = SCHM_REG1_DCT(reg1);
> +
> +    if (update && (reg2 & 0x0000000000000fff)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +
> +    css_do_schm(mbk, update, dct, update ? reg2 : 0);
> +
> +    return 0;
> +}
> +
> +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1)
> +{
> +    int cssid, ssid, schid, m;
> +    SubchDev *sch;
> +    int ret = -ENODEV;
> +    int cc;
> +
> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    trace_ioinst_sch_id("rsch", cssid, ssid, schid);
> +    sch = css_find_subch(m, cssid, ssid, schid);
> +    if (sch && css_subch_visible(sch)) {
> +        ret = css_do_rsch(sch);
> +    }
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EINVAL:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        cc = 1;
> +        break;
> +    }
> +
> +    return cc;
> +
> +}
> +
> +#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00)
> +#define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16)
> +#define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff)
> +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1)
> +{
> +    int cc;
> +    uint8_t cssid;
> +    uint8_t chpid;
> +    int ret;
> +
> +    if (RCHP_REG1_RES(reg1)) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +
> +    cssid = RCHP_REG1_CSSID(reg1);
> +    chpid = RCHP_REG1_CHPID(reg1);
> +
> +    trace_ioinst_chp_id("rchp", cssid, chpid);
> +
> +    ret = css_do_rchp(cssid, chpid);
> +
> +    switch (ret) {
> +    case -ENODEV:
> +        cc = 3;
> +        break;
> +    case -EBUSY:
> +        cc = 2;
> +        break;
> +    case 0:
> +        cc = 0;
> +        break;
> +    default:
> +        /* Invalid channel subsystem. */
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +
> +    return cc;
> +}
> +
> +#define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000)
> +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1)
> +{
> +    /* We do not provide address limit checking, so let's suppress it. */
> +    if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
> +        program_interrupt(env, PGM_OPERAND, 2);
> +        return -EIO;
> +    }
> +    return 0;
> +}
> diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
> index 037aabc..a59742c 100644
> --- a/target-s390x/ioinst.h
> +++ b/target-s390x/ioinst.h
> @@ -204,4 +204,20 @@ typedef struct CRW {
>
>  int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>                                   int *schid);
> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1);
> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1);
> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1);
> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb);
> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb);
> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb);
> +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
> +                       uint32_t ipb);
> +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1);
> +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1);
> +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1);
> +
>  #endif
> diff --git a/trace-events b/trace-events
> index 09091e6..1b54542 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -1060,3 +1060,9 @@ xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
>  xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
>  xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]"
>  xics_ics_eoi(int nr) "ics_eoi: irq %#x"
> +
> +# target-s390x/ioinst.c
> +ioinst(const char *insn) "IOINST: %s"
> +ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)"
> +ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)"
> +ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command %04x, len %04x"
> --
> 1.6.0.2
>
Alexander Graf - Jan. 25, 2013, 7:28 p.m.
On 25.01.2013, at 20:20, Blue Swirl wrote:

> On Fri, Jan 25, 2013 at 12:48 PM, Alexander Graf <agraf@suse.de> wrote:
>> From: Cornelia Huck <cornelia.huck@de.ibm.com>
>> 
>> Provide handlers for (most) channel I/O instructions.
>> 
>> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>> ---
>> target-s390x/cpu.h    |  100 +++++++
>> target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
>> target-s390x/ioinst.h |   16 ++
>> trace-events          |    6 +
>> 4 files changed, 838 insertions(+), 0 deletions(-)
>> 
>> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
>> index 3e00d38..76a822c 100644
>> --- a/target-s390x/cpu.h
>> +++ b/target-s390x/cpu.h
>> @@ -147,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
>> }
>> #endif
>> 
>> +/* distinguish between 24 bit and 31 bit addressing */
>> +#define HIGH_ORDER_BIT 0x80000000
>> +
>> /* Interrupt Codes */
>> /* Program Interrupts */
>> #define PGM_OPERATION                   0x0001
>> @@ -331,6 +334,20 @@ void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
>>                                    int is_write);
>> void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
>>                                     int is_write);
>> +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
>> +{
>> +    hwaddr addr = 0;
>> +    uint8_t reg;
>> +
>> +    reg = ipb >> 28;
>> +    if (reg > 0) {
>> +        addr = env->regs[reg];
>> +    }
>> +    addr += (ipb >> 16) & 0xfff;
>> +
>> +    return addr;
>> +}
>> +
>> void s390x_tod_timer(void *opaque);
>> void s390x_cpu_timer(void *opaque);
>> 
>> @@ -380,6 +397,89 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>> void cpu_lock(void);
>> void cpu_unlock(void);
>> 
>> +typedef struct SubchDev SubchDev;
>> +
>> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
>> +                                       uint16_t schid)
>> +{
>> +    return NULL;
>> +}
>> +static inline bool css_subch_visible(SubchDev *sch)
>> +{
>> +    return false;
>> +}
>> +static inline void css_conditional_io_interrupt(SubchDev *sch)
>> +{
>> +}
>> +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
>> +{
>> +    return true;
>> +}
>> +static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_xsch(SubchDev *sch)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_csch(SubchDev *sch)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_hsch(SubchDev *sch)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_ssch(SubchDev *sch, ORB *orb)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_tsch(SubchDev *sch, IRB *irb)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_stcrw(CRW *crw)
>> +{
>> +    return 1;
>> +}
>> +static inline int css_do_tpi(uint64_t addr, int lowcore)
>> +{
>> +    return 0;
>> +}
>> +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
>> +                                       int rfmt, uint8_t l_chpid, void *buf)
>> +{
>> +    return 0;
>> +}
>> +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
>> +{
>> +}
>> +static inline int css_enable_mss(void)
>> +{
>> +    return -EINVAL;
>> +}
>> +static inline int css_enable_mcsse(void)
>> +{
>> +    return -EINVAL;
>> +}
>> +static inline int css_do_rsch(SubchDev *sch)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
>> +{
>> +    return -ENODEV;
>> +}
>> +static inline bool css_present(uint8_t cssid)
>> +{
>> +    return false;
>> +}
>> +
>> static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>> {
>>     env->aregs[0] = newtls >> 32;
>> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
>> index 06a16ee..4ef2d73 100644
>> --- a/target-s390x/ioinst.c
>> +++ b/target-s390x/ioinst.c
>> @@ -13,6 +13,7 @@
>> 
>> #include "cpu.h"
>> #include "ioinst.h"
>> +#include "trace.h"
>> 
>> int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>                                  int *schid)
>> @@ -34,3 +35,718 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>     *schid = IOINST_SCHID_NR(value);
>>     return 0;
>> }
>> +
>> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_xsch(sch);
>> +    }
>> +    switch (ret) {
>> +    case -ENODEV:
>> +        cc = 3;
>> +        break;
>> +    case -EBUSY:
>> +        cc = 2;
>> +        break;
>> +    case 0:
>> +        cc = 0;
>> +        break;
>> +    default:
>> +        cc = 1;
>> +        break;
>> +    }
>> +
>> +    return cc;
>> +}
>> +
>> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("csch", cssid, ssid, schid);
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_csch(sch);
>> +    }
>> +    if (ret == -ENODEV) {
>> +        cc = 3;
>> +    } else {
>> +        cc = 0;
>> +    }
>> +    return cc;
>> +}
>> +
>> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_hsch(sch);
>> +    }
>> +    switch (ret) {
>> +    case -ENODEV:
>> +        cc = 3;
>> +        break;
>> +    case -EBUSY:
>> +        cc = 2;
>> +        break;
>> +    case 0:
>> +        cc = 0;
>> +        break;
>> +    default:
>> +        cc = 1;
>> +        break;
>> +    }
>> +
>> +    return cc;
>> +}
>> +
>> +static int ioinst_schib_valid(SCHIB *schib)
>> +{
>> +    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
>> +        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
>> +        return 0;
>> +    }
>> +    /* Disallow extended measurements for now. */
>> +    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
>> +        return 0;
>> +    }
>> +    return 1;
>> +}
>> +
>> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    SCHIB *schib;
>> +    uint64_t addr;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +    hwaddr len = sizeof(*schib);
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("msch", cssid, ssid, schid);
>> +    addr = decode_basedisp_s(env, ipb);
>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
>> +    if (!schib || len != sizeof(*schib)) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    if (!ioinst_schib_valid(schib)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_msch(sch, schib);
>> +    }
>> +    switch (ret) {
>> +    case -ENODEV:
>> +        cc = 3;
>> +        break;
>> +    case -EBUSY:
>> +        cc = 2;
>> +        break;
>> +    case 0:
>> +        cc = 0;
>> +        break;
>> +    default:
>> +        cc = 1;
>> +        break;
>> +    }
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, schib, len, 0);
>> +    return cc;
>> +}
>> +
>> +static void copy_orb_from_guest(ORB *dest, const ORB *src)
>> +{
>> +    dest->intparm = be32_to_cpu(src->intparm);
>> +    dest->ctrl0 = be16_to_cpu(src->ctrl0);
>> +    dest->lpm = src->lpm;
>> +    dest->ctrl1 = src->ctrl1;
>> +    dest->cpa = be32_to_cpu(src->cpa);
>> +}
>> +
>> +static int ioinst_orb_valid(ORB *orb)
>> +{
>> +    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
>> +        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
>> +        return 0;
>> +    }
>> +    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
>> +        return 0;
>> +    }
>> +    return 1;
>> +}
>> +
>> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    ORB *orig_orb, orb;
>> +    uint64_t addr;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +    hwaddr len = sizeof(*orig_orb);
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
>> +    addr = decode_basedisp_s(env, ipb);
>> +    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
>> +    if (!orig_orb || len != sizeof(*orig_orb)) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    copy_orb_from_guest(&orb, orig_orb);
>> +    if (!ioinst_orb_valid(&orb)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_ssch(sch, &orb);
>> +    }
>> +    switch (ret) {
>> +    case -ENODEV:
>> +        cc = 3;
>> +        break;
>> +    case -EBUSY:
>> +        cc = 2;
>> +        break;
>> +    case 0:
>> +        cc = 0;
>> +        break;
>> +    default:
>> +        cc = 1;
>> +        break;
>> +    }
>> +
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
>> +    return cc;
>> +}
>> +
>> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
>> +{
>> +    CRW *crw;
>> +    uint64_t addr;
>> +    int cc;
>> +    hwaddr len = sizeof(*crw);
>> +
>> +    addr = decode_basedisp_s(env, ipb);
>> +    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
>> +    if (!crw || len != sizeof(*crw)) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    cc = css_do_stcrw(crw);
>> +    /* 0 - crw stored, 1 - zeroes stored */
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, crw, len, 1);
>> +    return cc;
>> +}
>> +
>> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    uint64_t addr;
>> +    int cc;
>> +    SCHIB *schib;
>> +    hwaddr len = sizeof(*schib);
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
>> +    addr = decode_basedisp_s(env, ipb);
>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
>> +    if (!schib || len != sizeof(*schib)) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch) {
>> +        if (css_subch_visible(sch)) {
>> +            css_do_stsch(sch, schib);
>> +            cc = 0;
>> +        } else {
>> +            /* Indicate no more subchannels in this css/ss */
>> +            cc = 3;
>> +        }
>> +    } else {
>> +        if (css_schid_final(cssid, ssid, schid)) {
>> +            cc = 3; /* No more subchannels in this css/ss */
>> +        } else {
>> +            /* Store an empty schib. */
>> +            memset(schib, 0, sizeof(*schib));
>> +            cc = 0;
>> +        }
>> +    }
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, schib, len, 1);
>> +    return cc;
>> +}
>> +
>> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>> +{
>> +    int cssid, ssid, schid, m;
>> +    SubchDev *sch;
>> +    IRB *irb;
>> +    uint64_t addr;
>> +    int ret = -ENODEV;
>> +    int cc;
>> +    hwaddr len = sizeof(*irb);
>> +
>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        return -EIO;
>> +    }
>> +    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
>> +    addr = decode_basedisp_s(env, ipb);
>> +    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
>> +    if (!irb || len != sizeof(*irb)) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        cc = -EIO;
>> +        goto out;
>> +    }
>> +    sch = css_find_subch(m, cssid, ssid, schid);
>> +    if (sch && css_subch_visible(sch)) {
>> +        ret = css_do_tsch(sch, irb);
>> +        /* 0 - status pending, 1 - not status pending */
>> +        cc = ret;
>> +    } else {
>> +        cc = 3;
>> +    }
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
>> +    return cc;
>> +}
>> +
>> +typedef struct ChscReq {
>> +    uint16_t len;
>> +    uint16_t command;
>> +    uint32_t param0;
>> +    uint32_t param1;
>> +    uint32_t param2;
>> +} QEMU_PACKED ChscReq;
>> +
>> +typedef struct ChscResp {
>> +    uint16_t len;
>> +    uint16_t code;
>> +    uint32_t param;
>> +    char data[0];
>> +} QEMU_PACKED ChscResp;
>> +
>> +#define CHSC_MIN_RESP_LEN 0x0008
>> +
>> +#define CHSC_SCPD 0x0002
>> +#define CHSC_SCSC 0x0010
>> +#define CHSC_SDA  0x0031
>> +
>> +#define CHSC_SCPD_0_M 0x20000000
>> +#define CHSC_SCPD_0_C 0x10000000
>> +#define CHSC_SCPD_0_FMT 0x0f000000
>> +#define CHSC_SCPD_0_CSSID 0x00ff0000
>> +#define CHSC_SCPD_0_RFMT 0x00000f00
>> +#define CHSC_SCPD_0_RES 0xc000f000
>> +#define CHSC_SCPD_1_RES 0xffffff00
>> +#define CHSC_SCPD_01_CHPID 0x000000ff
>> +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
>> +{
>> +    uint16_t len = be16_to_cpu(req->len);
>> +    uint32_t param0 = be32_to_cpu(req->param0);
>> +    uint32_t param1 = be32_to_cpu(req->param1);
>> +    uint16_t resp_code;
>> +    int rfmt;
>> +    uint16_t cssid;
>> +    uint8_t f_chpid, l_chpid;
>> +    int desc_size;
>> +    int m;
>> +
>> +    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
>> +    if ((rfmt == 0) ||  (rfmt == 1)) {
>> +        rfmt = !!(param0 & CHSC_SCPD_0_C);
>> +    }
>> +    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
>> +        (param1 & CHSC_SCPD_1_RES) || req->param2) {
>> +        resp_code = 0x0003;
>> +        goto out_err;
>> +    }
>> +    if (param0 & CHSC_SCPD_0_FMT) {
>> +        resp_code = 0x0007;
>> +        goto out_err;
>> +    }
>> +    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
>> +    m = param0 & CHSC_SCPD_0_M;
>> +    if (cssid != 0) {
>> +        if (!m || !css_present(cssid)) {
>> +            resp_code = 0x0008;
>> +            goto out_err;
>> +        }
>> +    }
>> +    f_chpid = param0 & CHSC_SCPD_01_CHPID;
>> +    l_chpid = param1 & CHSC_SCPD_01_CHPID;
>> +    if (l_chpid < f_chpid) {
>> +        resp_code = 0x0003;
>> +        goto out_err;
>> +    }
>> +    /* css_collect_chp_desc() is endian-aware */
>> +    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
>> +                                     &res->data);
>> +    res->code = cpu_to_be16(0x0001);
>> +    res->len = cpu_to_be16(8 + desc_size);
>> +    res->param = cpu_to_be32(rfmt);
>> +    return;
>> +
>> +  out_err:
>> +    res->code = cpu_to_be16(resp_code);
>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>> +    res->param = cpu_to_be32(rfmt);
>> +}
>> +
>> +#define CHSC_SCSC_0_M 0x20000000
>> +#define CHSC_SCSC_0_FMT 0x000f0000
>> +#define CHSC_SCSC_0_CSSID 0x0000ff00
>> +#define CHSC_SCSC_0_RES 0xdff000ff
>> +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
>> +{
>> +    uint16_t len = be16_to_cpu(req->len);
>> +    uint32_t param0 = be32_to_cpu(req->param0);
>> +    uint8_t cssid;
>> +    uint16_t resp_code;
>> +    uint32_t general_chars[510];
>> +    uint32_t chsc_chars[508];
>> +
>> +    if (len != 0x0010) {
>> +        resp_code = 0x0003;
>> +        goto out_err;
>> +    }
>> +
>> +    if (param0 & CHSC_SCSC_0_FMT) {
>> +        resp_code = 0x0007;
>> +        goto out_err;
>> +    }
>> +    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
>> +    if (cssid != 0) {
>> +        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
>> +            resp_code = 0x0008;
>> +            goto out_err;
>> +        }
>> +    }
>> +    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
>> +        resp_code = 0x0003;
>> +        goto out_err;
>> +    }
>> +    res->code = cpu_to_be16(0x0001);
>> +    res->len = cpu_to_be16(4080);
>> +    res->param = 0;
>> +
>> +    memset(general_chars, 0, sizeof(general_chars));
>> +    memset(chsc_chars, 0, sizeof(chsc_chars));
>> +
>> +    general_chars[0] = cpu_to_be32(0x03000000);
>> +    general_chars[1] = cpu_to_be32(0x00059000);
>> +
>> +    chsc_chars[0] = cpu_to_be32(0x40000000);
>> +    chsc_chars[3] = cpu_to_be32(0x00040000);
>> +
>> +    memcpy(res->data, general_chars, sizeof(general_chars));
>> +    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
>> +    return;
>> +
>> +  out_err:
>> +    res->code = cpu_to_be16(resp_code);
>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>> +    res->param = 0;
>> +}
>> +
>> +#define CHSC_SDA_0_FMT 0x0f000000
>> +#define CHSC_SDA_0_OC 0x0000ffff
>> +#define CHSC_SDA_0_RES 0xf0ff0000
>> +#define CHSC_SDA_OC_MCSSE 0x0
>> +#define CHSC_SDA_OC_MSS 0x2
>> +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
>> +{
>> +    uint16_t resp_code = 0x0001;
>> +    uint16_t len = be16_to_cpu(req->len);
>> +    uint32_t param0 = be32_to_cpu(req->param0);
>> +    uint16_t oc;
>> +    int ret;
>> +
>> +    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
>> +        resp_code = 0x0003;
>> +        goto out;
>> +    }
>> +
>> +    if (param0 & CHSC_SDA_0_FMT) {
>> +        resp_code = 0x0007;
>> +        goto out;
>> +    }
>> +
>> +    oc = param0 & CHSC_SDA_0_OC;
>> +    switch (oc) {
>> +    case CHSC_SDA_OC_MCSSE:
>> +        ret = css_enable_mcsse();
>> +        if (ret == -EINVAL) {
>> +            resp_code = 0x0101;
>> +            goto out;
>> +        }
>> +        break;
>> +    case CHSC_SDA_OC_MSS:
>> +        ret = css_enable_mss();
>> +        if (ret == -EINVAL) {
>> +            resp_code = 0x0101;
>> +            goto out;
>> +        }
>> +        break;
>> +    default:
>> +        resp_code = 0x0003;
>> +        goto out;
>> +    }
>> +
>> +out:
>> +    res->code = cpu_to_be16(resp_code);
>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>> +    res->param = 0;
>> +}
>> +
>> +static void ioinst_handle_chsc_unimplemented(ChscResp *res)
>> +{
>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>> +    res->code = cpu_to_be16(0x0004);
>> +    res->param = 0;
>> +}
>> +
>> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
>> +{
>> +    ChscReq *req;
>> +    ChscResp *res;
>> +    uint64_t addr;
>> +    int reg;
>> +    uint16_t len;
>> +    uint16_t command;
>> +    hwaddr map_size = TARGET_PAGE_SIZE;
>> +    int ret = 0;
>> +
>> +    trace_ioinst("chsc");
>> +    reg = (ipb >> 20) & 0x00f;
>> +    addr = env->regs[reg];
>> +    /* Page boundary? */
>> +    if (addr & 0xfff) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        return -EIO;
>> +    }
>> +    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
>> +    if (!req || map_size != TARGET_PAGE_SIZE) {
>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>> +        ret = -EIO;
>> +        goto out;
>> +    }
>> +    len = be16_to_cpu(req->len);
>> +    /* Length field valid? */
>> +    if ((len < 16) || (len > 4088) || (len & 7)) {
>> +        program_interrupt(env, PGM_OPERAND, 2);
>> +        ret = -EIO;
>> +        goto out;
>> +    }
>> +    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
>> +    res = (void *)((char *)req + len);
>> +    command = be16_to_cpu(req->command);
>> +    trace_ioinst_chsc_cmd(command, len);
>> +    switch (command) {
>> +    case CHSC_SCSC:
>> +        ioinst_handle_chsc_scsc(req, res);
>> +        break;
>> +    case CHSC_SCPD:
>> +        ioinst_handle_chsc_scpd(req, res);
>> +        break;
>> +    case CHSC_SDA:
>> +        ioinst_handle_chsc_sda(req, res);
>> +        break;
>> +    default:
>> +        ioinst_handle_chsc_unimplemented(res);
>> +        break;
>> +    }
>> +
>> +out:
>> +    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
>> +    return ret;
>> +}
>> +
>> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
>> +{
>> +    uint64_t addr;
>> +    int lowcore;
>> +
>> +    trace_ioinst("tpi");
>> +    addr = decode_basedisp_s(env, ipb);
>> +    lowcore = addr ? 0 : 1;
>> +    if (addr < 8192) {
>> +        addr += env->psa;
>> +    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
>> +        addr -= env->psa;
>> +    }
> 
> Doesn't this replicate the logic in s390_cpu_physical_memory_map()?
> Perhaps the memory API should be used instead to create a new address
> space which the devices can use for their memory accesses.

The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.

However, I do agree that this duplicates logic. Cornelia, mind to instead call our map helper in css_do_tpi?


Alex
Blue Swirl - Jan. 25, 2013, 7:53 p.m.
On Fri, Jan 25, 2013 at 7:28 PM, Alexander Graf <agraf@suse.de> wrote:
>
> On 25.01.2013, at 20:20, Blue Swirl wrote:
>
>> On Fri, Jan 25, 2013 at 12:48 PM, Alexander Graf <agraf@suse.de> wrote:
>>> From: Cornelia Huck <cornelia.huck@de.ibm.com>
>>>
>>> Provide handlers for (most) channel I/O instructions.
>>>
>>> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>> ---
>>> target-s390x/cpu.h    |  100 +++++++
>>> target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
>>> target-s390x/ioinst.h |   16 ++
>>> trace-events          |    6 +
>>> 4 files changed, 838 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
>>> index 3e00d38..76a822c 100644
>>> --- a/target-s390x/cpu.h
>>> +++ b/target-s390x/cpu.h
>>> @@ -147,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
>>> }
>>> #endif
>>>
>>> +/* distinguish between 24 bit and 31 bit addressing */
>>> +#define HIGH_ORDER_BIT 0x80000000
>>> +
>>> /* Interrupt Codes */
>>> /* Program Interrupts */
>>> #define PGM_OPERATION                   0x0001
>>> @@ -331,6 +334,20 @@ void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
>>>                                    int is_write);
>>> void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
>>>                                     int is_write);
>>> +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
>>> +{
>>> +    hwaddr addr = 0;
>>> +    uint8_t reg;
>>> +
>>> +    reg = ipb >> 28;
>>> +    if (reg > 0) {
>>> +        addr = env->regs[reg];
>>> +    }
>>> +    addr += (ipb >> 16) & 0xfff;
>>> +
>>> +    return addr;
>>> +}
>>> +
>>> void s390x_tod_timer(void *opaque);
>>> void s390x_cpu_timer(void *opaque);
>>>
>>> @@ -380,6 +397,89 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>>> void cpu_lock(void);
>>> void cpu_unlock(void);
>>>
>>> +typedef struct SubchDev SubchDev;
>>> +
>>> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
>>> +                                       uint16_t schid)
>>> +{
>>> +    return NULL;
>>> +}
>>> +static inline bool css_subch_visible(SubchDev *sch)
>>> +{
>>> +    return false;
>>> +}
>>> +static inline void css_conditional_io_interrupt(SubchDev *sch)
>>> +{
>>> +}
>>> +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
>>> +{
>>> +    return true;
>>> +}
>>> +static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_xsch(SubchDev *sch)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_csch(SubchDev *sch)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_hsch(SubchDev *sch)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_ssch(SubchDev *sch, ORB *orb)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_tsch(SubchDev *sch, IRB *irb)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_stcrw(CRW *crw)
>>> +{
>>> +    return 1;
>>> +}
>>> +static inline int css_do_tpi(uint64_t addr, int lowcore)
>>> +{
>>> +    return 0;
>>> +}
>>> +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
>>> +                                       int rfmt, uint8_t l_chpid, void *buf)
>>> +{
>>> +    return 0;
>>> +}
>>> +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
>>> +{
>>> +}
>>> +static inline int css_enable_mss(void)
>>> +{
>>> +    return -EINVAL;
>>> +}
>>> +static inline int css_enable_mcsse(void)
>>> +{
>>> +    return -EINVAL;
>>> +}
>>> +static inline int css_do_rsch(SubchDev *sch)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
>>> +{
>>> +    return -ENODEV;
>>> +}
>>> +static inline bool css_present(uint8_t cssid)
>>> +{
>>> +    return false;
>>> +}
>>> +
>>> static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>>> {
>>>     env->aregs[0] = newtls >> 32;
>>> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
>>> index 06a16ee..4ef2d73 100644
>>> --- a/target-s390x/ioinst.c
>>> +++ b/target-s390x/ioinst.c
>>> @@ -13,6 +13,7 @@
>>>
>>> #include "cpu.h"
>>> #include "ioinst.h"
>>> +#include "trace.h"
>>>
>>> int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>                                  int *schid)
>>> @@ -34,3 +35,718 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>     *schid = IOINST_SCHID_NR(value);
>>>     return 0;
>>> }
>>> +
>>> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_xsch(sch);
>>> +    }
>>> +    switch (ret) {
>>> +    case -ENODEV:
>>> +        cc = 3;
>>> +        break;
>>> +    case -EBUSY:
>>> +        cc = 2;
>>> +        break;
>>> +    case 0:
>>> +        cc = 0;
>>> +        break;
>>> +    default:
>>> +        cc = 1;
>>> +        break;
>>> +    }
>>> +
>>> +    return cc;
>>> +}
>>> +
>>> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("csch", cssid, ssid, schid);
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_csch(sch);
>>> +    }
>>> +    if (ret == -ENODEV) {
>>> +        cc = 3;
>>> +    } else {
>>> +        cc = 0;
>>> +    }
>>> +    return cc;
>>> +}
>>> +
>>> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_hsch(sch);
>>> +    }
>>> +    switch (ret) {
>>> +    case -ENODEV:
>>> +        cc = 3;
>>> +        break;
>>> +    case -EBUSY:
>>> +        cc = 2;
>>> +        break;
>>> +    case 0:
>>> +        cc = 0;
>>> +        break;
>>> +    default:
>>> +        cc = 1;
>>> +        break;
>>> +    }
>>> +
>>> +    return cc;
>>> +}
>>> +
>>> +static int ioinst_schib_valid(SCHIB *schib)
>>> +{
>>> +    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
>>> +        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
>>> +        return 0;
>>> +    }
>>> +    /* Disallow extended measurements for now. */
>>> +    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
>>> +        return 0;
>>> +    }
>>> +    return 1;
>>> +}
>>> +
>>> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    SCHIB *schib;
>>> +    uint64_t addr;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +    hwaddr len = sizeof(*schib);
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("msch", cssid, ssid, schid);
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>> +    if (!schib || len != sizeof(*schib)) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    if (!ioinst_schib_valid(schib)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_msch(sch, schib);
>>> +    }
>>> +    switch (ret) {
>>> +    case -ENODEV:
>>> +        cc = 3;
>>> +        break;
>>> +    case -EBUSY:
>>> +        cc = 2;
>>> +        break;
>>> +    case 0:
>>> +        cc = 0;
>>> +        break;
>>> +    default:
>>> +        cc = 1;
>>> +        break;
>>> +    }
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 0);
>>> +    return cc;
>>> +}
>>> +
>>> +static void copy_orb_from_guest(ORB *dest, const ORB *src)
>>> +{
>>> +    dest->intparm = be32_to_cpu(src->intparm);
>>> +    dest->ctrl0 = be16_to_cpu(src->ctrl0);
>>> +    dest->lpm = src->lpm;
>>> +    dest->ctrl1 = src->ctrl1;
>>> +    dest->cpa = be32_to_cpu(src->cpa);
>>> +}
>>> +
>>> +static int ioinst_orb_valid(ORB *orb)
>>> +{
>>> +    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
>>> +        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
>>> +        return 0;
>>> +    }
>>> +    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
>>> +        return 0;
>>> +    }
>>> +    return 1;
>>> +}
>>> +
>>> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    ORB *orig_orb, orb;
>>> +    uint64_t addr;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +    hwaddr len = sizeof(*orig_orb);
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>> +    if (!orig_orb || len != sizeof(*orig_orb)) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    copy_orb_from_guest(&orb, orig_orb);
>>> +    if (!ioinst_orb_valid(&orb)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_ssch(sch, &orb);
>>> +    }
>>> +    switch (ret) {
>>> +    case -ENODEV:
>>> +        cc = 3;
>>> +        break;
>>> +    case -EBUSY:
>>> +        cc = 2;
>>> +        break;
>>> +    case 0:
>>> +        cc = 0;
>>> +        break;
>>> +    default:
>>> +        cc = 1;
>>> +        break;
>>> +    }
>>> +
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
>>> +    return cc;
>>> +}
>>> +
>>> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
>>> +{
>>> +    CRW *crw;
>>> +    uint64_t addr;
>>> +    int cc;
>>> +    hwaddr len = sizeof(*crw);
>>> +
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>> +    if (!crw || len != sizeof(*crw)) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    cc = css_do_stcrw(crw);
>>> +    /* 0 - crw stored, 1 - zeroes stored */
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, crw, len, 1);
>>> +    return cc;
>>> +}
>>> +
>>> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    uint64_t addr;
>>> +    int cc;
>>> +    SCHIB *schib;
>>> +    hwaddr len = sizeof(*schib);
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>> +    if (!schib || len != sizeof(*schib)) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch) {
>>> +        if (css_subch_visible(sch)) {
>>> +            css_do_stsch(sch, schib);
>>> +            cc = 0;
>>> +        } else {
>>> +            /* Indicate no more subchannels in this css/ss */
>>> +            cc = 3;
>>> +        }
>>> +    } else {
>>> +        if (css_schid_final(cssid, ssid, schid)) {
>>> +            cc = 3; /* No more subchannels in this css/ss */
>>> +        } else {
>>> +            /* Store an empty schib. */
>>> +            memset(schib, 0, sizeof(*schib));
>>> +            cc = 0;
>>> +        }
>>> +    }
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 1);
>>> +    return cc;
>>> +}
>>> +
>>> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>> +{
>>> +    int cssid, ssid, schid, m;
>>> +    SubchDev *sch;
>>> +    IRB *irb;
>>> +    uint64_t addr;
>>> +    int ret = -ENODEV;
>>> +    int cc;
>>> +    hwaddr len = sizeof(*irb);
>>> +
>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        return -EIO;
>>> +    }
>>> +    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>> +    if (!irb || len != sizeof(*irb)) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        cc = -EIO;
>>> +        goto out;
>>> +    }
>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>> +    if (sch && css_subch_visible(sch)) {
>>> +        ret = css_do_tsch(sch, irb);
>>> +        /* 0 - status pending, 1 - not status pending */
>>> +        cc = ret;
>>> +    } else {
>>> +        cc = 3;
>>> +    }
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
>>> +    return cc;
>>> +}
>>> +
>>> +typedef struct ChscReq {
>>> +    uint16_t len;
>>> +    uint16_t command;
>>> +    uint32_t param0;
>>> +    uint32_t param1;
>>> +    uint32_t param2;
>>> +} QEMU_PACKED ChscReq;
>>> +
>>> +typedef struct ChscResp {
>>> +    uint16_t len;
>>> +    uint16_t code;
>>> +    uint32_t param;
>>> +    char data[0];
>>> +} QEMU_PACKED ChscResp;
>>> +
>>> +#define CHSC_MIN_RESP_LEN 0x0008
>>> +
>>> +#define CHSC_SCPD 0x0002
>>> +#define CHSC_SCSC 0x0010
>>> +#define CHSC_SDA  0x0031
>>> +
>>> +#define CHSC_SCPD_0_M 0x20000000
>>> +#define CHSC_SCPD_0_C 0x10000000
>>> +#define CHSC_SCPD_0_FMT 0x0f000000
>>> +#define CHSC_SCPD_0_CSSID 0x00ff0000
>>> +#define CHSC_SCPD_0_RFMT 0x00000f00
>>> +#define CHSC_SCPD_0_RES 0xc000f000
>>> +#define CHSC_SCPD_1_RES 0xffffff00
>>> +#define CHSC_SCPD_01_CHPID 0x000000ff
>>> +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
>>> +{
>>> +    uint16_t len = be16_to_cpu(req->len);
>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>> +    uint32_t param1 = be32_to_cpu(req->param1);
>>> +    uint16_t resp_code;
>>> +    int rfmt;
>>> +    uint16_t cssid;
>>> +    uint8_t f_chpid, l_chpid;
>>> +    int desc_size;
>>> +    int m;
>>> +
>>> +    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
>>> +    if ((rfmt == 0) ||  (rfmt == 1)) {
>>> +        rfmt = !!(param0 & CHSC_SCPD_0_C);
>>> +    }
>>> +    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
>>> +        (param1 & CHSC_SCPD_1_RES) || req->param2) {
>>> +        resp_code = 0x0003;
>>> +        goto out_err;
>>> +    }
>>> +    if (param0 & CHSC_SCPD_0_FMT) {
>>> +        resp_code = 0x0007;
>>> +        goto out_err;
>>> +    }
>>> +    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
>>> +    m = param0 & CHSC_SCPD_0_M;
>>> +    if (cssid != 0) {
>>> +        if (!m || !css_present(cssid)) {
>>> +            resp_code = 0x0008;
>>> +            goto out_err;
>>> +        }
>>> +    }
>>> +    f_chpid = param0 & CHSC_SCPD_01_CHPID;
>>> +    l_chpid = param1 & CHSC_SCPD_01_CHPID;
>>> +    if (l_chpid < f_chpid) {
>>> +        resp_code = 0x0003;
>>> +        goto out_err;
>>> +    }
>>> +    /* css_collect_chp_desc() is endian-aware */
>>> +    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
>>> +                                     &res->data);
>>> +    res->code = cpu_to_be16(0x0001);
>>> +    res->len = cpu_to_be16(8 + desc_size);
>>> +    res->param = cpu_to_be32(rfmt);
>>> +    return;
>>> +
>>> +  out_err:
>>> +    res->code = cpu_to_be16(resp_code);
>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>> +    res->param = cpu_to_be32(rfmt);
>>> +}
>>> +
>>> +#define CHSC_SCSC_0_M 0x20000000
>>> +#define CHSC_SCSC_0_FMT 0x000f0000
>>> +#define CHSC_SCSC_0_CSSID 0x0000ff00
>>> +#define CHSC_SCSC_0_RES 0xdff000ff
>>> +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
>>> +{
>>> +    uint16_t len = be16_to_cpu(req->len);
>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>> +    uint8_t cssid;
>>> +    uint16_t resp_code;
>>> +    uint32_t general_chars[510];
>>> +    uint32_t chsc_chars[508];
>>> +
>>> +    if (len != 0x0010) {
>>> +        resp_code = 0x0003;
>>> +        goto out_err;
>>> +    }
>>> +
>>> +    if (param0 & CHSC_SCSC_0_FMT) {
>>> +        resp_code = 0x0007;
>>> +        goto out_err;
>>> +    }
>>> +    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
>>> +    if (cssid != 0) {
>>> +        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
>>> +            resp_code = 0x0008;
>>> +            goto out_err;
>>> +        }
>>> +    }
>>> +    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
>>> +        resp_code = 0x0003;
>>> +        goto out_err;
>>> +    }
>>> +    res->code = cpu_to_be16(0x0001);
>>> +    res->len = cpu_to_be16(4080);
>>> +    res->param = 0;
>>> +
>>> +    memset(general_chars, 0, sizeof(general_chars));
>>> +    memset(chsc_chars, 0, sizeof(chsc_chars));
>>> +
>>> +    general_chars[0] = cpu_to_be32(0x03000000);
>>> +    general_chars[1] = cpu_to_be32(0x00059000);
>>> +
>>> +    chsc_chars[0] = cpu_to_be32(0x40000000);
>>> +    chsc_chars[3] = cpu_to_be32(0x00040000);
>>> +
>>> +    memcpy(res->data, general_chars, sizeof(general_chars));
>>> +    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
>>> +    return;
>>> +
>>> +  out_err:
>>> +    res->code = cpu_to_be16(resp_code);
>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>> +    res->param = 0;
>>> +}
>>> +
>>> +#define CHSC_SDA_0_FMT 0x0f000000
>>> +#define CHSC_SDA_0_OC 0x0000ffff
>>> +#define CHSC_SDA_0_RES 0xf0ff0000
>>> +#define CHSC_SDA_OC_MCSSE 0x0
>>> +#define CHSC_SDA_OC_MSS 0x2
>>> +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
>>> +{
>>> +    uint16_t resp_code = 0x0001;
>>> +    uint16_t len = be16_to_cpu(req->len);
>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>> +    uint16_t oc;
>>> +    int ret;
>>> +
>>> +    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
>>> +        resp_code = 0x0003;
>>> +        goto out;
>>> +    }
>>> +
>>> +    if (param0 & CHSC_SDA_0_FMT) {
>>> +        resp_code = 0x0007;
>>> +        goto out;
>>> +    }
>>> +
>>> +    oc = param0 & CHSC_SDA_0_OC;
>>> +    switch (oc) {
>>> +    case CHSC_SDA_OC_MCSSE:
>>> +        ret = css_enable_mcsse();
>>> +        if (ret == -EINVAL) {
>>> +            resp_code = 0x0101;
>>> +            goto out;
>>> +        }
>>> +        break;
>>> +    case CHSC_SDA_OC_MSS:
>>> +        ret = css_enable_mss();
>>> +        if (ret == -EINVAL) {
>>> +            resp_code = 0x0101;
>>> +            goto out;
>>> +        }
>>> +        break;
>>> +    default:
>>> +        resp_code = 0x0003;
>>> +        goto out;
>>> +    }
>>> +
>>> +out:
>>> +    res->code = cpu_to_be16(resp_code);
>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>> +    res->param = 0;
>>> +}
>>> +
>>> +static void ioinst_handle_chsc_unimplemented(ChscResp *res)
>>> +{
>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>> +    res->code = cpu_to_be16(0x0004);
>>> +    res->param = 0;
>>> +}
>>> +
>>> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
>>> +{
>>> +    ChscReq *req;
>>> +    ChscResp *res;
>>> +    uint64_t addr;
>>> +    int reg;
>>> +    uint16_t len;
>>> +    uint16_t command;
>>> +    hwaddr map_size = TARGET_PAGE_SIZE;
>>> +    int ret = 0;
>>> +
>>> +    trace_ioinst("chsc");
>>> +    reg = (ipb >> 20) & 0x00f;
>>> +    addr = env->regs[reg];
>>> +    /* Page boundary? */
>>> +    if (addr & 0xfff) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        return -EIO;
>>> +    }
>>> +    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
>>> +    if (!req || map_size != TARGET_PAGE_SIZE) {
>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>> +        ret = -EIO;
>>> +        goto out;
>>> +    }
>>> +    len = be16_to_cpu(req->len);
>>> +    /* Length field valid? */
>>> +    if ((len < 16) || (len > 4088) || (len & 7)) {
>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>> +        ret = -EIO;
>>> +        goto out;
>>> +    }
>>> +    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
>>> +    res = (void *)((char *)req + len);
>>> +    command = be16_to_cpu(req->command);
>>> +    trace_ioinst_chsc_cmd(command, len);
>>> +    switch (command) {
>>> +    case CHSC_SCSC:
>>> +        ioinst_handle_chsc_scsc(req, res);
>>> +        break;
>>> +    case CHSC_SCPD:
>>> +        ioinst_handle_chsc_scpd(req, res);
>>> +        break;
>>> +    case CHSC_SDA:
>>> +        ioinst_handle_chsc_sda(req, res);
>>> +        break;
>>> +    default:
>>> +        ioinst_handle_chsc_unimplemented(res);
>>> +        break;
>>> +    }
>>> +
>>> +out:
>>> +    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
>>> +    return ret;
>>> +}
>>> +
>>> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
>>> +{
>>> +    uint64_t addr;
>>> +    int lowcore;
>>> +
>>> +    trace_ioinst("tpi");
>>> +    addr = decode_basedisp_s(env, ipb);
>>> +    lowcore = addr ? 0 : 1;
>>> +    if (addr < 8192) {
>>> +        addr += env->psa;
>>> +    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
>>> +        addr -= env->psa;
>>> +    }
>>
>> Doesn't this replicate the logic in s390_cpu_physical_memory_map()?
>> Perhaps the memory API should be used instead to create a new address
>> space which the devices can use for their memory accesses.
>
> The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.

Each CPU should have a private address space managed by it. The areas
which are not in CPU specific map should then refer to the common
address space (which is currently used for all CPUs).

>
> However, I do agree that this duplicates logic. Cornelia, mind to instead call our map helper in css_do_tpi?

I don't mind either way. This just made me think that a new address
space should be used (in the future).

>
>
> Alex
>
Alexander Graf - Jan. 25, 2013, 8:50 p.m.
On 25.01.2013, at 20:53, Blue Swirl wrote:

> On Fri, Jan 25, 2013 at 7:28 PM, Alexander Graf <agraf@suse.de> wrote:
>> 
>> On 25.01.2013, at 20:20, Blue Swirl wrote:
>> 
>>> On Fri, Jan 25, 2013 at 12:48 PM, Alexander Graf <agraf@suse.de> wrote:
>>>> From: Cornelia Huck <cornelia.huck@de.ibm.com>
>>>> 
>>>> Provide handlers for (most) channel I/O instructions.
>>>> 
>>>> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
>>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>>> ---
>>>> target-s390x/cpu.h    |  100 +++++++
>>>> target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>> target-s390x/ioinst.h |   16 ++
>>>> trace-events          |    6 +
>>>> 4 files changed, 838 insertions(+), 0 deletions(-)
>>>> 
>>>> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
>>>> index 3e00d38..76a822c 100644
>>>> --- a/target-s390x/cpu.h
>>>> +++ b/target-s390x/cpu.h
>>>> @@ -147,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
>>>> }
>>>> #endif
>>>> 
>>>> +/* distinguish between 24 bit and 31 bit addressing */
>>>> +#define HIGH_ORDER_BIT 0x80000000
>>>> +
>>>> /* Interrupt Codes */
>>>> /* Program Interrupts */
>>>> #define PGM_OPERATION                   0x0001
>>>> @@ -331,6 +334,20 @@ void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
>>>>                                   int is_write);
>>>> void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
>>>>                                    int is_write);
>>>> +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
>>>> +{
>>>> +    hwaddr addr = 0;
>>>> +    uint8_t reg;
>>>> +
>>>> +    reg = ipb >> 28;
>>>> +    if (reg > 0) {
>>>> +        addr = env->regs[reg];
>>>> +    }
>>>> +    addr += (ipb >> 16) & 0xfff;
>>>> +
>>>> +    return addr;
>>>> +}
>>>> +
>>>> void s390x_tod_timer(void *opaque);
>>>> void s390x_cpu_timer(void *opaque);
>>>> 
>>>> @@ -380,6 +397,89 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>>>> void cpu_lock(void);
>>>> void cpu_unlock(void);
>>>> 
>>>> +typedef struct SubchDev SubchDev;
>>>> +
>>>> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
>>>> +                                       uint16_t schid)
>>>> +{
>>>> +    return NULL;
>>>> +}
>>>> +static inline bool css_subch_visible(SubchDev *sch)
>>>> +{
>>>> +    return false;
>>>> +}
>>>> +static inline void css_conditional_io_interrupt(SubchDev *sch)
>>>> +{
>>>> +}
>>>> +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
>>>> +{
>>>> +    return true;
>>>> +}
>>>> +static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_xsch(SubchDev *sch)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_csch(SubchDev *sch)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_hsch(SubchDev *sch)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_ssch(SubchDev *sch, ORB *orb)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_tsch(SubchDev *sch, IRB *irb)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_stcrw(CRW *crw)
>>>> +{
>>>> +    return 1;
>>>> +}
>>>> +static inline int css_do_tpi(uint64_t addr, int lowcore)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
>>>> +                                       int rfmt, uint8_t l_chpid, void *buf)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
>>>> +{
>>>> +}
>>>> +static inline int css_enable_mss(void)
>>>> +{
>>>> +    return -EINVAL;
>>>> +}
>>>> +static inline int css_enable_mcsse(void)
>>>> +{
>>>> +    return -EINVAL;
>>>> +}
>>>> +static inline int css_do_rsch(SubchDev *sch)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
>>>> +{
>>>> +    return -ENODEV;
>>>> +}
>>>> +static inline bool css_present(uint8_t cssid)
>>>> +{
>>>> +    return false;
>>>> +}
>>>> +
>>>> static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>>>> {
>>>>    env->aregs[0] = newtls >> 32;
>>>> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
>>>> index 06a16ee..4ef2d73 100644
>>>> --- a/target-s390x/ioinst.c
>>>> +++ b/target-s390x/ioinst.c
>>>> @@ -13,6 +13,7 @@
>>>> 
>>>> #include "cpu.h"
>>>> #include "ioinst.h"
>>>> +#include "trace.h"
>>>> 
>>>> int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>>                                 int *schid)
>>>> @@ -34,3 +35,718 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>>    *schid = IOINST_SCHID_NR(value);
>>>>    return 0;
>>>> }
>>>> +
>>>> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_xsch(sch);
>>>> +    }
>>>> +    switch (ret) {
>>>> +    case -ENODEV:
>>>> +        cc = 3;
>>>> +        break;
>>>> +    case -EBUSY:
>>>> +        cc = 2;
>>>> +        break;
>>>> +    case 0:
>>>> +        cc = 0;
>>>> +        break;
>>>> +    default:
>>>> +        cc = 1;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("csch", cssid, ssid, schid);
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_csch(sch);
>>>> +    }
>>>> +    if (ret == -ENODEV) {
>>>> +        cc = 3;
>>>> +    } else {
>>>> +        cc = 0;
>>>> +    }
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_hsch(sch);
>>>> +    }
>>>> +    switch (ret) {
>>>> +    case -ENODEV:
>>>> +        cc = 3;
>>>> +        break;
>>>> +    case -EBUSY:
>>>> +        cc = 2;
>>>> +        break;
>>>> +    case 0:
>>>> +        cc = 0;
>>>> +        break;
>>>> +    default:
>>>> +        cc = 1;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +static int ioinst_schib_valid(SCHIB *schib)
>>>> +{
>>>> +    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
>>>> +        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
>>>> +        return 0;
>>>> +    }
>>>> +    /* Disallow extended measurements for now. */
>>>> +    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
>>>> +        return 0;
>>>> +    }
>>>> +    return 1;
>>>> +}
>>>> +
>>>> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    SCHIB *schib;
>>>> +    uint64_t addr;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +    hwaddr len = sizeof(*schib);
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("msch", cssid, ssid, schid);
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>>> +    if (!schib || len != sizeof(*schib)) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    if (!ioinst_schib_valid(schib)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_msch(sch, schib);
>>>> +    }
>>>> +    switch (ret) {
>>>> +    case -ENODEV:
>>>> +        cc = 3;
>>>> +        break;
>>>> +    case -EBUSY:
>>>> +        cc = 2;
>>>> +        break;
>>>> +    case 0:
>>>> +        cc = 0;
>>>> +        break;
>>>> +    default:
>>>> +        cc = 1;
>>>> +        break;
>>>> +    }
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 0);
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +static void copy_orb_from_guest(ORB *dest, const ORB *src)
>>>> +{
>>>> +    dest->intparm = be32_to_cpu(src->intparm);
>>>> +    dest->ctrl0 = be16_to_cpu(src->ctrl0);
>>>> +    dest->lpm = src->lpm;
>>>> +    dest->ctrl1 = src->ctrl1;
>>>> +    dest->cpa = be32_to_cpu(src->cpa);
>>>> +}
>>>> +
>>>> +static int ioinst_orb_valid(ORB *orb)
>>>> +{
>>>> +    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
>>>> +        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
>>>> +        return 0;
>>>> +    }
>>>> +    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
>>>> +        return 0;
>>>> +    }
>>>> +    return 1;
>>>> +}
>>>> +
>>>> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    ORB *orig_orb, orb;
>>>> +    uint64_t addr;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +    hwaddr len = sizeof(*orig_orb);
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>>> +    if (!orig_orb || len != sizeof(*orig_orb)) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    copy_orb_from_guest(&orb, orig_orb);
>>>> +    if (!ioinst_orb_valid(&orb)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_ssch(sch, &orb);
>>>> +    }
>>>> +    switch (ret) {
>>>> +    case -ENODEV:
>>>> +        cc = 3;
>>>> +        break;
>>>> +    case -EBUSY:
>>>> +        cc = 2;
>>>> +        break;
>>>> +    case 0:
>>>> +        cc = 0;
>>>> +        break;
>>>> +    default:
>>>> +        cc = 1;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
>>>> +{
>>>> +    CRW *crw;
>>>> +    uint64_t addr;
>>>> +    int cc;
>>>> +    hwaddr len = sizeof(*crw);
>>>> +
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>> +    if (!crw || len != sizeof(*crw)) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    cc = css_do_stcrw(crw);
>>>> +    /* 0 - crw stored, 1 - zeroes stored */
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, crw, len, 1);
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    uint64_t addr;
>>>> +    int cc;
>>>> +    SCHIB *schib;
>>>> +    hwaddr len = sizeof(*schib);
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>> +    if (!schib || len != sizeof(*schib)) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch) {
>>>> +        if (css_subch_visible(sch)) {
>>>> +            css_do_stsch(sch, schib);
>>>> +            cc = 0;
>>>> +        } else {
>>>> +            /* Indicate no more subchannels in this css/ss */
>>>> +            cc = 3;
>>>> +        }
>>>> +    } else {
>>>> +        if (css_schid_final(cssid, ssid, schid)) {
>>>> +            cc = 3; /* No more subchannels in this css/ss */
>>>> +        } else {
>>>> +            /* Store an empty schib. */
>>>> +            memset(schib, 0, sizeof(*schib));
>>>> +            cc = 0;
>>>> +        }
>>>> +    }
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 1);
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>> +{
>>>> +    int cssid, ssid, schid, m;
>>>> +    SubchDev *sch;
>>>> +    IRB *irb;
>>>> +    uint64_t addr;
>>>> +    int ret = -ENODEV;
>>>> +    int cc;
>>>> +    hwaddr len = sizeof(*irb);
>>>> +
>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>> +    if (!irb || len != sizeof(*irb)) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        cc = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>> +    if (sch && css_subch_visible(sch)) {
>>>> +        ret = css_do_tsch(sch, irb);
>>>> +        /* 0 - status pending, 1 - not status pending */
>>>> +        cc = ret;
>>>> +    } else {
>>>> +        cc = 3;
>>>> +    }
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
>>>> +    return cc;
>>>> +}
>>>> +
>>>> +typedef struct ChscReq {
>>>> +    uint16_t len;
>>>> +    uint16_t command;
>>>> +    uint32_t param0;
>>>> +    uint32_t param1;
>>>> +    uint32_t param2;
>>>> +} QEMU_PACKED ChscReq;
>>>> +
>>>> +typedef struct ChscResp {
>>>> +    uint16_t len;
>>>> +    uint16_t code;
>>>> +    uint32_t param;
>>>> +    char data[0];
>>>> +} QEMU_PACKED ChscResp;
>>>> +
>>>> +#define CHSC_MIN_RESP_LEN 0x0008
>>>> +
>>>> +#define CHSC_SCPD 0x0002
>>>> +#define CHSC_SCSC 0x0010
>>>> +#define CHSC_SDA  0x0031
>>>> +
>>>> +#define CHSC_SCPD_0_M 0x20000000
>>>> +#define CHSC_SCPD_0_C 0x10000000
>>>> +#define CHSC_SCPD_0_FMT 0x0f000000
>>>> +#define CHSC_SCPD_0_CSSID 0x00ff0000
>>>> +#define CHSC_SCPD_0_RFMT 0x00000f00
>>>> +#define CHSC_SCPD_0_RES 0xc000f000
>>>> +#define CHSC_SCPD_1_RES 0xffffff00
>>>> +#define CHSC_SCPD_01_CHPID 0x000000ff
>>>> +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
>>>> +{
>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>> +    uint32_t param1 = be32_to_cpu(req->param1);
>>>> +    uint16_t resp_code;
>>>> +    int rfmt;
>>>> +    uint16_t cssid;
>>>> +    uint8_t f_chpid, l_chpid;
>>>> +    int desc_size;
>>>> +    int m;
>>>> +
>>>> +    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
>>>> +    if ((rfmt == 0) ||  (rfmt == 1)) {
>>>> +        rfmt = !!(param0 & CHSC_SCPD_0_C);
>>>> +    }
>>>> +    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
>>>> +        (param1 & CHSC_SCPD_1_RES) || req->param2) {
>>>> +        resp_code = 0x0003;
>>>> +        goto out_err;
>>>> +    }
>>>> +    if (param0 & CHSC_SCPD_0_FMT) {
>>>> +        resp_code = 0x0007;
>>>> +        goto out_err;
>>>> +    }
>>>> +    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
>>>> +    m = param0 & CHSC_SCPD_0_M;
>>>> +    if (cssid != 0) {
>>>> +        if (!m || !css_present(cssid)) {
>>>> +            resp_code = 0x0008;
>>>> +            goto out_err;
>>>> +        }
>>>> +    }
>>>> +    f_chpid = param0 & CHSC_SCPD_01_CHPID;
>>>> +    l_chpid = param1 & CHSC_SCPD_01_CHPID;
>>>> +    if (l_chpid < f_chpid) {
>>>> +        resp_code = 0x0003;
>>>> +        goto out_err;
>>>> +    }
>>>> +    /* css_collect_chp_desc() is endian-aware */
>>>> +    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
>>>> +                                     &res->data);
>>>> +    res->code = cpu_to_be16(0x0001);
>>>> +    res->len = cpu_to_be16(8 + desc_size);
>>>> +    res->param = cpu_to_be32(rfmt);
>>>> +    return;
>>>> +
>>>> +  out_err:
>>>> +    res->code = cpu_to_be16(resp_code);
>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>> +    res->param = cpu_to_be32(rfmt);
>>>> +}
>>>> +
>>>> +#define CHSC_SCSC_0_M 0x20000000
>>>> +#define CHSC_SCSC_0_FMT 0x000f0000
>>>> +#define CHSC_SCSC_0_CSSID 0x0000ff00
>>>> +#define CHSC_SCSC_0_RES 0xdff000ff
>>>> +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
>>>> +{
>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>> +    uint8_t cssid;
>>>> +    uint16_t resp_code;
>>>> +    uint32_t general_chars[510];
>>>> +    uint32_t chsc_chars[508];
>>>> +
>>>> +    if (len != 0x0010) {
>>>> +        resp_code = 0x0003;
>>>> +        goto out_err;
>>>> +    }
>>>> +
>>>> +    if (param0 & CHSC_SCSC_0_FMT) {
>>>> +        resp_code = 0x0007;
>>>> +        goto out_err;
>>>> +    }
>>>> +    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
>>>> +    if (cssid != 0) {
>>>> +        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
>>>> +            resp_code = 0x0008;
>>>> +            goto out_err;
>>>> +        }
>>>> +    }
>>>> +    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
>>>> +        resp_code = 0x0003;
>>>> +        goto out_err;
>>>> +    }
>>>> +    res->code = cpu_to_be16(0x0001);
>>>> +    res->len = cpu_to_be16(4080);
>>>> +    res->param = 0;
>>>> +
>>>> +    memset(general_chars, 0, sizeof(general_chars));
>>>> +    memset(chsc_chars, 0, sizeof(chsc_chars));
>>>> +
>>>> +    general_chars[0] = cpu_to_be32(0x03000000);
>>>> +    general_chars[1] = cpu_to_be32(0x00059000);
>>>> +
>>>> +    chsc_chars[0] = cpu_to_be32(0x40000000);
>>>> +    chsc_chars[3] = cpu_to_be32(0x00040000);
>>>> +
>>>> +    memcpy(res->data, general_chars, sizeof(general_chars));
>>>> +    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
>>>> +    return;
>>>> +
>>>> +  out_err:
>>>> +    res->code = cpu_to_be16(resp_code);
>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>> +    res->param = 0;
>>>> +}
>>>> +
>>>> +#define CHSC_SDA_0_FMT 0x0f000000
>>>> +#define CHSC_SDA_0_OC 0x0000ffff
>>>> +#define CHSC_SDA_0_RES 0xf0ff0000
>>>> +#define CHSC_SDA_OC_MCSSE 0x0
>>>> +#define CHSC_SDA_OC_MSS 0x2
>>>> +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
>>>> +{
>>>> +    uint16_t resp_code = 0x0001;
>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>> +    uint16_t oc;
>>>> +    int ret;
>>>> +
>>>> +    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
>>>> +        resp_code = 0x0003;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    if (param0 & CHSC_SDA_0_FMT) {
>>>> +        resp_code = 0x0007;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    oc = param0 & CHSC_SDA_0_OC;
>>>> +    switch (oc) {
>>>> +    case CHSC_SDA_OC_MCSSE:
>>>> +        ret = css_enable_mcsse();
>>>> +        if (ret == -EINVAL) {
>>>> +            resp_code = 0x0101;
>>>> +            goto out;
>>>> +        }
>>>> +        break;
>>>> +    case CHSC_SDA_OC_MSS:
>>>> +        ret = css_enable_mss();
>>>> +        if (ret == -EINVAL) {
>>>> +            resp_code = 0x0101;
>>>> +            goto out;
>>>> +        }
>>>> +        break;
>>>> +    default:
>>>> +        resp_code = 0x0003;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +out:
>>>> +    res->code = cpu_to_be16(resp_code);
>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>> +    res->param = 0;
>>>> +}
>>>> +
>>>> +static void ioinst_handle_chsc_unimplemented(ChscResp *res)
>>>> +{
>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>> +    res->code = cpu_to_be16(0x0004);
>>>> +    res->param = 0;
>>>> +}
>>>> +
>>>> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
>>>> +{
>>>> +    ChscReq *req;
>>>> +    ChscResp *res;
>>>> +    uint64_t addr;
>>>> +    int reg;
>>>> +    uint16_t len;
>>>> +    uint16_t command;
>>>> +    hwaddr map_size = TARGET_PAGE_SIZE;
>>>> +    int ret = 0;
>>>> +
>>>> +    trace_ioinst("chsc");
>>>> +    reg = (ipb >> 20) & 0x00f;
>>>> +    addr = env->regs[reg];
>>>> +    /* Page boundary? */
>>>> +    if (addr & 0xfff) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        return -EIO;
>>>> +    }
>>>> +    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
>>>> +    if (!req || map_size != TARGET_PAGE_SIZE) {
>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>> +        ret = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    len = be16_to_cpu(req->len);
>>>> +    /* Length field valid? */
>>>> +    if ((len < 16) || (len > 4088) || (len & 7)) {
>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>> +        ret = -EIO;
>>>> +        goto out;
>>>> +    }
>>>> +    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
>>>> +    res = (void *)((char *)req + len);
>>>> +    command = be16_to_cpu(req->command);
>>>> +    trace_ioinst_chsc_cmd(command, len);
>>>> +    switch (command) {
>>>> +    case CHSC_SCSC:
>>>> +        ioinst_handle_chsc_scsc(req, res);
>>>> +        break;
>>>> +    case CHSC_SCPD:
>>>> +        ioinst_handle_chsc_scpd(req, res);
>>>> +        break;
>>>> +    case CHSC_SDA:
>>>> +        ioinst_handle_chsc_sda(req, res);
>>>> +        break;
>>>> +    default:
>>>> +        ioinst_handle_chsc_unimplemented(res);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +out:
>>>> +    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
>>>> +{
>>>> +    uint64_t addr;
>>>> +    int lowcore;
>>>> +
>>>> +    trace_ioinst("tpi");
>>>> +    addr = decode_basedisp_s(env, ipb);
>>>> +    lowcore = addr ? 0 : 1;
>>>> +    if (addr < 8192) {
>>>> +        addr += env->psa;
>>>> +    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
>>>> +        addr -= env->psa;
>>>> +    }
>>> 
>>> Doesn't this replicate the logic in s390_cpu_physical_memory_map()?
>>> Perhaps the memory API should be used instead to create a new address
>>> space which the devices can use for their memory accesses.
>> 
>> The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.
> 
> Each CPU should have a private address space managed by it. The areas
> which are not in CPU specific map should then refer to the common
> address space (which is currently used for all CPUs).

I was thinking of this before too. But how do you reference to this new mapping then? How do you map anything inside there? And where would you hang it off of?

The system mapping still has to be linear and CPU agnostic - and that's what cpu_physical_memory_map and ldx_phys are going to use.


Alex
Andreas Färber - Jan. 26, 2013, 8:02 a.m.
Am 25.01.2013 20:53, schrieb Blue Swirl:
> On Fri, Jan 25, 2013 at 7:28 PM, Alexander Graf <agraf@suse.de> wrote:
>> The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.
> 
> Each CPU should have a private address space managed by it. The areas
> which are not in CPU specific map should then refer to the common
> address space (which is currently used for all CPUs).

I have that on my TODO list but had no reason to shortlist it yet. :)
Patches welcome.

Andreas
Blue Swirl - Jan. 26, 2013, 12:15 p.m.
On Fri, Jan 25, 2013 at 8:50 PM, Alexander Graf <agraf@suse.de> wrote:
>
> On 25.01.2013, at 20:53, Blue Swirl wrote:
>
>> On Fri, Jan 25, 2013 at 7:28 PM, Alexander Graf <agraf@suse.de> wrote:
>>>
>>> On 25.01.2013, at 20:20, Blue Swirl wrote:
>>>
>>>> On Fri, Jan 25, 2013 at 12:48 PM, Alexander Graf <agraf@suse.de> wrote:
>>>>> From: Cornelia Huck <cornelia.huck@de.ibm.com>
>>>>>
>>>>> Provide handlers for (most) channel I/O instructions.
>>>>>
>>>>> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
>>>>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>>>> ---
>>>>> target-s390x/cpu.h    |  100 +++++++
>>>>> target-s390x/ioinst.c |  716 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>>> target-s390x/ioinst.h |   16 ++
>>>>> trace-events          |    6 +
>>>>> 4 files changed, 838 insertions(+), 0 deletions(-)
>>>>>
>>>>> diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
>>>>> index 3e00d38..76a822c 100644
>>>>> --- a/target-s390x/cpu.h
>>>>> +++ b/target-s390x/cpu.h
>>>>> @@ -147,6 +147,9 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
>>>>> }
>>>>> #endif
>>>>>
>>>>> +/* distinguish between 24 bit and 31 bit addressing */
>>>>> +#define HIGH_ORDER_BIT 0x80000000
>>>>> +
>>>>> /* Interrupt Codes */
>>>>> /* Program Interrupts */
>>>>> #define PGM_OPERATION                   0x0001
>>>>> @@ -331,6 +334,20 @@ void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
>>>>>                                   int is_write);
>>>>> void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
>>>>>                                    int is_write);
>>>>> +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
>>>>> +{
>>>>> +    hwaddr addr = 0;
>>>>> +    uint8_t reg;
>>>>> +
>>>>> +    reg = ipb >> 28;
>>>>> +    if (reg > 0) {
>>>>> +        addr = env->regs[reg];
>>>>> +    }
>>>>> +    addr += (ipb >> 16) & 0xfff;
>>>>> +
>>>>> +    return addr;
>>>>> +}
>>>>> +
>>>>> void s390x_tod_timer(void *opaque);
>>>>> void s390x_cpu_timer(void *opaque);
>>>>>
>>>>> @@ -380,6 +397,89 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env)
>>>>> void cpu_lock(void);
>>>>> void cpu_unlock(void);
>>>>>
>>>>> +typedef struct SubchDev SubchDev;
>>>>> +
>>>>> +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
>>>>> +                                       uint16_t schid)
>>>>> +{
>>>>> +    return NULL;
>>>>> +}
>>>>> +static inline bool css_subch_visible(SubchDev *sch)
>>>>> +{
>>>>> +    return false;
>>>>> +}
>>>>> +static inline void css_conditional_io_interrupt(SubchDev *sch)
>>>>> +{
>>>>> +}
>>>>> +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
>>>>> +{
>>>>> +    return true;
>>>>> +}
>>>>> +static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_xsch(SubchDev *sch)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_csch(SubchDev *sch)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_hsch(SubchDev *sch)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_ssch(SubchDev *sch, ORB *orb)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_tsch(SubchDev *sch, IRB *irb)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_stcrw(CRW *crw)
>>>>> +{
>>>>> +    return 1;
>>>>> +}
>>>>> +static inline int css_do_tpi(uint64_t addr, int lowcore)
>>>>> +{
>>>>> +    return 0;
>>>>> +}
>>>>> +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
>>>>> +                                       int rfmt, uint8_t l_chpid, void *buf)
>>>>> +{
>>>>> +    return 0;
>>>>> +}
>>>>> +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
>>>>> +{
>>>>> +}
>>>>> +static inline int css_enable_mss(void)
>>>>> +{
>>>>> +    return -EINVAL;
>>>>> +}
>>>>> +static inline int css_enable_mcsse(void)
>>>>> +{
>>>>> +    return -EINVAL;
>>>>> +}
>>>>> +static inline int css_do_rsch(SubchDev *sch)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
>>>>> +{
>>>>> +    return -ENODEV;
>>>>> +}
>>>>> +static inline bool css_present(uint8_t cssid)
>>>>> +{
>>>>> +    return false;
>>>>> +}
>>>>> +
>>>>> static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
>>>>> {
>>>>>    env->aregs[0] = newtls >> 32;
>>>>> diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
>>>>> index 06a16ee..4ef2d73 100644
>>>>> --- a/target-s390x/ioinst.c
>>>>> +++ b/target-s390x/ioinst.c
>>>>> @@ -13,6 +13,7 @@
>>>>>
>>>>> #include "cpu.h"
>>>>> #include "ioinst.h"
>>>>> +#include "trace.h"
>>>>>
>>>>> int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>>>                                 int *schid)
>>>>> @@ -34,3 +35,718 @@ int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
>>>>>    *schid = IOINST_SCHID_NR(value);
>>>>>    return 0;
>>>>> }
>>>>> +
>>>>> +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_xsch(sch);
>>>>> +    }
>>>>> +    switch (ret) {
>>>>> +    case -ENODEV:
>>>>> +        cc = 3;
>>>>> +        break;
>>>>> +    case -EBUSY:
>>>>> +        cc = 2;
>>>>> +        break;
>>>>> +    case 0:
>>>>> +        cc = 0;
>>>>> +        break;
>>>>> +    default:
>>>>> +        cc = 1;
>>>>> +        break;
>>>>> +    }
>>>>> +
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("csch", cssid, ssid, schid);
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_csch(sch);
>>>>> +    }
>>>>> +    if (ret == -ENODEV) {
>>>>> +        cc = 3;
>>>>> +    } else {
>>>>> +        cc = 0;
>>>>> +    }
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_hsch(sch);
>>>>> +    }
>>>>> +    switch (ret) {
>>>>> +    case -ENODEV:
>>>>> +        cc = 3;
>>>>> +        break;
>>>>> +    case -EBUSY:
>>>>> +        cc = 2;
>>>>> +        break;
>>>>> +    case 0:
>>>>> +        cc = 0;
>>>>> +        break;
>>>>> +    default:
>>>>> +        cc = 1;
>>>>> +        break;
>>>>> +    }
>>>>> +
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +static int ioinst_schib_valid(SCHIB *schib)
>>>>> +{
>>>>> +    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
>>>>> +        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
>>>>> +        return 0;
>>>>> +    }
>>>>> +    /* Disallow extended measurements for now. */
>>>>> +    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
>>>>> +        return 0;
>>>>> +    }
>>>>> +    return 1;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    SCHIB *schib;
>>>>> +    uint64_t addr;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +    hwaddr len = sizeof(*schib);
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("msch", cssid, ssid, schid);
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>>>> +    if (!schib || len != sizeof(*schib)) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    if (!ioinst_schib_valid(schib)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_msch(sch, schib);
>>>>> +    }
>>>>> +    switch (ret) {
>>>>> +    case -ENODEV:
>>>>> +        cc = 3;
>>>>> +        break;
>>>>> +    case -EBUSY:
>>>>> +        cc = 2;
>>>>> +        break;
>>>>> +    case 0:
>>>>> +        cc = 0;
>>>>> +        break;
>>>>> +    default:
>>>>> +        cc = 1;
>>>>> +        break;
>>>>> +    }
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 0);
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +static void copy_orb_from_guest(ORB *dest, const ORB *src)
>>>>> +{
>>>>> +    dest->intparm = be32_to_cpu(src->intparm);
>>>>> +    dest->ctrl0 = be16_to_cpu(src->ctrl0);
>>>>> +    dest->lpm = src->lpm;
>>>>> +    dest->ctrl1 = src->ctrl1;
>>>>> +    dest->cpa = be32_to_cpu(src->cpa);
>>>>> +}
>>>>> +
>>>>> +static int ioinst_orb_valid(ORB *orb)
>>>>> +{
>>>>> +    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
>>>>> +        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
>>>>> +        return 0;
>>>>> +    }
>>>>> +    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
>>>>> +        return 0;
>>>>> +    }
>>>>> +    return 1;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    ORB *orig_orb, orb;
>>>>> +    uint64_t addr;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +    hwaddr len = sizeof(*orig_orb);
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
>>>>> +    if (!orig_orb || len != sizeof(*orig_orb)) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    copy_orb_from_guest(&orb, orig_orb);
>>>>> +    if (!ioinst_orb_valid(&orb)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_ssch(sch, &orb);
>>>>> +    }
>>>>> +    switch (ret) {
>>>>> +    case -ENODEV:
>>>>> +        cc = 3;
>>>>> +        break;
>>>>> +    case -EBUSY:
>>>>> +        cc = 2;
>>>>> +        break;
>>>>> +    case 0:
>>>>> +        cc = 0;
>>>>> +        break;
>>>>> +    default:
>>>>> +        cc = 1;
>>>>> +        break;
>>>>> +    }
>>>>> +
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
>>>>> +{
>>>>> +    CRW *crw;
>>>>> +    uint64_t addr;
>>>>> +    int cc;
>>>>> +    hwaddr len = sizeof(*crw);
>>>>> +
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>>> +    if (!crw || len != sizeof(*crw)) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    cc = css_do_stcrw(crw);
>>>>> +    /* 0 - crw stored, 1 - zeroes stored */
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, crw, len, 1);
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    uint64_t addr;
>>>>> +    int cc;
>>>>> +    SCHIB *schib;
>>>>> +    hwaddr len = sizeof(*schib);
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>>> +    if (!schib || len != sizeof(*schib)) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch) {
>>>>> +        if (css_subch_visible(sch)) {
>>>>> +            css_do_stsch(sch, schib);
>>>>> +            cc = 0;
>>>>> +        } else {
>>>>> +            /* Indicate no more subchannels in this css/ss */
>>>>> +            cc = 3;
>>>>> +        }
>>>>> +    } else {
>>>>> +        if (css_schid_final(cssid, ssid, schid)) {
>>>>> +            cc = 3; /* No more subchannels in this css/ss */
>>>>> +        } else {
>>>>> +            /* Store an empty schib. */
>>>>> +            memset(schib, 0, sizeof(*schib));
>>>>> +            cc = 0;
>>>>> +        }
>>>>> +    }
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, schib, len, 1);
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
>>>>> +{
>>>>> +    int cssid, ssid, schid, m;
>>>>> +    SubchDev *sch;
>>>>> +    IRB *irb;
>>>>> +    uint64_t addr;
>>>>> +    int ret = -ENODEV;
>>>>> +    int cc;
>>>>> +    hwaddr len = sizeof(*irb);
>>>>> +
>>>>> +    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
>>>>> +    if (!irb || len != sizeof(*irb)) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        cc = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    sch = css_find_subch(m, cssid, ssid, schid);
>>>>> +    if (sch && css_subch_visible(sch)) {
>>>>> +        ret = css_do_tsch(sch, irb);
>>>>> +        /* 0 - status pending, 1 - not status pending */
>>>>> +        cc = ret;
>>>>> +    } else {
>>>>> +        cc = 3;
>>>>> +    }
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
>>>>> +    return cc;
>>>>> +}
>>>>> +
>>>>> +typedef struct ChscReq {
>>>>> +    uint16_t len;
>>>>> +    uint16_t command;
>>>>> +    uint32_t param0;
>>>>> +    uint32_t param1;
>>>>> +    uint32_t param2;
>>>>> +} QEMU_PACKED ChscReq;
>>>>> +
>>>>> +typedef struct ChscResp {
>>>>> +    uint16_t len;
>>>>> +    uint16_t code;
>>>>> +    uint32_t param;
>>>>> +    char data[0];
>>>>> +} QEMU_PACKED ChscResp;
>>>>> +
>>>>> +#define CHSC_MIN_RESP_LEN 0x0008
>>>>> +
>>>>> +#define CHSC_SCPD 0x0002
>>>>> +#define CHSC_SCSC 0x0010
>>>>> +#define CHSC_SDA  0x0031
>>>>> +
>>>>> +#define CHSC_SCPD_0_M 0x20000000
>>>>> +#define CHSC_SCPD_0_C 0x10000000
>>>>> +#define CHSC_SCPD_0_FMT 0x0f000000
>>>>> +#define CHSC_SCPD_0_CSSID 0x00ff0000
>>>>> +#define CHSC_SCPD_0_RFMT 0x00000f00
>>>>> +#define CHSC_SCPD_0_RES 0xc000f000
>>>>> +#define CHSC_SCPD_1_RES 0xffffff00
>>>>> +#define CHSC_SCPD_01_CHPID 0x000000ff
>>>>> +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
>>>>> +{
>>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>>> +    uint32_t param1 = be32_to_cpu(req->param1);
>>>>> +    uint16_t resp_code;
>>>>> +    int rfmt;
>>>>> +    uint16_t cssid;
>>>>> +    uint8_t f_chpid, l_chpid;
>>>>> +    int desc_size;
>>>>> +    int m;
>>>>> +
>>>>> +    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
>>>>> +    if ((rfmt == 0) ||  (rfmt == 1)) {
>>>>> +        rfmt = !!(param0 & CHSC_SCPD_0_C);
>>>>> +    }
>>>>> +    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
>>>>> +        (param1 & CHSC_SCPD_1_RES) || req->param2) {
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +    if (param0 & CHSC_SCPD_0_FMT) {
>>>>> +        resp_code = 0x0007;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
>>>>> +    m = param0 & CHSC_SCPD_0_M;
>>>>> +    if (cssid != 0) {
>>>>> +        if (!m || !css_present(cssid)) {
>>>>> +            resp_code = 0x0008;
>>>>> +            goto out_err;
>>>>> +        }
>>>>> +    }
>>>>> +    f_chpid = param0 & CHSC_SCPD_01_CHPID;
>>>>> +    l_chpid = param1 & CHSC_SCPD_01_CHPID;
>>>>> +    if (l_chpid < f_chpid) {
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +    /* css_collect_chp_desc() is endian-aware */
>>>>> +    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
>>>>> +                                     &res->data);
>>>>> +    res->code = cpu_to_be16(0x0001);
>>>>> +    res->len = cpu_to_be16(8 + desc_size);
>>>>> +    res->param = cpu_to_be32(rfmt);
>>>>> +    return;
>>>>> +
>>>>> +  out_err:
>>>>> +    res->code = cpu_to_be16(resp_code);
>>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>>> +    res->param = cpu_to_be32(rfmt);
>>>>> +}
>>>>> +
>>>>> +#define CHSC_SCSC_0_M 0x20000000
>>>>> +#define CHSC_SCSC_0_FMT 0x000f0000
>>>>> +#define CHSC_SCSC_0_CSSID 0x0000ff00
>>>>> +#define CHSC_SCSC_0_RES 0xdff000ff
>>>>> +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
>>>>> +{
>>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>>> +    uint8_t cssid;
>>>>> +    uint16_t resp_code;
>>>>> +    uint32_t general_chars[510];
>>>>> +    uint32_t chsc_chars[508];
>>>>> +
>>>>> +    if (len != 0x0010) {
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +
>>>>> +    if (param0 & CHSC_SCSC_0_FMT) {
>>>>> +        resp_code = 0x0007;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
>>>>> +    if (cssid != 0) {
>>>>> +        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
>>>>> +            resp_code = 0x0008;
>>>>> +            goto out_err;
>>>>> +        }
>>>>> +    }
>>>>> +    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out_err;
>>>>> +    }
>>>>> +    res->code = cpu_to_be16(0x0001);
>>>>> +    res->len = cpu_to_be16(4080);
>>>>> +    res->param = 0;
>>>>> +
>>>>> +    memset(general_chars, 0, sizeof(general_chars));
>>>>> +    memset(chsc_chars, 0, sizeof(chsc_chars));
>>>>> +
>>>>> +    general_chars[0] = cpu_to_be32(0x03000000);
>>>>> +    general_chars[1] = cpu_to_be32(0x00059000);
>>>>> +
>>>>> +    chsc_chars[0] = cpu_to_be32(0x40000000);
>>>>> +    chsc_chars[3] = cpu_to_be32(0x00040000);
>>>>> +
>>>>> +    memcpy(res->data, general_chars, sizeof(general_chars));
>>>>> +    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
>>>>> +    return;
>>>>> +
>>>>> +  out_err:
>>>>> +    res->code = cpu_to_be16(resp_code);
>>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>>> +    res->param = 0;
>>>>> +}
>>>>> +
>>>>> +#define CHSC_SDA_0_FMT 0x0f000000
>>>>> +#define CHSC_SDA_0_OC 0x0000ffff
>>>>> +#define CHSC_SDA_0_RES 0xf0ff0000
>>>>> +#define CHSC_SDA_OC_MCSSE 0x0
>>>>> +#define CHSC_SDA_OC_MSS 0x2
>>>>> +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
>>>>> +{
>>>>> +    uint16_t resp_code = 0x0001;
>>>>> +    uint16_t len = be16_to_cpu(req->len);
>>>>> +    uint32_t param0 = be32_to_cpu(req->param0);
>>>>> +    uint16_t oc;
>>>>> +    int ret;
>>>>> +
>>>>> +    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out;
>>>>> +    }
>>>>> +
>>>>> +    if (param0 & CHSC_SDA_0_FMT) {
>>>>> +        resp_code = 0x0007;
>>>>> +        goto out;
>>>>> +    }
>>>>> +
>>>>> +    oc = param0 & CHSC_SDA_0_OC;
>>>>> +    switch (oc) {
>>>>> +    case CHSC_SDA_OC_MCSSE:
>>>>> +        ret = css_enable_mcsse();
>>>>> +        if (ret == -EINVAL) {
>>>>> +            resp_code = 0x0101;
>>>>> +            goto out;
>>>>> +        }
>>>>> +        break;
>>>>> +    case CHSC_SDA_OC_MSS:
>>>>> +        ret = css_enable_mss();
>>>>> +        if (ret == -EINVAL) {
>>>>> +            resp_code = 0x0101;
>>>>> +            goto out;
>>>>> +        }
>>>>> +        break;
>>>>> +    default:
>>>>> +        resp_code = 0x0003;
>>>>> +        goto out;
>>>>> +    }
>>>>> +
>>>>> +out:
>>>>> +    res->code = cpu_to_be16(resp_code);
>>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>>> +    res->param = 0;
>>>>> +}
>>>>> +
>>>>> +static void ioinst_handle_chsc_unimplemented(ChscResp *res)
>>>>> +{
>>>>> +    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
>>>>> +    res->code = cpu_to_be16(0x0004);
>>>>> +    res->param = 0;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
>>>>> +{
>>>>> +    ChscReq *req;
>>>>> +    ChscResp *res;
>>>>> +    uint64_t addr;
>>>>> +    int reg;
>>>>> +    uint16_t len;
>>>>> +    uint16_t command;
>>>>> +    hwaddr map_size = TARGET_PAGE_SIZE;
>>>>> +    int ret = 0;
>>>>> +
>>>>> +    trace_ioinst("chsc");
>>>>> +    reg = (ipb >> 20) & 0x00f;
>>>>> +    addr = env->regs[reg];
>>>>> +    /* Page boundary? */
>>>>> +    if (addr & 0xfff) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        return -EIO;
>>>>> +    }
>>>>> +    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
>>>>> +    if (!req || map_size != TARGET_PAGE_SIZE) {
>>>>> +        program_interrupt(env, PGM_SPECIFICATION, 2);
>>>>> +        ret = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    len = be16_to_cpu(req->len);
>>>>> +    /* Length field valid? */
>>>>> +    if ((len < 16) || (len > 4088) || (len & 7)) {
>>>>> +        program_interrupt(env, PGM_OPERAND, 2);
>>>>> +        ret = -EIO;
>>>>> +        goto out;
>>>>> +    }
>>>>> +    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
>>>>> +    res = (void *)((char *)req + len);
>>>>> +    command = be16_to_cpu(req->command);
>>>>> +    trace_ioinst_chsc_cmd(command, len);
>>>>> +    switch (command) {
>>>>> +    case CHSC_SCSC:
>>>>> +        ioinst_handle_chsc_scsc(req, res);
>>>>> +        break;
>>>>> +    case CHSC_SCPD:
>>>>> +        ioinst_handle_chsc_scpd(req, res);
>>>>> +        break;
>>>>> +    case CHSC_SDA:
>>>>> +        ioinst_handle_chsc_sda(req, res);
>>>>> +        break;
>>>>> +    default:
>>>>> +        ioinst_handle_chsc_unimplemented(res);
>>>>> +        break;
>>>>> +    }
>>>>> +
>>>>> +out:
>>>>> +    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
>>>>> +{
>>>>> +    uint64_t addr;
>>>>> +    int lowcore;
>>>>> +
>>>>> +    trace_ioinst("tpi");
>>>>> +    addr = decode_basedisp_s(env, ipb);
>>>>> +    lowcore = addr ? 0 : 1;
>>>>> +    if (addr < 8192) {
>>>>> +        addr += env->psa;
>>>>> +    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
>>>>> +        addr -= env->psa;
>>>>> +    }
>>>>
>>>> Doesn't this replicate the logic in s390_cpu_physical_memory_map()?
>>>> Perhaps the memory API should be used instead to create a new address
>>>> space which the devices can use for their memory accesses.
>>>
>>> The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.
>>
>> Each CPU should have a private address space managed by it. The areas
>> which are not in CPU specific map should then refer to the common
>> address space (which is currently used for all CPUs).
>
> I was thinking of this before too. But how do you reference to this new mapping then? How do you map anything inside there? And where would you hang it off of?

The mapping should be passed to root bus devices instead of
system_memory. Devices should use the DMA functions to access memory.

Mappings and changes to them should be handled by the CPU, the address
space would be a part of the one of the CPU*State structures.

This should simplify and fix problems with some CPU local devices like LAPIC.

> The system mapping still has to be linear and CPU agnostic -

Yes.

> and that's what cpu_physical_memory_map and ldx_phys are going to use.

But eventually that needs to be changed, or rather, the devices should
be converted to use something else (maybe DMA functions).

We may also need to move from cpu_physical_memory_map() to two sets of
functions, something like cpu_local_physical_memory_map() and
system_memory_map(), or just pass the effective address space to a
generic function.

>
>
> Alex
>
Blue Swirl - Jan. 26, 2013, 12:16 p.m.
On Sat, Jan 26, 2013 at 8:02 AM, Andreas Färber <afaerber@suse.de> wrote:
> Am 25.01.2013 20:53, schrieb Blue Swirl:
>> On Fri, Jan 25, 2013 at 7:28 PM, Alexander Graf <agraf@suse.de> wrote:
>>> The memory API is CPU agnostic today, and the special case here is that bytes [0..8192] are a CPU specific map.
>>
>> Each CPU should have a private address space managed by it. The areas
>> which are not in CPU specific map should then refer to the common
>> address space (which is currently used for all CPUs).
>
> I have that on my TODO list but had no reason to shortlist it yet. :)
> Patches welcome.

I was hoping that this would be also on Avi's list of memory API
enhancements, but it looks like that is not going to happen.

>
> Andreas
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

Patch

diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 3e00d38..76a822c 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -147,6 +147,9 @@  static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp)
 }
 #endif
 
+/* distinguish between 24 bit and 31 bit addressing */
+#define HIGH_ORDER_BIT 0x80000000
+
 /* Interrupt Codes */
 /* Program Interrupts */
 #define PGM_OPERATION                   0x0001
@@ -331,6 +334,20 @@  void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len,
                                    int is_write);
 void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len,
                                     int is_write);
+static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb)
+{
+    hwaddr addr = 0;
+    uint8_t reg;
+
+    reg = ipb >> 28;
+    if (reg > 0) {
+        addr = env->regs[reg];
+    }
+    addr += (ipb >> 16) & 0xfff;
+
+    return addr;
+}
+
 void s390x_tod_timer(void *opaque);
 void s390x_cpu_timer(void *opaque);
 
@@ -380,6 +397,89 @@  static inline unsigned s390_del_running_cpu(CPUS390XState *env)
 void cpu_lock(void);
 void cpu_unlock(void);
 
+typedef struct SubchDev SubchDev;
+
+static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid,
+                                       uint16_t schid)
+{
+    return NULL;
+}
+static inline bool css_subch_visible(SubchDev *sch)
+{
+    return false;
+}
+static inline void css_conditional_io_interrupt(SubchDev *sch)
+{
+}
+static inline int css_do_stsch(SubchDev *sch, SCHIB *schib)
+{
+    return -ENODEV;
+}
+static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid)
+{
+    return true;
+}
+static inline int css_do_msch(SubchDev *sch, SCHIB *schib)
+{
+    return -ENODEV;
+}
+static inline int css_do_xsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_csch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_hsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_ssch(SubchDev *sch, ORB *orb)
+{
+    return -ENODEV;
+}
+static inline int css_do_tsch(SubchDev *sch, IRB *irb)
+{
+    return -ENODEV;
+}
+static inline int css_do_stcrw(CRW *crw)
+{
+    return 1;
+}
+static inline int css_do_tpi(uint64_t addr, int lowcore)
+{
+    return 0;
+}
+static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid,
+                                       int rfmt, uint8_t l_chpid, void *buf)
+{
+    return 0;
+}
+static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo)
+{
+}
+static inline int css_enable_mss(void)
+{
+    return -EINVAL;
+}
+static inline int css_enable_mcsse(void)
+{
+    return -EINVAL;
+}
+static inline int css_do_rsch(SubchDev *sch)
+{
+    return -ENODEV;
+}
+static inline int css_do_rchp(uint8_t cssid, uint8_t chpid)
+{
+    return -ENODEV;
+}
+static inline bool css_present(uint8_t cssid)
+{
+    return false;
+}
+
 static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
 {
     env->aregs[0] = newtls >> 32;
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
index 06a16ee..4ef2d73 100644
--- a/target-s390x/ioinst.c
+++ b/target-s390x/ioinst.c
@@ -13,6 +13,7 @@ 
 
 #include "cpu.h"
 #include "ioinst.h"
+#include "trace.h"
 
 int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
                                  int *schid)
@@ -34,3 +35,718 @@  int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
     *schid = IOINST_SCHID_NR(value);
     return 0;
 }
+
+int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_xsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("csch", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_csch(sch);
+    }
+    if (ret == -ENODEV) {
+        cc = 3;
+    } else {
+        cc = 0;
+    }
+    return cc;
+}
+
+int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_hsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+}
+
+static int ioinst_schib_valid(SCHIB *schib)
+{
+    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
+        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
+        return 0;
+    }
+    /* Disallow extended measurements for now. */
+    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
+        return 0;
+    }
+    return 1;
+}
+
+int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    SCHIB *schib;
+    uint64_t addr;
+    int ret = -ENODEV;
+    int cc;
+    hwaddr len = sizeof(*schib);
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("msch", cssid, ssid, schid);
+    addr = decode_basedisp_s(env, ipb);
+    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
+    if (!schib || len != sizeof(*schib)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        cc = -EIO;
+        goto out;
+    }
+    if (!ioinst_schib_valid(schib)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        cc = -EIO;
+        goto out;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_msch(sch, schib);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+out:
+    s390_cpu_physical_memory_unmap(env, schib, len, 0);
+    return cc;
+}
+
+static void copy_orb_from_guest(ORB *dest, const ORB *src)
+{
+    dest->intparm = be32_to_cpu(src->intparm);
+    dest->ctrl0 = be16_to_cpu(src->ctrl0);
+    dest->lpm = src->lpm;
+    dest->ctrl1 = src->ctrl1;
+    dest->cpa = be32_to_cpu(src->cpa);
+}
+
+static int ioinst_orb_valid(ORB *orb)
+{
+    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
+        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
+        return 0;
+    }
+    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
+        return 0;
+    }
+    return 1;
+}
+
+int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    ORB *orig_orb, orb;
+    uint64_t addr;
+    int ret = -ENODEV;
+    int cc;
+    hwaddr len = sizeof(*orig_orb);
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
+    addr = decode_basedisp_s(env, ipb);
+    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
+    if (!orig_orb || len != sizeof(*orig_orb)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        cc = -EIO;
+        goto out;
+    }
+    copy_orb_from_guest(&orb, orig_orb);
+    if (!ioinst_orb_valid(&orb)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        cc = -EIO;
+        goto out;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_ssch(sch, &orb);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+out:
+    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
+    return cc;
+}
+
+int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb)
+{
+    CRW *crw;
+    uint64_t addr;
+    int cc;
+    hwaddr len = sizeof(*crw);
+
+    addr = decode_basedisp_s(env, ipb);
+    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
+    if (!crw || len != sizeof(*crw)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        cc = -EIO;
+        goto out;
+    }
+    cc = css_do_stcrw(crw);
+    /* 0 - crw stored, 1 - zeroes stored */
+out:
+    s390_cpu_physical_memory_unmap(env, crw, len, 1);
+    return cc;
+}
+
+int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    uint64_t addr;
+    int cc;
+    SCHIB *schib;
+    hwaddr len = sizeof(*schib);
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
+    addr = decode_basedisp_s(env, ipb);
+    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
+    if (!schib || len != sizeof(*schib)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        cc = -EIO;
+        goto out;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch) {
+        if (css_subch_visible(sch)) {
+            css_do_stsch(sch, schib);
+            cc = 0;
+        } else {
+            /* Indicate no more subchannels in this css/ss */
+            cc = 3;
+        }
+    } else {
+        if (css_schid_final(cssid, ssid, schid)) {
+            cc = 3; /* No more subchannels in this css/ss */
+        } else {
+            /* Store an empty schib. */
+            memset(schib, 0, sizeof(*schib));
+            cc = 0;
+        }
+    }
+out:
+    s390_cpu_physical_memory_unmap(env, schib, len, 1);
+    return cc;
+}
+
+int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    IRB *irb;
+    uint64_t addr;
+    int ret = -ENODEV;
+    int cc;
+    hwaddr len = sizeof(*irb);
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
+    addr = decode_basedisp_s(env, ipb);
+    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
+    if (!irb || len != sizeof(*irb)) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        cc = -EIO;
+        goto out;
+    }
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_tsch(sch, irb);
+        /* 0 - status pending, 1 - not status pending */
+        cc = ret;
+    } else {
+        cc = 3;
+    }
+out:
+    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
+    return cc;
+}
+
+typedef struct ChscReq {
+    uint16_t len;
+    uint16_t command;
+    uint32_t param0;
+    uint32_t param1;
+    uint32_t param2;
+} QEMU_PACKED ChscReq;
+
+typedef struct ChscResp {
+    uint16_t len;
+    uint16_t code;
+    uint32_t param;
+    char data[0];
+} QEMU_PACKED ChscResp;
+
+#define CHSC_MIN_RESP_LEN 0x0008
+
+#define CHSC_SCPD 0x0002
+#define CHSC_SCSC 0x0010
+#define CHSC_SDA  0x0031
+
+#define CHSC_SCPD_0_M 0x20000000
+#define CHSC_SCPD_0_C 0x10000000
+#define CHSC_SCPD_0_FMT 0x0f000000
+#define CHSC_SCPD_0_CSSID 0x00ff0000
+#define CHSC_SCPD_0_RFMT 0x00000f00
+#define CHSC_SCPD_0_RES 0xc000f000
+#define CHSC_SCPD_1_RES 0xffffff00
+#define CHSC_SCPD_01_CHPID 0x000000ff
+static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
+{
+    uint16_t len = be16_to_cpu(req->len);
+    uint32_t param0 = be32_to_cpu(req->param0);
+    uint32_t param1 = be32_to_cpu(req->param1);
+    uint16_t resp_code;
+    int rfmt;
+    uint16_t cssid;
+    uint8_t f_chpid, l_chpid;
+    int desc_size;
+    int m;
+
+    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
+    if ((rfmt == 0) ||  (rfmt == 1)) {
+        rfmt = !!(param0 & CHSC_SCPD_0_C);
+    }
+    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
+        (param1 & CHSC_SCPD_1_RES) || req->param2) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    if (param0 & CHSC_SCPD_0_FMT) {
+        resp_code = 0x0007;
+        goto out_err;
+    }
+    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
+    m = param0 & CHSC_SCPD_0_M;
+    if (cssid != 0) {
+        if (!m || !css_present(cssid)) {
+            resp_code = 0x0008;
+            goto out_err;
+        }
+    }
+    f_chpid = param0 & CHSC_SCPD_01_CHPID;
+    l_chpid = param1 & CHSC_SCPD_01_CHPID;
+    if (l_chpid < f_chpid) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    /* css_collect_chp_desc() is endian-aware */
+    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
+                                     &res->data);
+    res->code = cpu_to_be16(0x0001);
+    res->len = cpu_to_be16(8 + desc_size);
+    res->param = cpu_to_be32(rfmt);
+    return;
+
+  out_err:
+    res->code = cpu_to_be16(resp_code);
+    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
+    res->param = cpu_to_be32(rfmt);
+}
+
+#define CHSC_SCSC_0_M 0x20000000
+#define CHSC_SCSC_0_FMT 0x000f0000
+#define CHSC_SCSC_0_CSSID 0x0000ff00
+#define CHSC_SCSC_0_RES 0xdff000ff
+static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
+{
+    uint16_t len = be16_to_cpu(req->len);
+    uint32_t param0 = be32_to_cpu(req->param0);
+    uint8_t cssid;
+    uint16_t resp_code;
+    uint32_t general_chars[510];
+    uint32_t chsc_chars[508];
+
+    if (len != 0x0010) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+
+    if (param0 & CHSC_SCSC_0_FMT) {
+        resp_code = 0x0007;
+        goto out_err;
+    }
+    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
+    if (cssid != 0) {
+        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
+            resp_code = 0x0008;
+            goto out_err;
+        }
+    }
+    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
+        resp_code = 0x0003;
+        goto out_err;
+    }
+    res->code = cpu_to_be16(0x0001);
+    res->len = cpu_to_be16(4080);
+    res->param = 0;
+
+    memset(general_chars, 0, sizeof(general_chars));
+    memset(chsc_chars, 0, sizeof(chsc_chars));
+
+    general_chars[0] = cpu_to_be32(0x03000000);
+    general_chars[1] = cpu_to_be32(0x00059000);
+
+    chsc_chars[0] = cpu_to_be32(0x40000000);
+    chsc_chars[3] = cpu_to_be32(0x00040000);
+
+    memcpy(res->data, general_chars, sizeof(general_chars));
+    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
+    return;
+
+  out_err:
+    res->code = cpu_to_be16(resp_code);
+    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
+    res->param = 0;
+}
+
+#define CHSC_SDA_0_FMT 0x0f000000
+#define CHSC_SDA_0_OC 0x0000ffff
+#define CHSC_SDA_0_RES 0xf0ff0000
+#define CHSC_SDA_OC_MCSSE 0x0
+#define CHSC_SDA_OC_MSS 0x2
+static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
+{
+    uint16_t resp_code = 0x0001;
+    uint16_t len = be16_to_cpu(req->len);
+    uint32_t param0 = be32_to_cpu(req->param0);
+    uint16_t oc;
+    int ret;
+
+    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
+        resp_code = 0x0003;
+        goto out;
+    }
+
+    if (param0 & CHSC_SDA_0_FMT) {
+        resp_code = 0x0007;
+        goto out;
+    }
+
+    oc = param0 & CHSC_SDA_0_OC;
+    switch (oc) {
+    case CHSC_SDA_OC_MCSSE:
+        ret = css_enable_mcsse();
+        if (ret == -EINVAL) {
+            resp_code = 0x0101;
+            goto out;
+        }
+        break;
+    case CHSC_SDA_OC_MSS:
+        ret = css_enable_mss();
+        if (ret == -EINVAL) {
+            resp_code = 0x0101;
+            goto out;
+        }
+        break;
+    default:
+        resp_code = 0x0003;
+        goto out;
+    }
+
+out:
+    res->code = cpu_to_be16(resp_code);
+    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
+    res->param = 0;
+}
+
+static void ioinst_handle_chsc_unimplemented(ChscResp *res)
+{
+    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
+    res->code = cpu_to_be16(0x0004);
+    res->param = 0;
+}
+
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb)
+{
+    ChscReq *req;
+    ChscResp *res;
+    uint64_t addr;
+    int reg;
+    uint16_t len;
+    uint16_t command;
+    hwaddr map_size = TARGET_PAGE_SIZE;
+    int ret = 0;
+
+    trace_ioinst("chsc");
+    reg = (ipb >> 20) & 0x00f;
+    addr = env->regs[reg];
+    /* Page boundary? */
+    if (addr & 0xfff) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        return -EIO;
+    }
+    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
+    if (!req || map_size != TARGET_PAGE_SIZE) {
+        program_interrupt(env, PGM_SPECIFICATION, 2);
+        ret = -EIO;
+        goto out;
+    }
+    len = be16_to_cpu(req->len);
+    /* Length field valid? */
+    if ((len < 16) || (len > 4088) || (len & 7)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        ret = -EIO;
+        goto out;
+    }
+    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
+    res = (void *)((char *)req + len);
+    command = be16_to_cpu(req->command);
+    trace_ioinst_chsc_cmd(command, len);
+    switch (command) {
+    case CHSC_SCSC:
+        ioinst_handle_chsc_scsc(req, res);
+        break;
+    case CHSC_SCPD:
+        ioinst_handle_chsc_scpd(req, res);
+        break;
+    case CHSC_SDA:
+        ioinst_handle_chsc_sda(req, res);
+        break;
+    default:
+        ioinst_handle_chsc_unimplemented(res);
+        break;
+    }
+
+out:
+    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
+    return ret;
+}
+
+int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
+{
+    uint64_t addr;
+    int lowcore;
+
+    trace_ioinst("tpi");
+    addr = decode_basedisp_s(env, ipb);
+    lowcore = addr ? 0 : 1;
+    if (addr < 8192) {
+        addr += env->psa;
+    } else if ((env->psa <= addr) && (addr < env->psa + 8192)) {
+        addr -= env->psa;
+    }
+    return css_do_tpi(addr, lowcore);
+}
+
+#define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc)
+#define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28)
+#define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1)
+#define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001)
+
+int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
+                       uint32_t ipb)
+{
+    uint8_t mbk;
+    int update;
+    int dct;
+
+    trace_ioinst("schm");
+
+    if (SCHM_REG1_RES(reg1)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    mbk = SCHM_REG1_MBK(reg1);
+    update = SCHM_REG1_UPD(reg1);
+    dct = SCHM_REG1_DCT(reg1);
+
+    if (update && (reg2 & 0x0000000000000fff)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    css_do_schm(mbk, update, dct, update ? reg2 : 0);
+
+    return 0;
+}
+
+int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1)
+{
+    int cssid, ssid, schid, m;
+    SubchDev *sch;
+    int ret = -ENODEV;
+    int cc;
+
+    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    trace_ioinst_sch_id("rsch", cssid, ssid, schid);
+    sch = css_find_subch(m, cssid, ssid, schid);
+    if (sch && css_subch_visible(sch)) {
+        ret = css_do_rsch(sch);
+    }
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EINVAL:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        cc = 1;
+        break;
+    }
+
+    return cc;
+
+}
+
+#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00)
+#define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16)
+#define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff)
+int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1)
+{
+    int cc;
+    uint8_t cssid;
+    uint8_t chpid;
+    int ret;
+
+    if (RCHP_REG1_RES(reg1)) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    cssid = RCHP_REG1_CSSID(reg1);
+    chpid = RCHP_REG1_CHPID(reg1);
+
+    trace_ioinst_chp_id("rchp", cssid, chpid);
+
+    ret = css_do_rchp(cssid, chpid);
+
+    switch (ret) {
+    case -ENODEV:
+        cc = 3;
+        break;
+    case -EBUSY:
+        cc = 2;
+        break;
+    case 0:
+        cc = 0;
+        break;
+    default:
+        /* Invalid channel subsystem. */
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+
+    return cc;
+}
+
+#define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000)
+int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1)
+{
+    /* We do not provide address limit checking, so let's suppress it. */
+    if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
+        program_interrupt(env, PGM_OPERAND, 2);
+        return -EIO;
+    }
+    return 0;
+}
diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
index 037aabc..a59742c 100644
--- a/target-s390x/ioinst.h
+++ b/target-s390x/ioinst.h
@@ -204,4 +204,20 @@  typedef struct CRW {
 
 int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
                                  int *schid);
+int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb);
+int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb);
+int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2,
+                       uint32_t ipb);
+int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1);
+int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1);
+
 #endif
diff --git a/trace-events b/trace-events
index 09091e6..1b54542 100644
--- a/trace-events
+++ b/trace-events
@@ -1060,3 +1060,9 @@  xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]"
 xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x"
 xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]"
 xics_ics_eoi(int nr) "ics_eoi: irq %#x"
+
+# target-s390x/ioinst.c
+ioinst(const char *insn) "IOINST: %s"
+ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)"
+ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)"
+ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command %04x, len %04x"