qemu/target-s390x/ioinst.c
<<
>>
Prefs
   1/*
   2 * I/O instructions for S/390
   3 *
   4 * Copyright 2012 IBM Corp.
   5 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   8 * your option) any later version. See the COPYING file in the top-level
   9 * directory.
  10 */
  11
  12#include <sys/types.h>
  13
  14#include "cpu.h"
  15#include "ioinst.h"
  16#include "trace.h"
  17
  18int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
  19                                 int *schid)
  20{
  21    if (!IOINST_SCHID_ONE(value)) {
  22        return -EINVAL;
  23    }
  24    if (!IOINST_SCHID_M(value)) {
  25        if (IOINST_SCHID_CSSID(value)) {
  26            return -EINVAL;
  27        }
  28        *cssid = 0;
  29        *m = 0;
  30    } else {
  31        *cssid = IOINST_SCHID_CSSID(value);
  32        *m = 1;
  33    }
  34    *ssid = IOINST_SCHID_SSID(value);
  35    *schid = IOINST_SCHID_NR(value);
  36    return 0;
  37}
  38
  39void ioinst_handle_xsch(S390CPU *cpu, uint64_t reg1)
  40{
  41    int cssid, ssid, schid, m;
  42    SubchDev *sch;
  43    int ret = -ENODEV;
  44    int cc;
  45
  46    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
  47        program_interrupt(&cpu->env, PGM_OPERAND, 2);
  48        return;
  49    }
  50    trace_ioinst_sch_id("xsch", cssid, ssid, schid);
  51    sch = css_find_subch(m, cssid, ssid, schid);
  52    if (sch && css_subch_visible(sch)) {
  53        ret = css_do_xsch(sch);
  54    }
  55    switch (ret) {
  56    case -ENODEV:
  57        cc = 3;
  58        break;
  59    case -EBUSY:
  60        cc = 2;
  61        break;
  62    case 0:
  63        cc = 0;
  64        break;
  65    default:
  66        cc = 1;
  67        break;
  68    }
  69    setcc(cpu, cc);
  70}
  71
  72void ioinst_handle_csch(S390CPU *cpu, uint64_t reg1)
  73{
  74    int cssid, ssid, schid, m;
  75    SubchDev *sch;
  76    int ret = -ENODEV;
  77    int cc;
  78
  79    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
  80        program_interrupt(&cpu->env, PGM_OPERAND, 2);
  81        return;
  82    }
  83    trace_ioinst_sch_id("csch", cssid, ssid, schid);
  84    sch = css_find_subch(m, cssid, ssid, schid);
  85    if (sch && css_subch_visible(sch)) {
  86        ret = css_do_csch(sch);
  87    }
  88    if (ret == -ENODEV) {
  89        cc = 3;
  90    } else {
  91        cc = 0;
  92    }
  93    setcc(cpu, cc);
  94}
  95
  96void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1)
  97{
  98    int cssid, ssid, schid, m;
  99    SubchDev *sch;
 100    int ret = -ENODEV;
 101    int cc;
 102
 103    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
 104        program_interrupt(&cpu->env, PGM_OPERAND, 2);
 105        return;
 106    }
 107    trace_ioinst_sch_id("hsch", cssid, ssid, schid);
 108    sch = css_find_subch(m, cssid, ssid, schid);
 109    if (sch && css_subch_visible(sch)) {
 110        ret = css_do_hsch(sch);
 111    }
 112    switch (ret) {
 113    case -ENODEV:
 114        cc = 3;
 115        break;
 116    case -EBUSY:
 117        cc = 2;
 118        break;
 119    case 0:
 120        cc = 0;
 121        break;
 122    default:
 123        cc = 1;
 124        break;
 125    }
 126    setcc(cpu, cc);
 127}
 128
 129static int ioinst_schib_valid(SCHIB *schib)
 130{
 131    if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
 132        (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
 133        return 0;
 134    }
 135    /* Disallow extended measurements for now. */
 136    if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
 137        return 0;
 138    }
 139    return 1;
 140}
 141
 142void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
 143{
 144    int cssid, ssid, schid, m;
 145    SubchDev *sch;
 146    SCHIB *schib;
 147    uint64_t addr;
 148    int ret = -ENODEV;
 149    int cc;
 150    hwaddr len = sizeof(*schib);
 151    CPUS390XState *env = &cpu->env;
 152
 153    addr = decode_basedisp_s(env, ipb);
 154    if (addr & 3) {
 155        program_interrupt(env, PGM_SPECIFICATION, 2);
 156        return;
 157    }
 158    schib = s390_cpu_physical_memory_map(env, addr, &len, 0);
 159    if (!schib || len != sizeof(*schib)) {
 160        program_interrupt(env, PGM_ADDRESSING, 2);
 161        goto out;
 162    }
 163    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
 164        !ioinst_schib_valid(schib)) {
 165        program_interrupt(env, PGM_OPERAND, 2);
 166        goto out;
 167    }
 168    trace_ioinst_sch_id("msch", cssid, ssid, schid);
 169    sch = css_find_subch(m, cssid, ssid, schid);
 170    if (sch && css_subch_visible(sch)) {
 171        ret = css_do_msch(sch, schib);
 172    }
 173    switch (ret) {
 174    case -ENODEV:
 175        cc = 3;
 176        break;
 177    case -EBUSY:
 178        cc = 2;
 179        break;
 180    case 0:
 181        cc = 0;
 182        break;
 183    default:
 184        cc = 1;
 185        break;
 186    }
 187    setcc(cpu, cc);
 188
 189out:
 190    s390_cpu_physical_memory_unmap(env, schib, len, 0);
 191}
 192
 193static void copy_orb_from_guest(ORB *dest, const ORB *src)
 194{
 195    dest->intparm = be32_to_cpu(src->intparm);
 196    dest->ctrl0 = be16_to_cpu(src->ctrl0);
 197    dest->lpm = src->lpm;
 198    dest->ctrl1 = src->ctrl1;
 199    dest->cpa = be32_to_cpu(src->cpa);
 200}
 201
 202static int ioinst_orb_valid(ORB *orb)
 203{
 204    if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) ||
 205        (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) {
 206        return 0;
 207    }
 208    if ((orb->cpa & HIGH_ORDER_BIT) != 0) {
 209        return 0;
 210    }
 211    return 1;
 212}
 213
 214void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
 215{
 216    int cssid, ssid, schid, m;
 217    SubchDev *sch;
 218    ORB *orig_orb, orb;
 219    uint64_t addr;
 220    int ret = -ENODEV;
 221    int cc;
 222    hwaddr len = sizeof(*orig_orb);
 223    CPUS390XState *env = &cpu->env;
 224
 225    addr = decode_basedisp_s(env, ipb);
 226    if (addr & 3) {
 227        program_interrupt(env, PGM_SPECIFICATION, 2);
 228        return;
 229    }
 230    orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0);
 231    if (!orig_orb || len != sizeof(*orig_orb)) {
 232        program_interrupt(env, PGM_ADDRESSING, 2);
 233        goto out;
 234    }
 235    copy_orb_from_guest(&orb, orig_orb);
 236    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid) ||
 237        !ioinst_orb_valid(&orb)) {
 238        program_interrupt(env, PGM_OPERAND, 2);
 239        goto out;
 240    }
 241    trace_ioinst_sch_id("ssch", cssid, ssid, schid);
 242    sch = css_find_subch(m, cssid, ssid, schid);
 243    if (sch && css_subch_visible(sch)) {
 244        ret = css_do_ssch(sch, &orb);
 245    }
 246    switch (ret) {
 247    case -ENODEV:
 248        cc = 3;
 249        break;
 250    case -EBUSY:
 251        cc = 2;
 252        break;
 253    case 0:
 254        cc = 0;
 255        break;
 256    default:
 257        cc = 1;
 258        break;
 259    }
 260    setcc(cpu, cc);
 261
 262out:
 263    s390_cpu_physical_memory_unmap(env, orig_orb, len, 0);
 264}
 265
 266void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb)
 267{
 268    CRW *crw;
 269    uint64_t addr;
 270    int cc;
 271    hwaddr len = sizeof(*crw);
 272    CPUS390XState *env = &cpu->env;
 273
 274    addr = decode_basedisp_s(env, ipb);
 275    if (addr & 3) {
 276        program_interrupt(env, PGM_SPECIFICATION, 2);
 277        return;
 278    }
 279    crw = s390_cpu_physical_memory_map(env, addr, &len, 1);
 280    if (!crw || len != sizeof(*crw)) {
 281        program_interrupt(env, PGM_ADDRESSING, 2);
 282        goto out;
 283    }
 284    cc = css_do_stcrw(crw);
 285    /* 0 - crw stored, 1 - zeroes stored */
 286    setcc(cpu, cc);
 287
 288out:
 289    s390_cpu_physical_memory_unmap(env, crw, len, 1);
 290}
 291
 292void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb)
 293{
 294    int cssid, ssid, schid, m;
 295    SubchDev *sch;
 296    uint64_t addr;
 297    int cc;
 298    SCHIB *schib;
 299    hwaddr len = sizeof(*schib);
 300    CPUS390XState *env = &cpu->env;
 301
 302    addr = decode_basedisp_s(env, ipb);
 303    if (addr & 3) {
 304        program_interrupt(env, PGM_SPECIFICATION, 2);
 305        return;
 306    }
 307    schib = s390_cpu_physical_memory_map(env, addr, &len, 1);
 308    if (!schib || len != sizeof(*schib)) {
 309        program_interrupt(env, PGM_ADDRESSING, 2);
 310        goto out;
 311    }
 312
 313    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
 314        program_interrupt(env, PGM_OPERAND, 2);
 315        goto out;
 316    }
 317    trace_ioinst_sch_id("stsch", cssid, ssid, schid);
 318    sch = css_find_subch(m, cssid, ssid, schid);
 319    if (sch) {
 320        if (css_subch_visible(sch)) {
 321            css_do_stsch(sch, schib);
 322            cc = 0;
 323        } else {
 324            /* Indicate no more subchannels in this css/ss */
 325            cc = 3;
 326        }
 327    } else {
 328        if (css_schid_final(m, cssid, ssid, schid)) {
 329            cc = 3; /* No more subchannels in this css/ss */
 330        } else {
 331            /* Store an empty schib. */
 332            memset(schib, 0, sizeof(*schib));
 333            cc = 0;
 334        }
 335    }
 336    setcc(cpu, cc);
 337
 338out:
 339    s390_cpu_physical_memory_unmap(env, schib, len, 1);
 340}
 341
 342int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb)
 343{
 344    int cssid, ssid, schid, m;
 345    SubchDev *sch;
 346    IRB *irb;
 347    uint64_t addr;
 348    int ret = -ENODEV;
 349    int cc;
 350    hwaddr len = sizeof(*irb);
 351
 352    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
 353        program_interrupt(env, PGM_OPERAND, 2);
 354        return -EIO;
 355    }
 356    trace_ioinst_sch_id("tsch", cssid, ssid, schid);
 357    addr = decode_basedisp_s(env, ipb);
 358    if (addr & 3) {
 359        program_interrupt(env, PGM_SPECIFICATION, 2);
 360        return -EIO;
 361    }
 362    irb = s390_cpu_physical_memory_map(env, addr, &len, 1);
 363    if (!irb || len != sizeof(*irb)) {
 364        program_interrupt(env, PGM_ADDRESSING, 2);
 365        cc = -EIO;
 366        goto out;
 367    }
 368    sch = css_find_subch(m, cssid, ssid, schid);
 369    if (sch && css_subch_visible(sch)) {
 370        ret = css_do_tsch(sch, irb);
 371        /* 0 - status pending, 1 - not status pending */
 372        cc = ret;
 373    } else {
 374        cc = 3;
 375    }
 376out:
 377    s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1);
 378    return cc;
 379}
 380
 381typedef struct ChscReq {
 382    uint16_t len;
 383    uint16_t command;
 384    uint32_t param0;
 385    uint32_t param1;
 386    uint32_t param2;
 387} QEMU_PACKED ChscReq;
 388
 389typedef struct ChscResp {
 390    uint16_t len;
 391    uint16_t code;
 392    uint32_t param;
 393    char data[0];
 394} QEMU_PACKED ChscResp;
 395
 396#define CHSC_MIN_RESP_LEN 0x0008
 397
 398#define CHSC_SCPD 0x0002
 399#define CHSC_SCSC 0x0010
 400#define CHSC_SDA  0x0031
 401
 402#define CHSC_SCPD_0_M 0x20000000
 403#define CHSC_SCPD_0_C 0x10000000
 404#define CHSC_SCPD_0_FMT 0x0f000000
 405#define CHSC_SCPD_0_CSSID 0x00ff0000
 406#define CHSC_SCPD_0_RFMT 0x00000f00
 407#define CHSC_SCPD_0_RES 0xc000f000
 408#define CHSC_SCPD_1_RES 0xffffff00
 409#define CHSC_SCPD_01_CHPID 0x000000ff
 410static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res)
 411{
 412    uint16_t len = be16_to_cpu(req->len);
 413    uint32_t param0 = be32_to_cpu(req->param0);
 414    uint32_t param1 = be32_to_cpu(req->param1);
 415    uint16_t resp_code;
 416    int rfmt;
 417    uint16_t cssid;
 418    uint8_t f_chpid, l_chpid;
 419    int desc_size;
 420    int m;
 421
 422    rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8;
 423    if ((rfmt == 0) ||  (rfmt == 1)) {
 424        rfmt = !!(param0 & CHSC_SCPD_0_C);
 425    }
 426    if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) ||
 427        (param1 & CHSC_SCPD_1_RES) || req->param2) {
 428        resp_code = 0x0003;
 429        goto out_err;
 430    }
 431    if (param0 & CHSC_SCPD_0_FMT) {
 432        resp_code = 0x0007;
 433        goto out_err;
 434    }
 435    cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16;
 436    m = param0 & CHSC_SCPD_0_M;
 437    if (cssid != 0) {
 438        if (!m || !css_present(cssid)) {
 439            resp_code = 0x0008;
 440            goto out_err;
 441        }
 442    }
 443    f_chpid = param0 & CHSC_SCPD_01_CHPID;
 444    l_chpid = param1 & CHSC_SCPD_01_CHPID;
 445    if (l_chpid < f_chpid) {
 446        resp_code = 0x0003;
 447        goto out_err;
 448    }
 449    /* css_collect_chp_desc() is endian-aware */
 450    desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt,
 451                                     &res->data);
 452    res->code = cpu_to_be16(0x0001);
 453    res->len = cpu_to_be16(8 + desc_size);
 454    res->param = cpu_to_be32(rfmt);
 455    return;
 456
 457  out_err:
 458    res->code = cpu_to_be16(resp_code);
 459    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
 460    res->param = cpu_to_be32(rfmt);
 461}
 462
 463#define CHSC_SCSC_0_M 0x20000000
 464#define CHSC_SCSC_0_FMT 0x000f0000
 465#define CHSC_SCSC_0_CSSID 0x0000ff00
 466#define CHSC_SCSC_0_RES 0xdff000ff
 467static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res)
 468{
 469    uint16_t len = be16_to_cpu(req->len);
 470    uint32_t param0 = be32_to_cpu(req->param0);
 471    uint8_t cssid;
 472    uint16_t resp_code;
 473    uint32_t general_chars[510];
 474    uint32_t chsc_chars[508];
 475
 476    if (len != 0x0010) {
 477        resp_code = 0x0003;
 478        goto out_err;
 479    }
 480
 481    if (param0 & CHSC_SCSC_0_FMT) {
 482        resp_code = 0x0007;
 483        goto out_err;
 484    }
 485    cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8;
 486    if (cssid != 0) {
 487        if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) {
 488            resp_code = 0x0008;
 489            goto out_err;
 490        }
 491    }
 492    if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) {
 493        resp_code = 0x0003;
 494        goto out_err;
 495    }
 496    res->code = cpu_to_be16(0x0001);
 497    res->len = cpu_to_be16(4080);
 498    res->param = 0;
 499
 500    memset(general_chars, 0, sizeof(general_chars));
 501    memset(chsc_chars, 0, sizeof(chsc_chars));
 502
 503    general_chars[0] = cpu_to_be32(0x03000000);
 504    general_chars[1] = cpu_to_be32(0x00059000);
 505
 506    chsc_chars[0] = cpu_to_be32(0x40000000);
 507    chsc_chars[3] = cpu_to_be32(0x00040000);
 508
 509    memcpy(res->data, general_chars, sizeof(general_chars));
 510    memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars));
 511    return;
 512
 513  out_err:
 514    res->code = cpu_to_be16(resp_code);
 515    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
 516    res->param = 0;
 517}
 518
 519#define CHSC_SDA_0_FMT 0x0f000000
 520#define CHSC_SDA_0_OC 0x0000ffff
 521#define CHSC_SDA_0_RES 0xf0ff0000
 522#define CHSC_SDA_OC_MCSSE 0x0
 523#define CHSC_SDA_OC_MSS 0x2
 524static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res)
 525{
 526    uint16_t resp_code = 0x0001;
 527    uint16_t len = be16_to_cpu(req->len);
 528    uint32_t param0 = be32_to_cpu(req->param0);
 529    uint16_t oc;
 530    int ret;
 531
 532    if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) {
 533        resp_code = 0x0003;
 534        goto out;
 535    }
 536
 537    if (param0 & CHSC_SDA_0_FMT) {
 538        resp_code = 0x0007;
 539        goto out;
 540    }
 541
 542    oc = param0 & CHSC_SDA_0_OC;
 543    switch (oc) {
 544    case CHSC_SDA_OC_MCSSE:
 545        ret = css_enable_mcsse();
 546        if (ret == -EINVAL) {
 547            resp_code = 0x0101;
 548            goto out;
 549        }
 550        break;
 551    case CHSC_SDA_OC_MSS:
 552        ret = css_enable_mss();
 553        if (ret == -EINVAL) {
 554            resp_code = 0x0101;
 555            goto out;
 556        }
 557        break;
 558    default:
 559        resp_code = 0x0003;
 560        goto out;
 561    }
 562
 563out:
 564    res->code = cpu_to_be16(resp_code);
 565    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
 566    res->param = 0;
 567}
 568
 569static void ioinst_handle_chsc_unimplemented(ChscResp *res)
 570{
 571    res->len = cpu_to_be16(CHSC_MIN_RESP_LEN);
 572    res->code = cpu_to_be16(0x0004);
 573    res->param = 0;
 574}
 575
 576void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb)
 577{
 578    ChscReq *req;
 579    ChscResp *res;
 580    uint64_t addr;
 581    int reg;
 582    uint16_t len;
 583    uint16_t command;
 584    hwaddr map_size = TARGET_PAGE_SIZE;
 585    CPUS390XState *env = &cpu->env;
 586
 587    trace_ioinst("chsc");
 588    reg = (ipb >> 20) & 0x00f;
 589    addr = env->regs[reg];
 590    /* Page boundary? */
 591    if (addr & 0xfff) {
 592        program_interrupt(env, PGM_SPECIFICATION, 2);
 593        return;
 594    }
 595    req = s390_cpu_physical_memory_map(env, addr, &map_size, 1);
 596    if (!req || map_size != TARGET_PAGE_SIZE) {
 597        program_interrupt(env, PGM_ADDRESSING, 2);
 598        goto out;
 599    }
 600    len = be16_to_cpu(req->len);
 601    /* Length field valid? */
 602    if ((len < 16) || (len > 4088) || (len & 7)) {
 603        program_interrupt(env, PGM_OPERAND, 2);
 604        goto out;
 605    }
 606    memset((char *)req + len, 0, TARGET_PAGE_SIZE - len);
 607    res = (void *)((char *)req + len);
 608    command = be16_to_cpu(req->command);
 609    trace_ioinst_chsc_cmd(command, len);
 610    switch (command) {
 611    case CHSC_SCSC:
 612        ioinst_handle_chsc_scsc(req, res);
 613        break;
 614    case CHSC_SCPD:
 615        ioinst_handle_chsc_scpd(req, res);
 616        break;
 617    case CHSC_SDA:
 618        ioinst_handle_chsc_sda(req, res);
 619        break;
 620    default:
 621        ioinst_handle_chsc_unimplemented(res);
 622        break;
 623    }
 624
 625    setcc(cpu, 0);    /* Command execution complete */
 626out:
 627    s390_cpu_physical_memory_unmap(env, req, map_size, 1);
 628}
 629
 630int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb)
 631{
 632    uint64_t addr;
 633    int lowcore;
 634    IOIntCode *int_code;
 635    hwaddr len, orig_len;
 636    int ret;
 637
 638    trace_ioinst("tpi");
 639    addr = decode_basedisp_s(env, ipb);
 640    if (addr & 3) {
 641        program_interrupt(env, PGM_SPECIFICATION, 2);
 642        return -EIO;
 643    }
 644
 645    lowcore = addr ? 0 : 1;
 646    len = lowcore ? 8 /* two words */ : 12 /* three words */;
 647    orig_len = len;
 648    int_code = s390_cpu_physical_memory_map(env, addr, &len, 1);
 649    if (!int_code || (len != orig_len)) {
 650        program_interrupt(env, PGM_ADDRESSING, 2);
 651        ret = -EIO;
 652        goto out;
 653    }
 654    ret = css_do_tpi(int_code, lowcore);
 655out:
 656    s390_cpu_physical_memory_unmap(env, int_code, len, 1);
 657    return ret;
 658}
 659
 660#define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc)
 661#define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28)
 662#define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1)
 663#define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001)
 664
 665void ioinst_handle_schm(S390CPU *cpu, uint64_t reg1, uint64_t reg2,
 666                        uint32_t ipb)
 667{
 668    uint8_t mbk;
 669    int update;
 670    int dct;
 671    CPUS390XState *env = &cpu->env;
 672
 673    trace_ioinst("schm");
 674
 675    if (SCHM_REG1_RES(reg1)) {
 676        program_interrupt(env, PGM_OPERAND, 2);
 677        return;
 678    }
 679
 680    mbk = SCHM_REG1_MBK(reg1);
 681    update = SCHM_REG1_UPD(reg1);
 682    dct = SCHM_REG1_DCT(reg1);
 683
 684    if (update && (reg2 & 0x000000000000001f)) {
 685        program_interrupt(env, PGM_OPERAND, 2);
 686        return;
 687    }
 688
 689    css_do_schm(mbk, update, dct, update ? reg2 : 0);
 690}
 691
 692void ioinst_handle_rsch(S390CPU *cpu, uint64_t reg1)
 693{
 694    int cssid, ssid, schid, m;
 695    SubchDev *sch;
 696    int ret = -ENODEV;
 697    int cc;
 698
 699    if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
 700        program_interrupt(&cpu->env, PGM_OPERAND, 2);
 701        return;
 702    }
 703    trace_ioinst_sch_id("rsch", cssid, ssid, schid);
 704    sch = css_find_subch(m, cssid, ssid, schid);
 705    if (sch && css_subch_visible(sch)) {
 706        ret = css_do_rsch(sch);
 707    }
 708    switch (ret) {
 709    case -ENODEV:
 710        cc = 3;
 711        break;
 712    case -EINVAL:
 713        cc = 2;
 714        break;
 715    case 0:
 716        cc = 0;
 717        break;
 718    default:
 719        cc = 1;
 720        break;
 721    }
 722    setcc(cpu, cc);
 723}
 724
 725#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00)
 726#define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16)
 727#define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff)
 728void ioinst_handle_rchp(S390CPU *cpu, uint64_t reg1)
 729{
 730    int cc;
 731    uint8_t cssid;
 732    uint8_t chpid;
 733    int ret;
 734    CPUS390XState *env = &cpu->env;
 735
 736    if (RCHP_REG1_RES(reg1)) {
 737        program_interrupt(env, PGM_OPERAND, 2);
 738        return;
 739    }
 740
 741    cssid = RCHP_REG1_CSSID(reg1);
 742    chpid = RCHP_REG1_CHPID(reg1);
 743
 744    trace_ioinst_chp_id("rchp", cssid, chpid);
 745
 746    ret = css_do_rchp(cssid, chpid);
 747
 748    switch (ret) {
 749    case -ENODEV:
 750        cc = 3;
 751        break;
 752    case -EBUSY:
 753        cc = 2;
 754        break;
 755    case 0:
 756        cc = 0;
 757        break;
 758    default:
 759        /* Invalid channel subsystem. */
 760        program_interrupt(env, PGM_OPERAND, 2);
 761        return;
 762    }
 763    setcc(cpu, cc);
 764}
 765
 766#define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000)
 767void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1)
 768{
 769    /* We do not provide address limit checking, so let's suppress it. */
 770    if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) {
 771        program_interrupt(&cpu->env, PGM_OPERAND, 2);
 772    }
 773}
 774