/* IO.C (c) Copyright Roger Bowler, 1994-2006 */ /* ESA/390 CPU Emulator */ /* Interpretive Execution - (c) Copyright Jan Jaeger, 1999-2006 */ /* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2006 */ /*-------------------------------------------------------------------*/ /* This module implements all I/O instructions of the */ /* S/370 and ESA/390 architectures, as described in the manuals */ /* GA22-7000-03 System/370 Principles of Operation */ /* SA22-7201-06 ESA/390 Principles of Operation */ /*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/ /* Additional credits: */ /* STCPS and SCHM instructions by Jan Jaeger */ /* STCRW instruction by Jan Jaeger */ /* Instruction decode by macros - Jan Jaeger */ /* Instruction decode rework - Jan Jaeger */ /* Correct nullification of TIO and TSCH - Jan Jaeger */ /* Lock device during MSCH update - Jan Jaeger */ /* SIE support - Jan Jaeger */ /* SAL instruction added - Jan Jaeger */ /* RCHP instruction added - Jan Jaeger */ /* CONCS instruction added - Jan Jaeger */ /* DISCS instruction added - Jan Jaeger */ /* TPI fix - Jay Maynard, found by Greg Smith */ /* STCRW instruction nullification correction - Jan Jaeger */ /* I/O rate counter - Valery Pogonchenko */ /* 64-bit IDAW support - Roger Bowler v209 @IWZ*/ /*-------------------------------------------------------------------*/ #include "hstdinc.h" #if !defined(_HENGINE_DLL_) #define _HENGINE_DLL_ #endif #if !defined(_IO_C_) #define _IO_C_ #endif #include "hercules.h" #include "opcode.h" #include "inline.h" #if defined(FEATURE_CHANNEL_SUBSYSTEM) /*-------------------------------------------------------------------*/ /* B230 CSCH - Clear Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(clear_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, or is not enabled */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* Perform clear subchannel and set condition code zero */ clear_subchan (regs, dev); regs->psw.cc = 0; } /*-------------------------------------------------------------------*/ /* B231 HSCH - Halt Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(halt_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, or is not enabled */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* Perform halt subchannel and set condition code */ regs->psw.cc = halt_subchan (regs, dev); } /*-------------------------------------------------------------------*/ /* B232 MSCH - Modify Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(modify_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ PMCW pmcw; /* Path management ctl word */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); FW_CHECK(effective_addr2, regs); /* Fetch the updated path management control word */ ARCH_DEP(vfetchc) ( &pmcw, sizeof(PMCW)-1, effective_addr2, b2, regs ); /* Program check if reserved bits are not zero */ if ((pmcw.flag4 & PMCW4_RESV) || (pmcw.flag5 & PMCW5_LM) == PMCW5_LM_RESV #if !defined(_FEATURE_IO_ASSIST) || (pmcw.flag4 & PMCW4_A) || (pmcw.zone != 0) || (pmcw.flag25 & PMCW25_VISC) || (pmcw.flag27 & PMCW27_I) #endif || (pmcw.flag26 != 0) || (pmcw.flag27 & PMCW27_RESV)) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist */ if (dev == NULL) { regs->psw.cc = 3; return; } /* If the subchannel is invalid then return cc0 */ if (!(dev->pmcw.flag5 & PMCW5_V)) { regs->psw.cc = 0; return; } /* Perform serialization and checkpoint-synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); /* Obtain the device lock */ obtain_lock (&dev->lock); /* Condition code 1 if subchannel is status pending with other than intermediate status */ if ((dev->scsw.flag3 & SCSW3_SC_PEND) && !(dev->scsw.flag3 & SCSW3_SC_INTER)) { regs->psw.cc = 1; release_lock (&dev->lock); return; } /* Condition code 2 if subchannel is busy */ if (dev->busy || IOPENDING(dev)) { regs->psw.cc = 2; release_lock (&dev->lock); return; } /* Update the enabled (E), limit mode (LM), and measurement mode (MM), and multipath (D) bits */ dev->pmcw.flag5 &= ~(PMCW5_E | PMCW5_LM | PMCW5_MM | PMCW5_D); dev->pmcw.flag5 |= (pmcw.flag5 & (PMCW5_E | PMCW5_LM | PMCW5_MM | PMCW5_D)); /* Update the measurement block index */ memcpy (dev->pmcw.mbi, pmcw.mbi, sizeof(HWORD)); /* Update the interruption parameter */ memcpy (dev->pmcw.intparm, pmcw.intparm, sizeof(FWORD)); /* Update the ISC and A fields */ dev->pmcw.flag4 &= ~(PMCW4_ISC | PMCW4_A); dev->pmcw.flag4 |= (pmcw.flag4 & (PMCW4_ISC | PMCW4_A)); /* Update the path management (LPM and POM) fields */ dev->pmcw.lpm = pmcw.lpm; dev->pmcw.pom = pmcw.pom; /* Update zone, VISC, I and S bit */ dev->pmcw.zone = pmcw.zone; dev->pmcw.flag25 &= ~(PMCW25_VISC); dev->pmcw.flag25 |= (pmcw.flag25 & PMCW25_VISC); dev->pmcw.flag26 = pmcw.flag26; dev->pmcw.flag27 = pmcw.flag27; #if defined(_FEATURE_IO_ASSIST) /* Relate the device storage view to the requested zone */ { RADR mso, msl; mso = sysblk.zpb[dev->pmcw.zone].mso << 20; msl = (sysblk.zpb[dev->pmcw.zone].msl << 20) | 0xFFFFF; /* Ensure channel program checks on incorrect zone defs */ if(mso > (sysblk.mainsize-1) || msl > (sysblk.mainsize-1) || mso > msl) mso = msl = 0; dev->mainstor = &(sysblk.mainstor[mso]); dev->mainlim = msl - mso; dev->storkeys = &(STORAGE_KEY(mso, &sysblk)); } #endif /*defined(_FEATURE_IO_ASSIST)*/ /* Set device priority from the interruption subclass bits */ dev->priority = (dev->pmcw.flag4 & PMCW4_ISC) >> 3; release_lock (&dev->lock); /* Set condition code 0 */ regs->psw.cc = 0; } /*-------------------------------------------------------------------*/ /* B23B RCHP - Reset Channel Path [S] */ /*-------------------------------------------------------------------*/ DEF_INST(reset_channel_path) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ BYTE chpid; S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-23 not zero */ if(regs->GR_L(1) & 0xFFFFFF00) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); chpid = regs->GR_L(1) & 0xFF; if( !(regs->psw.cc = chp_reset(chpid)) ) { obtain_lock(&sysblk.intlock); sysblk.chp_reset[chpid/32] |= 0x80000000 >> (chpid % 32); ON_IC_CHANRPT; WAKEUP_CPUS_MASK (sysblk.waiting_mask); release_lock (&sysblk.intlock); } RETURN_INTCHECK(regs); } /*-------------------------------------------------------------------*/ /* B238 RSCH - Resume Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(resume_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, or is not enabled */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* Perform resume subchannel and set condition code */ regs->psw.cc = resume_subchan (regs, dev); regs->siocount++; } /*-------------------------------------------------------------------*/ /* B237 SAL - Set Address Limit [S] */ /*-------------------------------------------------------------------*/ DEF_INST(set_address_limit) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); if(regs->GR_L(1) & 0x8000FFFF) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); else sysblk.addrlimval = regs->GR_L(1); } /*-------------------------------------------------------------------*/ /* B23C SCHM - Set Channel Monitor [S] */ /*-------------------------------------------------------------------*/ DEF_INST(set_channel_monitor) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); /* Reserved bits in gpr1 must be zero */ if (regs->GR_L(1) & CHM_GPR1_RESV) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Program check if M bit one and gpr2 address not on a 32 byte boundary or highorder bit set in ESA/390 mode */ if ((regs->GR_L(1) & CHM_GPR1_M) && (regs->GR_L(2) & CHM_GPR2_RESV)) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); #if defined(_FEATURE_IO_ASSIST) /* Virtual use of I/O Assist features must be intercepted */ if(SIE_MODE(regs) && ( (regs->GR_L(1) & CHM_GPR1_ZONE) || (regs->GR_L(1) & CHM_GPR1_A) )) SIE_INTERCEPT(regs); /* Zone must be a valid zone number */ if (((regs->GR_L(1) & CHM_GPR1_ZONE) >> 16) >= FEATURE_SIE_MAXZONES) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); if(regs->GR_L(1) & CHM_GPR1_A) #endif /*defined(_FEATURE_IO_ASSIST)*/ { /* Set the measurement block origin address */ if (regs->GR_L(1) & CHM_GPR1_M) { sysblk.mbo = regs->GR(2); sysblk.mbk = (regs->GR_L(1) & CHM_GPR1_MBK) >> 24; sysblk.mbm = 1; } else sysblk.mbm = 0; sysblk.mbd = regs->GR_L(1) & CHM_GPR1_D; } #if defined(_FEATURE_IO_ASSIST) else { int zone = SIE_MODE(regs) ? regs->siebk->zone : ((regs->GR_L(1) & CHM_GPR1_ZONE) >> 16); /* Set the measurement block origin address */ if (regs->GR_L(1) & CHM_GPR1_M) { sysblk.zpb[zone].mbo = regs->GR(2); sysblk.zpb[zone].mbk = (regs->GR_L(1) & CHM_GPR1_MBK) >> 24; sysblk.zpb[zone].mbm = 1; } else sysblk.zpb[zone].mbm = 0; sysblk.zpb[zone].mbd = regs->GR_L(1) & CHM_GPR1_D; } #endif /*defined(_FEATURE_IO_ASSIST)*/ } /*-------------------------------------------------------------------*/ /* B233 SSCH - Start Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(start_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ ORB orb; /* Operation request block */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); FW_CHECK(effective_addr2, regs); /* Fetch the operation request block */ ARCH_DEP(vfetchc) ( &orb, sizeof(ORB)-1, effective_addr2, b2, regs ); /* Program check if reserved bits are not zero */ if (orb.flag5 & ORB5_RESV || orb.flag7 & ORB7_RESV || orb.ccwaddr[0] & 0x80) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); #if !defined(FEATURE_INCORRECT_LENGTH_INDICATION_SUPPRESSION) /* Program check if incorrect length suppression */ if (orb.flag7 & ORB7_L) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); #endif /*!defined(FEATURE_INCORRECT_LENGTH_INDICATION_SUPPRESSION)*/ #if !defined(FEATURE_MIDAW) /*@MW*/ /* Program check if modified indirect data addressing requested */ if (orb.flag7 & ORB7_D) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); #endif /*!defined(FEATURE_MIDAW)*/ /*@MW*/ /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, is not enabled, or no path available */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0 || (orb.lpm & dev->pmcw.pam) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* Perform serialization and checkpoint-synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); /* Clear the path not operational mask */ dev->pmcw.pnom = 0; /* Copy the logical path mask */ dev->pmcw.lpm = orb.lpm; /* Start the channel program and set the condition code */ regs->psw.cc = ARCH_DEP(startio) (regs, dev, &orb); /*@IWZ*/ regs->siocount++; /* Set the last path used mask */ if (0 == regs->psw.cc) dev->pmcw.lpum = 0x80; } /*-------------------------------------------------------------------*/ /* B23A STCPS - Store Channel Path Status [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_channel_path_status) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ BYTE work[32]; /* Work area */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Program check if operand not on 32 byte boundary */ if ( effective_addr2 & 0x0000001F ) ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION); /*INCOMPLETE, SET TO ALL ZEROS*/ memset(work,0x00,32); /* Store channel path status word at operand address */ ARCH_DEP(vstorec) ( work, 32-1, effective_addr2, b2, regs ); } /*-------------------------------------------------------------------*/ /* B239 STCRW - Store Channel Report Word [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_channel_report_word) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ U32 n; /* Integer work area */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); FW_CHECK(effective_addr2, regs); /* Validate write access to operand before taking any pending channel report word off the queue */ ARCH_DEP(validate_operand) (effective_addr2, b2, 0, ACCTYPE_WRITE, regs); /* Obtain any pending channel report */ n = channel_report(); /* Store channel report word at operand address */ ARCH_DEP(vstore4) ( n, effective_addr2, b2, regs ); /* Indicate if channel report or zeros were stored */ regs->psw.cc = (n == 0) ? 1 : 0; } /*-------------------------------------------------------------------*/ /* B234 STSCH - Store Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ SCHIB schib; /* Subchannel information blk*/ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Set condition code 3 if subchannel does not exist */ if (dev == NULL) { regs->psw.cc = 3; return; } FW_CHECK(effective_addr2, regs); /* Perform serialization and checkpoint-synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); /* Build the subchannel information block */ schib.pmcw = dev->pmcw; obtain_lock (&dev->lock); if (dev->pciscsw.flag3 & SCSW3_SC_PEND) schib.scsw = dev->pciscsw; else schib.scsw = dev->scsw; release_lock (&dev->lock); memset (schib.moddep, 0x00, sizeof(schib.moddep)); /* Store the subchannel information block */ ARCH_DEP(vstorec) ( &schib, sizeof(SCHIB)-1, effective_addr2, b2, regs ); /* Set condition code 0 */ regs->psw.cc = 0; } /*-------------------------------------------------------------------*/ /* B236 TPI - Test Pending Interruption [S] */ /*-------------------------------------------------------------------*/ DEF_INST(test_pending_interruption) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ PSA *psa; /* -> Prefixed storage area */ U64 dreg; /* Double register work area */ U32 ioid; /* I/O interruption address */ U32 ioparm; /* I/O interruption parameter*/ U32 iointid; /* I/O interruption ident */ int icode; /* Intercept code */ RADR pfx; /* Prefix */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); FW_CHECK(effective_addr2, regs); /* validate operand before taking any action */ if ( effective_addr2 != 0 ) ARCH_DEP(validate_operand) (effective_addr2, b2, 8-1, ACCTYPE_WRITE, regs); /* Perform serialization and checkpoint-synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); if( IS_IC_IOPENDING ) { /* Obtain the interrupt lock */ obtain_lock (&sysblk.intlock); /* Test and clear pending interrupt, set condition code */ icode = ARCH_DEP(present_io_interrupt) (regs, &ioid, &ioparm, &iointid, NULL); /* Release the interrupt lock */ release_lock (&sysblk.intlock); /* Store the SSID word and I/O parameter if an interrupt was pending */ if (icode) { if ( effective_addr2 == 0 #if defined(_FEATURE_IO_ASSIST) || icode != SIE_NO_INTERCEPT #endif ) { #if defined(_FEATURE_IO_ASSIST) if(icode != SIE_NO_INTERCEPT) { /* Point to SIE copy of PSA in state descriptor */ psa = (void*)(regs->hostregs->mainstor + SIE_STATE(regs) + SIE_II_PSA_OFFSET); STORAGE_KEY(SIE_STATE(regs), regs->hostregs) |= (STORKEY_REF | STORKEY_CHANGE); } else #endif { /* Point to PSA in main storage */ pfx = regs->PX; SIE_TRANSLATE(&pfx, ACCTYPE_SIE, regs); psa = (void*)(regs->mainstor + pfx); STORAGE_KEY(pfx, regs) |= (STORKEY_REF | STORKEY_CHANGE); } /* If operand address is zero, store in PSA */ STORE_FW(psa->ioid,ioid); STORE_FW(psa->ioparm,ioparm); #if defined(FEATURE_ESAME) || defined(_FEATURE_IO_ASSIST) STORE_FW(psa->iointid,iointid); #endif /*defined(FEATURE_ESAME)*/ #if defined(_FEATURE_IO_ASSIST) if(icode != SIE_NO_INTERCEPT) longjmp(regs->progjmp,SIE_INTERCEPT_IOINST); #endif } else { /* Otherwise store at operand location */ dreg = ((U64)ioid << 32) | ioparm; ARCH_DEP(vstore8) ( dreg, effective_addr2, b2, regs ); } } } else { #if defined(_FEATURE_IO_ASSIST) /* If no I/O assisted devices have pending interrupts then we must intercept */ SIE_INTERCEPT(regs); #endif icode = 0; } regs->psw.cc = (icode == 0) ? 0 : 1; } /*-------------------------------------------------------------------*/ /* B235 TSCH - Test Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(test_subchannel) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ IRB irb; /* Interruption response blk */ int cc; /* Condition Code */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); FW_CHECK(effective_addr2, regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, or is not enabled */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* validate operand before taking any action */ ARCH_DEP(validate_operand) (effective_addr2, b2, sizeof(IRB)-1, ACCTYPE_WRITE_SKP, regs); /* Perform serialization and checkpoint-synchronization */ PERFORM_SERIALIZATION (regs); PERFORM_CHKPT_SYNC (regs); /* Test and clear pending status, set condition code */ cc = test_subchan (regs, dev, &irb); /* Store the interruption response block */ ARCH_DEP(vstorec) ( &irb, sizeof(IRB)-1, effective_addr2, b2, regs ); regs->psw.cc = cc; } #if defined(FEATURE_CANCEL_IO_FACILITY) /*-------------------------------------------------------------------*/ /* B276 XSCH - Cancel Subchannel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(cancel_subchannel) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_IO_ASSIST) if(SIE_STATNB(regs, EC0, IOA) && !regs->sie_pref) #endif SIE_INTERCEPT(regs); /* Program check if reg 1 bits 0-15 not X'0001' */ if ( regs->GR_LHH(1) != 0x0001 ) ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION); /* Locate the device block for this subchannel */ dev = find_device_by_subchan (regs->GR_LHL(1)); /* Condition code 3 if subchannel does not exist, is not valid, or is not enabled */ if (dev == NULL || (dev->pmcw.flag5 & PMCW5_V) == 0 || (dev->pmcw.flag5 & PMCW5_E) == 0) { #if defined(_FEATURE_IO_ASSIST) SIE_INTERCEPT(regs); #endif regs->psw.cc = 3; return; } /* Perform cancel subchannel and set condition code */ regs->psw.cc = cancel_subchan (regs, dev); } #endif /*defined(FEATURE_CANCEL_IO_FACILITY)*/ #endif /*defined(FEATURE_CHANNEL_SUBSYSTEM)*/ #if defined(FEATURE_S370_CHANNEL) /*-------------------------------------------------------------------*/ /* 9C00 SIO - Start I/O [S] */ /* 9C01 SIOF - Start I/O Fast Release [S] */ /* 9C02 RIO - Resume I/O - ZZ INCOMPLETE [S] */ /*-------------------------------------------------------------------*/ DEF_INST(start_io) { int b2; /* Effective addr base */ VADR effective_addr2; /* Effective address */ PSA *psa; /* -> prefixed storage area */ DEVBLK *dev; /* -> device block for SIO */ ORB orb; /* Operation request blk @IZW*/ VADR ccwaddr; /* CCW address for start I/O */ BYTE ccwkey; /* Bits 0-3=key, 4=7=zeroes */ S(inst, regs, b2, effective_addr2); #if defined(FEATURE_ECPSVM) if((inst[1])!=0x02) { if(ecpsvm_dosio(regs,b2,effective_addr2)==0) { return; } } #endif PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Locate the device block */ dev = find_device_by_devnum (effective_addr2); /* Set condition code 3 if device does not exist */ if (dev == NULL #if defined(FEATURE_CHANNEL_SWITCHING) || regs->chanset != dev->chanset #endif /*defined(FEATURE_CHANNEL_SWITCHING)*/ ) { regs->psw.cc = 3; return; } /* Fetch key and CCW address from the CAW at PSA+X'48' */ psa = (PSA*)(regs->mainstor + regs->PX); ccwkey = psa->caw[0] & 0xF0; ccwaddr = (psa->caw[1] << 16) | (psa->caw[2] << 8) | psa->caw[3]; /* Build the I/O operation request block */ /*@IZW*/ memset (&orb, 0, sizeof(ORB)); /*@IZW*/ orb.flag4 = ccwkey & ORB4_KEY; /*@IZW*/ STORE_FW(orb.ccwaddr,ccwaddr); /*@IZW*/ /* Start the channel program and set the condition code */ regs->psw.cc = ARCH_DEP(startio) (regs, dev, &orb); /*@IZW*/ regs->siocount++; } /*-------------------------------------------------------------------*/ /* 9D00 TIO - Test I/O [S] */ /* 9D01 CLRIO - Clear I/O [S] */ /*-------------------------------------------------------------------*/ DEF_INST(test_io) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block for SIO */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Locate the device block */ dev = find_device_by_devnum (effective_addr2); /* Set condition code 3 if device does not exist */ if (dev == NULL #if defined(FEATURE_CHANNEL_SWITCHING) || regs->chanset != dev->chanset #endif /*defined(FEATURE_CHANNEL_SWITCHING)*/ ) { regs->psw.cc = 3; return; } /* Test the device and set the condition code */ regs->psw.cc = testio (regs, dev, inst[1]); /* Yield time slice so that device handler may get some time */ /* to possibly complete an I/O - to prevent a TIO Busy Loop */ if(regs->psw.cc==2) { sched_yield(); } } /*-------------------------------------------------------------------*/ /* 9E00 HIO - Halt I/O [S] */ /* 9E01 HDV - Halt Device [S] */ /*-------------------------------------------------------------------*/ DEF_INST(halt_io) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ DEVBLK *dev; /* -> device block for SIO */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Locate the device block */ dev = find_device_by_devnum (effective_addr2); /* Set condition code 3 if device does not exist */ if (dev == NULL #if defined(FEATURE_CHANNEL_SWITCHING) || regs->chanset != dev->chanset #endif /*defined(FEATURE_CHANNEL_SWITCHING)*/ ) { regs->psw.cc = 3; return; } /* Test the device and set the condition code */ regs->psw.cc = haltio (regs, dev, inst[1]); } /*-------------------------------------------------------------------*/ /* 9F00 TCH - Test Channel [S] */ /*-------------------------------------------------------------------*/ DEF_INST(test_channel) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ #if defined(_FEATURE_SIE) BYTE channelid; U16 tch_ctl; #endif /*defined(_FEATURE_SIE)*/ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); #if defined(_FEATURE_SIE) if(!SIE_MODE(regs)) { #endif /*defined(_FEATURE_SIE)*/ /* Test for pending interrupt and set condition code */ regs->psw.cc = testch (regs, effective_addr2 & 0xFF00); #if defined(_FEATURE_SIE) } else { channelid = (effective_addr2 >> 8) & 0xFF; FETCH_HW(tch_ctl,((SIEBK*)(regs->siebk))->tch_ctl); if((channelid > 15) || ((0x8000 >> channelid) & tch_ctl)) longjmp(regs->progjmp, SIE_INTERCEPT_INST); else regs->psw.cc = 0; } #endif /*defined(_FEATURE_SIE)*/ } /*-------------------------------------------------------------------*/ /* B203 STIDC - Store Channel ID [S] */ /*-------------------------------------------------------------------*/ DEF_INST(store_channel_id) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Store Channel ID and set condition code */ regs->psw.cc = stchan_id (regs, effective_addr2 & 0xFF00); } #if defined(FEATURE_CHANNEL_SWITCHING) /*-------------------------------------------------------------------*/ /* B200 CONCS - Connect Channel Set [S] */ /*-------------------------------------------------------------------*/ DEF_INST(connect_channel_set) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ int i; S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); effective_addr2 &= 0xFFFF; /* Hercules has as many channelsets as CPU's */ if(effective_addr2 >= MAX_CPU) { regs->psw.cc = 3; return; } /* If the addressed channel set is currently connected then return with cc0 */ if(regs->chanset == effective_addr2) { regs->psw.cc = 0; return; } /* Disconnect channel set */ regs->chanset = 0xFFFF; obtain_lock(&sysblk.intlock); /* If the addressed channelset is connected to another CPU then return with cc1 */ for (i = 0; i < MAX_CPU; i++) { if (IS_CPU_ONLINE(i) && sysblk.regs[i]->chanset == effective_addr2) { release_lock(&sysblk.intlock); regs->psw.cc = 1; return; } } /* Set channel set connected to current CPU */ regs->chanset = effective_addr2; /* Interrupts may be pending on this channelset */ ON_IC_IOPENDING; release_lock(&sysblk.intlock); regs->psw.cc = 0; } /*-------------------------------------------------------------------*/ /* B201 DISCS - Disconnect Channel Set [S] */ /*-------------------------------------------------------------------*/ DEF_INST(disconnect_channel_set) { int b2; /* Base of effective addr */ VADR effective_addr2; /* Effective address */ int i; S(inst, regs, b2, effective_addr2); PRIV_CHECK(regs); SIE_INTERCEPT(regs); /* Hercules has as many channelsets as CPU's */ if(effective_addr2 >= MAX_CPU) { regs->psw.cc = 3; return; } /* If the addressed channel set is currently connected then disconect channel set and return with cc0 */ if(regs->chanset == effective_addr2 && regs->chanset != 0xFFFF) { regs->chanset = 0xFFFF; regs->psw.cc = 0; return; } obtain_lock(&sysblk.intlock); /* If the addressed channelset is connected to another CPU then return with cc0 */ for(i = 0; i < MAX_CPU; i++) { if (IS_CPU_ONLINE(i) && sysblk.regs[i]->chanset == effective_addr2) { if(sysblk.regs[i]->cpustate != CPUSTATE_STARTED) { sysblk.regs[i]->chanset = 0xFFFF; regs->psw.cc = 0; } else regs->psw.cc = 1; release_lock(&sysblk.intlock); return; } } release_lock(&sysblk.intlock); /* The channel set is not connected, no operation is performed */ regs->psw.cc = 0; } #endif /*defined(FEATURE_CHANNEL_SWITCHING)*/ #endif /*defined(FEATURE_S370_CHANNEL)*/ #if !defined(_GEN_ARCH) #if defined(_ARCHMODE2) #define _GEN_ARCH _ARCHMODE2 #include "io.c" #endif #if defined(_ARCHMODE3) #undef _GEN_ARCH #define _GEN_ARCH _ARCHMODE3 #include "io.c" #endif #endif /*!defined(_GEN_ARCH)*/