/***********************************************************/ /* HERCULES ECPS:VM Support */ /* (c) Copyright 2003-2006 Roger Bowler and Others */ /* Use of this program is governed by the QPL License */ /* Original Author : Ivan Warren */ /* Prime Maintainer : Ivan Warren */ /* */ /* General guidelines about E6XX instruction class */ /* this is an implementation of ECPS:VM Level 20 */ /* */ /* General rule is : Only do what is safe to do. In doubt, */ /* give control to CP back (and act as */ /* a NO-OP). All instructions have this */ /* behaviour, therefore allowing only */ /* partial implementation, or bypassing */ /* tricky cases */ /* */ /* NOTE : ECPS:VM is only available for S/370 architecture */ /* */ /* In order for CP ASSIST to be active, a CONFIGURATION */ /* statement is added : ECPS:VM lvl|no */ /* lvl is the ASSIST level (20 is recommended) */ /* no means CP ASSIST is disabled (default) */ /* */ /* Currently supported CP ASSIST instructions : */ /* +-----+-------+----------------------------------------+*/ /* |opc | Mnemo | Function |*/ /* +-----+-------+----------------------------------------+*/ /* |E602 | LCKPG | Lock Page in core table |*/ /* |E603 | ULKPG | Unlock page in core table |*/ /* |E608 | TRBRG | LRA + Basic checks on VPAGE |*/ /* |E609 | TRLOK | Same as TRBRG + Lock page in core |*/ /* |E60E | SCNRU | Scan Real Unit control blocks |*/ /* |E612 | STLVL | Store ECPS:VM Level |*/ /* |E614 | FREEX | Allocate CP FREE Storage from subpool |*/ /* |E615 | FRETX | Release CP FREE Storage to subpool |*/ /* +-----+-------+----------------------------------------+*/ /* */ /* Currently supported VM ASSIST instructions : */ /* +-----+-------+----------------------------------------+*/ /* |opc | Mnemo | Function |*/ /* +-----+-------+----------------------------------------+*/ /* |0A | SVC | Virtual SVC Assist |*/ /* |80 | SSM | Virtual SSM Assist |*/ /* |82 | LPSW | Virtual LPSW Assist |*/ /* +-----+-------+----------------------------------------+*/ /* */ /***********************************************************/ #include "hstdinc.h" #if !defined(_HENGINE_DLL_) #define _HENGINE_DLL_ #endif #if !defined(_ECPSVM_C_) #define _ECPSVM_C_ #endif #include "hercules.h" #include "opcode.h" #include "inline.h" #include "ecpsvm.h" #ifdef FEATURE_ECPSVM ECPSVM_CMDENT *ecpsvm_getcmdent(char *cmd); struct _ECPSVM_CPSTATS { ECPSVM_STAT_DCL(SVC); ECPSVM_STAT_DCL(SSM); ECPSVM_STAT_DCL(LPSW); ECPSVM_STAT_DCL(STNSM); ECPSVM_STAT_DCL(STOSM); ECPSVM_STAT_DCL(SIO); ECPSVM_STAT_DCL(VTIMER); ECPSVM_STAT_DCL(STCTL); ECPSVM_STAT_DCL(LCTL); ECPSVM_STAT_DCL(DIAG); ECPSVM_STAT_DCL(IUCV); } ecpsvm_sastats={ ECPSVM_STAT_DEF(SVC), ECPSVM_STAT_DEF(SSM), ECPSVM_STAT_DEF(LPSW), ECPSVM_STAT_DEFU(STNSM), ECPSVM_STAT_DEFU(STOSM), ECPSVM_STAT_DEFU(SIO), ECPSVM_STAT_DEF(VTIMER), ECPSVM_STAT_DEFU(STCTL), ECPSVM_STAT_DEF(LCTL), ECPSVM_STAT_DEFU(DIAG), ECPSVM_STAT_DEFU(IUCV), }; struct _ECPSVM_SASTATS { ECPSVM_STAT_DCL(FREE); ECPSVM_STAT_DCL(FRET); ECPSVM_STAT_DCL(LCKPG); ECPSVM_STAT_DCL(ULKPG); ECPSVM_STAT_DCL(SCNRU); ECPSVM_STAT_DCL(SCNVU); ECPSVM_STAT_DCL(DISP0); ECPSVM_STAT_DCL(DISP1); ECPSVM_STAT_DCL(DISP2); ECPSVM_STAT_DCL(DNCCW); ECPSVM_STAT_DCL(DFCCW); ECPSVM_STAT_DCL(FCCWS); ECPSVM_STAT_DCL(CCWGN); ECPSVM_STAT_DCL(UXCCW); ECPSVM_STAT_DCL(TRBRG); ECPSVM_STAT_DCL(TRLOK); ECPSVM_STAT_DCL(VIST); ECPSVM_STAT_DCL(VIPT); ECPSVM_STAT_DCL(STEVL); ECPSVM_STAT_DCL(FREEX); ECPSVM_STAT_DCL(FRETX); ECPSVM_STAT_DCL(PMASS); ECPSVM_STAT_DCL(LCSPG); } ecpsvm_cpstats={ ECPSVM_STAT_DEFU(FREE), ECPSVM_STAT_DEFU(FRET), ECPSVM_STAT_DEF(LCKPG), ECPSVM_STAT_DEF(ULKPG), ECPSVM_STAT_DEF(SCNRU), ECPSVM_STAT_DEF(SCNVU), ECPSVM_STAT_DEF(DISP0), ECPSVM_STAT_DEF(DISP1), ECPSVM_STAT_DEF(DISP2), ECPSVM_STAT_DEFU(DNCCW), ECPSVM_STAT_DEFU(DFCCW), ECPSVM_STAT_DEFU(FCCWS), ECPSVM_STAT_DEFU(CCWGN), ECPSVM_STAT_DEFU(UXCCW), ECPSVM_STAT_DEF(TRBRG), ECPSVM_STAT_DEF(TRLOK), ECPSVM_STAT_DEFU(VIST), ECPSVM_STAT_DEFU(VIPT), ECPSVM_STAT_DEF(STEVL), ECPSVM_STAT_DEF(FREEX), ECPSVM_STAT_DEF(FRETX), ECPSVM_STAT_DEFU(PMASS), ECPSVM_STAT_DEFU(LCSPG), }; #define DEBUG_CPASSIST #define DEBUG_SASSIST #define DODEBUG_ASSIST(_cond,x) \ { \ if((_cond)) \ { \ x; \ }\ } #if defined(DEBUG_SASSIST) #define DEBUG_SASSISTX(_inst,x) \ { \ DODEBUG_ASSIST(ecpsvm_sastats._inst.debug,x) \ } #else #define DEBUG_SASSISTX(_cond,x) #endif #if defined(DEBUG_CPASSIST) #define DEBUG_CPASSISTX(_inst,x) \ { \ DODEBUG_ASSIST(ecpsvm_cpstats._inst.debug,x) \ } #else #define DEBUG_CPASSISTX(_cond,x) #endif /* Utility macros because I am very lazy */ #define EVM_IC( x ) ARCH_DEP(vfetchb) ( ( ( x ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_LH( x ) ARCH_DEP(vfetch2) ( ( ( x ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_L( x ) ARCH_DEP(vfetch4) ( ( ( x ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_LD( x ) ARCH_DEP(vfetch8) ( ( ( x ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_STD( x , y ) ARCH_DEP(vstore8) ( ( x ) , ( ( y ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_ST( x , y ) ARCH_DEP(vstore4) ( ( x ) , ( ( y ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_STH( x , y ) ARCH_DEP(vstore2) ( ( x ) , ( ( y ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_STC( x , y ) ARCH_DEP(vstoreb) ( ( x ) , ( ( y ) & ADDRESS_MAXWRAP(regs) ) , USE_REAL_ADDR , regs ) #define EVM_MVC( x , y , z ) ARCH_DEP(vfetchc) ( ( x ) , ( z ) , ( y ) , USE_REAL_ADDR , regs ) #define BR14 do { \ regs->psw.IA=regs->GR_L(14) & ADDRESS_MAXWRAP(regs); \ VALIDATE_AIA(regs); \ } while(0) #define INITPSEUDOIP(_regs) \ do { \ (_regs).ip=(BYTE*)"\0\0"; \ } while(0) #define INITPSEUDOREGS(_regs) \ do { \ memset(&(_regs),0,sizeof((_regs))); \ INITPSEUDOIP((_regs)); \ } while(0) #define CPASSIST_HIT(_stat) ecpsvm_cpstats._stat.hit++ #define SASSIST_HIT(_stat) ecpsvm_sastats._stat.hit++ #define SASSIST_LPSW(_regs) \ do { \ regs->psw.IA=(_regs.psw.IA & ADDRESS_MAXWRAP(_regs)); \ regs->psw.cc=_regs.psw.cc; \ regs->psw.pkey=_regs.psw.pkey; \ regs->psw.progmask=_regs.psw.progmask; \ VALIDATE_AIA(regs); \ } \ while(0) #define SASSIST_PROLOG( _instname) \ VADR amicblok; \ VADR vpswa; \ BYTE *vpswa_p; \ REGS vpregs; \ BYTE micpend; \ U32 CR6; \ ECPSVM_MICBLOK micblok; \ BYTE micevma; \ BYTE micevma2; \ BYTE micevma3; \ BYTE micevma4; \ if(SIE_STATE(regs)) \ return(1); \ if(!PROBSTATE(®s->psw)) \ { \ return(1); \ } \ if(!sysblk.ecpsvm.available) \ { \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" ECPS:VM Disabled in configuration\n"))); \ return(1); \ } \ if(!ecpsvm_sastats._instname.enabled) \ { \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" ECPS:VM Disabled by command\n"))); \ return(1); \ } \ CR6=regs->CR_L(6); \ regs->ecps_vtmrpt = NULL; /* Assume vtimer off until validated */ \ if(!(CR6 & ECPSVM_CR6_VMASSIST)) \ { \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : EVMA Disabled by guest\n"))); \ return(1); \ } \ /* Increment call now (don't count early misses) */ \ ecpsvm_sastats._instname.call++; \ amicblok=CR6 & ECPSVM_CR6_MICBLOK; \ /* Ensure MICBLOK resides on a single 2K page */ \ /* Then set ref bit by calling LOG_TO_ABS */ \ if((amicblok & 0x007ff) > 0x7e0) \ { \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" Micblok @ %6.6X crosses page frame\n"),amicblok)); \ return(1); \ } \ /* Load the micblok copy */ \ micblok.MICRSEG=EVM_L(amicblok); \ micblok.MICCREG=EVM_L(amicblok+4); \ micblok.MICVPSW=EVM_L(amicblok+8); \ micblok.MICWORK=EVM_L(amicblok+12); \ micblok.MICVTMR=EVM_L(amicblok+16); \ micblok.MICACF=EVM_L(amicblok+20); \ micpend=(micblok.MICVPSW >> 24); \ vpswa=micblok.MICVPSW & ADDRESS_MAXWRAP(regs); \ micevma=(micblok.MICACF >> 24); \ micevma2=((micblok.MICACF & 0x00ff0000) >> 16); \ micevma3=((micblok.MICACF & 0x0000ff00) >> 8); \ micevma4=(micblok.MICACF & 0x000000ff); \ if((CR6 & ECPSVM_CR6_VIRTTIMR)) \ { \ regs->ecps_vtmrpt = MADDR(micblok.MICVTMR,USE_REAL_ADDR,regs,ACCTYPE_READ,0); \ } \ /* Set ref bit on page where Virtual PSW is stored */ \ vpswa_p=MADDR(vpswa,USE_REAL_ADDR,regs,ACCTYPE_READ,0); \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" VPSWA= %8.8X Virtual "),vpswa)); \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" CR6= %8.8X\n"),CR6)); \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" MICVTMR= %8.8X\n"),micblok.MICVTMR)); \ DEBUG_SASSISTX(_instname,logmsg(_("HHCEV300D : SASSIST "#_instname" Real "))); \ DEBUG_SASSISTX(_instname,display_psw(regs)); \ /* Load the Virtual PSW in a temporary REGS structure */ \ INITPSEUDOREGS(vpregs); \ ARCH_DEP(load_psw) (&vpregs,vpswa_p); \ DEBUG_SASSISTX(_instname,display_psw(&vpregs)); \ #define ECPSVM_PROLOG(_inst) \ int b1, b2; \ VADR effective_addr1, \ effective_addr2; \ SSE(inst, regs, b1, effective_addr1, b2, effective_addr2); \ PRIV_CHECK(regs); \ SIE_INTERCEPT(regs); \ if(!sysblk.ecpsvm.available) \ { \ DEBUG_CPASSISTX(_inst,logmsg(_("HHCEV300D : CPASSTS "#_inst" ECPS:VM Disabled in configuration "))); \ ARCH_DEP(program_interrupt) (regs, PGM_OPERATION_EXCEPTION); \ } \ PRIV_CHECK(regs); /* No problem state please */ \ if(!ecpsvm_cpstats._inst.enabled) \ { \ DEBUG_CPASSISTX(_inst,logmsg(_("HHCEV300D : CPASSTS "#_inst" Disabled by command"))); \ return; \ } \ if(!(regs->CR_L(6) & 0x02000000)) \ { \ return; \ } \ ecpsvm_cpstats._inst.call++; \ DEBUG_CPASSISTX(_inst,logmsg(_("HHCEV300D : "#_inst" called\n"))); /* DISPx Utility macros */ #define STPT(_x) \ { \ EVM_STD(cpu_timer(regs),_x); \ } #define SPT(_x) \ { \ set_cpu_timer(regs,EVM_LD(_x)); \ obtain_lock(&sysblk.intlock); \ if(CPU_TIMER(regs) < 0) \ { \ ON_IC_PTIMER(regs); \ } \ else \ { \ OFF_IC_PTIMER(regs); \ } \ release_lock(&sysblk.intlock); \ } #define CHARGE_STOP(_x) \ { \ STPT(_x+VMTTIME); \ } #define CHARGE_START(_x) \ { \ SPT(_x+VMTTIME); \ } #define CHARGE_SWITCH(_x,_y) \ { \ CHARGE_STOP(_x); \ CHARGE_START(_y); \ (_x)=(_y); \ } int ecpsvm_do_fretx(REGS *regs,VADR block,U16 numdw,VADR maxsztbl,VADR fretl); /* CPASSIST FREE (Basic) Not supported */ /* This is part of ECPS:VM Level 18 and 19 */ /* ECPS:VM Level 20 use FREEX */ DEF_INST(ecpsvm_basic_freex) { ECPSVM_PROLOG(FREE); } /* CPASSIST FRET (Basic) Not supported */ /* This is part of ECPS:VM Level 18 and 19 */ /* ECPS:VM Level 20 use FRETX */ DEF_INST(ecpsvm_basic_fretx) { ECPSVM_PROLOG(FRET); } /* Lockpage common code (LCKPG/TRLOK) */ static void ecpsvm_lockpage1(REGS *regs,RADR cortab,RADR pg) { BYTE corcode; VADR corte; U32 lockcount; RADR cortbl; DEBUG_CPASSISTX(LCKPG,logmsg(_("HHCEV300D : LKPG coreptr = "F_RADR" Frame = "F_RADR"\n"),cortab,pg)); cortbl=EVM_L(cortab); corte=cortbl+((pg & 0xfff000)>>8); DEBUG_CPASSISTX(LCKPG,logmsg(_("HHCEV300D : LKPG corete = %6.6X\n"),corte)); corcode=EVM_IC(corte+8); if(corcode & 0x80) { lockcount=EVM_L(corte+4); lockcount++; } else { lockcount=1; corcode|=0x80; EVM_STC(corcode,corte+8); } EVM_ST(lockcount,corte+4); DEBUG_CPASSISTX(LCKPG,logmsg(_("HHCEV300D : LKPG Page locked. Count = %6.6X\n"),lockcount)); return; } /* E602 LCKPG Instruction */ /* LCKPG D1(R1,B1),D2(R2,B2) */ /* 1st operand : PTR_PL -> Address of coretable */ /* 2nd Operand : Page address to be locked */ DEF_INST(ecpsvm_lock_page) { VADR ptr_pl; VADR pg; ECPSVM_PROLOG(LCKPG); ptr_pl=effective_addr1; pg=effective_addr2; DEBUG_CPASSISTX(LCKPG,logmsg(_("HHCEV300D : LKPG PAGE=%6.6X, PTRPL=%6.6X\n"),pg,ptr_pl)); ecpsvm_lockpage1(regs,ptr_pl,pg); regs->psw.cc=0; BR14; CPASSIST_HIT(LCKPG); return; } /* E603 ULKPG Instruction */ /* ULKPG D1(R1,B1),D2(R2,B2) */ /* 1st operand : PTR_PL -> +0 - Maxsize, +4 Coretable */ /* 2nd Operand : Page address to be unlocked */ DEF_INST(ecpsvm_unlock_page) { VADR ptr_pl; VADR pg; VADR corsz; VADR cortbl; VADR corte; BYTE corcode; U32 lockcount; ECPSVM_PROLOG(ULKPG); ptr_pl=effective_addr1; pg=effective_addr2; DEBUG_CPASSISTX(ULKPG,logmsg(_("HHCEV300D : ULKPG PAGE=%6.6X, PTRPL=%6.6X\n"),pg,ptr_pl)); corsz=EVM_L(ptr_pl); cortbl=EVM_L(ptr_pl+4); if((pg+4095)>corsz) { DEBUG_CPASSISTX(ULKPG,logmsg(_("HHCEV300D : ULKPG Page beyond core size of %6.6X\n"),corsz)); return; } corte=cortbl+((pg & 0xfff000)>>8); corcode=EVM_IC(corte+8); if(corcode & 0x80) { lockcount=EVM_L(corte+4); lockcount--; } else { DEBUG_CPASSISTX(ULKPG,logmsg(_("HHCEV300D : ULKPG Attempting to unlock page that is not locked\n"))); return; } if(lockcount==0) { corcode &= ~(0x80|0x02); EVM_STC(corcode,corte+8); DEBUG_CPASSISTX(ULKPG,logmsg(_("HHCEV300D : ULKPG now unlocked\n"))); } else { DEBUG_CPASSISTX(ULKPG,logmsg(_("HHCEV300D : ULKPG Page still locked. Count = %6.6X\n"),lockcount)); } EVM_ST(lockcount,corte+4); CPASSIST_HIT(ULKPG); BR14; return; } /* DNCCW : Not supported */ DEF_INST(ecpsvm_decode_next_ccw) { ECPSVM_PROLOG(DNCCW); } /* FCCWS : Not supported */ DEF_INST(ecpsvm_free_ccwstor) { ECPSVM_PROLOG(FCCWS); } /* SCNVU : Scan for Virtual Device blocks */ DEF_INST(ecpsvm_locate_vblock) { U32 vdev; U32 vchix; U32 vcuix; U32 vdvix; VADR vchtbl; VADR vch; VADR vcu; VADR vdv; ECPSVM_PROLOG(SCNVU); vdev=regs->GR_L(1); vchtbl=effective_addr1; vchix=EVM_LH(vchtbl+((vdev & 0xf00)>>7)); /* Get Index */ if(vchix & 0x8000) { DEBUG_CPASSISTX(SCNVU,logmsg(_("HHCEV300D SCNVU Virtual Device %4.4X has no VCHAN block\n"),vdev)); return; } vch=EVM_L(effective_addr2)+vchix; vcuix=EVM_LH(vch+8+((vdev & 0xf0)>>3)); if(vcuix & 0x8000) { DEBUG_CPASSISTX(SCNVU,logmsg(_("HHCEV300D SCNVU Virtual Device %4.4X has no VCU block\n"),vdev)); return; } vcu=EVM_L(effective_addr2+4)+vcuix; vdvix=EVM_LH(vcu+8+((vdev & 0xf)<<1)); if(vdvix & 0x8000) { DEBUG_CPASSISTX(SCNVU,logmsg(_("HHCEV300D SCNVU Virtual Device %4.4X has no VDEV block\n"),vdev)); return; } vdv=EVM_L(effective_addr2+8)+vdvix; DEBUG_CPASSISTX(SCNVU,logmsg(_("HHCEV300D SCNVU %4.4X : VCH = %8.8X, VCU = %8.8X, VDEV = %8.8X\n"), vdev, vch, vcu, vdv)); regs->GR_L(6)=vch; regs->GR_L(7)=vcu; regs->GR_L(8)=vdv; regs->psw.cc=0; CPASSIST_HIT(SCNVU); BR14; return; } /* DISP1 Core */ /* rc : 0 - Done */ /* rc : 1 - No-op */ /* rc : 2 - Invoke DISP2 */ int ecpsvm_do_disp1(REGS *regs,VADR dl,VADR el) { VADR vmb; U32 F_VMFLGS; /* Aggregate for quick test */ U32 F_SCHMASK; /* Flags to test */ U32 F_SCHMON; /* Flags allowed on for quick dispatch */ VADR F_ASYSVM; /* System VMBLOK */ VADR SCHDL; /* SCHDL Exit */ BYTE B_VMOSTAT; BYTE B_VMQSTAT; BYTE B_VMRSTAT; vmb=regs->GR_L(11); DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Data list = %6.6X VM=%6.6X\n",dl,vmb)); F_VMFLGS=EVM_L(vmb+VMRSTAT); F_SCHMASK=EVM_L(dl+64); F_SCHMON=EVM_L(dl+68); if((F_VMFLGS & F_SCHMASK) == F_SCHMON) { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Quick Check complete\n")); return(2); } else { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Quick Check failed : %8.8X != %8.8X\n",(F_VMFLGS & F_SCHMASK),F_SCHMON)); } F_ASYSVM=EVM_L(ASYSVM); if(vmb==F_ASYSVM) { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 VMB is SYSTEM VMBLOCK\n")); return(2); } SCHDL=EVM_L(el+4); B_VMOSTAT=EVM_IC(vmb+VMOSTAT); if(!(B_VMOSTAT & VMKILL)) { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Call SCHEDULE because VMKILL not set\n")); regs->psw.IA=SCHDL; VALIDATE_AIA(regs); return(0); } B_VMQSTAT=EVM_IC(vmb+VMQSTAT); if(!(B_VMQSTAT & VMCFREAD)) { if(B_VMOSTAT & VMCF) { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Call SCHEDULE because VMKILL & VMCF & !VMCFREAD set\n")); regs->psw.IA=SCHDL; VALIDATE_AIA(regs); return(0); } } /* At DSP - OFF */ B_VMQSTAT &= ~VMCFREAD; B_VMOSTAT &= ~VMKILL; EVM_STC(B_VMQSTAT,vmb+VMQSTAT); EVM_STC(B_VMOSTAT,vmb+VMOSTAT); B_VMRSTAT=EVM_IC(vmb+VMRSTAT); if(B_VMRSTAT & VMLOGOFF) { DEBUG_CPASSISTX(DISP1,logmsg("DISP1 Continue because already logging off\n")); return(2); } B_VMRSTAT |= VMLOGOFF; EVM_STC(B_VMRSTAT,vmb+VMRSTAT); regs->psw.IA=EVM_L(el+0); VALIDATE_AIA(regs); DEBUG_CPASSISTX(DISP1,logmsg("DISP1 : Call USOFF\n")); return(0); } /* DISP2 Core */ int ecpsvm_do_disp2(REGS *regs,VADR dl,VADR el) { VADR vmb; /* Current VMBLOK */ VADR svmb; /* ASYSVM */ VADR runu; /* RUNUSER */ VADR lastu; /* LASTUSER */ VADR F_TRQB; VADR F_CPEXB; VADR F,B; U16 HW1; U32 FW1; U64 DW1; U32 CPEXBKUP[15]; /* CPEXBLOK Regs backup except GPR15 which is useless */ VADR F_ECBLOK; /* Pointer to user's EC block for extended VM */ VADR F_CPEXADD; U32 F_QUANTUM; REGS wregs; /* Work REGS structure of PSW manipulation for Virtual PSW */ REGS rregs; /* Work REGS structure of PSW manipulation for Real PSW */ int i; BYTE B_VMDSTAT,B_VMRSTAT,B_VMESTAT,B_VMPSTAT,B_VMMCR6,B_MICVIP; BYTE B_VMOSTAT,B_VMPEND; VADR F_MICBLOK; U32 F_VMIOINT,F_VMPXINT; U32 F_VMVCR0; U32 NCR0,NCR1; BYTE *work_p; vmb=regs->GR_L(11); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 Data list=%6.6X VM=%6.6X\n",dl,vmb)); CHARGE_STOP(vmb); if(EVM_IC(XTENDLOCK) == XTENDLOCKSET) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 Exit 8 : System extending\n")); /* System in Extend process */ regs->psw.IA=EVM_L(el+8); VALIDATE_AIA(regs); return(0); } if(EVM_IC(APSTAT2) & CPMCHLK) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 Exit 8 : MCH Recovery\n")); /* Machine Check recovery in progress */ regs->psw.IA=EVM_L(el+8); VALIDATE_AIA(regs); return(0); } svmb=EVM_L(ASYSVM); /* Check IOB/TRQ for dispatch */ F_TRQB=EVM_L(dl+8); if(F_TRQB!=dl) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 TRQ/IOB @ %6.6X Exit being routed\n",F_TRQB)); /* We have a TRQ/IOB */ /* Update stack */ F=EVM_L(F_TRQB+8); B=EVM_L(F_TRQB+12); EVM_ST(F,B+8); EVM_ST(B,F+12); /* Get VMBLOK Responsible for this block */ vmb=EVM_L(F_TRQB+0x18); /* Update stack count for the VMBLOK */ HW1=EVM_LH(vmb+VMSTKCNT); HW1--; EVM_STH(HW1,vmb+VMSTKCNT); /* Start charging user for processor time */ CHARGE_START(vmb); EVM_ST(vmb,STACKVM); /* Update registers for TRQ/IOB exit */ regs->GR_L(10)=F_TRQB; regs->GR_L(11)=vmb; regs->GR_L(12)=EVM_L(F_TRQB+0x1C); regs->psw.IA=regs->GR_L(12) & ADDRESS_MAXWRAP(regs); VALIDATE_AIA(regs); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 TRQ/IOB @ %6.6X IA = %6.6X\n",F_TRQB,regs->GR_L(12))); return(0); } /* Check CPEX BLOCK for dispatch */ F_CPEXB=EVM_L(dl+0); if(F_CPEXB!=dl) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 CPEXBLOK Exit being routed CPEX=%6.6X\n",F_CPEXB)); /* We have a CPEXBLOCK */ /* Update stack */ F=EVM_L(F_CPEXB+0); B=EVM_L(F_CPEXB+4); EVM_ST(F,B+0); EVM_ST(B,F+4); vmb=EVM_L(F_CPEXB+0x10+(11*4)); HW1=EVM_LH(vmb+VMSTKCNT); HW1--; EVM_STH(HW1,vmb+VMSTKCNT); CHARGE_START(vmb); /* Copy CPEXBLOCK Contents, and attempt FRET */ /* If fret fails, use exit #12 */ for(i=0;i<15;i++) { CPEXBKUP[i]=EVM_L(F_CPEXB+0x10+(i*4)); } F_CPEXADD=EVM_L(F_CPEXB+0x0C); if(ecpsvm_do_fretx(regs,F_CPEXB,10,EVM_L(dl+28),EVM_L(dl+32))!=0) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 CPEXBLOK CPEX=%6.6X Fret Failed\n",F_CPEXB)); regs->GR_L(0)=10; regs->GR_L(1)=F_CPEXB; for(i=2;i<12;i++) { regs->GR_L(i)=CPEXBKUP[i]; } /* Save GPRS 12-1 (wraping) in DSPSAVE (datalist +40) */ /* So that LM 12,1,DSPSAVE in DMKDSP works after call to DMKFRET */ EVM_ST(dl+40,CPEXBKUP[12]); EVM_ST(dl+44,CPEXBKUP[13]); EVM_ST(dl+48,CPEXBKUP[14]); EVM_ST(dl+52,CPEXBKUP[15]); EVM_ST(dl+56,CPEXBKUP[0]); EVM_ST(dl+60,CPEXBKUP[1]); /* Note : DMKDSP Is wrong - SCHMASK is at +64 (not +60) */ /* Upon taking this exit, GPRS 12-15 are same as entry */ regs->psw.IA=EVM_L(el+12); VALIDATE_AIA(regs); return(0); } for(i=0;i<15;i++) { regs->GR_L(i)=CPEXBKUP[i]; } regs->GR_L(15)=F_CPEXADD; regs->psw.IA=F_CPEXADD & ADDRESS_MAXWRAP(regs); VALIDATE_AIA(regs); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 CPEXBLOK CPEX=%6.6X IA=%6.6X\n",F_CPEXB,F_CPEXADD)); return(0); /* CPEXBLOCK Branch taken */ } /* Check for a USER run */ /* AT DMKDSP - DONE */ if(EVM_IC(CPSTAT2) & CPSHRLK) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 Exit 24 : CPSHRLK Set in CPSTAT2\n")); regs->psw.IA=EVM_L(el+24); /* IDLEECPS */ VALIDATE_AIA(regs); return(0); } /* Scan Scheduler IN-Q */ DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Scanning Scheduler IN-Queue\n")); FW1=EVM_L(dl+24); for(vmb=EVM_L(FW1);vmb!=FW1;vmb=EVM_L(vmb)) { if(!(EVM_IC(vmb+VMDSTAT) & VMRUN)) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X Not eligible : VMRUN not set\n",vmb)); continue; } if(EVM_IC(vmb+VMRSTAT) & VMCPWAIT) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X Not eligible : VMCPWAIT set\n",vmb)); continue; } if(EVM_IC(vmb+VMNOECPS)) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Exit 20 : VMB @ %6.6X Has VMNOECPS Set to %2.2X\n",vmb,EVM_IC(vmb+VMNOECPS))); regs->GR_L(1)=vmb; regs->GR_L(11)=EVM_L(ASYSVM); regs->psw.IA=EVM_L(el+20); /* FREELOCK */ VALIDATE_AIA(regs); return(0); } DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X Will now be dispatched\n",vmb)); runu=EVM_L(RUNUSER); F_QUANTUM=EVM_L(QUANTUM); if(vmb!=runu) { /* User switching */ /* DMKDSP - FNDUSRD */ DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : User switch from %6.6X to %6.6X\n",runu,vmb)); runu=EVM_L(RUNUSER); EVM_STC(EVM_IC(runu+VMDSTAT) & ~VMDSP,runu+VMDSTAT); lastu=EVM_L(LASTUSER); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : RUNU=%6.6X, LASTU=%6.6X\n",runu,lastu)); if(lastu!=svmb && lastu!=vmb) { if(EVM_IC(lastu+VMOSTAT) & VMSHR) /* Running shared sys */ { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Exit 16 : LASTU=%6.6X has shared sys & LCSHPG not impl\n",lastu)); CHARGE_START(lastu); /* LCSHRPG not implemented yet */ regs->GR_L(10)=vmb; regs->GR_L(11)=lastu; regs->psw.IA=EVM_L(el+16); VALIDATE_AIA(regs); return(0); /* A CHARGE_STOP(runu) is due when LCSHRPG is implemented */ } } } if(vmb!=runu || (vmb==runu && (F_QUANTUM & 0x80000000))) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Restarting Time Slice\n")); F_QUANTUM=EVM_L(dl+16); if(EVM_IC(vmb+VMQLEVEL) & VMCOMP) { F_QUANTUM <<= 2; } } EVM_ST(F_QUANTUM,INTTIMER); CHARGE_START(vmb); EVM_ST(vmb,LASTUSER); EVM_ST(vmb,RUNUSER); /*** Prepare to run a user ***/ /* Cache some important VMBLOK flag bytes */ B_VMDSTAT=EVM_IC(vmb+VMDSTAT); B_VMRSTAT=EVM_IC(vmb+VMRSTAT); B_VMPSTAT=EVM_IC(vmb+VMPSTAT); B_VMESTAT=EVM_IC(vmb+VMESTAT); B_VMOSTAT=EVM_IC(vmb+VMOSTAT); B_VMPEND =EVM_IC(vmb+VMPEND); B_VMMCR6=EVM_IC(vmb+VMMCR6); F_MICBLOK=EVM_L(vmb+VMMCR6) & ADDRESS_MAXWRAP(regs); /* LOAD FPRS */ for(i=0;i<8;i+=2) { FW1=EVM_L(vmb+VMFPRS+(i*16)); regs->fpr[i*4]=FW1; FW1=EVM_L(vmb+VMFPRS+(i*16)+4); regs->fpr[i*4+1]=FW1; FW1=EVM_L(vmb+VMFPRS+(i*16)+8); regs->fpr[i*4+2]=FW1; FW1=EVM_L(vmb+VMFPRS+(i*16)+12); regs->fpr[i*4+3]=FW1; } INITPSEUDOREGS(wregs); work_p=MADDR(vmb+VMPSW,0,regs,USE_REAL_ADDR,0); ARCH_DEP(load_psw) (&wregs,work_p); /* Load user's Virtual PSW in work structure */ /* Build REAL PSW */ INITPSEUDOREGS(rregs); /* Copy IAR */ rregs.psw.IA=wregs.psw.IA & ADDRESS_MAXWRAP(regs); VALIDATE_AIA(&rregs); /* Copy CC, PSW KEYs and PGM Mask */ rregs.psw.cc=wregs.psw.cc; rregs.psw.pkey=wregs.psw.pkey; /* Indicate Translation + I/O + Ext + Ecmode + Problem + MC */ rregs.psw.sysmask=0x07; /* I/O + EXT + Trans */ rregs.psw.states = BIT(PSW_EC_BIT) /* ECMODE */ | BIT(PSW_PROB_BIT) /* Problem state */ | BIT(PSW_MACH_BIT); /* MC Enabled */ rregs.psw.intcode=0; /* Clear intcode */ rregs.psw.progmask=wregs.psw.progmask; NCR0=EVM_L(CPCREG0); /* Assume for now */ NCR1=EVM_L(vmb+VMSEG); /* Ditto */ /* Disable ECPS:VM in VM-REAL CR6 For now */ B_VMMCR6&=~(VMMSHADT|VMMPROB|VMMNOSK|VMMFE); /* We load VMECEXT Even if it's not a ECMODE VM */ /* in which case F_ECBLOK is also Virtual CR0 */ F_ECBLOK=EVM_L(vmb+VMECEXT); /* ECMODE VM ? */ if(B_VMPSTAT & VMV370R) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X has ECMODE ON\n",vmb)); /* Is this an ECMODE PSW Machine ? */ if(B_VMESTAT & VMEXTCM) { if((B_VMESTAT & (VMINVSEG|VMNEWCR0)) == (VMINVSEG|VMNEWCR0)) { /* CP Say this is NOT good */ /* Take exit 28 */ logmsg(_("HHCEV004W : Abend condition detected in DISP2 instr\n")); regs->psw.IA=EVM_L(el+28); VALIDATE_AIA(regs); return(0); } /* Check 3rd level translation */ if(wregs.psw.sysmask & 0x04) { NCR0=EVM_L(F_ECBLOK+EXTSHCR0); NCR1=EVM_L(F_ECBLOK+EXTSHCR1); B_VMMCR6|=VMMSHADT; /* re-enable Shadow Table management in CR6 */ } } } /* Invalidate Shadow Tables if necessary */ if(B_VMESTAT & (VMINVPAG | VMSHADT)) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X Refusing to simulate DMKVATAB\n",vmb)); /* Really looks like DMKVATAB is a huge thing to simulate */ /* My belief is that the assist can't handle this one */ /* Return to caller as a NO-OP on this one */ return(1); /* ecpsvm_inv_shadtab_pages(regs,vmb); */ } B_VMESTAT&=~VMINVPAG; B_VMDSTAT|=VMDSP; /* Test for CPMICON in DMKDSP useless here */ /* if CPMICON was off, we would have never */ /* been called anyway */ if(F_MICBLOK!=0) /* That is SET ASSIST ON */ { B_MICVIP=0; /* Check tracing (incompatible with assist) */ if(!(EVM_IC(vmb+VMTRCTL) & (VMTRSVC|VMTRPRV|VMTRBRIN))) { B_VMMCR6|=VMMFE; if(B_VMOSTAT & VMSHR) { /* Cannot allow ISK/SSK in shared sys VM */ B_VMMCR6|=VMMNOSK; } if(PROBSTATE(&wregs.psw)) { B_VMMCR6|=VMMPROB; } /* Set MICPEND if necessary */ /* (assist stuff to ensure LPSW/SSM/SVC sim */ /* does not re-enable VPSW when an interrupt */ /* is pending) */ while(1) { B_MICVIP=0; F_VMIOINT=EVM_LH(vmb+VMIOINT); if(EVM_LH(vmb+VMIOINT)!=0) { F_VMIOINT<<=16; if(B_VMESTAT & VMEXTCM) { if(F_VMIOINT&=EVM_L(F_ECBLOK)) { B_MICVIP|=0x80; break; } } else { B_MICVIP|=0x80; break; } } if(B_VMESTAT & VMEXTCM) { if(B_VMPEND & VMPGPND) { B_MICVIP|=0x80; } } if(B_VMPSTAT & VMV370R) { F_VMVCR0=EVM_L(F_ECBLOK+0); } else { F_VMVCR0=F_ECBLOK; } for(F_VMPXINT=EVM_L(vmb+VMPXINT);F_VMPXINT;F_VMPXINT=EVM_L(F_VMPXINT)) /* XINTNEXT at +0 */ { if(F_VMVCR0 & EVM_LH(F_VMPXINT+10)) { B_MICVIP|=0x80; break; } } break; /* Terminate dummy while loop */ } /* While dummy loop for MICPEND */ } /* if(Not tracing) */ EVM_STC(B_MICVIP,F_MICBLOK+8); /* Save new MICVIP */ } /* if(F_MICBLOCK!=0) */ /* If an Extended VM, Load CRs 3-13 */ /* CR6 Will be overwritten in a second */ if(B_VMESTAT & VMV370R) { for(i=4;i<14;i++) { regs->CR_L(i)=EVM_L(F_ECBLOK+(3*4)+(i*4)); } } /* Update VMMICRO */ EVM_STC(B_VMMCR6,vmb+VMMCR6); /* Update PER Control */ if(EVM_IC(vmb+VMTRCTL) & VMTRPER) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : PER ON\n")); FW1=EVM_L(vmb+VMTREXT); regs->CR_L( 9)=EVM_L(FW1+0x1C); regs->CR_L(10)=EVM_L(FW1+0x20); regs->CR_L(11)=EVM_L(FW1+0x24); rregs.psw.sysmask |= 0x40; /* PER Mask in PSW */ } /* Update CR6 */ regs->CR_L(6)=EVM_L(vmb+VMMCR6); /* Insure proper re-entry */ EVM_ST(0,STACKVM); /* Update PROBLEM Start time */ DW1=EVM_LD(vmb+VMTMOUTQ); EVM_STD(DW1,PROBSTRT); /* Checkpoint Interval Timer */ FW1=EVM_L(INTTIMER); EVM_ST(FW1,QUANTUM); /* Update REAL CR0/CR1 */ regs->CR_L(0)=NCR0; regs->CR_L(1)=NCR1; /* Indicate RUNNING a user */ EVM_STC(CPRUN,CPSTATUS); /* Update real PSW with working PSW */ /* Update regs */ for(i=0;i<16;i++) { regs->GR_L(i)=EVM_L(vmb+VMGPRS+(i*4)); } /* Clear I/O Old PSW Byte 0 */ EVM_STC(0,IOOPSW); /* Issue PTLB if necessary */ if(EVM_IC(APSTAT2) & CPPTLBR) { DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Purging TLB\n")); ARCH_DEP(purge_tlb)(regs); EVM_STC(EVM_IC(APSTAT2) & ~CPPTLBR,APSTAT2); } /* Update cached VMBLOK flags */ EVM_STC(B_VMDSTAT,vmb+VMDSTAT); EVM_STC(B_VMRSTAT,vmb+VMRSTAT); EVM_STC(B_VMESTAT,vmb+VMESTAT); EVM_STC(B_VMPSTAT,vmb+VMPSTAT); EVM_STC(B_VMOSTAT,vmb+VMOSTAT); work_p=MADDR(vmb+VMPSW,USE_REAL_ADDR,regs,ACCTYPE_WRITE,0); \ ARCH_DEP(store_psw) (&wregs,work_p); /* Stop charging current VM Block for Supervisor time */ CHARGE_STOP(vmb); /* Rest goes for problem state */ SPT(vmb+VMTMOUTQ); /* Save RUNCR0, RUNCR1 & RUNPSW */ /* Might be used by later CP Modules (including DMKPRV) */ EVM_ST(NCR0,RUNCR0); EVM_ST(NCR1,RUNCR1); work_p=MADDR(RUNPSW,USE_REAL_ADDR,regs,ACCTYPE_WRITE,0); \ ARCH_DEP(store_psw) (&rregs,work_p); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Entry Real ")); DEBUG_CPASSISTX(DISP2,display_psw(regs)); ARCH_DEP(load_psw) (regs,work_p); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : VMB @ %6.6X Now being dispatched\n",vmb)); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Real ")); DEBUG_CPASSISTX(DISP2,display_psw(regs)); DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Virtual ")); DEBUG_CPASSISTX(DISP2,display_psw(&wregs)); /* TEST */ ARCH_DEP(purge_tlb)(regs); SET_IC_MASK(regs); SET_AEA_MODE(regs); SET_AEA_COMMON(regs); VALIDATE_AIA(regs); /* Dispatch..... */ DEBUG_CPASSISTX(DISP2,logmsg(_("HHCPEV300D : DISP2 - Next Instruction : %2.2X\n"),ARCH_DEP(vfetchb)(regs->psw.IA,USE_PRIMARY_SPACE,regs))); DEBUG_CPASSISTX(DISP2,display_regs(regs)); DEBUG_CPASSISTX(DISP2,display_cregs(regs)); return(2); /* OK - Perform INTCHECK */ } /* Nothing else to do - wait state */ DEBUG_CPASSISTX(DISP2,logmsg("DISP2 : Nothing to dispatch - IDLEECPS\n")); regs->psw.IA=EVM_L(el+24); /* IDLEECPS */ VALIDATE_AIA(regs); return(0); } /* DISP1 : Early tests part 2 */ /* DISP1 Checks if the user is OK to run */ /* early tests part 1 already done by DISP0 */ DEF_INST(ecpsvm_disp1) { ECPSVM_PROLOG(DISP1); switch(ecpsvm_do_disp1(regs,effective_addr1,effective_addr2)) { case 0: /* Done */ CPASSIST_HIT(DISP1); return; case 1: /* No-op */ break; case 2: /* Call DISP2 - INTCHECK NOT needed */ switch(ecpsvm_do_disp2(regs,effective_addr1,effective_addr2)) { case 0: CPASSIST_HIT(DISP1); return; case 1: return; case 2: CPASSIST_HIT(DISP1); RETURN_INTCHECK(regs); default: break; } return; default: return; } } static int ecpsvm_int_lra(REGS *regs,VADR pgadd,RADR *raddr) { int cc; cc = ARCH_DEP(translate_addr) (pgadd , USE_PRIMARY_SPACE, regs, ACCTYPE_LRA); *raddr = regs->dat.raddr; return cc; } /* TRANBRNG/TRANLOCK Common code */ static int ecpsvm_tranbrng(REGS *regs,VADR cortabad,VADR pgadd,RADR *raddr) { int cc; int corcode; #if defined(FEATURE_2K_STORAGE_KEYS) RADR pg1,pg2; #endif VADR cortab; cc=ecpsvm_int_lra(regs,pgadd,raddr); if(cc!=0) { DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Tranbring : LRA cc = %d\n"),cc)); return(1); } /* Get the core table entry from the Real address */ cortab=EVM_L( cortabad ); cortab+=((*raddr) & 0xfff000) >> 8; corcode=EVM_IC(cortab+8); if(!(corcode & 0x08)) { DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Page not shared - OK %d\n"),cc)); return(0); /* Page is NOT shared.. All OK */ } #if defined(FEATURE_2K_STORAGE_KEYS) pg1=(*raddr & 0xfff000); pg2=pg1+0x800; DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Checking 2K Storage keys @"F_RADR" & "F_RADR"\n"),pg1,pg2)); if((STORAGE_KEY(pg1,regs) & STORKEY_CHANGE) || (STORAGE_KEY(pg2,regs) & STORKEY_CHANGE)) { #else DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Checking 4K Storage keys @"F_RADR"\n"),*raddr)); if(STORAGE_KEY(*raddr,regs) & STORKEY_CHANGE) { #endif DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Page shared and changed\n"))); return(1); /* Page shared AND changed */ } DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : Page shared but not changed\n"))); return(0); /* All done */ } /* TRBRG : Translate a page address */ /* TRBRG D1(R1,B1),D2(R2,B2) */ /* 1st operand : Coretable address */ /* 2nd operand : Virtual address */ /* Note : CR1 Contains the relevant segment table */ /* pointers */ /* The REAL address is resolved. If the page is flagged */ /* as shared in the core table, the page is checked for */ /* the change bit */ /* If no unusual condition is detected, control is returned */ /* to the address in GPR 14. Otherwise, TRBRG is a no-op */ DEF_INST(ecpsvm_tpage) { int rc; RADR raddr; ECPSVM_PROLOG(TRBRG); DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : TRANBRNG\n"))); rc=ecpsvm_tranbrng(regs,effective_addr1,regs->GR_L(1),&raddr); if(rc) { DEBUG_CPASSISTX(TRBRG,logmsg(_("HHCEV300D : TRANBRNG - Back to CP\n"))); return; /* Something not right : NO OP */ } regs->psw.cc=0; regs->GR_L(2)=raddr; regs->psw.IA = effective_addr2 & ADDRESS_MAXWRAP(regs); VALIDATE_AIA(regs); CPASSIST_HIT(TRBRG); return; } /* TRLOK : Translate a page address and lock */ /* TRLOK D1(R1,B1),D2(R2,B2) */ /* See TRBRG. */ /* If sucessfull, the page is also locked in the core table */ DEF_INST(ecpsvm_tpage_lock) { int rc; RADR raddr; ECPSVM_PROLOG(TRLOK); DEBUG_CPASSISTX(TRLOK,logmsg(_("HHCEV300D : TRANLOCK\n"))); rc=ecpsvm_tranbrng(regs,effective_addr1,regs->GR_L(1),&raddr); if(rc) { DEBUG_CPASSISTX(TRLOK,logmsg(_("HHCEV300D : TRANLOCK - Back to CP\n"))); return; /* Something not right : NO OP */ } /* * Lock the page in Core Table */ ecpsvm_lockpage1(regs,effective_addr1,raddr); regs->psw.cc=0; regs->GR_L(2)=raddr; regs->psw.IA = effective_addr2 & ADDRESS_MAXWRAP(regs); VALIDATE_AIA(regs); CPASSIST_HIT(TRLOK); return; } /* VIST : Not supported */ DEF_INST(ecpsvm_inval_segtab) { ECPSVM_PROLOG(VIST); } /* VIPT : Not supported */ DEF_INST(ecpsvm_inval_ptable) { ECPSVM_PROLOG(VIPT); } /* DFCCW : Not Supported */ DEF_INST(ecpsvm_decode_first_ccw) { ECPSVM_PROLOG(DFCCW); } /* DISP0 Utility functions */ /* DMKDSP - INCPROBT */ static int ecpsvm_disp_incprobt(REGS *regs,VADR vmb) { U64 tspent; U64 DW_VMTMOUTQ; U64 DW_PROBSTRT; U64 DW_PROBTIME; DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT Entry : VMBLOK @ %8.8X\n",vmb)); DW_VMTMOUTQ=EVM_LD(vmb+VMTMOUTQ); DW_PROBSTRT=EVM_LD(PROBSTRT); DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT Entry : VMTMOUTQ = %16.16" I64_FMT "x\n",DW_VMTMOUTQ)); DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT Entry : PROBSTRT = %16.16" I64_FMT "x\n",DW_PROBSTRT)); if(DW_VMTMOUTQ==DW_PROBSTRT) { DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT Already performed")); return(2); /* continue */ } tspent=DW_PROBSTRT-DW_VMTMOUTQ; DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT TSPENT = %16.16" I64_FMT "x\n",tspent)); DW_PROBTIME=EVM_LD(PROBTIME); DW_PROBTIME-=tspent; EVM_STD(DW_PROBTIME,PROBTIME); DEBUG_CPASSISTX(DISP0,logmsg("INCPROBT NEW PROBTIME = %16.16" I64_FMT "x\n",DW_PROBTIME)); return(2); } /* DMKDSP RUNTIME */ static int ecpsvm_disp_runtime(REGS *regs,VADR *vmb_p,VADR dlist,VADR exitlist) { U64 DW_VMTTIME; U64 DW_VMTMINQ; BYTE B_VMDSTAT; BYTE B_VMTLEVEL; BYTE B_VMMCR6; U32 F_QUANTUM; U32 F_QUANTUMR; U32 F_ITIMER; int cc; RADR raddr; VADR tmraddr; U32 oldtimer,newtimer; VADR vmb; VADR runu; vmb=*vmb_p; DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME Entry : VMBLOK @ %8.8X\n",vmb)); runu=EVM_L(RUNUSER); /* BAL RUNTIME Processing */ EVM_STC(CPEX+CPSUPER,CPSTATUS); CHARGE_STOP(vmb); if(vmb!=runu) { DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME Switching to RUNUSER VMBLOK @ %8.8X\n",runu)); CHARGE_SWITCH(vmb,runu); /* Charge RUNUSER */ F_ITIMER=EVM_L(QUANTUMR); *vmb_p=vmb; } else { F_ITIMER=EVM_L(INTTIMER); } DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : VMBLOK @ %8.8X\n",vmb)); /* vmb is now RUNUSER */ /* Check if time slice is over */ if(F_ITIMER & 0x80000000) { B_VMDSTAT=EVM_IC(vmb+VMDSTAT); B_VMDSTAT&=~VMDSP; B_VMDSTAT|=VMTSEND; EVM_STC(B_VMDSTAT,vmb+VMDSTAT); } /* Check if still eligible for current run Q */ DW_VMTTIME=EVM_LD(vmb+VMTTIME); DW_VMTMINQ=EVM_LD(vmb+VMTMINQ); /* Check 1st 5 bytes */ if((DW_VMTTIME & 0xffffffffff000000ULL) < (DW_VMTMINQ & 0xffffffffff000000ULL)) { B_VMDSTAT=EVM_IC(vmb+VMDSTAT); B_VMDSTAT&=~VMDSP; B_VMDSTAT|=VMQSEND; EVM_STC(B_VMDSTAT,vmb+VMDSTAT); } ecpsvm_disp_incprobt(regs,vmb); F_QUANTUM=EVM_L(QUANTUM); EVM_ST(F_ITIMER,QUANTUM); /* Check if Virtual Timer assist is active */ B_VMMCR6=EVM_IC(vmb+VMMCR6); if(B_VMMCR6 & 0x01) /* Virtual Timer Flag */ { DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : Complete - VTIMER Assist active\n")); return(2); /* End of "RUNTIME" here */ } /* Check SET TIMER ON or SET TIMER REAL */ B_VMTLEVEL=EVM_IC(vmb+VMTLEVEL); if(!(B_VMTLEVEL & (VMTON | VMRON))) { DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : Complete - SET TIMER OFF\n")); return(2); } /* Update virtual interval timer */ F_QUANTUMR=EVM_L(QUANTUMR); F_QUANTUM-=F_QUANTUMR; if(F_QUANTUM & 0x80000000) { /* Abend condition detected during virtual time update - exit at +32 */ DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : Bad ITIMER - Taking Exist #32\n")); regs->psw.IA=EVM_L(exitlist+32); VALIDATE_AIA(regs); return(0); } /* Load CR1 with the vmblock's VMSEG */ regs->CR_L(1)=EVM_L(vmb+VMSEG); /* Do LRA - Don't access the page directly yet - Could yield a Paging fault */ cc = ecpsvm_int_lra(regs,INTTIMER,&raddr); if(cc!=0) { /* Update VMTIMER instead */ tmraddr=vmb+VMTIMER; } else { tmraddr=(VADR)raddr; } oldtimer=EVM_L(tmraddr); newtimer=oldtimer-F_QUANTUM; EVM_ST(newtimer,tmraddr); if((newtimer & 0x80000000) != (oldtimer & 0x80000000)) { /* Indicate XINT to be generated (exit + 8) */ /* Setup a few regs 1st */ regs->GR_L(3)=0; regs->GR_L(4)=0x00800080; regs->GR_L(9)=EVM_L(dlist+4); regs->GR_L(11)=vmb; regs->psw.IA = EVM_L(exitlist+8); VALIDATE_AIA(regs); DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : Complete - Taking exit #8\n")); return(0); } /* Return - Continue DISP0 Processing */ DEBUG_CPASSISTX(DISP0,logmsg("RUNTIME : Complete - ITIMER Updated\n")); return(2); } /* DISP0 : Operand 1 : DISP0 Data list, Operand 2 : DISP0 Exit list */ /* R11 : User to dispatch */ DEF_INST(ecpsvm_dispatch_main) { VADR dlist; VADR elist; VADR vmb; /* PSA Fetched Values */ BYTE B_CPSTATUS; BYTE B_VMDSTAT; BYTE B_VMPSTAT; BYTE B_VMRSTAT; BYTE B_VMPEND; BYTE B_VMESTAT; VADR F_VMPXINT; VADR OXINT; /* Back chain ptr for exit 20 */ U32 F_VMPSWHI; U32 F_VMVCR0; U32 F_VMIOINT; U32 F_VMVCR2; U32 DISPCNT; U16 H_XINTMASK; U32 iomask; BYTE extendmsk; /* Extended I/O mask */ ECPSVM_PROLOG(DISP0); dlist=effective_addr1; elist=effective_addr2; vmb=regs->GR_L(11); DISPCNT=EVM_L(dlist); DISPCNT++; /* Question #1 : Are we currently running a user */ B_CPSTATUS=EVM_IC(CPSTATUS); if((B_CPSTATUS & CPRUN)) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : CPRUN On\n")); switch(ecpsvm_disp_runtime(regs,&vmb,dlist,elist)) { case 0: /* Exit taken - success */ EVM_ST(DISPCNT,dlist); CPASSIST_HIT(DISP0); return; case 1: /* no-op DISP0 */ return; default: /* Continue processing */ break; } /* Load VMDSTAT */ B_VMDSTAT=EVM_IC(vmb+VMDSTAT); /* Check if I/O Old PSW has tranlation on */ if(regs->mainstor[0x38] & 0x04) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : I/O Old as XLATE on\n")); /* Yes - I/O Interrupt while running a USER */ if(B_VMDSTAT & VMDSP) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : VMDSP on in VMBLOK - Clean status (Exit #36)\n")); /* Clean status - Do exit 36 */ regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+36); VALIDATE_AIA(regs); EVM_ST(DISPCNT,dlist); CPASSIST_HIT(DISP0); return; } } } else { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : CPRUN Off\n")); /* Check if was in Wait State */ if(B_CPSTATUS & CPWAIT) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : CPWAIT On : Exit #4\n")); /* Take exit #4 : Coming out of wait state */ /* DMKDSPC3 */ /* No need to update R11 */ CPASSIST_HIT(DISP0); regs->psw.IA=EVM_L(elist+4); VALIDATE_AIA(regs); EVM_ST(DISPCNT,dlist); return; } } /* VMB is now either original GPR11 or RUNUSER */ /* DMKDSP - UNSTACK */ DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : At UNSTACK : VMBLOK = %8.8X\n",vmb)); B_VMRSTAT=EVM_IC(vmb+VMRSTAT); if(B_VMRSTAT & VMCPWAIT) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : VMRSTAT VMCPWAIT On (%2.2X) - Taking exit #12\n",B_VMRSTAT)); /* Take Exit 12 */ regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+12); VALIDATE_AIA(regs); CPASSIST_HIT(DISP0); EVM_ST(DISPCNT,dlist); return; } /* Check for PER/PPF (CKPEND) */ B_VMPEND=EVM_IC(vmb+VMPEND); if(B_VMPEND & (VMPERPND | VMPGPND)) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : PER/PPF Pending - Taking exit #16\n")); /* Take Exit 16 */ regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+16); VALIDATE_AIA(regs); CPASSIST_HIT(DISP0); EVM_ST(DISPCNT,dlist); return; } /* Now, check if we should unstack an external int */ /* 1st check if VMPXINT is NULL */ F_VMPSWHI=EVM_L(vmb+VMPSW); /* Load top of virt PSW - Will need it */ B_VMPSTAT=EVM_IC(vmb+VMPSTAT); /* Will need VMPSTAT for I/O ints too */ F_VMPXINT=EVM_L(vmb+VMPXINT); DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : Checking for EXT; Base VMPXINT=%8.8X\n",F_VMPXINT)); /* This is DMKDSP - CKEXT */ if(F_VMPXINT!=0) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : VPSW HI = %8.8X\n",F_VMPSWHI)); OXINT=vmb+VMPXINT; /* Check if Virtual PSW enabled for Externals */ /* (works in both BC & EC modes) */ if(F_VMPSWHI & 0x01000000) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : PSW Enabled for EXT\n")); /* Use VMVCR0 or CR0 in ECBLOK */ F_VMVCR0=EVM_L(vmb+VMVCR0); /* CR0 or ECBLOK Address */ if(B_VMPSTAT & VMV370R) /* SET ECMODE ON ?? */ { F_VMVCR0=EVM_L(F_VMVCR0+0); /* EXTCR0 at disp +0 in ECBLOK */ } DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : CR0 = %8.8X\n",F_VMVCR0)); /* scan the XINTBLOKS for a mask match */ /* Save OXINT in the loop for exit 20 */ for(;F_VMPXINT;OXINT=F_VMPXINT,F_VMPXINT=EVM_L(F_VMPXINT)) /* XINTNEXT @ +0 in XINTBLOK */ { H_XINTMASK=EVM_LH(F_VMPXINT+10); /* Get interrupt subclass in XINTBLOK */ DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : XINTMASK = %4.4X\n",H_XINTMASK)); H_XINTMASK &= F_VMVCR0; if(H_XINTMASK) /* Check against CR0 (External subclass mask) */ { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : EXT Hit - Taking exit #20\n")); /* Enabled for this external */ /* Take exit 20 */ regs->GR_L(4)=H_XINTMASK; /* Enabled subclass bits */ regs->GR_L(5)=OXINT; /* XINTBLOK Back pointer (or VMPXINT) */ regs->GR_L(6)=F_VMPXINT; /* Current XINTBLOK */ regs->GR_L(11)=vmb; /* RUNUSER */ regs->psw.IA=EVM_L(elist+20); /* Exit +20 */ VALIDATE_AIA(regs); EVM_ST(DISPCNT,dlist); CPASSIST_HIT(DISP0); return; } } } } /* After CKEXT : No external pending/reflectable */ /* This is DMKDSP UNSTIO */ /* Check for pending I/O Interrupt */ /* Load PIM */ F_VMIOINT=EVM_LH(vmb+VMIOINT); DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : Checking for I/O; VMIOINT=%8.8X\n",F_VMIOINT)); if(F_VMIOINT!=0) /* If anything in the pipe */ { F_VMIOINT <<=16; /* Put IOINT mask in bits 0-15 */ /* Is V-PSW in EC Mode ? */ iomask=0; extendmsk=0; B_VMESTAT=EVM_L(VMESTAT); if(B_VMESTAT & VMEXTCM) /* Implies VMV370R on */ { /* Check I/O bit */ if(F_VMPSWHI & 0x02000000) { iomask=0; extendmsk=1; } } else { /* BC Mode PSW */ /* Isolate channel masks for channels 0-5 */ iomask=F_VMPSWHI & 0xfc000000; if(B_VMPSTAT & VMV370R) /* SET ECMODE ON ? */ { if(F_VMPSWHI & 0x02000000) { extendmsk=1; } } } if(extendmsk) { F_VMVCR2=EVM_L(vmb+VMECEXT); F_VMVCR2=EVM_L(F_VMVCR2+8); iomask |= F_VMVCR2; } if(iomask & 0xffff0000) { F_VMIOINT&=iomask; if(F_VMIOINT) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : I/O Hit - Taking exit #24\n")); /* Take Exit 24 */ regs->GR_L(7)=F_VMIOINT; regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+24); /* Exit +24 */ VALIDATE_AIA(regs); EVM_ST(DISPCNT,dlist); CPASSIST_HIT(DISP0); return; } } } /* DMKDSP - CKWAIT */ /* Clear Wait / Idle bits in VMRSTAT */ B_VMRSTAT=EVM_IC(vmb+VMRSTAT); B_VMRSTAT &= ~(VMPSWAIT | VMIDLE); EVM_STC(B_VMRSTAT,vmb+VMRSTAT); if(F_VMPSWHI & 0x00020000) { DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : VWAIT - Taking exit #28\n")); /* Take exit 28 */ regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+28); /* Exit +28 */ VALIDATE_AIA(regs); CPASSIST_HIT(DISP0); EVM_ST(DISPCNT,dlist); return; } /* Take exit 0 (DISPATCH) */ DEBUG_CPASSISTX(DISP0,logmsg("DISP0 : DISPATCH - Taking exit #0\n")); regs->GR_L(11)=vmb; regs->psw.IA=EVM_L(elist+0); /* Exit +0 */ VALIDATE_AIA(regs); CPASSIST_HIT(DISP0); EVM_ST(DISPCNT,dlist); return; } /******************************************************/ /* SCNRU Instruction : Scan Real Unit */ /* Invoked by DMKSCN */ /* E60D */ /* SCNRU D1(R1,B1),D2(R2,B2) */ /* */ /* Operations */ /* The Device Address specified in operand 1 is */ /* the real device address for which control block */ /* addresses are to be returned. The storage area */ /* designated as the 2nd operand is a list of 4 */ /* consecutive fullword (note : the 2nd operand is */ /* treated as a Real Address, regardless of any */ /* translation mode that may be in effect) */ /* +--------------------+-------------------+ */ /* | CHNLINDEX | RCHTABLE | */ /* +--------------------+-------------------+ */ /* | RCUTABLE | RDVTABLE | */ /* +--------------------+-------------------+ */ /* CHNLINDEX is an array of 16 halfwords, each */ /* representing the offset of the target's device */ /* channel's RCHBLOCK. If the channel is not defined */ /* in the RIO table, the index has bit 0, byte 0 set */ /* to 1 */ /* */ /* The RCHBLOK has at offset +X'20' a table of */ /* 32 possible control unit indices */ /* the device's address bit 8 to 12 are used to fetch */ /* the index specified at the table. If it has */ /* bit 0 byte 0 set, then the same operation is */ /* attempted with bits 8 to 11 */ /* The RCUBLOK then fetched from RCUTABLE + the index */ /* fetched from the RCHBLOK has a DEVICE index table */ /* at offset +X'28' which can be fetched by using */ /* bits 5-7 */ /* If the RCUBLOK designates an alternate control */ /* unit block, (Offset +X'5' bit 1 set), then */ /* the primary RCUBLOK is fetched from offset X'10' */ /* */ /* If no RCHBLOK is found, R6,R7 and R8 contain -1 */ /* and Condition Code 3 is set */ /* */ /* if no RCUBLOK is found, R6 contains the RCHBLOK, */ /* R7 and R8 contain -1, and Condition Code 2 is set */ /* */ /* if no RDVBLOK is found, R6 contains the RCHBLOK, */ /* R7 contains the RCUBLOK and R8 contains -1, and */ /* condition code 1 is set */ /* */ /* If all 3 control blocks are found, R6 contains the */ /* the RCHBLOK, R7 Contains the RCUBLOK and R8 cont- */ /* ains the RDVBLOK. Condition code 0 is set */ /* */ /* If the instruction is sucesfull, control is */ /* returned at the address specified by GPR14. */ /* Otherwise, the next sequential instruction is */ /* executed, and no GPR or condition code is changed. */ /* */ /* Exceptions : */ /* Operation Exception : ECPS:VM Disabled */ /* Priviledged Exception : PSW in problem state */ /* */ /* Note : no access exception is generated for */ /* the second operand. */ /* */ /* Note : as of yet, for any other situation than */ /* finding all 3 control blocks, SCNRU acts */ /* as a NO-OP */ /******************************************************/ DEF_INST(ecpsvm_locate_rblock) { U32 chix; /* offset of RCH in RCH Array */ U32 cuix; /* Offset of RCU in RCU Array */ U32 dvix; /* Offset of RDV in RDV Array */ VADR rchixtbl; /* RCH Index Table */ VADR rchtbl; /* RCH Array */ VADR rcutbl; /* RCU Array */ VADR rdvtbl; /* RDV Array */ VADR arioct; /* Data list for SCNRU */ VADR rchblk; /* Effective RCHBLOK Address */ VADR rcublk; /* Effective RCUBLOK Address */ VADR rdvblk; /* Effective RDVBLOK Address */ U16 rdev; ECPSVM_PROLOG(SCNRU); /* Obtain the Device address */ rdev=(effective_addr1 & 0xfff); /* And the DMKRIO tables addresses */ arioct=effective_addr2; DEBUG_CPASSISTX(SCNRU,logmsg(_("HHCEV300D : ECPS:VM SCNRU called; RDEV=%4.4X ARIOCT=%6.6X\n"),effective_addr1,arioct)); /* Get the Channel Index Table */ rchixtbl= EVM_L(effective_addr2); /* Obtain the RCH offset */ chix=EVM_LH(rchixtbl+((rdev & 0xf00) >> 7)); DEBUG_CPASSISTX(SCNRU,logmsg(_("HHCEV300D : ECPS:VM SCNRU : RCH IX = %x\n"),chix)); /* Check if Bit 0 set (no RCH) */ if(chix & 0x8000) { // logmsg(_("HHCEV300D : ECPS:VM SCNRU : NO CHANNEL\n")); /* regs->GR_L(6)=~0; regs->GR_L(7)=~0; regs->GR_L(8)=~0; regs->psw.IA=regs->GR_L(14) & ADDRESS_MAXWREP(regs); VALIDATE_AIA(regs); regs->psw.cc=1; */ /* Right now, let CP handle the case */ return; } /* Obtain the RCH Table pointer */ rchtbl=EVM_L(arioct+4); /* Add the RCH Index offset */ rchblk=rchtbl+chix; /* Try to obtain RCU index with bits 8-12 of the device */ cuix=EVM_LH(rchblk+0x20+((rdev & 0xf8)>>2)); if(cuix & 0x8000) { /* Try with bits 8-11 */ cuix=EVM_LH(rchblk+0x20+((rdev & 0xf0)>>2)); if(cuix & 0x8000) { // logmsg(_("HHCEV300D : ECPS:VM SCNRU : NO CONTROL UNIT\n")); /* regs->GR_L(6)=rchblk; regs->GR_L(7)=~0; regs->GR_L(8)=~0; regs->psw.IA=regs->GR_L(14) & ADDRESS_MAXWREP(regs); regs->psw.cc=2; VALIDATE_AIA(regs); */ return; } } DEBUG_CPASSISTX(SCNRU,logmsg(_("HHCEV300D : ECPS:VM SCNRU : RCU IX = %x\n"),cuix)); rcutbl=EVM_L(arioct+8); rcublk=rcutbl+cuix; dvix=EVM_LH(rcublk+0x28+((rdev & 0x00f)<<1)); if(EVM_IC(rcublk+5)&0x40) { rcublk=EVM_L(rcublk+0x10); } if(dvix & 0x8000) { // logmsg(_("HHCEV300D : ECPS:VM SCNRU : NO RDEVBLOK\n")); /* regs->GR_L(6)=rchblk; regs->GR_L(7)=rcublk; regs->GR_L(8)=~0; regs->psw.IA=regs->GR_L(14) & ADDRESS_MAXWREP(regs); VALIDATE_AIA(regs); regs->psw.cc=3; */ return; } DEBUG_CPASSISTX(SCNRU,logmsg(_("HHCEV300D : ECPS:VM SCNRU : RDV IX = %x\n"),dvix)); dvix<<=3; rdvtbl=EVM_L(arioct+12); rdvblk=rdvtbl+dvix; DEBUG_CPASSISTX(SCNRU,logmsg(_("HHCEV300D : ECPS:VM SCNRU : RCH = %6.6X, RCU = %6.6X, RDV = %6.6X\n"),rchblk,rcublk,rdvblk)); regs->GR_L(6)=rchblk; regs->GR_L(7)=rcublk; regs->GR_L(8)=rdvblk; regs->psw.cc=0; regs->GR_L(15)=0; BR14; CPASSIST_HIT(SCNRU); } /* CCWGN : Not supported */ DEF_INST(ecpsvm_comm_ccwproc) { ECPSVM_PROLOG(CCWGN); } /* UXCCW : Not supported */ DEF_INST(ecpsvm_unxlate_ccw) { ECPSVM_PROLOG(UXCCW); } /* DISP2 : Not supported */ DEF_INST(ecpsvm_disp2) { ECPSVM_PROLOG(DISP2); switch(ecpsvm_do_disp2(regs,effective_addr1,effective_addr2)) { case 0: /* Done */ CPASSIST_HIT(DISP2); return; case 1: /* No-op */ return; case 2: /* Done */ CPASSIST_HIT(DISP2); RETURN_INTCHECK(regs); } return; } /* STEVL : Store ECPS:VM support level */ /* STEVL D1(R1,B1),D2(R2,B2) */ /* 1st operand : Fullword address in which to store ECPS:VM Support level */ /* 2nd operand : ignored */ DEF_INST(ecpsvm_store_level) { ECPSVM_PROLOG(STEVL); EVM_ST(sysblk.ecpsvm.level,effective_addr1); DEBUG_CPASSISTX(STEVL,logmsg(_("HHCEV300D : ECPS:VM STORE LEVEL %d called\n"),sysblk.ecpsvm.level)); CPASSIST_HIT(STEVL); } /* LCSPG : Locate Changed Shared Page */ /* LCSPG : Not supported */ DEF_INST(ecpsvm_loc_chgshrpg) { ECPSVM_PROLOG(LCSPG); } /*************************************************************/ /* FREEX : Allocate CP Storage Extended */ /* FREEX B1(R1,B1),D2(R2,B2) */ /* 1st operand : Address of FREEX Parameter list */ /* +0 : Maxsize = Max number of DW allocatable */ /* with FREEX */ /* +4- : Subpool index table */ /* 2nd operand : Subpool table (indexed) */ /* GPR 0 : Number of DWs to allocate */ /* */ /* Each allocatable block is forward chained */ /* if the subpool is empty, return to caller */ /* if the subpool has an entry, allocate from the subpool */ /* and save the next block address as the subpool chain head */ /* return allocated block in GPR1. return at address in GPR14*/ /* if allocation succeeded */ /* if allocate fails, return at next sequential instruction */ /*************************************************************/ DEF_INST(ecpsvm_extended_freex) { U32 maxdw; U32 numdw; U32 maxsztbl; U32 spixtbl; BYTE spix; U32 freeblock; U32 nextblk; ECPSVM_PROLOG(FREEX); numdw=regs->GR_L(0); spixtbl=effective_addr2; maxsztbl=effective_addr1; DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : ECPS:VM FREEX DW = %4.4X\n"),numdw)); if(numdw==0) { return; } DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : MAXSIZE ADDR = %6.6X, SUBPOOL INDEX TABLE = %6.6X\n"),maxsztbl,spixtbl)); /* E1 = @ of MAXSIZE (maximum # of DW allocatable by FREEX from subpools) */ /* followed by subpool pointers */ /* E2 = @ of subpool indices */ maxdw=EVM_L(maxsztbl); if(regs->GR_L(0)>maxdw) { DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : FREEX request beyond subpool capacity\n"))); return; } /* Fetch subpool index */ spix=EVM_IC(spixtbl+numdw); DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : Subpool index = %X\n"),spix)); /* Fetch value */ freeblock=EVM_L(maxsztbl+4+spix); DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : Value in subpool table = %6.6X\n"),freeblock)); if(freeblock==0) { /* Can't fullfill request here */ return; } nextblk=EVM_L(freeblock); EVM_ST(nextblk,maxsztbl+4+spix); DEBUG_CPASSISTX(FREEX,logmsg(_("HHCEV300D : New Value in subpool table = %6.6X\n"),nextblk)); regs->GR_L(1)=freeblock; regs->psw.cc=0; BR14; CPASSIST_HIT(FREEX); return; } /*************************************************************/ /* FRETX : Return CP Free storage */ /* FRETX D1(R1,B1),R2(D2,B2) */ /* 1st operand : Max DW for subpool free/fret */ /* 2nd Operand : FRET PLIST */ /* +0 Coretable address */ /* +4 CL4'FREE' */ /* +8 Maxsize (same as operand 1) */ /* +12 Subpool Table Index */ /* The block is checked VS the core table to check if it */ /* is eligible to be returned to the subpool chains */ /* If it is, then it is returned. Control is returned at */ /* the address in GPR 14. Otherwise, if anything cannot */ /* be resolved, control is returned at the next sequential */ /* Instruction */ /*************************************************************/ int ecpsvm_do_fretx(REGS *regs,VADR block,U16 numdw,VADR maxsztbl,VADR fretl) { U32 cortbl; U32 maxdw; U32 cortbe; /* Core table Page entry for fretted block */ U32 prevblk; BYTE spix; DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : X fretx called AREA=%6.6X, DW=%4.4X\n"),regs->GR_L(1),regs->GR_L(0))); if(numdw==0) { DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : ECPS:VM Cannot FRETX : DWORDS = 0\n"))); return(1); } maxdw=EVM_L(maxsztbl); if(numdw>maxdw) { DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : ECPS:VM Cannot FRETX : DWORDS = %d > MAXDW %d\n"),numdw,maxdw)); return(1); } cortbl=EVM_L(fretl); cortbe=cortbl+((block & 0xfff000)>>8); if(EVM_L(cortbe)!=EVM_L(fretl+4)) { DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : ECPS:VM Cannot FRETX : Area not in Core Free area\n"))); return(1); } if(EVM_IC(cortbe+8)!=0x02) { DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : ECPS:VM Cannot FRETX : Area flag != 0x02\n"))); return(1); } spix=EVM_IC(fretl+11+numdw); prevblk=EVM_L(maxsztbl+4+spix); if(prevblk==block) { DEBUG_CPASSISTX(FRETX,logmsg(_("HHCEV300D : ECPS:VM Cannot FRETX : fretted block already on subpool chain\n"))); return(1); } EVM_ST(block,maxsztbl+4+spix); EVM_ST(prevblk,block); return(0); } DEF_INST(ecpsvm_extended_fretx) { U32 fretl; U32 maxsztbl; U32 numdw; U32 block; ECPSVM_PROLOG(FRETX); numdw=regs->GR_L(0); block=regs->GR_L(1) & ADDRESS_MAXWRAP(regs); maxsztbl=effective_addr1 & ADDRESS_MAXWRAP(regs); fretl=effective_addr2 & ADDRESS_MAXWRAP(regs); if(ecpsvm_do_fretx(regs,block,numdw,maxsztbl,fretl)==0) { BR14; CPASSIST_HIT(FRETX); } return; } DEF_INST(ecpsvm_prefmach_assist) { ECPSVM_PROLOG(PMASS); } /**********************************/ /* VM ASSISTS */ /**********************************/ /******************************************************************/ /* LPSW/SSM/STxSM : */ /* Not sure about the current processing .. */ /* *MAYBE* we need to invoke DMKDSPCH when the newly loaded PSW */ /* does not need further checking. Now.. I wonder what the point */ /* is to return to CP anyway, as we have entirelly validated the */ /* new PSW (i.e. for most of it, this is essentially a BRANCH */ /* However... */ /* Maybe we should call DMKDSPCH (from the DMKPRVMA list) */ /* only if re-enabling bits (and no Int pending) */ /* */ /* For the time being, we do THIS : */ /* If the new PSW 'disables' bits or enables bit but MICPEND=0 */ /* we just update the VPSW and continue */ /* Same for LPSW.. But we also update the IA */ /* If we encounter ANY issue, we just return to caller (which will*/ /* generate a PRIVOP) thus invoking CP like for non-EVMA */ /******************************************************************/ /******************************************************************/ /* Check psw Transition validity */ /******************************************************************/ /* NOTE : oldr/newr Only have the PSW field valid (the rest is not initialised) */ int ecpsvm_check_pswtrans(REGS *regs,ECPSVM_MICBLOK *micblok, BYTE micpend, REGS *oldr, REGS *newr) { UNREFERENCED(micblok); UNREFERENCED(regs); /* Check for a switch from BC->EC or EC->BC */ if(ECMODE(&oldr->psw)!=ECMODE(&newr->psw)) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New and Old PSW have a EC/BC transition\n"))); return(1); } /* Check if PER or DAT is being changed */ if(ECMODE(&newr->psw)) { if((newr->psw.sysmask & 0x44) != (oldr->psw.sysmask & 0x44)) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW Enables DAT or PER\n"))); return(1); } } /* Check if a Virtual interrupt is pending and new interrupts are being enabled */ if(micpend & 0x80) { if(ECMODE(&newr->psw)) { if(((~oldr->psw.sysmask) & 0x03) & newr->psw.sysmask) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW Enables interrupts and MICPEND (EC)\n"))); return(1); } } else { if(~oldr->psw.sysmask & newr->psw.sysmask) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW Enables interrupts and MICPEND (BC)\n"))); return(1); } } } if(WAITSTATE(&newr->psw)) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW is a WAIT PSW\n"))); return(1); } if(ECMODE(&newr->psw)) { if(newr->psw.sysmask & 0xb8) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW sysmask incorrect\n"))); return(1); } } if(newr->psw.IA & 0x01) { DEBUG_SASSISTX(LPSW,logmsg(_("HHCEV300D : New PSW has ODD IA\n"))); return(1); } return(0); } int ecpsvm_dossm(REGS *regs,int b2,VADR effective_addr2) { BYTE reqmask; BYTE *cregs; U32 creg0; REGS npregs; SASSIST_PROLOG(SSM); /* Reject if V PSW is in problem state */ if(CR6 & ECPSVM_CR6_VIRTPROB) { DEBUG_SASSISTX(SSM,logmsg("HHCEV300D : SASSIST SSM reject : V PB State\n")); return(1); } /* if(!(micevma & MICSTSM)) { DEBUG_SASSISTX(SSM,logmsg("HHCEV300D : SASSIST SSM reject : SSM Disabled in MICEVMA; EVMA=%2.2X\n",micevma)); return(1); } */ /* Get CR0 - set ref bit on fetched CR0 (already done in prolog for MICBLOK) */ cregs=MADDR(micblok.MICCREG,USE_REAL_ADDR,regs,ACCTYPE_READ,0); FETCH_FW(creg0,cregs); /* Reject if V CR0 specifies SSM Suppression */ if(creg0 & 0x40000000) { DEBUG_SASSISTX(SSM,logmsg("HHCEV300D : SASSIST SSM reject : V SSM Suppr\n")); return(1); } /* Load the requested SSM Mask */ /* USE Normal vfetchb here ! not only do we want tranlsation */ /* but also fetch protection control, ref bit, etc.. */ reqmask=ARCH_DEP(vfetchb) (effective_addr2,b2,regs); INITPSEUDOREGS(npregs); /* Load the virtual PSW AGAIN in a new structure */ ARCH_DEP(load_psw) (&npregs,vpswa_p); npregs.psw.sysmask=reqmask; if(ecpsvm_check_pswtrans(regs,&micblok,micpend,&vpregs,&npregs)) /* Check PSW transition capability */ { DEBUG_SASSISTX(SSM,logmsg("HHCEV300D : SASSIST SSM Reject : New PSW too complex\n")); return(1); /* Something in the NEW PSW we can't handle.. let CP do it */ } /* While we are at it, set the IA in the V PSW */ npregs.psw.IA=regs->psw.IA & ADDRESS_MAXWRAP(regs); /* Set the change bit */ MADDR(vpswa,USE_REAL_ADDR,regs,ACCTYPE_WRITE,0); /* store the new PSW */ ARCH_DEP(store_psw) (&npregs,vpswa_p); DEBUG_SASSISTX(SSM,logmsg("HHCEV300D : SASSIST SSM Complete : new SM = %2.2X\n",reqmask)); DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST SSM New VIRT ")); DEBUG_SASSISTX(LPSW,display_psw(&npregs)); DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST SSM New REAL ")); DEBUG_SASSISTX(LPSW,display_psw(regs)); SASSIST_HIT(SSM); return(0); } int ecpsvm_dosvc(REGS *regs,int svccode) { PSA_3XX *psa; REGS newr; SASSIST_PROLOG(SVC); if(svccode==76) /* NEVER trap SVC 76 */ { DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC Reject : SVC 76\n")); return(1); } if(CR6 & ECPSVM_CR6_SVCINHIB) { DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC Reject : SVC Assist Inhibit\n")); return(1); /* SVC SASSIST INHIBIT ON */ } /* Get what the NEW PSW should be */ psa=(PSA_3XX *)MADDR((VADR)0 , USE_PRIMARY_SPACE, regs, ACCTYPE_READ, 0); /* Use all around access key 0 */ /* Also sets reference bit */ INITPSEUDOREGS(newr); ARCH_DEP(load_psw) (&newr, (BYTE *)&psa->svcnew); /* Ref bit set above */ DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC NEW VIRT ")); DEBUG_SASSISTX(SVC,display_psw(&newr)); /* Get some stuff from the REAL Running PSW to put in OLD SVC PSW */ vpregs.psw.IA=(regs->psw.IA & ADDRESS_MAXWRAP(regs)); /* Instruction Address */ vpregs.psw.cc=regs->psw.cc; /* Condition Code */ vpregs.psw.pkey=regs->psw.pkey; /* Protection Key */ vpregs.psw.progmask=regs->psw.progmask; /* Program Mask */ vpregs.psw.intcode=svccode; /* SVC Interrupt code */ DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC OLD VIRT ")); DEBUG_SASSISTX(SVC,display_psw(&vpregs)); if(ecpsvm_check_pswtrans(regs,&micblok,micpend,&vpregs,&newr)) /* Check PSW transition capability */ { DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC Reject : Cannot make transition to new PSW\n")); return(1); /* Something in the NEW PSW we can't handle.. let CP do it */ } /* Store the OLD SVC PSW */ // ZZ psa=(PSA_3XX *)MADDR((VADR)0, USE_PRIMARY_SPACE, regs, ACCTYPE_WRITE, 0); /* Use all around access key 0 */ /* Also sets change bit */ /* Set intcode in PSW (for BC mode) */ ARCH_DEP(store_psw) (&vpregs, (BYTE *)&psa->svcold); if(ECMODE(&vpregs.psw)) { /* Also set SVC interrupt code */ /* and ILC */ STORE_FW((BYTE *)&psa->svcint,0x00020000 | svccode); } /* * Now, update some stuff in the REAL PSW */ SASSIST_LPSW(newr); /* * Now store the new PSW in the area pointed by the MICBLOK */ ARCH_DEP(store_psw) (&newr,vpswa_p); DEBUG_SASSISTX(SVC,logmsg("HHCEV300D : SASSIST SVC Done\n")); SASSIST_HIT(SVC); return(0); } /* LPSW Assist */ int ecpsvm_dolpsw(REGS *regs,int b2,VADR e2) { BYTE * nlpsw; REGS nregs; SASSIST_PROLOG(LPSW); /* Reject if V PSW is in problem state */ if(CR6 & ECPSVM_CR6_VIRTPROB) { DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW reject : V PB State\n")); return(1); } /* Reject if MICEVMA says not to do LPSW sim */ if(!(micevma & MICLPSW)) { DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW reject : LPSW disabled in MICEVMA\n")); return(1); } if(e2&0x03) { DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW %6.6X - Alignement error\n",e2)); return(1); } nlpsw=MADDR(e2,b2,regs,ACCTYPE_READ,regs->psw.pkey); INITPSEUDOREGS(nregs); ARCH_DEP(load_psw) (&nregs,nlpsw); if(ecpsvm_check_pswtrans(regs,&micblok,micpend,&vpregs,&nregs)) { DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW Rejected - Cannot make PSW transition\n")); return(1); } SASSIST_LPSW(nregs); MADDR(vpswa,USE_REAL_ADDR,regs,ACCTYPE_WRITE,0); /* Set ref bit in address pointed by MICBLOK */ ARCH_DEP(store_psw) (&nregs,vpswa_p); DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW New VIRT ")); DEBUG_SASSISTX(LPSW,display_psw(&nregs)); DEBUG_SASSISTX(LPSW,logmsg("HHCEV300D : SASSIST LPSW New REAL ")); DEBUG_SASSISTX(LPSW,display_psw(regs)); SASSIST_HIT(LPSW); return(0); } int ecpsvm_virttmr_ext(REGS *regs) { DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Checking if we can IRPT\n")); DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Virtual")); DEBUG_SASSISTX(VTIMER,display_psw(regs)); if(IS_IC_ECPSVTIMER(regs)) { DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Not pending\n")); return(1); } if(!PROBSTATE(®s->psw)) { DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Not dispatching a VM\n")); return(1); } if(!(regs->psw.sysmask & PSW_EXTMASK)) { DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Test int : Not enabled for EXT\n")); return(1); } if(!(regs->CR_L(6) & ECPSVM_CR6_VIRTTIMR)) { DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Test int : Not enabled for VTIMER\n")); return(1); } DEBUG_SASSISTX(VTIMER,logmsg("HHCEV300D : SASSIST VTIMER Please, do\n")); return(0); } /* SIO/SIOF Assist */ int ecpsvm_dosio(REGS *regs,int b2,VADR e2) { SASSIST_PROLOG(SIO); UNREFERENCED(b2); UNREFERENCED(e2); return(1); } int ecpsvm_dostnsm(REGS *regs,int b1,VADR effective_addr1,int imm2) { SASSIST_PROLOG(STNSM); UNREFERENCED(b1); UNREFERENCED(effective_addr1); UNREFERENCED(imm2); return(1); } int ecpsvm_dostosm(REGS *regs,int b1,VADR effective_addr1,int imm2) { SASSIST_PROLOG(STOSM); UNREFERENCED(b1); UNREFERENCED(effective_addr1); UNREFERENCED(imm2); return(1); } int ecpsvm_dostctl(REGS *regs,int r1,int r3,int b2,VADR effective_addr2) { SASSIST_PROLOG(STCTL); UNREFERENCED(r1); UNREFERENCED(r3); UNREFERENCED(b2); UNREFERENCED(effective_addr2); return(1); } int ecpsvm_dolctl(REGS *regs,int r1,int r3,int b2,VADR effective_addr2) { U32 crs[16]; /* New CRs */ U32 rcrs[16]; /* REAL CRs */ U32 ocrs[16]; /* Old CRs */ BYTE *ecb_p; VADR F_ECBLOK,vmb; BYTE B_VMPSTAT; int i,j,numcrs; SASSIST_PROLOG(LCTL); if(effective_addr2 & 0x03) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : Not aligned\n")); return(1); } vmb=vpswa-0xA8; B_VMPSTAT=EVM_IC(vmb+VMPSTAT); if((!(B_VMPSTAT & VMV370R)) && ((r1!=r3) || (r1!=0))) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : BC Mode VM & LCTL != 0,0\n")); return(1); } /* Determine the range of CRs to be loaded */ if(r1>r3) { numcrs=(r3+16)-r1; } else { numcrs=r3-r1; } numcrs++; for(j=r1,i=0;i15) { j-=16; } crs[j]=ARCH_DEP(vfetch4)((effective_addr2+(i*4)) & ADDRESS_MAXWRAP(regs),b2,regs); } if(B_VMPSTAT & VMV370R) { F_ECBLOK=fetch_fw(regs->mainstor+vmb+VMECEXT); for(i=0;i<16;i++) { ecb_p=MADDR(F_ECBLOK+(i*4),USE_REAL_ADDR,regs,ACCTYPE_READ,0); ocrs[i]=fetch_fw(ecb_p); } } else { F_ECBLOK=vmb+VMECEXT; /* Update ECBLOK ADDRESS for VCR0 Update */ ecb_p=MADDR(F_ECBLOK,USE_REAL_ADDR,regs,ACCTYPE_READ,0); /* Load OLD CR0 From VMBLOK */ ocrs[0]=fetch_fw(ecb_p); } for(i=0;i<16;i++) { rcrs[i]=regs->CR_L(i); } /* Source safely loaded into "crs" array */ /* Load the CRS - exit from loop if it's not possible */ DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL %d,%d : Modifying %d cregs\n",r1,r3,numcrs)); for(j=r1,i=0;i15) { j-=16; } switch(j) { case 0: /* CR0 Case */ /* Check 1st 2 bytes of CR0 - No change allowed */ if((ocrs[0] & 0xffff0000) != (crs[0] & 0xffff0000)) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : CR0 High changed\n")); return 1; } /* Not allowed if : NEW mask is being enabled AND MICPEND AND PSW has EXT enabled */ if(vpregs.psw.sysmask & 0x01) { if(micpend & 0x80) { if((~(ocrs[0] & 0xffff)) & (crs[0] & 0xffff)) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : CR0 EXTSM Enables new EXTS\n")); return 1; } } } ocrs[0]=crs[0]; break; case 1: if(ocrs[1] != crs[1]) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : CR1 Updates shadow table\n")); return 1; } break; case 2: /* Not allowed if : NEW Channel mask is turned on AND micpend AND PSW Extended I/O Mask is on */ if(vpregs.psw.sysmask & 0x02) { if((~ocrs[2]) & crs[2]) { if(micpend & 0x80) { DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : CR2 IOCSM Enables I/O Ints\n")); return(1); } } } ocrs[2]=crs[2]; break; case 3: /* DAS Control regs (not used under VM/370) */ case 4: case 5: case 7: ocrs[j]=crs[j]; rcrs[j]=crs[j]; break; case 6: /* VCR6 Ignored on real machine */ ocrs[j]=crs[j]; break; case 8: /* Monitor Calls */ DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : MC CR8 Update\n")); return(1); case 9: /* PER Control Regs */ case 10: case 11: DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL Reject : PER CR%d Update\n",j)); return(1); case 12: case 13: /* 12-13 : Unused */ ocrs[j]=crs[j]; rcrs[j]=crs[j]; break; case 14: case 15: /* 14-15 Machine Check & I/O Logout control (plus DAS) */ ocrs[j]=crs[j]; break; default: break; } } /* Update REAL Control regs */ for(i=0;i<16;i++) { regs->CR_L(i)=rcrs[i]; } /* Update ECBLOK/VMBLOK Control regs */ /* Note : if F_ECBLOK addresses VMVCR0 in the VMBLOCK */ /* check has already been done to make sure */ /* r1=0 and numcrs=1 */ for(j=r1,i=0;i15) { j-=16; } ecb_p=MADDR(F_ECBLOK+(j*4),USE_REAL_ADDR,regs,ACCTYPE_WRITE,0); store_fw(ecb_p,ocrs[j]); } DEBUG_SASSISTX(LCTL,logmsg("HHCEV300D : SASSIST LCTL %d,%d Done\n",r1,r3)); SASSIST_HIT(LCTL); return 0; } int ecpsvm_doiucv(REGS *regs,int b2,VADR effective_addr2) { SASSIST_PROLOG(IUCV); UNREFERENCED(b2); UNREFERENCED(effective_addr2); return(1); } int ecpsvm_dodiag(REGS *regs,int r1,int r3,int b2,VADR effective_addr2) { SASSIST_PROLOG(DIAG); UNREFERENCED(r1); UNREFERENCED(r3); UNREFERENCED(b2); UNREFERENCED(effective_addr2); return(1); } static char *ecpsvm_stat_sep="HHCEV003I +-----------+----------+----------+-------+\n"; static int ecpsvm_sortstats(const void *a,const void *b) { ECPSVM_STAT *ea,*eb; ea=(ECPSVM_STAT *)a; eb=(ECPSVM_STAT *)b; return(eb->call-ea->call); } static void ecpsvm_showstats2(ECPSVM_STAT *ar,size_t count) { char *sep=ecpsvm_stat_sep; char nname[32]; int havedisp=0; int notshown=0; size_t unsupcc=0; int haveunsup=0; int callt=0; int hitt=0; size_t i; for(i=0;iname)==0) { *fclass="VM ASSIST"; return(es); } } esl=(ECPSVM_STAT *)&ecpsvm_cpstats; fcount=sizeof(ecpsvm_cpstats)/sizeof(ECPSVM_STAT); for(i=0;iname)==0) { *fclass="CP ASSIST"; return(es); } } return(NULL); } void ecpsvm_enadisaall(char *fclass,ECPSVM_STAT *tbl,size_t count,int onoff,int debug) { ECPSVM_STAT *es; size_t i; char *enadisa,*debugonoff; enadisa=onoff?"Enabled":"Disabled"; debugonoff=debug?"On":"Off"; for(i=0;i=0) { es->enabled=onoff; logmsg(_("HHCEV015I ECPS:VM %s feature %s %s\n"),fclass,es->name,enadisa); } if(debug>=0) { es->debug=debug; logmsg(_("HHCEV015I ECPS:VM %s feature %s Debug %s\n"),fclass,es->name,debugonoff); } } if(onoff>=0) { logmsg(_("HHCEV016I All ECPS:VM %s features %s\n"),fclass,enadisa); } if(debug>=0) { logmsg(_("HHCEV016I All ECPS:VM %s features Debug %s\n"),fclass,debugonoff); } } void ecpsvm_enable_disable(int ac,char **av,int onoff,int debug) { char *fclass; char *enadisa,*debugonoff; int i; size_t sacount,cpcount; ECPSVM_STAT *es; ECPSVM_STAT *sal,*cpl; sal=(ECPSVM_STAT *)&ecpsvm_sastats; cpl=(ECPSVM_STAT *)&ecpsvm_cpstats; sacount=sizeof(ecpsvm_sastats)/sizeof(ECPSVM_STAT); cpcount=sizeof(ecpsvm_cpstats)/sizeof(ECPSVM_STAT); enadisa=onoff?"Enabled":"Disabled"; debugonoff=debug?"On":"Off"; if(ac==1) { ecpsvm_enadisaall("VM ASSIST",sal,sacount,onoff,debug); ecpsvm_enadisaall("CP ASSIST",cpl,cpcount,onoff,debug); if(debug>=0) { sysblk.ecpsvm.debug=debug; logmsg(_("HHCEV013I ECPS:VM Global Debug %s\n"),debugonoff); } return; } for(i=1;i=0) { es->enabled=onoff; logmsg(_("HHCEV014I ECPS:VM %s feature %s %s\n"),fclass,es->name,enadisa); } if(debug>=0) { es->debug=onoff; logmsg(_("HHCEV014I ECPS:VM %s feature %s Debug %s\n"),fclass,es->name,debugonoff); } } else { logmsg(_("HHCEV014I Unknown ECPS:VM feature %s; Ignored\n"),av[i]); } } } void ecpsvm_disable(int ac,char **av) { ecpsvm_enable_disable(ac,av,0,-1); } void ecpsvm_enable(int ac,char **av) { ecpsvm_enable_disable(ac,av,1,-1); } void ecpsvm_debug(int ac,char **av) { ecpsvm_enable_disable(ac,av,-1,1); } void ecpsvm_nodebug(int ac,char **av) { ecpsvm_enable_disable(ac,av,-1,0); } void ecpsvm_level(int ac,char **av) { int lvl; if(sysblk.ecpsvm.available) { logmsg(_("HHCEV016I Current reported ECPS:VM Level is %d\n"),sysblk.ecpsvm.level); } else { logmsg(_("HHCEV016I Current reported ECPS:VM Level is %d\n"),sysblk.ecpsvm.level); logmsg(_("HHCEV017I But ECPS:VM is currently disabled\n")); } if(ac>1) { lvl=atoi(av[1]); logmsg(_("HHCEV016I Level reported to guest program is now %d\n"),lvl); sysblk.ecpsvm.level=lvl; } if(sysblk.ecpsvm.level!=20) { logmsg(_("HHCEV017W WARNING ! current level (%d) is not supported\n"),sysblk.ecpsvm.level); logmsg(_("HHCEV018W WARNING ! Unpredictable results may occur\n")); logmsg(_("HHCEV019I The microcode support level is 20\n")); } } static void ecpsvm_helpcmd(int,char **); static ECPSVM_CMDENT ecpsvm_cmdtab[]={ {"Help",1,ecpsvm_helpcmd,"Show help","format : \"evm help [cmd]\" Shows help on the specified\n" " ECPSVM subcommand\n"}, {"STats",2,ecpsvm_showstats,"Show statistical counters","format : evm stats : Shows various ECPS:VM Counters\n"}, {"DIsable",2,ecpsvm_disable,"Disable ECPS:VM Features","format : evm disable [ALL|feat1[ feat2|...]\n"}, {"ENable",2,ecpsvm_enable,"Enable ECPS:VM Features","format : evm enable [ALL|feat1[ feat2|...]\n"}, #if defined(DEBUG_SASSIST) || defined(DEBUG_CPASSIST) {"DEBUG",5,ecpsvm_debug,"Debug ECPS:VM Features","format : evm debug [ALL|feat1[ feat2|...]\n"}, {"NODebug",3,ecpsvm_nodebug,"Turn Debug off for ECPS:VM Features","format : evm NODebug [ALL|feat1[ feat2|...]\n"}, #endif {"Level",1,ecpsvm_level,"Set/Show ECPS:VM level","format : evm Level [nn]\n"}, {NULL,0,NULL,NULL,NULL}}; static void ecpsvm_helpcmdlist(void) { int i; ECPSVM_CMDENT *ce; for(i=0;ecpsvm_cmdtab[i].name;i++) { ce=&ecpsvm_cmdtab[i]; logmsg(_("HHCEV010I : %s : %s\n"),ce->name,ce->expl); } return; } void ecpsvm_helpcmd(int ac,char **av) { ECPSVM_CMDENT *ce; if(ac==1) { ecpsvm_helpcmdlist(); return; } ce=ecpsvm_getcmdent(av[1]); if(ce==NULL) { logmsg(_("HHCEV011E Unknown subcommand %s - valid subcommands are :\n"),av[1]); ecpsvm_helpcmdlist(); return; } logmsg(_("HHCEV012I : %s : %s"),ce->name,ce->help); return; } ECPSVM_CMDENT *ecpsvm_getcmdent(char *cmd) { ECPSVM_CMDENT *ce; int i; int clen; for(i=0;ecpsvm_cmdtab[i].name;i++) { ce=&ecpsvm_cmdtab[i]; if(strlen(cmd)<=strlen(ce->name) && strlen(cmd)>=(size_t)ce->abbrev) { clen=strlen(cmd); if(strncasecmp(cmd,ce->name,clen)==0) { return(ce); } } } return(NULL); } void ecpsvm_command(int ac,char **av) { ECPSVM_CMDENT *ce; logmsg(_("HHCEV011I ECPS:VM Command processor invoked\n")); if(ac==1) { logmsg(_("HHCEV008E NO EVM subcommand. Type \"evm help\" for a list of valid subcommands\n")); return; } ce=ecpsvm_getcmdent(av[1]); if(ce==NULL) { logmsg(_("HHCEV008E Unknown EVM subcommand %s\n"),av[1]); return; } ce->fun(ac-1,av+1); logmsg(_("HHCEV011I ECPS:VM Command processor complete\n")); } #endif /* ifdef FEATURE_ECPSVM */ /* Note : The following forces inclusion for S/390 & z/ARCH */ /* This is necessary just in order to properly define */ /* S/390 auxialiary routines invoked by S/370 routines */ /* because of SIE */ #if !defined(_GEN_ARCH) #if defined(_ARCHMODE2) #define _GEN_ARCH _ARCHMODE2 #include "ecpsvm.c" #endif #if defined(_ARCHMODE3) #undef _GEN_ARCH #define _GEN_ARCH _ARCHMODE3 #include "ecpsvm.c" #endif #endif /*!defined(_GEN_ARCH)*/