1 /* 2 * Channel subsystem base support. 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 <hw/qdev.h> 13 #include "qemu/bitops.h" 14 #include "exec/address-spaces.h" 15 #include "cpu.h" 16 #include "ioinst.h" 17 #include "css.h" 18 #include "trace.h" 19 20 typedef struct CrwContainer { 21 CRW crw; 22 QTAILQ_ENTRY(CrwContainer) sibling; 23 } CrwContainer; 24 25 typedef struct ChpInfo { 26 uint8_t in_use; 27 uint8_t type; 28 uint8_t is_virtual; 29 } ChpInfo; 30 31 typedef struct SubchSet { 32 SubchDev *sch[MAX_SCHID + 1]; 33 unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)]; 34 unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)]; 35 } SubchSet; 36 37 typedef struct CssImage { 38 SubchSet *sch_set[MAX_SSID + 1]; 39 ChpInfo chpids[MAX_CHPID + 1]; 40 } CssImage; 41 42 typedef struct ChannelSubSys { 43 QTAILQ_HEAD(, CrwContainer) pending_crws; 44 bool do_crw_mchk; 45 bool crws_lost; 46 uint8_t max_cssid; 47 uint8_t max_ssid; 48 bool chnmon_active; 49 uint64_t chnmon_area; 50 CssImage *css[MAX_CSSID + 1]; 51 uint8_t default_cssid; 52 } ChannelSubSys; 53 54 static ChannelSubSys *channel_subsys; 55 56 int css_create_css_image(uint8_t cssid, bool default_image) 57 { 58 trace_css_new_image(cssid, default_image ? "(default)" : ""); 59 if (cssid > MAX_CSSID) { 60 return -EINVAL; 61 } 62 if (channel_subsys->css[cssid]) { 63 return -EBUSY; 64 } 65 channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage)); 66 if (default_image) { 67 channel_subsys->default_cssid = cssid; 68 } 69 return 0; 70 } 71 72 uint16_t css_build_subchannel_id(SubchDev *sch) 73 { 74 if (channel_subsys->max_cssid > 0) { 75 return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; 76 } 77 return (sch->ssid << 1) | 1; 78 } 79 80 static void css_inject_io_interrupt(SubchDev *sch) 81 { 82 S390CPU *cpu = s390_cpu_addr2state(0); 83 uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; 84 85 trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, 86 sch->curr_status.pmcw.intparm, isc, ""); 87 s390_io_interrupt(cpu, 88 css_build_subchannel_id(sch), 89 sch->schid, 90 sch->curr_status.pmcw.intparm, 91 isc << 27); 92 } 93 94 void css_conditional_io_interrupt(SubchDev *sch) 95 { 96 /* 97 * If the subchannel is not currently status pending, make it pending 98 * with alert status. 99 */ 100 if (!(sch->curr_status.scsw.ctrl & SCSW_STCTL_STATUS_PEND)) { 101 S390CPU *cpu = s390_cpu_addr2state(0); 102 uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; 103 104 trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, 105 sch->curr_status.pmcw.intparm, isc, 106 "(unsolicited)"); 107 sch->curr_status.scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; 108 sch->curr_status.scsw.ctrl |= 109 SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 110 /* Inject an I/O interrupt. */ 111 s390_io_interrupt(cpu, 112 css_build_subchannel_id(sch), 113 sch->schid, 114 sch->curr_status.pmcw.intparm, 115 isc << 27); 116 } 117 } 118 119 void css_adapter_interrupt(uint8_t isc) 120 { 121 S390CPU *cpu = s390_cpu_addr2state(0); 122 uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; 123 124 trace_css_adapter_interrupt(isc); 125 s390_io_interrupt(cpu, 0, 0, 0, io_int_word); 126 } 127 128 static void sch_handle_clear_func(SubchDev *sch) 129 { 130 PMCW *p = &sch->curr_status.pmcw; 131 SCSW *s = &sch->curr_status.scsw; 132 int path; 133 134 /* Path management: In our simple css, we always choose the only path. */ 135 path = 0x80; 136 137 /* Reset values prior to 'issuing the clear signal'. */ 138 p->lpum = 0; 139 p->pom = 0xff; 140 s->flags &= ~SCSW_FLAGS_MASK_PNO; 141 142 /* We always 'attempt to issue the clear signal', and we always succeed. */ 143 sch->channel_prog = 0x0; 144 sch->last_cmd_valid = false; 145 s->ctrl &= ~SCSW_ACTL_CLEAR_PEND; 146 s->ctrl |= SCSW_STCTL_STATUS_PEND; 147 148 s->dstat = 0; 149 s->cstat = 0; 150 p->lpum = path; 151 152 } 153 154 static void sch_handle_halt_func(SubchDev *sch) 155 { 156 157 PMCW *p = &sch->curr_status.pmcw; 158 SCSW *s = &sch->curr_status.scsw; 159 int path; 160 161 /* Path management: In our simple css, we always choose the only path. */ 162 path = 0x80; 163 164 /* We always 'attempt to issue the halt signal', and we always succeed. */ 165 sch->channel_prog = 0x0; 166 sch->last_cmd_valid = false; 167 s->ctrl &= ~SCSW_ACTL_HALT_PEND; 168 s->ctrl |= SCSW_STCTL_STATUS_PEND; 169 170 if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || 171 !((s->ctrl & SCSW_ACTL_START_PEND) || 172 (s->ctrl & SCSW_ACTL_SUSP))) { 173 s->dstat = SCSW_DSTAT_DEVICE_END; 174 } 175 s->cstat = 0; 176 p->lpum = path; 177 178 } 179 180 static void copy_sense_id_to_guest(SenseId *dest, SenseId *src) 181 { 182 int i; 183 184 dest->reserved = src->reserved; 185 dest->cu_type = cpu_to_be16(src->cu_type); 186 dest->cu_model = src->cu_model; 187 dest->dev_type = cpu_to_be16(src->dev_type); 188 dest->dev_model = src->dev_model; 189 dest->unused = src->unused; 190 for (i = 0; i < ARRAY_SIZE(dest->ciw); i++) { 191 dest->ciw[i].type = src->ciw[i].type; 192 dest->ciw[i].command = src->ciw[i].command; 193 dest->ciw[i].count = cpu_to_be16(src->ciw[i].count); 194 } 195 } 196 197 static CCW1 copy_ccw_from_guest(hwaddr addr) 198 { 199 CCW1 tmp; 200 CCW1 ret; 201 202 cpu_physical_memory_read(addr, &tmp, sizeof(tmp)); 203 ret.cmd_code = tmp.cmd_code; 204 ret.flags = tmp.flags; 205 ret.count = be16_to_cpu(tmp.count); 206 ret.cda = be32_to_cpu(tmp.cda); 207 208 return ret; 209 } 210 211 static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) 212 { 213 int ret; 214 bool check_len; 215 int len; 216 CCW1 ccw; 217 218 if (!ccw_addr) { 219 return -EIO; 220 } 221 222 ccw = copy_ccw_from_guest(ccw_addr); 223 224 /* Check for invalid command codes. */ 225 if ((ccw.cmd_code & 0x0f) == 0) { 226 return -EINVAL; 227 } 228 if (((ccw.cmd_code & 0x0f) == CCW_CMD_TIC) && 229 ((ccw.cmd_code & 0xf0) != 0)) { 230 return -EINVAL; 231 } 232 233 if (ccw.flags & CCW_FLAG_SUSPEND) { 234 return -EINPROGRESS; 235 } 236 237 check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); 238 239 /* Look at the command. */ 240 switch (ccw.cmd_code) { 241 case CCW_CMD_NOOP: 242 /* Nothing to do. */ 243 ret = 0; 244 break; 245 case CCW_CMD_BASIC_SENSE: 246 if (check_len) { 247 if (ccw.count != sizeof(sch->sense_data)) { 248 ret = -EINVAL; 249 break; 250 } 251 } 252 len = MIN(ccw.count, sizeof(sch->sense_data)); 253 cpu_physical_memory_write(ccw.cda, sch->sense_data, len); 254 sch->curr_status.scsw.count = ccw.count - len; 255 memset(sch->sense_data, 0, sizeof(sch->sense_data)); 256 ret = 0; 257 break; 258 case CCW_CMD_SENSE_ID: 259 { 260 SenseId sense_id; 261 262 copy_sense_id_to_guest(&sense_id, &sch->id); 263 /* Sense ID information is device specific. */ 264 if (check_len) { 265 if (ccw.count != sizeof(sense_id)) { 266 ret = -EINVAL; 267 break; 268 } 269 } 270 len = MIN(ccw.count, sizeof(sense_id)); 271 /* 272 * Only indicate 0xff in the first sense byte if we actually 273 * have enough place to store at least bytes 0-3. 274 */ 275 if (len >= 4) { 276 sense_id.reserved = 0xff; 277 } else { 278 sense_id.reserved = 0; 279 } 280 cpu_physical_memory_write(ccw.cda, &sense_id, len); 281 sch->curr_status.scsw.count = ccw.count - len; 282 ret = 0; 283 break; 284 } 285 case CCW_CMD_TIC: 286 if (sch->last_cmd_valid && (sch->last_cmd.cmd_code == CCW_CMD_TIC)) { 287 ret = -EINVAL; 288 break; 289 } 290 if (ccw.flags & (CCW_FLAG_CC | CCW_FLAG_DC)) { 291 ret = -EINVAL; 292 break; 293 } 294 sch->channel_prog = ccw.cda; 295 ret = -EAGAIN; 296 break; 297 default: 298 if (sch->ccw_cb) { 299 /* Handle device specific commands. */ 300 ret = sch->ccw_cb(sch, ccw); 301 } else { 302 ret = -ENOSYS; 303 } 304 break; 305 } 306 sch->last_cmd = ccw; 307 sch->last_cmd_valid = true; 308 if (ret == 0) { 309 if (ccw.flags & CCW_FLAG_CC) { 310 sch->channel_prog += 8; 311 ret = -EAGAIN; 312 } 313 } 314 315 return ret; 316 } 317 318 static void sch_handle_start_func(SubchDev *sch, ORB *orb) 319 { 320 321 PMCW *p = &sch->curr_status.pmcw; 322 SCSW *s = &sch->curr_status.scsw; 323 int path; 324 int ret; 325 326 /* Path management: In our simple css, we always choose the only path. */ 327 path = 0x80; 328 329 if (!(s->ctrl & SCSW_ACTL_SUSP)) { 330 /* Look at the orb and try to execute the channel program. */ 331 assert(orb != NULL); /* resume does not pass an orb */ 332 p->intparm = orb->intparm; 333 if (!(orb->lpm & path)) { 334 /* Generate a deferred cc 3 condition. */ 335 s->flags |= SCSW_FLAGS_MASK_CC; 336 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 337 s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); 338 return; 339 } 340 } else { 341 s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); 342 } 343 sch->last_cmd_valid = false; 344 do { 345 ret = css_interpret_ccw(sch, sch->channel_prog); 346 switch (ret) { 347 case -EAGAIN: 348 /* ccw chain, continue processing */ 349 break; 350 case 0: 351 /* success */ 352 s->ctrl &= ~SCSW_ACTL_START_PEND; 353 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 354 s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | 355 SCSW_STCTL_STATUS_PEND; 356 s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; 357 break; 358 case -ENOSYS: 359 /* unsupported command, generate unit check (command reject) */ 360 s->ctrl &= ~SCSW_ACTL_START_PEND; 361 s->dstat = SCSW_DSTAT_UNIT_CHECK; 362 /* Set sense bit 0 in ecw0. */ 363 sch->sense_data[0] = 0x80; 364 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 365 s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | 366 SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 367 break; 368 case -EFAULT: 369 /* memory problem, generate channel data check */ 370 s->ctrl &= ~SCSW_ACTL_START_PEND; 371 s->cstat = SCSW_CSTAT_DATA_CHECK; 372 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 373 s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | 374 SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 375 break; 376 case -EBUSY: 377 /* subchannel busy, generate deferred cc 1 */ 378 s->flags &= ~SCSW_FLAGS_MASK_CC; 379 s->flags |= (1 << 8); 380 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 381 s->ctrl |= SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 382 break; 383 case -EINPROGRESS: 384 /* channel program has been suspended */ 385 s->ctrl &= ~SCSW_ACTL_START_PEND; 386 s->ctrl |= SCSW_ACTL_SUSP; 387 break; 388 default: 389 /* error, generate channel program check */ 390 s->ctrl &= ~SCSW_ACTL_START_PEND; 391 s->cstat = SCSW_CSTAT_PROG_CHECK; 392 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 393 s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | 394 SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; 395 break; 396 } 397 } while (ret == -EAGAIN); 398 399 } 400 401 /* 402 * On real machines, this would run asynchronously to the main vcpus. 403 * We might want to make some parts of the ssch handling (interpreting 404 * read/writes) asynchronous later on if we start supporting more than 405 * our current very simple devices. 406 */ 407 static void do_subchannel_work(SubchDev *sch, ORB *orb) 408 { 409 410 SCSW *s = &sch->curr_status.scsw; 411 412 if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { 413 sch_handle_clear_func(sch); 414 } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { 415 sch_handle_halt_func(sch); 416 } else if (s->ctrl & SCSW_FCTL_START_FUNC) { 417 sch_handle_start_func(sch, orb); 418 } else { 419 /* Cannot happen. */ 420 return; 421 } 422 css_inject_io_interrupt(sch); 423 } 424 425 static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) 426 { 427 int i; 428 429 dest->intparm = cpu_to_be32(src->intparm); 430 dest->flags = cpu_to_be16(src->flags); 431 dest->devno = cpu_to_be16(src->devno); 432 dest->lpm = src->lpm; 433 dest->pnom = src->pnom; 434 dest->lpum = src->lpum; 435 dest->pim = src->pim; 436 dest->mbi = cpu_to_be16(src->mbi); 437 dest->pom = src->pom; 438 dest->pam = src->pam; 439 for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { 440 dest->chpid[i] = src->chpid[i]; 441 } 442 dest->chars = cpu_to_be32(src->chars); 443 } 444 445 static void copy_scsw_to_guest(SCSW *dest, const SCSW *src) 446 { 447 dest->flags = cpu_to_be16(src->flags); 448 dest->ctrl = cpu_to_be16(src->ctrl); 449 dest->cpa = cpu_to_be32(src->cpa); 450 dest->dstat = src->dstat; 451 dest->cstat = src->cstat; 452 dest->count = cpu_to_be16(src->count); 453 } 454 455 static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) 456 { 457 int i; 458 459 copy_pmcw_to_guest(&dest->pmcw, &src->pmcw); 460 copy_scsw_to_guest(&dest->scsw, &src->scsw); 461 dest->mba = cpu_to_be64(src->mba); 462 for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { 463 dest->mda[i] = src->mda[i]; 464 } 465 } 466 467 int css_do_stsch(SubchDev *sch, SCHIB *schib) 468 { 469 /* Use current status. */ 470 copy_schib_to_guest(schib, &sch->curr_status); 471 return 0; 472 } 473 474 static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) 475 { 476 int i; 477 478 dest->intparm = be32_to_cpu(src->intparm); 479 dest->flags = be16_to_cpu(src->flags); 480 dest->devno = be16_to_cpu(src->devno); 481 dest->lpm = src->lpm; 482 dest->pnom = src->pnom; 483 dest->lpum = src->lpum; 484 dest->pim = src->pim; 485 dest->mbi = be16_to_cpu(src->mbi); 486 dest->pom = src->pom; 487 dest->pam = src->pam; 488 for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { 489 dest->chpid[i] = src->chpid[i]; 490 } 491 dest->chars = be32_to_cpu(src->chars); 492 } 493 494 static void copy_scsw_from_guest(SCSW *dest, const SCSW *src) 495 { 496 dest->flags = be16_to_cpu(src->flags); 497 dest->ctrl = be16_to_cpu(src->ctrl); 498 dest->cpa = be32_to_cpu(src->cpa); 499 dest->dstat = src->dstat; 500 dest->cstat = src->cstat; 501 dest->count = be16_to_cpu(src->count); 502 } 503 504 static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) 505 { 506 int i; 507 508 copy_pmcw_from_guest(&dest->pmcw, &src->pmcw); 509 copy_scsw_from_guest(&dest->scsw, &src->scsw); 510 dest->mba = be64_to_cpu(src->mba); 511 for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { 512 dest->mda[i] = src->mda[i]; 513 } 514 } 515 516 int css_do_msch(SubchDev *sch, SCHIB *orig_schib) 517 { 518 SCSW *s = &sch->curr_status.scsw; 519 PMCW *p = &sch->curr_status.pmcw; 520 int ret; 521 SCHIB schib; 522 523 if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_DNV)) { 524 ret = 0; 525 goto out; 526 } 527 528 if (s->ctrl & SCSW_STCTL_STATUS_PEND) { 529 ret = -EINPROGRESS; 530 goto out; 531 } 532 533 if (s->ctrl & 534 (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) { 535 ret = -EBUSY; 536 goto out; 537 } 538 539 copy_schib_from_guest(&schib, orig_schib); 540 /* Only update the program-modifiable fields. */ 541 p->intparm = schib.pmcw.intparm; 542 p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | 543 PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | 544 PMCW_FLAGS_MASK_MP); 545 p->flags |= schib.pmcw.flags & 546 (PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | 547 PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | 548 PMCW_FLAGS_MASK_MP); 549 p->lpm = schib.pmcw.lpm; 550 p->mbi = schib.pmcw.mbi; 551 p->pom = schib.pmcw.pom; 552 p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); 553 p->chars |= schib.pmcw.chars & 554 (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); 555 sch->curr_status.mba = schib.mba; 556 557 ret = 0; 558 559 out: 560 return ret; 561 } 562 563 int css_do_xsch(SubchDev *sch) 564 { 565 SCSW *s = &sch->curr_status.scsw; 566 PMCW *p = &sch->curr_status.pmcw; 567 int ret; 568 569 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 570 ret = -ENODEV; 571 goto out; 572 } 573 574 if (!(s->ctrl & SCSW_CTRL_MASK_FCTL) || 575 ((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || 576 (!(s->ctrl & 577 (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) || 578 (s->ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { 579 ret = -EINPROGRESS; 580 goto out; 581 } 582 583 if (s->ctrl & SCSW_CTRL_MASK_STCTL) { 584 ret = -EBUSY; 585 goto out; 586 } 587 588 /* Cancel the current operation. */ 589 s->ctrl &= ~(SCSW_FCTL_START_FUNC | 590 SCSW_ACTL_RESUME_PEND | 591 SCSW_ACTL_START_PEND | 592 SCSW_ACTL_SUSP); 593 sch->channel_prog = 0x0; 594 sch->last_cmd_valid = false; 595 s->dstat = 0; 596 s->cstat = 0; 597 ret = 0; 598 599 out: 600 return ret; 601 } 602 603 int css_do_csch(SubchDev *sch) 604 { 605 SCSW *s = &sch->curr_status.scsw; 606 PMCW *p = &sch->curr_status.pmcw; 607 int ret; 608 609 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 610 ret = -ENODEV; 611 goto out; 612 } 613 614 /* Trigger the clear function. */ 615 s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); 616 s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC; 617 618 do_subchannel_work(sch, NULL); 619 ret = 0; 620 621 out: 622 return ret; 623 } 624 625 int css_do_hsch(SubchDev *sch) 626 { 627 SCSW *s = &sch->curr_status.scsw; 628 PMCW *p = &sch->curr_status.pmcw; 629 int ret; 630 631 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 632 ret = -ENODEV; 633 goto out; 634 } 635 636 if (((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || 637 (s->ctrl & (SCSW_STCTL_PRIMARY | 638 SCSW_STCTL_SECONDARY | 639 SCSW_STCTL_ALERT))) { 640 ret = -EINPROGRESS; 641 goto out; 642 } 643 644 if (s->ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { 645 ret = -EBUSY; 646 goto out; 647 } 648 649 /* Trigger the halt function. */ 650 s->ctrl |= SCSW_FCTL_HALT_FUNC; 651 s->ctrl &= ~SCSW_FCTL_START_FUNC; 652 if (((s->ctrl & SCSW_CTRL_MASK_ACTL) == 653 (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) && 654 ((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_INTERMEDIATE)) { 655 s->ctrl &= ~SCSW_STCTL_STATUS_PEND; 656 } 657 s->ctrl |= SCSW_ACTL_HALT_PEND; 658 659 do_subchannel_work(sch, NULL); 660 ret = 0; 661 662 out: 663 return ret; 664 } 665 666 static void css_update_chnmon(SubchDev *sch) 667 { 668 if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_MME)) { 669 /* Not active. */ 670 return; 671 } 672 /* The counter is conveniently located at the beginning of the struct. */ 673 if (sch->curr_status.pmcw.chars & PMCW_CHARS_MASK_MBFC) { 674 /* Format 1, per-subchannel area. */ 675 uint32_t count; 676 677 count = ldl_phys(&address_space_memory, sch->curr_status.mba); 678 count++; 679 stl_phys(&address_space_memory, sch->curr_status.mba, count); 680 } else { 681 /* Format 0, global area. */ 682 uint32_t offset; 683 uint16_t count; 684 685 offset = sch->curr_status.pmcw.mbi << 5; 686 count = lduw_phys(&address_space_memory, 687 channel_subsys->chnmon_area + offset); 688 count++; 689 stw_phys(&address_space_memory, 690 channel_subsys->chnmon_area + offset, count); 691 } 692 } 693 694 int css_do_ssch(SubchDev *sch, ORB *orb) 695 { 696 SCSW *s = &sch->curr_status.scsw; 697 PMCW *p = &sch->curr_status.pmcw; 698 int ret; 699 700 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 701 ret = -ENODEV; 702 goto out; 703 } 704 705 if (s->ctrl & SCSW_STCTL_STATUS_PEND) { 706 ret = -EINPROGRESS; 707 goto out; 708 } 709 710 if (s->ctrl & (SCSW_FCTL_START_FUNC | 711 SCSW_FCTL_HALT_FUNC | 712 SCSW_FCTL_CLEAR_FUNC)) { 713 ret = -EBUSY; 714 goto out; 715 } 716 717 /* If monitoring is active, update counter. */ 718 if (channel_subsys->chnmon_active) { 719 css_update_chnmon(sch); 720 } 721 sch->channel_prog = orb->cpa; 722 /* Trigger the start function. */ 723 s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); 724 s->flags &= ~SCSW_FLAGS_MASK_PNO; 725 726 do_subchannel_work(sch, orb); 727 ret = 0; 728 729 out: 730 return ret; 731 } 732 733 static void copy_irb_to_guest(IRB *dest, const IRB *src) 734 { 735 int i; 736 737 copy_scsw_to_guest(&dest->scsw, &src->scsw); 738 739 for (i = 0; i < ARRAY_SIZE(dest->esw); i++) { 740 dest->esw[i] = cpu_to_be32(src->esw[i]); 741 } 742 for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) { 743 dest->ecw[i] = cpu_to_be32(src->ecw[i]); 744 } 745 for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { 746 dest->emw[i] = cpu_to_be32(src->emw[i]); 747 } 748 } 749 750 int css_do_tsch(SubchDev *sch, IRB *target_irb) 751 { 752 SCSW *s = &sch->curr_status.scsw; 753 PMCW *p = &sch->curr_status.pmcw; 754 uint16_t stctl; 755 uint16_t fctl; 756 uint16_t actl; 757 IRB irb; 758 int ret; 759 760 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 761 ret = 3; 762 goto out; 763 } 764 765 stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; 766 fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; 767 actl = s->ctrl & SCSW_CTRL_MASK_ACTL; 768 769 /* Prepare the irb for the guest. */ 770 memset(&irb, 0, sizeof(IRB)); 771 772 /* Copy scsw from current status. */ 773 memcpy(&irb.scsw, s, sizeof(SCSW)); 774 if (stctl & SCSW_STCTL_STATUS_PEND) { 775 if (s->cstat & (SCSW_CSTAT_DATA_CHECK | 776 SCSW_CSTAT_CHN_CTRL_CHK | 777 SCSW_CSTAT_INTF_CTRL_CHK)) { 778 irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF; 779 irb.esw[0] = 0x04804000; 780 } else { 781 irb.esw[0] = 0x00800000; 782 } 783 /* If a unit check is pending, copy sense data. */ 784 if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && 785 (p->chars & PMCW_CHARS_MASK_CSENSE)) { 786 irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL; 787 memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data)); 788 irb.esw[1] = 0x01000000 | (sizeof(sch->sense_data) << 8); 789 } 790 } 791 /* Store the irb to the guest. */ 792 copy_irb_to_guest(target_irb, &irb); 793 794 /* Clear conditions on subchannel, if applicable. */ 795 if (stctl & SCSW_STCTL_STATUS_PEND) { 796 s->ctrl &= ~SCSW_CTRL_MASK_STCTL; 797 if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) || 798 ((fctl & SCSW_FCTL_HALT_FUNC) && 799 (actl & SCSW_ACTL_SUSP))) { 800 s->ctrl &= ~SCSW_CTRL_MASK_FCTL; 801 } 802 if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) { 803 s->flags &= ~SCSW_FLAGS_MASK_PNO; 804 s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | 805 SCSW_ACTL_START_PEND | 806 SCSW_ACTL_HALT_PEND | 807 SCSW_ACTL_CLEAR_PEND | 808 SCSW_ACTL_SUSP); 809 } else { 810 if ((actl & SCSW_ACTL_SUSP) && 811 (fctl & SCSW_FCTL_START_FUNC)) { 812 s->flags &= ~SCSW_FLAGS_MASK_PNO; 813 if (fctl & SCSW_FCTL_HALT_FUNC) { 814 s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | 815 SCSW_ACTL_START_PEND | 816 SCSW_ACTL_HALT_PEND | 817 SCSW_ACTL_CLEAR_PEND | 818 SCSW_ACTL_SUSP); 819 } else { 820 s->ctrl &= ~SCSW_ACTL_RESUME_PEND; 821 } 822 } 823 } 824 /* Clear pending sense data. */ 825 if (p->chars & PMCW_CHARS_MASK_CSENSE) { 826 memset(sch->sense_data, 0 , sizeof(sch->sense_data)); 827 } 828 } 829 830 ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0); 831 832 out: 833 return ret; 834 } 835 836 static void copy_crw_to_guest(CRW *dest, const CRW *src) 837 { 838 dest->flags = cpu_to_be16(src->flags); 839 dest->rsid = cpu_to_be16(src->rsid); 840 } 841 842 int css_do_stcrw(CRW *crw) 843 { 844 CrwContainer *crw_cont; 845 int ret; 846 847 crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws); 848 if (crw_cont) { 849 QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); 850 copy_crw_to_guest(crw, &crw_cont->crw); 851 g_free(crw_cont); 852 ret = 0; 853 } else { 854 /* List was empty, turn crw machine checks on again. */ 855 memset(crw, 0, sizeof(*crw)); 856 channel_subsys->do_crw_mchk = true; 857 ret = 1; 858 } 859 860 return ret; 861 } 862 863 int css_do_tpi(IOIntCode *int_code, int lowcore) 864 { 865 /* No pending interrupts for !KVM. */ 866 return 0; 867 } 868 869 int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, 870 int rfmt, void *buf) 871 { 872 int i, desc_size; 873 uint32_t words[8]; 874 uint32_t chpid_type_word; 875 CssImage *css; 876 877 if (!m && !cssid) { 878 css = channel_subsys->css[channel_subsys->default_cssid]; 879 } else { 880 css = channel_subsys->css[cssid]; 881 } 882 if (!css) { 883 return 0; 884 } 885 desc_size = 0; 886 for (i = f_chpid; i <= l_chpid; i++) { 887 if (css->chpids[i].in_use) { 888 chpid_type_word = 0x80000000 | (css->chpids[i].type << 8) | i; 889 if (rfmt == 0) { 890 words[0] = cpu_to_be32(chpid_type_word); 891 words[1] = 0; 892 memcpy(buf + desc_size, words, 8); 893 desc_size += 8; 894 } else if (rfmt == 1) { 895 words[0] = cpu_to_be32(chpid_type_word); 896 words[1] = 0; 897 words[2] = 0; 898 words[3] = 0; 899 words[4] = 0; 900 words[5] = 0; 901 words[6] = 0; 902 words[7] = 0; 903 memcpy(buf + desc_size, words, 32); 904 desc_size += 32; 905 } 906 } 907 } 908 return desc_size; 909 } 910 911 void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) 912 { 913 /* dct is currently ignored (not really meaningful for our devices) */ 914 /* TODO: Don't ignore mbk. */ 915 if (update && !channel_subsys->chnmon_active) { 916 /* Enable measuring. */ 917 channel_subsys->chnmon_area = mbo; 918 channel_subsys->chnmon_active = true; 919 } 920 if (!update && channel_subsys->chnmon_active) { 921 /* Disable measuring. */ 922 channel_subsys->chnmon_area = 0; 923 channel_subsys->chnmon_active = false; 924 } 925 } 926 927 int css_do_rsch(SubchDev *sch) 928 { 929 SCSW *s = &sch->curr_status.scsw; 930 PMCW *p = &sch->curr_status.pmcw; 931 int ret; 932 933 if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { 934 ret = -ENODEV; 935 goto out; 936 } 937 938 if (s->ctrl & SCSW_STCTL_STATUS_PEND) { 939 ret = -EINPROGRESS; 940 goto out; 941 } 942 943 if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || 944 (s->ctrl & SCSW_ACTL_RESUME_PEND) || 945 (!(s->ctrl & SCSW_ACTL_SUSP))) { 946 ret = -EINVAL; 947 goto out; 948 } 949 950 /* If monitoring is active, update counter. */ 951 if (channel_subsys->chnmon_active) { 952 css_update_chnmon(sch); 953 } 954 955 s->ctrl |= SCSW_ACTL_RESUME_PEND; 956 do_subchannel_work(sch, NULL); 957 ret = 0; 958 959 out: 960 return ret; 961 } 962 963 int css_do_rchp(uint8_t cssid, uint8_t chpid) 964 { 965 uint8_t real_cssid; 966 967 if (cssid > channel_subsys->max_cssid) { 968 return -EINVAL; 969 } 970 if (channel_subsys->max_cssid == 0) { 971 real_cssid = channel_subsys->default_cssid; 972 } else { 973 real_cssid = cssid; 974 } 975 if (!channel_subsys->css[real_cssid]) { 976 return -EINVAL; 977 } 978 979 if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) { 980 return -ENODEV; 981 } 982 983 if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) { 984 fprintf(stderr, 985 "rchp unsupported for non-virtual chpid %x.%02x!\n", 986 real_cssid, chpid); 987 return -ENODEV; 988 } 989 990 /* We don't really use a channel path, so we're done here. */ 991 css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 992 channel_subsys->max_cssid > 0 ? 1 : 0, chpid); 993 if (channel_subsys->max_cssid > 0) { 994 css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8); 995 } 996 return 0; 997 } 998 999 bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid) 1000 { 1001 SubchSet *set; 1002 uint8_t real_cssid; 1003 1004 real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; 1005 if (real_cssid > MAX_CSSID || ssid > MAX_SSID || 1006 !channel_subsys->css[real_cssid] || 1007 !channel_subsys->css[real_cssid]->sch_set[ssid]) { 1008 return true; 1009 } 1010 set = channel_subsys->css[real_cssid]->sch_set[ssid]; 1011 return schid > find_last_bit(set->schids_used, 1012 (MAX_SCHID + 1) / sizeof(unsigned long)); 1013 } 1014 1015 static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) 1016 { 1017 CssImage *css; 1018 1019 trace_css_chpid_add(cssid, chpid, type); 1020 if (cssid > MAX_CSSID) { 1021 return -EINVAL; 1022 } 1023 css = channel_subsys->css[cssid]; 1024 if (!css) { 1025 return -EINVAL; 1026 } 1027 if (css->chpids[chpid].in_use) { 1028 return -EEXIST; 1029 } 1030 css->chpids[chpid].in_use = 1; 1031 css->chpids[chpid].type = type; 1032 css->chpids[chpid].is_virtual = 1; 1033 1034 css_generate_chp_crws(cssid, chpid); 1035 1036 return 0; 1037 } 1038 1039 void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type) 1040 { 1041 PMCW *p = &sch->curr_status.pmcw; 1042 SCSW *s = &sch->curr_status.scsw; 1043 int i; 1044 CssImage *css = channel_subsys->css[sch->cssid]; 1045 1046 assert(css != NULL); 1047 memset(p, 0, sizeof(PMCW)); 1048 p->flags |= PMCW_FLAGS_MASK_DNV; 1049 p->devno = sch->devno; 1050 /* single path */ 1051 p->pim = 0x80; 1052 p->pom = 0xff; 1053 p->pam = 0x80; 1054 p->chpid[0] = chpid; 1055 if (!css->chpids[chpid].in_use) { 1056 css_add_virtual_chpid(sch->cssid, chpid, type); 1057 } 1058 1059 memset(s, 0, sizeof(SCSW)); 1060 sch->curr_status.mba = 0; 1061 for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { 1062 sch->curr_status.mda[i] = 0; 1063 } 1064 } 1065 1066 SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) 1067 { 1068 uint8_t real_cssid; 1069 1070 real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; 1071 1072 if (!channel_subsys->css[real_cssid]) { 1073 return NULL; 1074 } 1075 1076 if (!channel_subsys->css[real_cssid]->sch_set[ssid]) { 1077 return NULL; 1078 } 1079 1080 return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid]; 1081 } 1082 1083 bool css_subch_visible(SubchDev *sch) 1084 { 1085 if (sch->ssid > channel_subsys->max_ssid) { 1086 return false; 1087 } 1088 1089 if (sch->cssid != channel_subsys->default_cssid) { 1090 return (channel_subsys->max_cssid > 0); 1091 } 1092 1093 return true; 1094 } 1095 1096 bool css_present(uint8_t cssid) 1097 { 1098 return (channel_subsys->css[cssid] != NULL); 1099 } 1100 1101 bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno) 1102 { 1103 if (!channel_subsys->css[cssid]) { 1104 return false; 1105 } 1106 if (!channel_subsys->css[cssid]->sch_set[ssid]) { 1107 return false; 1108 } 1109 1110 return !!test_bit(devno, 1111 channel_subsys->css[cssid]->sch_set[ssid]->devnos_used); 1112 } 1113 1114 void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, 1115 uint16_t devno, SubchDev *sch) 1116 { 1117 CssImage *css; 1118 SubchSet *s_set; 1119 1120 trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid, 1121 devno); 1122 if (!channel_subsys->css[cssid]) { 1123 fprintf(stderr, 1124 "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n", 1125 __func__, cssid, ssid, schid); 1126 return; 1127 } 1128 css = channel_subsys->css[cssid]; 1129 1130 if (!css->sch_set[ssid]) { 1131 css->sch_set[ssid] = g_malloc0(sizeof(SubchSet)); 1132 } 1133 s_set = css->sch_set[ssid]; 1134 1135 s_set->sch[schid] = sch; 1136 if (sch) { 1137 set_bit(schid, s_set->schids_used); 1138 set_bit(devno, s_set->devnos_used); 1139 } else { 1140 clear_bit(schid, s_set->schids_used); 1141 clear_bit(devno, s_set->devnos_used); 1142 } 1143 } 1144 1145 void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) 1146 { 1147 CrwContainer *crw_cont; 1148 1149 trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); 1150 /* TODO: Maybe use a static crw pool? */ 1151 crw_cont = g_try_malloc0(sizeof(CrwContainer)); 1152 if (!crw_cont) { 1153 channel_subsys->crws_lost = true; 1154 return; 1155 } 1156 crw_cont->crw.flags = (rsc << 8) | erc; 1157 if (chain) { 1158 crw_cont->crw.flags |= CRW_FLAGS_MASK_C; 1159 } 1160 crw_cont->crw.rsid = rsid; 1161 if (channel_subsys->crws_lost) { 1162 crw_cont->crw.flags |= CRW_FLAGS_MASK_R; 1163 channel_subsys->crws_lost = false; 1164 } 1165 1166 QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling); 1167 1168 if (channel_subsys->do_crw_mchk) { 1169 S390CPU *cpu = s390_cpu_addr2state(0); 1170 1171 channel_subsys->do_crw_mchk = false; 1172 /* Inject crw pending machine check. */ 1173 s390_crw_mchk(cpu); 1174 } 1175 } 1176 1177 void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, 1178 int hotplugged, int add) 1179 { 1180 uint8_t guest_cssid; 1181 bool chain_crw; 1182 1183 if (add && !hotplugged) { 1184 return; 1185 } 1186 if (channel_subsys->max_cssid == 0) { 1187 /* Default cssid shows up as 0. */ 1188 guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid; 1189 } else { 1190 /* Show real cssid to the guest. */ 1191 guest_cssid = cssid; 1192 } 1193 /* 1194 * Only notify for higher subchannel sets/channel subsystems if the 1195 * guest has enabled it. 1196 */ 1197 if ((ssid > channel_subsys->max_ssid) || 1198 (guest_cssid > channel_subsys->max_cssid) || 1199 ((channel_subsys->max_cssid == 0) && 1200 (cssid != channel_subsys->default_cssid))) { 1201 return; 1202 } 1203 chain_crw = (channel_subsys->max_ssid > 0) || 1204 (channel_subsys->max_cssid > 0); 1205 css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid); 1206 if (chain_crw) { 1207 css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0, 1208 (guest_cssid << 8) | (ssid << 4)); 1209 } 1210 } 1211 1212 void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) 1213 { 1214 /* TODO */ 1215 } 1216 1217 int css_enable_mcsse(void) 1218 { 1219 trace_css_enable_facility("mcsse"); 1220 channel_subsys->max_cssid = MAX_CSSID; 1221 return 0; 1222 } 1223 1224 int css_enable_mss(void) 1225 { 1226 trace_css_enable_facility("mss"); 1227 channel_subsys->max_ssid = MAX_SSID; 1228 return 0; 1229 } 1230 1231 static void css_init(void) 1232 { 1233 channel_subsys = g_malloc0(sizeof(*channel_subsys)); 1234 QTAILQ_INIT(&channel_subsys->pending_crws); 1235 channel_subsys->do_crw_mchk = true; 1236 channel_subsys->crws_lost = false; 1237 channel_subsys->chnmon_active = false; 1238 } 1239 machine_init(css_init); 1240 1241 void css_reset_sch(SubchDev *sch) 1242 { 1243 PMCW *p = &sch->curr_status.pmcw; 1244 1245 p->intparm = 0; 1246 p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | 1247 PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | 1248 PMCW_FLAGS_MASK_MP | PMCW_FLAGS_MASK_TF); 1249 p->flags |= PMCW_FLAGS_MASK_DNV; 1250 p->devno = sch->devno; 1251 p->pim = 0x80; 1252 p->lpm = p->pim; 1253 p->pnom = 0; 1254 p->lpum = 0; 1255 p->mbi = 0; 1256 p->pom = 0xff; 1257 p->pam = 0x80; 1258 p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | 1259 PMCW_CHARS_MASK_CSENSE); 1260 1261 memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw)); 1262 sch->curr_status.mba = 0; 1263 1264 sch->channel_prog = 0x0; 1265 sch->last_cmd_valid = false; 1266 sch->thinint_active = false; 1267 } 1268 1269 void css_reset(void) 1270 { 1271 CrwContainer *crw_cont; 1272 1273 /* Clean up monitoring. */ 1274 channel_subsys->chnmon_active = false; 1275 channel_subsys->chnmon_area = 0; 1276 1277 /* Clear pending CRWs. */ 1278 while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) { 1279 QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); 1280 g_free(crw_cont); 1281 } 1282 channel_subsys->do_crw_mchk = true; 1283 channel_subsys->crws_lost = false; 1284 1285 /* Reset maximum ids. */ 1286 channel_subsys->max_cssid = 0; 1287 channel_subsys->max_ssid = 0; 1288 } 1289