Files
org-hyperion-cules/ecpsvm.c
2012-12-26 22:34:02 -08:00

3030 lines
96 KiB
C

/*ECPSVM.C (c) Copyright Roger Bowler, 2000-2012 */
/* Hercules ECPS:VM Support */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/***********************************************************/
/* HERCULES ECPS:VM Support */
/* (c) Copyright 2003-2009 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 UPD_PSW_IA(regs, regs->GR_L(14))
#define INITPSEUDOIP(_regs) \
do { \
(_regs).ip=(BYTE*)"\0\0"; \
} while(0)
#define INITPSEUDOREGS(_regs) \
do { \
memset(&(_regs), 0, sysblk.regs_copy_len); \
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 { \
SET_PSW_IA(&(_regs)); \
UPD_PSW_IA(regs, _regs.psw.IA); \
regs->psw.cc=_regs.psw.cc; \
regs->psw.pkey=_regs.psw.pkey; \
regs->psw.progmask=_regs.psw.progmask; \
} \
while(0)
#define SASSIST_PROLOG( _instname) \
VADR amicblok; \
char buf[512]; \
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(&regs->psw)) \
{ \
return(1); \
} \
if(!sysblk.ecpsvm.available) \
{ \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", "SASSIST "#_instname" ECPS:VM Disabled in configuration")); \
return(1); \
} \
if(!ecpsvm_sastats._instname.enabled) \
{ \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", "SASSIST "#_instname" ECPS:VM Disabled by command")); \
return(1); \
} \
CR6=regs->CR_L(6); \
regs->ecps_vtmrpt = NULL; /* Assume vtimer off until validated */ \
if(!(CR6 & ECPSVM_CR6_VMASSIST)) \
{ \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", "EVMA Disabled by guest")); \
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) \
{ \
MSGBUF(buf, "SASSIST "#_instname" Micblok @ %6.6X crosses page frame",amicblok); \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", buf)); \
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); \
MSGBUF(buf, "SASSIST "#_instname" VPSWA= %8.8X Virtual",vpswa); \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", buf)); \
MSGBUF(buf, "SASSIST "#_instname" CR6= %8.8X",CR6); \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", buf)); \
MSGBUF(buf, "SASSIST "#_instname" MICVTMR= %8.8X",micblok.MICVTMR); \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", buf)); \
MSGBUF(buf, "SASSIST "#_instname" Real PSW="); \
display_psw(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf)); \
/* Load the Virtual PSW in a temporary REGS structure */ \
INITPSEUDOREGS(vpregs); \
ARCH_DEP(load_psw) (&vpregs,vpswa_p); \
strlcat(buf, " PSW=", sizeof(buf)-(int)strlen(buf)-1); \
display_psw(&vpregs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf)); \
DEBUG_SASSISTX(_instname,WRMSG(HHC90000, "D", buf)); \
#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,WRMSG(HHC90000, "D", "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,WRMSG(HHC90000, "D", "CPASSTS "#_inst" Disabled by command")); \
return; \
} \
if(!(regs->CR_L(6) & 0x02000000)) \
{ \
return; \
} \
ecpsvm_cpstats._inst.call++; \
DEBUG_CPASSISTX(_inst,WRMSG(HHC90000, "D", #_inst" called"));
/* DISPx Utility macros */
#define STPT(_x) \
{ \
EVM_STD(cpu_timer(regs),_x); \
}
#define SPT(_x) \
{ \
OBTAIN_INTLOCK(regs); \
if(set_cpu_timer(regs,EVM_LD(_x)) < 0) \
{ \
ON_IC_PTIMER(regs); \
} \
else \
{ \
OFF_IC_PTIMER(regs); \
} \
RELEASE_INTLOCK(regs); \
}
#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)
{
char buf[256];
BYTE corcode;
VADR corte;
U32 lockcount;
RADR cortbl;
MSGBUF(buf, "LKPG coreptr = "F_RADR" Frame = "F_RADR,cortab,pg);
DEBUG_CPASSISTX(LCKPG,WRMSG(HHC90000, "D", buf));
cortbl=EVM_L(cortab);
corte=cortbl+((pg & 0xfff000)>>8);
MSGBUF(buf, "LKPG corete = %6.6X",corte);
DEBUG_CPASSISTX(LCKPG,WRMSG(HHC90000, "D", buf));
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);
MSGBUF(buf, "LKPG Page locked. Count = %6.6X",lockcount);
DEBUG_CPASSISTX(LCKPG,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
VADR ptr_pl;
VADR pg;
ECPSVM_PROLOG(LCKPG);
ptr_pl=effective_addr1;
pg=effective_addr2;
MSGBUF(buf, "LKPG PAGE=%6.6X, PTRPL=%6.6X",pg,ptr_pl);
DEBUG_CPASSISTX(LCKPG,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
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;
MSGBUF(buf, "ULKPG PAGE=%6.6X, PTRPL=%6.6X",pg,ptr_pl);
DEBUG_CPASSISTX(ULKPG,WRMSG(HHC90000, "D", buf));
corsz=EVM_L(ptr_pl);
cortbl=EVM_L(ptr_pl+4);
if((pg+4095)>corsz)
{
MSGBUF(buf, "ULKPG Page beyond core size of %6.6X",corsz);
DEBUG_CPASSISTX(ULKPG,WRMSG(HHC90000, "D", buf));
return;
}
corte=cortbl+((pg & 0xfff000)>>8);
corcode=EVM_IC(corte+8);
if(corcode & 0x80)
{
lockcount=EVM_L(corte+4);
lockcount--;
}
else
{
DEBUG_CPASSISTX(ULKPG,WRMSG(HHC90000, "D", "ULKPG Attempting to unlock page that is not locked"));
return;
}
if(lockcount==0)
{
corcode &= ~(0x80|0x02);
EVM_STC(corcode,corte+8);
DEBUG_CPASSISTX(ULKPG,WRMSG(HHC90000, "D", "ULKPG now unlocked"));
}
else
{
MSGBUF(buf, "ULKPG Page still locked. Count = %6.6X",lockcount);
DEBUG_CPASSISTX(ULKPG,WRMSG(HHC90000, "D", buf));
}
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)
{
char buf[256];
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)
{
MSGBUF(buf, "SCNVU Virtual Device %4.4X has no VCHAN block",vdev);
DEBUG_CPASSISTX(SCNVU,WRMSG(HHC90000, "D", buf));
return;
}
vch=EVM_L(effective_addr2)+vchix;
vcuix=EVM_LH(vch+8+((vdev & 0xf0)>>3));
if(vcuix & 0x8000)
{
MSGBUF(buf,"SCNVU Virtual Device %4.4X has no VCU block",vdev);
DEBUG_CPASSISTX(SCNVU,WRMSG(HHC90000, "D", buf));
return;
}
vcu=EVM_L(effective_addr2+4)+vcuix;
vdvix=EVM_LH(vcu+8+((vdev & 0xf)<<1));
if(vdvix & 0x8000)
{
MSGBUF(buf, "SCNVU Virtual Device %4.4X has no VDEV block",vdev);
DEBUG_CPASSISTX(SCNVU,WRMSG(HHC90000, "D", buf));
return;
}
vdv=EVM_L(effective_addr2+8)+vdvix;
MSGBUF(buf, "SCNVU %4.4X : VCH = %8.8X, VCU = %8.8X, VDEV = %8.8X",
vdev,
vch,
vcu,
vdv);
DEBUG_CPASSISTX(SCNVU,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
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);
MSGBUF(buf, "DISP1 Data list = %6.6X VM=%6.6X",dl,vmb);
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", buf));
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,WRMSG(HHC90000, "D", "DISP1 Quick Check complete"));
return(2);
}
else
{
MSGBUF(buf, "DISP1 Quick Check failed : %8.8X != %8.8X",(F_VMFLGS & F_SCHMASK),F_SCHMON);
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", buf));
}
F_ASYSVM=EVM_L(ASYSVM);
if(vmb==F_ASYSVM)
{
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", "DISP1 VMB is SYSTEM VMBLOCK"));
return(2);
}
SCHDL=EVM_L(el+4);
B_VMOSTAT=EVM_IC(vmb+VMOSTAT);
if(!(B_VMOSTAT & VMKILL))
{
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", "DISP1 Call SCHEDULE because VMKILL not set"));
UPD_PSW_IA(regs, SCHDL);
return(0);
}
B_VMQSTAT=EVM_IC(vmb+VMQSTAT);
if(!(B_VMQSTAT & VMCFREAD))
{
if(B_VMOSTAT & VMCF)
{
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", "DISP1 Call SCHEDULE because VMKILL & VMCF & !VMCFREAD set"));
UPD_PSW_IA(regs, SCHDL);
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,WRMSG(HHC90000, "D", "DISP1 Continue because already logging off"));
return(2);
}
B_VMRSTAT |= VMLOGOFF;
EVM_STC(B_VMRSTAT,vmb+VMRSTAT);
UPD_PSW_IA(regs, EVM_L(el+0));
DEBUG_CPASSISTX(DISP1,WRMSG(HHC90000, "D", "DISP1 : Call USOFF"));
return(0);
}
/* DISP2 Core */
int ecpsvm_do_disp2(REGS *regs,VADR dl,VADR el)
{
char buf[1024];
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);
MSGBUF(buf,"DISP2 Data list=%6.6X VM=%6.6X",dl,vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
CHARGE_STOP(vmb);
if(EVM_IC(XTENDLOCK) == XTENDLOCKSET)
{
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 Exit 8 : System extending"));
/* System in Extend process */
UPD_PSW_IA(regs, EVM_L(el+8));
return(0);
}
if(EVM_IC(APSTAT2) & CPMCHLK)
{
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 Exit 8 : MCH Recovery"));
/* Machine Check recovery in progress */
UPD_PSW_IA(regs, EVM_L(el+8));
return(0);
}
svmb=EVM_L(ASYSVM);
/* Check IOB/TRQ for dispatch */
F_TRQB=EVM_L(dl+8);
if(F_TRQB!=dl)
{
MSGBUF(buf, "DISP2 TRQ/IOB @ %6.6X Exit being routed",F_TRQB);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
/* 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);
UPD_PSW_IA(regs, regs->GR_L(12));
MSGBUF(buf, "DISP2 TRQ/IOB @ %6.6X IA = %6.6X",F_TRQB,regs->GR_L(12));
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
return(0);
}
/* Check CPEX BLOCK for dispatch */
F_CPEXB=EVM_L(dl+0);
if(F_CPEXB!=dl)
{
MSGBUF(buf, "DISP2 CPEXBLOK Exit being routed CPEX=%6.6X",F_CPEXB);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
/* 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)
{
MSGBUF(buf, "DISP2 CPEXBLOK CPEX=%6.6X Fret Failed",F_CPEXB);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
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,EVM_L(F_CPEXB+12)); /* DSPSAVE + 12 = CPEXADD */
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 */
UPD_PSW_IA(regs, EVM_L(el+12));
return(0);
}
for(i=0;i<15;i++)
{
regs->GR_L(i)=CPEXBKUP[i];
}
regs->GR_L(15)=F_CPEXADD;
UPD_PSW_IA(regs, F_CPEXADD);
MSGBUF(buf, "DISP2 CPEXBLOK CPEX=%6.6X IA=%6.6X",F_CPEXB,F_CPEXADD);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
return(0); /* CPEXBLOCK Branch taken */
}
/* Check for a USER run */
/* AT DMKDSP - DONE */
if(EVM_IC(CPSTAT2) & CPSHRLK)
{
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 Exit 24 : CPSHRLK Set in CPSTAT2"));
UPD_PSW_IA(regs, EVM_L(el+24)); /* IDLEECPS */
return(0);
}
/* Scan Scheduler IN-Q */
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 : Scanning Scheduler IN-Queue"));
FW1=EVM_L(dl+24);
for(vmb=EVM_L(FW1);vmb!=FW1;vmb=EVM_L(vmb))
{
if(!(EVM_IC(vmb+VMDSTAT) & VMRUN))
{
MSGBUF(buf, "DISP2 : VMB @ %6.6X Not eligible : VMRUN not set",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
continue;
}
if(EVM_IC(vmb+VMRSTAT) & VMCPWAIT)
{
MSGBUF(buf, "DISP2 : VMB @ %6.6X Not eligible : VMCPWAIT set",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
continue;
}
if(EVM_IC(vmb+VMNOECPS))
{
MSGBUF(buf, "DISP2 : Exit 20 : VMB @ %6.6X Has VMNOECPS Set to %2.2X",vmb,EVM_IC(vmb+VMNOECPS));
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
regs->GR_L(1)=vmb;
regs->GR_L(11)=EVM_L(ASYSVM);
UPD_PSW_IA(regs, EVM_L(el+20)); /* FREELOCK */
return(0);
}
MSGBUF(buf, "DISP2 : VMB @ %6.6X Will now be dispatched",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
runu=EVM_L(RUNUSER);
F_QUANTUM=EVM_L(QUANTUM);
if(vmb!=runu)
{
/* User switching */
/* DMKDSP - FNDUSRD */
MSGBUF(buf, "DISP2 : User switch from %6.6X to %6.6X",runu,vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
runu=EVM_L(RUNUSER);
EVM_STC(EVM_IC(runu+VMDSTAT) & ~VMDSP,runu+VMDSTAT);
lastu=EVM_L(LASTUSER);
MSGBUF(buf, "DISP2 : RUNU=%6.6X, LASTU=%6.6X",runu,lastu);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
if(lastu!=svmb && lastu!=vmb)
{
if(EVM_IC(lastu+VMOSTAT) & VMSHR) /* Running shared sys */
{
MSGBUF(buf, "DISP2 : Exit 16 : LASTU=%6.6X has shared sys & LCSHPG not impl",lastu);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
CHARGE_START(lastu);
/* LCSHRPG not implemented yet */
regs->GR_L(10)=vmb;
regs->GR_L(11)=lastu;
UPD_PSW_IA(regs, EVM_L(el+16));
return(0);
/* A CHARGE_STOP(runu) is due when LCSHRPG is implemented */
}
}
}
if(vmb!=runu || (vmb==runu && (F_QUANTUM & 0x80000000)))
{
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 : Restarting Time Slice"));
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 */
SET_PSW_IA(&wregs);
/* Build REAL PSW */
INITPSEUDOREGS(rregs);
/* Copy IAR */
UPD_PSW_IA(&rregs, wregs.psw.IA);
/* 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)
{
MSGBUF(buf, "DISP2 : VMB @ %6.6X has ECMODE ON",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
/* 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 */
WRMSG(HHC01700,"W");
UPD_PSW_IA(regs, EVM_L(el+28));
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))
{
MSGBUF(buf, "DISP2 : VMB @ %6.6X Refusing to simulate DMKVATAB",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
/* 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,WRMSG(HHC90000, "D", "DISP2 : PER ON"));
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,WRMSG(HHC90000, "D", "DISP2 : Purging TLB"));
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);
MSGBUF(buf, "DISP2 : Entry Real PSW=");
display_psw(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
ARCH_DEP(load_psw) (regs,work_p);
MSGBUF(buf, "DISP2 : VMB @ %6.6X Now being dispatched",vmb);
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
MSGBUF(buf, "DISP2 : Real PSW=");
display_psw(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
MSGBUF(buf, "DISP2 : Virtual PSW=");
display_psw(&wregs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
/* TEST */
ARCH_DEP(purge_tlb)(regs);
SET_IC_MASK(regs);
SET_AEA_MODE(regs);
SET_AEA_COMMON(regs);
SET_PSW_IA(regs);
/* Dispatch..... */
MSGBUF(buf, "DISP2 - Next Instruction : %2.2X\n",ARCH_DEP(vfetchb)(regs->psw.IA,USE_PRIMARY_SPACE,regs));
display_regs(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf), "HHC90000D ");
strlcat(buf, "\n", sizeof(buf));
display_cregs(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf), "HHC90000D ");
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", buf));
return(2); /* OK - Perform INTCHECK */
}
/* Nothing else to do - wait state */
DEBUG_CPASSISTX(DISP2,WRMSG(HHC90000, "D", "DISP2 : Nothing to dispatch - IDLEECPS"));
UPD_PSW_IA(regs, EVM_L(el+24)); /* IDLEECPS */
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)
{
char buf[256];
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)
{
MSGBUF(buf, "Tranbring : LRA cc = %d",cc);
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", buf));
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))
{
MSGBUF(buf, "Page not shared - OK %d",cc);
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", buf));
return(0); /* Page is NOT shared.. All OK */
}
#if defined(FEATURE_2K_STORAGE_KEYS)
pg1=(*raddr & 0xfff000);
pg2=pg1+0x800;
MSGBUF(buf, "Checking 2K Storage keys @"F_RADR" & "F_RADR"",pg1,pg2);
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", buf));
if((STORAGE_KEY(pg1,regs) & STORKEY_CHANGE) ||
(STORAGE_KEY(pg2,regs) & STORKEY_CHANGE))
{
#else
MSGBUF(buf, "Checking 4K Storage keys @"F_RADR,*raddr);
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", buf));
if(STORAGE_KEY(*raddr,regs) & STORKEY_CHANGE)
{
#endif
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", "Page shared and changed"));
return(1); /* Page shared AND changed */
}
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", "Page shared but not changed"));
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,WRMSG(HHC90000, "D", "TRANBRNG"));
rc=ecpsvm_tranbrng(regs,effective_addr1,regs->GR_L(1),&raddr);
if(rc)
{
DEBUG_CPASSISTX(TRBRG,WRMSG(HHC90000, "D", "TRANBRNG - Back to CP"));
return; /* Something not right : NO OP */
}
regs->psw.cc=0;
regs->GR_L(2)=raddr;
UPD_PSW_IA(regs, effective_addr2);
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,WRMSG(HHC90000, "D", "TRANLOCK"));
rc=ecpsvm_tranbrng(regs,effective_addr1,regs->GR_L(1),&raddr);
if(rc)
{
DEBUG_CPASSISTX(TRLOK,WRMSG(HHC90000, "D", "TRANLOCK - Back to CP"));
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;
UPD_PSW_IA(regs, effective_addr2);
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)
{
char buf[256];
U64 tspent;
U64 DW_VMTMOUTQ;
U64 DW_PROBSTRT;
U64 DW_PROBTIME;
MSGBUF(buf, "INCPROBT Entry : VMBLOK @ %8.8X",vmb);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
DW_VMTMOUTQ=EVM_LD(vmb+VMTMOUTQ);
DW_PROBSTRT=EVM_LD(PROBSTRT);
MSGBUF(buf, "INCPROBT Entry : VMTMOUTQ = %16.16" I64_FMT "x",DW_VMTMOUTQ);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
MSGBUF(buf, "INCPROBT Entry : PROBSTRT = %16.16" I64_FMT "x",DW_PROBSTRT);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
if(DW_VMTMOUTQ==DW_PROBSTRT)
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "INCPROBT Already performed"));
return(2); /* continue */
}
tspent=DW_PROBSTRT-DW_VMTMOUTQ;
MSGBUF(buf, "INCPROBT TSPENT = %16.16" I64_FMT "x",tspent);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
DW_PROBTIME=EVM_LD(PROBTIME);
DW_PROBTIME-=tspent;
EVM_STD(DW_PROBTIME,PROBTIME);
MSGBUF(buf, "INCPROBT NEW PROBTIME = %16.16" I64_FMT "x",DW_PROBTIME);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
return(2);
}
/* DMKDSP
RUNTIME
*/
static int ecpsvm_disp_runtime(REGS *regs,VADR *vmb_p,VADR dlist,VADR exitlist)
{
char buf[256];
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;
MSGBUF(buf, "RUNTIME Entry : VMBLOK @ %8.8X",vmb);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
runu=EVM_L(RUNUSER);
/* BAL RUNTIME Processing */
EVM_STC(CPEX+CPSUPER,CPSTATUS);
CHARGE_STOP(vmb);
if(vmb!=runu)
{
MSGBUF(buf, "RUNTIME Switching to RUNUSER VMBLOK @ %8.8X",runu);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
CHARGE_SWITCH(vmb,runu); /* Charge RUNUSER */
F_ITIMER=EVM_L(QUANTUMR);
*vmb_p=vmb;
}
else
{
F_ITIMER=EVM_L(INTTIMER);
}
MSGBUF(buf, "RUNTIME : VMBLOK @ %8.8X",vmb);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
/* 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,WRMSG(HHC90000, "D", "RUNTIME : Complete - VTIMER Assist active"));
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,WRMSG(HHC90000, "D", "RUNTIME : Complete - SET TIMER OFF"));
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,WRMSG(HHC90000, "D", "RUNTIME : Bad ITIMER - Taking Exist #32"));
UPD_PSW_IA(regs, EVM_L(exitlist+32));
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;
UPD_PSW_IA(regs, EVM_L(exitlist+8));
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "RUNTIME : Complete - Taking exit #8"));
return(0);
}
/* Return - Continue DISP0 Processing */
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "RUNTIME : Complete - ITIMER Updated"));
return(2);
}
/* DISP0 : Operand 1 : DISP0 Data list, Operand 2 : DISP0 Exit list */
/* R11 : User to dispatch */
DEF_INST(ecpsvm_dispatch_main)
{
char buf[256];
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,WRMSG(HHC90000, "D", "DISP0 : CPRUN On"));
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,WRMSG(HHC90000, "D", "DISP0 : I/O Old as XLATE on"));
/* Yes - I/O Interrupt while running a USER */
if(B_VMDSTAT & VMDSP)
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : VMDSP on in VMBLOK - Clean status (Exit #36)"));
/* Clean status - Do exit 36 */
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+36));
EVM_ST(DISPCNT,dlist);
CPASSIST_HIT(DISP0);
return;
}
}
}
else
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : CPRUN Off"));
/* Check if was in Wait State */
if(B_CPSTATUS & CPWAIT)
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : CPWAIT On : Exit #4"));
/* Take exit #4 : Coming out of wait state */
/* DMKDSPC3 */
/* No need to update R11 */
CPASSIST_HIT(DISP0);
UPD_PSW_IA(regs, EVM_L(elist+4));
EVM_ST(DISPCNT,dlist);
return;
}
}
/* VMB is now either original GPR11 or RUNUSER */
/* DMKDSP - UNSTACK */
MSGBUF(buf, "DISP0 : At UNSTACK : VMBLOK = %8.8X",vmb);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
B_VMRSTAT=EVM_IC(vmb+VMRSTAT);
if(B_VMRSTAT & VMCPWAIT)
{
MSGBUF(buf, "DISP0 : VMRSTAT VMCPWAIT On (%2.2X) - Taking exit #12",B_VMRSTAT);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
/* Take Exit 12 */
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+12));
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,WRMSG(HHC90000, "D", "DISP0 : PER/PPF Pending - Taking exit #16"));
/* Take Exit 16 */
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+16));
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);
MSGBUF(buf, "DISP0 : Checking for EXT; Base VMPXINT=%8.8X",F_VMPXINT);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
/* This is DMKDSP - CKEXT */
if(F_VMPXINT!=0)
{
MSGBUF(buf, "DISP0 : VPSW HI = %8.8X",F_VMPSWHI);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
OXINT=vmb+VMPXINT;
/* Check if Virtual PSW enabled for Externals */
/* (works in both BC & EC modes) */
if(F_VMPSWHI & 0x01000000)
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : PSW Enabled for EXT"));
/* 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 */
}
MSGBUF(buf, "DISP0 : CR0 = %8.8X",F_VMVCR0);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
/* 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 */
MSGBUF(buf, "DISP0 : XINTMASK = %4.4X\n",H_XINTMASK);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
H_XINTMASK &= F_VMVCR0;
if(H_XINTMASK) /* Check against CR0 (External subclass mask) */
{
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : EXT Hit - Taking exit #20"));
/* 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 */
UPD_PSW_IA(regs, EVM_L(elist+20)); /* Exit +20 */
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);
MSGBUF(buf, "DISP0 : Checking for I/O; VMIOINT=%8.8X",F_VMIOINT);
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", buf));
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,WRMSG(HHC90000, "D", "DISP0 : I/O Hit - Taking exit #24"));
/* Take Exit 24 */
regs->GR_L(7)=F_VMIOINT;
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+24)); /* Exit +24 */
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,WRMSG(HHC90000, "D", "DISP0 : VWAIT - Taking exit #28"));
/* Take exit 28 */
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+28)); /* Exit +28 */
CPASSIST_HIT(DISP0);
EVM_ST(DISPCNT,dlist);
return;
}
/* Take exit 0 (DISPATCH) */
DEBUG_CPASSISTX(DISP0,WRMSG(HHC90000, "D", "DISP0 : DISPATCH - Taking exit #0"));
regs->GR_L(11)=vmb;
UPD_PSW_IA(regs, EVM_L(elist+0)); /* Exit +0 */
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)
{
char buf[256];
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;
MSGBUF(buf, "ECPS:VM SCNRU called; RDEV=%4.4X ARIOCT=%6.6X",effective_addr1,arioct);
DEBUG_CPASSISTX(SCNRU,WRMSG(HHC90000, "D", buf));
/* Get the Channel Index Table */
rchixtbl= EVM_L(effective_addr2);
/* Obtain the RCH offset */
chix=EVM_LH(rchixtbl+((rdev & 0xf00) >> 7));
MSGBUF(buf, "ECPS:VM SCNRU : RCH IX = %x",chix);
DEBUG_CPASSISTX(SCNRU,WRMSG(HHC90000, "D", buf));
/* Check if Bit 0 set (no RCH) */
if(chix & 0x8000)
{
// WRMSG(HHC90000, "D", "ECPS:VM SCNRU : NO CHANNEL");
/*
regs->GR_L(6)=~0;
regs->GR_L(7)=~0;
regs->GR_L(8)=~0;
UPD_PSW_IA(regs, regs->GR_L(14));
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)
{
// WRMSG(HHC90000, "D", "ECPS:VM SCNRU : NO CONTROL UNIT");
/*
regs->GR_L(6)=rchblk;
regs->GR_L(7)=~0;
regs->GR_L(8)=~0;
UPD_PSW_IA(regs, regs->GR_L(14));
regs->psw.cc=2;
*/
return;
}
}
MSGBUF(buf, "ECPS:VM SCNRU : RCU IX = %x",cuix);
DEBUG_CPASSISTX(SCNRU,WRMSG(HHC90000, "D", buf));
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)
{
// WRMSG(HHC90000, "D", "ECPS:VM SCNRU : NO RDEVBLOK");
/*
regs->GR_L(6)=rchblk;
regs->GR_L(7)=rcublk;
regs->GR_L(8)=~0;
UPD_PSW_IA(regs, regs->GR_L(14));
regs->psw.cc=3;
*/
return;
}
MSGBUF(buf, "ECPS:VM SCNRU : RDV IX = %x",dvix);
DEBUG_CPASSISTX(SCNRU,WRMSG(HHC90000, "D", buf));
dvix<<=3;
rdvtbl=EVM_L(arioct+12);
rdvblk=rdvtbl+dvix;
MSGBUF(buf, "ECPS:VM SCNRU : RCH = %6.6X, RCU = %6.6X, RDV = %6.6X",rchblk,rcublk,rdvblk);
DEBUG_CPASSISTX(SCNRU,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
ECPSVM_PROLOG(STEVL);
EVM_ST(sysblk.ecpsvm.level,effective_addr1);
MSGBUF(buf, "ECPS:VM STORE LEVEL %d called",sysblk.ecpsvm.level);
DEBUG_CPASSISTX(STEVL,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
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;
MSGBUF(buf, "ECPS:VM FREEX DW = %4.4X",numdw);
DEBUG_CPASSISTX(FREEX,WRMSG(HHC90000, "D", buf));
if(numdw==0)
{
return;
}
MSGBUF(buf, "MAXSIZE ADDR = %6.6X, SUBPOOL INDEX TABLE = %6.6X",maxsztbl,spixtbl);
DEBUG_CPASSISTX(FREEX,WRMSG(HHC90000, "D", buf));
/* 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,WRMSG(HHC90000, "D", "FREEX request beyond subpool capacity"));
return;
}
/* Fetch subpool index */
spix=EVM_IC(spixtbl+numdw);
MSGBUF(buf, "Subpool index = %X",spix);
DEBUG_CPASSISTX(FREEX,WRMSG(HHC90000, "D", buf));
/* Fetch value */
freeblock=EVM_L(maxsztbl+4+spix);
MSGBUF(buf, "Value in subpool table = %6.6X",freeblock);
DEBUG_CPASSISTX(FREEX,WRMSG(HHC90000, "D", buf));
if(freeblock==0)
{
/* Can't fullfill request here */
return;
}
nextblk=EVM_L(freeblock);
EVM_ST(nextblk,maxsztbl+4+spix);
MSGBUF(buf, "New Value in subpool table = %6.6X",nextblk);
DEBUG_CPASSISTX(FREEX,WRMSG(HHC90000, "D", buf));
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)
{
char buf[256];
U32 cortbl;
U32 maxdw;
U32 cortbe; /* Core table Page entry for fretted block */
U32 prevblk;
BYTE spix;
MSGBUF(buf, "X fretx called AREA=%6.6X, DW=%4.4X",regs->GR_L(1),regs->GR_L(0));
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", buf));
if(numdw==0)
{
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", "ECPS:VM Cannot FRETX : DWORDS = 0"));
return(1);
}
maxdw=EVM_L(maxsztbl);
if(numdw>maxdw)
{
MSGBUF(buf, "ECPS:VM Cannot FRETX : DWORDS = %d > MAXDW %d",numdw,maxdw);
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", buf));
return(1);
}
cortbl=EVM_L(fretl);
cortbe=cortbl+((block & 0xfff000)>>8);
if(EVM_L(cortbe)!=EVM_L(fretl+4))
{
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", "ECPS:VM Cannot FRETX : Area not in Core Free area"));
return(1);
}
if(EVM_IC(cortbe+8)!=0x02)
{
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", "ECPS:VM Cannot FRETX : Area flag != 0x02"));
return(1);
}
spix=EVM_IC(fretl+11+numdw);
prevblk=EVM_L(maxsztbl+4+spix);
if(prevblk==block)
{
DEBUG_CPASSISTX(FRETX,WRMSG(HHC90000, "D", "ECPS:VM Cannot FRETX : fretted block already on subpool chain"));
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);
SET_PSW_IA(newr);
SET_PSW_IA(oldr);
/* Check for a switch from BC->EC or EC->BC */
if(ECMODE(&oldr->psw)!=ECMODE(&newr->psw))
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", "New and Old PSW have a EC/BC transition"));
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,WRMSG(HHC90000, "D", "New PSW Enables DAT or PER"));
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,WRMSG(HHC90000, "D", "New PSW Enables interrupts and MICPEND (EC)"));
return(1);
}
}
else
{
if(~oldr->psw.sysmask & newr->psw.sysmask)
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D","New PSW Enables interrupts and MICPEND (BC)"));
return(1);
}
}
}
if(WAITSTATE(&newr->psw))
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", "New PSW is a WAIT PSW"));
return(1);
}
if(ECMODE(&newr->psw))
{
if(newr->psw.sysmask & 0xb8)
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", "New PSW sysmask incorrect"));
return(1);
}
}
if(newr->psw.IA & 0x01)
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", "New PSW has ODD IA"));
return(1);
}
return(0);
}
int ecpsvm_dossm(REGS *regs,int b2,VADR effective_addr2)
{
char buf2[256];
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,WRMSG(HHC90000, "D", "SASSIST SSM reject : V PB State"));
return(1);
}
/*
if(!(micevma & MICSTSM))
{
MSGBUF(buf2, "SASSIST SSM reject : SSM Disabled in MICEVMA; EVMA=%2.2X",micevma);
DEBUG_SASSISTX(SSM,WRMSG(HHC90000, "D", buf2));
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,WRMSG(HHC90000, "D", "SASSIST SSM reject : V SSM Suppr"));
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,WRMSG(HHC90000, "D", "SASSIST SSM Reject : New PSW too complex"));
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 */
SET_PSW_IA(regs);
UPD_PSW_IA(&npregs, regs->psw.IA);
/* Set the change bit */
MADDR(vpswa,USE_REAL_ADDR,regs,ACCTYPE_WRITE,0);
/* store the new PSW */
ARCH_DEP(store_psw) (&npregs,vpswa_p);
MSGBUF(buf2, "SASSIST SSM Complete : new SM = %2.2X",reqmask);
DEBUG_SASSISTX(SSM,WRMSG(HHC90000, "D", buf2));
MSGBUF(buf2, "SASSIST SSM New VIRT ");
display_psw(&npregs, &buf2[strlen(buf2)], sizeof(buf2)-(int)strlen(buf2));
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", buf2));
MSGBUF(buf2, "SASSIST SSM New REAL ");
display_psw(regs, &buf2[strlen(buf2)], sizeof(buf2)-(int)strlen(buf2));
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", buf2));
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,WRMSG(HHC90000, "D", "SASSIST SVC Reject : SVC 76"));
return(1);
}
if(CR6 & ECPSVM_CR6_SVCINHIB)
{
DEBUG_SASSISTX(SVC,WRMSG(HHC90000, "D", "SASSIST SVC Reject : SVC Assist Inhibit"));
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 */
MSGBUF(buf, "SASSIST SVC NEW VIRT ");
display_psw(&newr, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_SASSISTX(SVC,WRMSG(HHC90000, "D", buf));
/* Get some stuff from the REAL Running PSW to put in OLD SVC PSW */
SET_PSW_IA(regs);
UPD_PSW_IA(&vpregs, regs->psw.IA); /* 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 */
MSGBUF(buf, "SASSIST SVC OLD VIRT ");
display_psw(&vpregs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_SASSISTX(SVC,WRMSG(HHC90000, "D", buf));
if(ecpsvm_check_pswtrans(regs,&micblok,micpend,&vpregs,&newr)) /* Check PSW transition capability */
{
DEBUG_SASSISTX(SVC,WRMSG(HHC90000, "D", "SASSIST SVC Reject : Cannot make transition to new PSW"));
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,WRMSG(HHC90000, "D", "SASSIST SVC Done"));
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,WRMSG(HHC90000, "D", "SASSIST LPSW reject : V PB State"));
return(1);
}
/* Reject if MICEVMA says not to do LPSW sim */
if(!(micevma & MICLPSW))
{
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", "SASSIST LPSW reject : LPSW disabled in MICEVMA"));
return(1);
}
if(e2&0x03)
{
MSGBUF(buf, "SASSIST LPSW %6.6X - Alignement error",e2);
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", buf));
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,WRMSG(HHC90000, "D", "SASSIST LPSW Rejected - Cannot make PSW transition"));
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);
MSGBUF(buf, "SASSIST LPSW New VIRT ");
display_psw(&nregs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", buf));
MSGBUF(buf, "SASSIST LPSW New REAL ");
display_psw(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_SASSISTX(LPSW,WRMSG(HHC90000, "D", buf));
SASSIST_HIT(LPSW);
return(0);
}
int ecpsvm_virttmr_ext(REGS *regs)
{
char buf[256];
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Checking if we can IRPT"));
MSGBUF(buf, "SASSIST VTIMER Virtual");
display_psw(regs, &buf[strlen(buf)], sizeof(buf)-(int)strlen(buf));
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", buf));
if(IS_IC_ECPSVTIMER(regs))
{
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Not pending"));
return(1);
}
if(!PROBSTATE(&regs->psw))
{
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Not dispatching a VM"));
return(1);
}
if(!(regs->psw.sysmask & PSW_EXTMASK))
{
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Test int : Not enabled for EXT"));
return(1);
}
if(!(regs->CR_L(6) & ECPSVM_CR6_VIRTTIMR))
{
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Test int : Not enabled for VTIMER"));
return(1);
}
DEBUG_SASSISTX(VTIMER,WRMSG(HHC90000, "D", "SASSIST VTIMER Please, do"));
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,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : Not aligned"));
return(1);
}
vmb=vpswa-0xA8;
B_VMPSTAT=EVM_IC(vmb+VMPSTAT);
if((!(B_VMPSTAT & VMV370R)) && ((r1!=r3) || (r1!=0)))
{
DEBUG_SASSISTX(LCTL,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : BC Mode VM & LCTL != 0,0"));
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;i<numcrs;i++,j++)
{
if(j>15)
{
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 */
MSGBUF(buf, "SASSIST LCTL %d,%d : Modifying %d cregs",r1,r3,numcrs);
DEBUG_SASSISTX(LCTL,WRMSG(HHC90000, "D", buf));
for(j=r1,i=0;i<numcrs;i++,j++)
{
if(j>15)
{
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,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : CR0 High changed"));
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,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : CR0 EXTSM Enables new EXTS"));
return 1;
}
}
}
ocrs[0]=crs[0];
break;
case 1:
if(ocrs[1] != crs[1])
{
DEBUG_SASSISTX(LCTL,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : CR1 Updates shadow table"));
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,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : CR2 IOCSM Enables I/O Ints"));
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,WRMSG(HHC90000, "D", "SASSIST LCTL Reject : MC CR8 Update"));
return(1);
case 9: /* PER Control Regs */
case 10:
case 11:
MSGBUF(buf, "SASSIST LCTL Reject : PER CR%d Update",j);
DEBUG_SASSISTX(LCTL,WRMSG(HHC90000, "D", buf));
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;i<numcrs;i++,j++)
{
if(j>15)
{
j-=16;
}
ecb_p=MADDR(F_ECBLOK+(j*4),USE_REAL_ADDR,regs,ACCTYPE_WRITE,0);
store_fw(ecb_p,ocrs[j]);
}
MSGBUF(buf, "SASSIST LCTL %d,%d Done",r1,r3);
DEBUG_SASSISTX(LCTL,WRMSG(HHC90000, "D", buf));
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 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 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;i<count;i++)
{
if(ar[i].call)
{
callt+=ar[i].call;
hitt+=ar[i].hit;
if(!ar[i].support)
{
unsupcc+=ar[i].call;
haveunsup++;
}
havedisp=1;
snprintf(nname,sizeof(nname),"%s%s",ar[i].name,ar[i].support ? "" : "*");
nname[sizeof(nname)-1] = '\0';
if(!ar[i].enabled)
{
strlcat(nname,"-",sizeof(nname));
}
if(ar[i].debug)
{
strlcat(nname,"%",sizeof(nname));
}
if(ar[i].total)
{
strlcat(nname,"+",sizeof(nname));
}
WRMSG(HHC01701,"I",
nname,
ar[i].call,
ar[i].hit,
ar[i].call ?
(ar[i].hit*100)/ar[i].call :
100);
}
else
{
notshown++;
}
}
if(havedisp)
{
WRMSG(HHC01702,"I");
}
WRMSG(HHC01701,"I",
"Total",
callt,
hitt,
callt ?
(hitt*100)/callt :
100);
WRMSG(HHC01702,"I");
if(haveunsup)
{
WRMSG(HHC01703,"I");
}
if(notshown)
{
WRMSG(HHC01704,"I",notshown);
}
if(unsupcc)
{
WRMSG(HHC01705,"I",(int)unsupcc);
}
return;
}
/* SHOW STATS */
void ecpsvm_showstats(int ac,char **av)
{
size_t asize;
ECPSVM_STAT *ar;
UNREFERENCED(ac);
UNREFERENCED(av);
WRMSG(HHC01702,"I");
WRMSG(HHC01706,"I","VM ASSIST","Calls","Hits","Ratio");
WRMSG(HHC01702,"I");
ar=malloc(sizeof(ecpsvm_sastats));
memcpy(ar,&ecpsvm_sastats,sizeof(ecpsvm_sastats));
asize=sizeof(ecpsvm_sastats)/sizeof(ECPSVM_STAT);
qsort(ar,asize,sizeof(ECPSVM_STAT),ecpsvm_sortstats);
ecpsvm_showstats2(ar,asize);
free(ar);
WRMSG(HHC01702,"I");
WRMSG(HHC01706,"I","CP ASSIST","Calls","Hits","Ratio");
WRMSG(HHC01702,"I");
ar=malloc(sizeof(ecpsvm_cpstats));
memcpy(ar,&ecpsvm_cpstats,sizeof(ecpsvm_cpstats));
asize=sizeof(ecpsvm_cpstats)/sizeof(ECPSVM_STAT);
qsort(ar,asize,sizeof(ECPSVM_STAT),ecpsvm_sortstats);
ecpsvm_showstats2(ar,asize);
free(ar);
}
ECPSVM_STAT *ecpsvm_findstat(char *feature,char **fclass)
{
ECPSVM_STAT *es;
ECPSVM_STAT *esl;
int i;
int fcount;
fcount=sizeof(ecpsvm_sastats)/sizeof(ECPSVM_STAT);
esl=(ECPSVM_STAT *)&ecpsvm_sastats;
for(i=0;i<fcount;i++)
{
es=&esl[i];
if(strcasecmp(feature,es->name)==0)
{
*fclass="VM ASSIST";
return(es);
}
}
esl=(ECPSVM_STAT *)&ecpsvm_cpstats;
fcount=sizeof(ecpsvm_cpstats)/sizeof(ECPSVM_STAT);
for(i=0;i<fcount;i++)
{
es=&esl[i];
if(strcasecmp(feature,es->name)==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<count;i++)
{
es=&tbl[i];
if(onoff>=0)
{
es->enabled=onoff;
WRMSG(HHC01707,"I",fclass,es->name," ", enadisa);
}
if(debug>=0)
{
es->debug=debug;
WRMSG(HHC01707,"I",fclass,es->name," Debug ", debugonoff);
}
}
if(onoff>=0)
{
WRMSG(HHC01708,"I",fclass,"",enadisa);
}
if(debug>=0)
{
WRMSG(HHC01708,"I",fclass,"Debug ",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;
WRMSG(HHC01709,"I",debugonoff);
}
return;
}
for(i=1;i<ac;i++)
{
if(strcasecmp(av[i],"ALL")==0)
{
ecpsvm_enadisaall("VM ASSIST",sal,sacount,onoff,debug);
ecpsvm_enadisaall("CP ASSIST",cpl,cpcount,onoff,debug);
return;
}
if(strcasecmp(av[i],"VMA")==0)
{
ecpsvm_enadisaall("VM ASSIST",sal,sacount,onoff,debug);
return;
}
if(strcasecmp(av[i],"CPA")==0)
{
ecpsvm_enadisaall("CP ASSIST",cpl,cpcount,onoff,debug);
return;
}
es=ecpsvm_findstat(av[i],&fclass);
if(es!=NULL)
{
if(onoff>=0)
{
es->enabled=onoff;
WRMSG(HHC01710,"I",fclass,es->name,"",enadisa);
}
if(debug>=0)
{
es->debug=onoff;
WRMSG(HHC01710,"I",fclass,es->name,"Debug ",debugonoff);
}
}
else
{
WRMSG(HHC01711,"I",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;
WRMSG(HHC01712,"I",sysblk.ecpsvm.level);
if(!sysblk.ecpsvm.available)
WRMSG(HHC01713,"I");
if( ac > 1 )
{
lvl=atoi(av[1]);
WRMSG(HHC01714,"I",lvl);
sysblk.ecpsvm.level=lvl;
}
if(sysblk.ecpsvm.level!=20)
{
WRMSG(HHC01715,"W",sysblk.ecpsvm.level);
WRMSG(HHC01716,"I");
}
}
static void ecpsvm_helpcmd(int,char **);
static ECPSVM_CMDENT ecpsvm_cmdtab[]={
{"Help", 1,ecpsvm_helpcmd,"Show help",
"format : \"ecpsvm help [cmd]\" Shows help on the specified\n"
" ECPSVM subcommand"},
{"STats", 2,ecpsvm_showstats,"Show statistical counters",
"format : ecpsvm stats : Shows various ECPSVM Counters"},
{"DIsable", 2,ecpsvm_disable,"Disable ECPS:VM Features",
"format : ecpsvm disable [ALL|feat1[ feat2|...]"},
{"ENable", 2,ecpsvm_enable,"Enable ECPS:VM Features",
"format : ecpsvm enable [ALL|feat1[ feat2|...]"},
#if defined(DEBUG_SASSIST) || defined(DEBUG_CPASSIST)
{"DEBUG", 5,ecpsvm_debug,"Debug ECPS:VM Features",
"format : ecpsvm debug [ALL|feat1[ feat2|...]"},
{"NODebug", 3,ecpsvm_nodebug,"Turn Debug off for ECPS:VM Features",
"format : ecpsvm NODebug [ALL|feat1[ feat2|...]"},
#endif
{"Level", 1,ecpsvm_level,"Set/Show ECPS:VM level",
"format : ecpsvm 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];
WRMSG(HHC01717,"I",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)
{
WRMSG(HHC01718,"E",av[1]);
ecpsvm_helpcmdlist();
return;
}
WRMSG(HHC01717,"I",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=(int)strlen(cmd);
if(strncasecmp(cmd,ce->name,clen)==0)
{
return(ce);
}
}
}
return(NULL);
}
void ecpsvm_command(int ac,char **av)
{
ECPSVM_CMDENT *ce;
WRMSG(HHC01719,"I");
if(ac==1)
{
WRMSG(HHC01720,"E");
return;
}
ce=ecpsvm_getcmdent(av[1]);
if(ce==NULL)
{
WRMSG(HHC01721,"E",av[1]);
return;
}
ce->fun(ac-1,av+1);
WRMSG(HHC01722,"I");
}
#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)*/