mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-10 06:03:45 +02:00
2827 lines
98 KiB
C
2827 lines
98 KiB
C
/* CPU.C (C) Copyright Roger Bowler, 1994-2012 */
|
|
/* (C) Copyright Jan Jaeger, 1999-2012 */
|
|
/* (C) and others 2013-2023 */
|
|
/* ESA/390 CPU Emulator */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* Interpretive Execution - (C) Copyright Jan Jaeger, 1999-2012 */
|
|
/* z/Architecture support - (C) Copyright Jan Jaeger, 1999-2012 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module implements the CPU instruction execution function of */
|
|
/* the S/370 and ESA/390 architectures, as described in the manuals */
|
|
/* GA22-7000-03 System/370 Principles of Operation */
|
|
/* SA22-7201-06 ESA/390 Principles of Operation */
|
|
/* SA22-7832-00 z/Architecture Principles of Operation */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Additional credits: */
|
|
/* Nullification corrections by Jan Jaeger */
|
|
/* Set priority by Reed H. Petty from an idea by Steve Gay */
|
|
/* Corrections to program check by Jan Jaeger */
|
|
/* Light optimization on critical path by Valery Pogonchenko */
|
|
/* OSTAILOR parameter by Jay Maynard */
|
|
/* CPU timer and clock comparator interrupt improvements by */
|
|
/* Jan Jaeger, after a suggestion by Willem Konynenberg */
|
|
/* Instruction decode rework - Jan Jaeger */
|
|
/* Modifications for Interpretive Execution (SIE) by Jan Jaeger */
|
|
/* Basic FP extensions support - Peter Kuschnerus */
|
|
/* ASN-and-LX-reuse facility - Roger Bowler, June 2004 */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
// #define SIE_DEBUG
|
|
|
|
#define _CPU_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "inline.h"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ARCH_DEP section: compiled multiple times, once for each arch. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
//-------------------------------------------------------------------
|
|
// ARCH_DEP() code
|
|
//-------------------------------------------------------------------
|
|
// ARCH_DEP (build-architecture / FEATURE-dependent) functions here.
|
|
// All BUILD architecture dependent (ARCH_DEP) function are compiled
|
|
// multiple times (once for each defined build architecture) and each
|
|
// time they are compiled with a different set of FEATURE_XXX defines
|
|
// appropriate for that architecture. Use #ifdef FEATURE_XXX guards
|
|
// to check whether the current BUILD architecture has that given
|
|
// feature #defined for it or not. WARNING: Do NOT use _FEATURE_XXX.
|
|
// The underscore feature #defines mean something else entirely. Only
|
|
// test for FEATURE_XXX. (WITHOUT the underscore)
|
|
//-------------------------------------------------------------------
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Forward references to internal static helper functions at end. */
|
|
/*-------------------------------------------------------------------*/
|
|
#if !defined( FWD_REFS)
|
|
#define FWD_REFS
|
|
static void CPU_Wait( REGS* regs );
|
|
static void* cpu_uninit( int cpu, REGS* regs );
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Put all the CPUs in the configuration in check-stop state */
|
|
/* Caller *MUST* hold INTLOCK! */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( checkstop_all_cpus )( REGS* regs )
|
|
{
|
|
int i;
|
|
|
|
if (!IS_INTLOCK_HELD( regs ))
|
|
CRASH();
|
|
|
|
for (i=0; i < sysblk.maxcpu; i++)
|
|
{
|
|
if (IS_CPU_ONLINE(i))
|
|
{
|
|
sysblk.regs[i]->cpustate = CPUSTATE_STOPPING;
|
|
sysblk.regs[i]->checkstop = 1;
|
|
ON_IC_INTERRUPT( sysblk.regs[i] );
|
|
}
|
|
}
|
|
WAKEUP_CPUS_MASK( sysblk.waiting_mask );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Store current PSW at a specified absolute address in main storage */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( store_psw )( REGS* regs, BYTE* addr )
|
|
{
|
|
/* Ensure psw.IA is set */
|
|
if (!regs->psw.zeroilc)
|
|
MAYBE_SET_PSW_IA_FROM_IP( regs );
|
|
|
|
#if defined( FEATURE_BCMODE )
|
|
if (ECMODE( ®s->psw ))
|
|
#endif
|
|
// 390 or 370 EC-mode
|
|
|
|
#if !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
STORE_DW( addr, make_psw64( regs, 390, false ));
|
|
#endif
|
|
|
|
#if defined( FEATURE_BCMODE )
|
|
|
|
else // 370 BC-mode
|
|
|
|
STORE_DW( addr, make_psw64( regs, 370, true ));
|
|
|
|
#elif defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
|
|
// 64-bit z/Architecture mode
|
|
|
|
STORE_DW( addr + 0, make_psw64( regs, 900, false ));
|
|
STORE_DW( addr + 8, regs->psw.IA_G );
|
|
|
|
#endif
|
|
} /* end function ARCH_DEP(store_psw) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Load current PSW from a specified address in main storage */
|
|
/* Returns 0 if valid, 0x0006 if specification exception */
|
|
/*-------------------------------------------------------------------*/
|
|
int ARCH_DEP(load_psw) (REGS *regs, BYTE *addr)
|
|
{
|
|
INVALIDATE_AIA(regs);
|
|
|
|
regs->psw.zeroilc = 1;
|
|
|
|
regs->psw.sysmask = addr[0];
|
|
regs->psw.pkey = (addr[1] & 0xF0);
|
|
regs->psw.states = (addr[1] & 0x0F);
|
|
|
|
#if defined(FEATURE_BCMODE)
|
|
if ( ECMODE(®s->psw) ) {
|
|
#endif /*defined(FEATURE_BCMODE)*/
|
|
|
|
SET_IC_ECMODE_MASK(regs);
|
|
|
|
/* Processing for EC mode PSW */
|
|
regs->psw.intcode = 0;
|
|
regs->psw.asc = (addr[2] & 0xC0);
|
|
regs->psw.cc = (addr[2] & 0x30) >> 4;
|
|
regs->psw.progmask = (addr[2] & 0x0F);
|
|
regs->psw.amode = (addr[4] & 0x80) ? 1 : 0;
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
regs->psw.zerobyte = addr[3] & 0xFE;
|
|
regs->psw.amode64 = addr[3] & 0x01;
|
|
regs->psw.zeroword = fetch_fw(addr+4) & 0x7FFFFFFF;
|
|
regs->psw.IA = fetch_dw (addr + 8);
|
|
regs->psw.AMASK = regs->psw.amode64 ? AMASK64
|
|
: regs->psw.amode ? AMASK31 : AMASK24;
|
|
#else
|
|
regs->psw.zerobyte = addr[3];
|
|
regs->psw.amode64 = 0;
|
|
regs->psw.IA = fetch_fw(addr + 4) & 0x7FFFFFFF;
|
|
regs->psw.AMASK = regs->psw.amode ? AMASK31 : AMASK24;
|
|
#endif
|
|
|
|
/* Bits 0 and 2-4 of system mask must be zero */
|
|
if ((addr[0] & 0xB8) != 0)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* For ESAME, bit 12 must be zero */
|
|
if (NOTESAME(®s->psw))
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
/* Bits 24-30 must be zero */
|
|
if (regs->psw.zerobyte)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
/* Bits 33-63 must be zero */
|
|
if ( regs->psw.zeroword )
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#else /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
/* Bits 24-31 must be zero */
|
|
if ( regs->psw.zerobyte )
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
/* For ESA/390, bit 12 must be one */
|
|
if (!ECMODE(®s->psw))
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#endif /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
|
|
#ifndef FEATURE_DUAL_ADDRESS_SPACE
|
|
/* If DAS feature not installed then bit 16 must be zero */
|
|
if (SPACE_BIT(®s->psw))
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#endif
|
|
|
|
#ifndef FEATURE_ACCESS_REGISTERS
|
|
/* If not ESA/370 or ESA/390 then bit 17 must be zero */
|
|
if (AR_BIT(®s->psw))
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#endif
|
|
|
|
/* Check validity of amode and instruction address */
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* For ESAME, bit 32 cannot be zero if bit 31 is one */
|
|
if (regs->psw.amode64 && !regs->psw.amode)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
/* If bit 32 is zero then IA cannot exceed 24 bits */
|
|
if (!regs->psw.amode && regs->psw.IA > 0x00FFFFFF)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
|
|
/* If bit 31 is zero then IA cannot exceed 31 bits */
|
|
if (!regs->psw.amode64 && regs->psw.IA > 0x7FFFFFFF)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#else /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
#ifdef FEATURE_BIMODAL_ADDRESSING
|
|
/* For 370-XA, ESA/370, and ESA/390,
|
|
if amode=24, bits 33-39 must be zero */
|
|
if (!regs->psw.amode && regs->psw.IA > 0x00FFFFFF)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#else
|
|
/* For S/370, bits 32-39 must be zero */
|
|
if (addr[4] != 0x00)
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#endif
|
|
#endif /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
|
|
#if defined( FEATURE_BCMODE )
|
|
} else {
|
|
|
|
SET_IC_BCMODE_MASK(regs);
|
|
|
|
/* Processing for S/370 BC mode PSW */
|
|
regs->psw.intcode = fetch_hw (addr + 2);
|
|
regs->psw.cc = (addr[4] & 0x30) >> 4;
|
|
regs->psw.progmask = (addr[4] & 0x0F);
|
|
|
|
FETCH_FW(regs->psw.IA, addr + 4);
|
|
regs->psw.IA &= 0x00FFFFFF;
|
|
regs->psw.AMASK = AMASK24;
|
|
|
|
regs->psw.zerobyte = 0;
|
|
regs->psw.asc = 0;
|
|
regs->psw.amode64 = regs->psw.amode = 0;
|
|
}
|
|
#endif /* defined( FEATURE_BCMODE ) */
|
|
|
|
#if defined( FEATURE_MULTIPLE_CONTROLLED_DATA_SPACE )
|
|
/* Bits 5 and 16 must be zero in XC mode */
|
|
if( SIE_STATE_BIT_ON(regs, MX, XC)
|
|
&& ( (regs->psw.sysmask & PSW_DATMODE) || SPACE_BIT(®s->psw)) )
|
|
return PGM_SPECIFICATION_EXCEPTION;
|
|
#endif
|
|
|
|
regs->psw.zeroilc = 0;
|
|
|
|
/* Check for wait state PSW */
|
|
if (1
|
|
&& WAITSTATE( ®s->psw )
|
|
&& CPU_STEPPING_OR_TRACING_ALL
|
|
&& !TXF_INSTR_TRACING()
|
|
)
|
|
{
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0800( regs );
|
|
else
|
|
{
|
|
char buf[40];
|
|
STR_PSW( regs, buf );
|
|
// "Processor %s%02X: loaded wait state PSW %s"
|
|
WRMSG( HHC00800, "I", PTYPSTR( regs->cpuad ), regs->cpuad, buf );
|
|
}
|
|
}
|
|
|
|
TEST_SET_AEA_MODE(regs);
|
|
|
|
return 0;
|
|
} /* end function ARCH_DEP(load_psw) */
|
|
|
|
#if defined( FEATURE_PER3 )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Set the Breaking-Event-Address Register */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void ARCH_DEP( Set_BEAR_Reg )( U64* bear, REGS* regs, BYTE* ip )
|
|
{
|
|
/* "If the instruction causing the breaking event is the
|
|
target of an execute-type instruction (EX or EXRL),
|
|
then the instruction address used to fetch the EX/EXRL
|
|
instruction is placed in the BEAR."
|
|
*/
|
|
if (1
|
|
&& bear != ®s->bear_ex /* NOT saving EX/EXRL address? */
|
|
&& regs->execflag /* EX/EXRL target caused event? */
|
|
)
|
|
{
|
|
regs->bear = regs->bear_ex; /* BEAR = EX/EXRL instr address */
|
|
PTT_INF( "bear = ex", regs->bear_ex, 0, 0 );
|
|
}
|
|
else if (ip)
|
|
{
|
|
/* BEAR = Address of the beginning of virtual ('aiv') page
|
|
plus same displacement from begin of mainstor ('ip') page
|
|
also know as 'aip'.
|
|
|
|
HOWEVER, since the 'ip' value passed to us might not match
|
|
regs->ip (it might have been passed to us as "regs->ip - 4"),
|
|
we cannot blindly rely on the 'ip' value passed to us being
|
|
on the same mainstor page as regs->aip. It could be pointing
|
|
before where regs->aip is currently pointing if regs->ip
|
|
was pointing to the first instruction on the page and thus
|
|
after backing up 4 bytes would cause it to point before
|
|
where regs->aip points. The below logic takes situations
|
|
such as that into consideration.
|
|
*/
|
|
BYTE* aip = regs->aip; /* Begin of mainstor page */
|
|
U64 aiv = regs->AIV; /* Begin of virtual page */
|
|
|
|
if (ip < regs->aip) /* pointing to prev page? */
|
|
{
|
|
/* The instruction pointer that was passed to us
|
|
points somewhere in the PREVIOUS mainstor page */
|
|
|
|
PTT_INF( "ip < aip", ip, aip, aiv );
|
|
|
|
aip -= PAGEFRAME_PAGESIZE;
|
|
aiv -= PAGEFRAME_PAGESIZE;
|
|
}
|
|
else if (ip >= (regs->aip + PAGEFRAME_PAGESIZE))
|
|
{
|
|
/* The instruction pointer that was passed to us
|
|
points somewhere in the NEXT mainstor page */
|
|
|
|
PTT_INF( "ip >= aip+page", ip, aip, aiv );
|
|
|
|
aip += PAGEFRAME_PAGESIZE;
|
|
aiv += PAGEFRAME_PAGESIZE;
|
|
}
|
|
|
|
/* Calculate and set BEAR appropriately */
|
|
*bear = aiv + (ip - aip); /* Save virtual address */
|
|
*bear &= ADDRESS_MAXWRAP( regs ); /* of the breaking event */
|
|
|
|
if (bear == ®s->bear)
|
|
PTT_INF( "bear =", *bear, 0, 0 );
|
|
else
|
|
PTT_INF( "bear_ex =", *bear, 0, 0 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SuccessfulBranch */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( SuccessfulBranch )( REGS* regs, VADR vaddr )
|
|
{
|
|
vaddr &= ADDRESS_MAXWRAP( regs );
|
|
|
|
/* Set BEAR to branch instruction. Note: for branch instructions
|
|
regs->ip is not updated to point to the next instruction and
|
|
thus is still pointing to the branch instruction itself.
|
|
*/
|
|
SET_BEAR_REG( regs, regs->ip );
|
|
|
|
/* Branch target still within same page as branch instruction? */
|
|
if (1
|
|
&& !regs->permode
|
|
&& !regs->execflag
|
|
&& (vaddr & (PAGEFRAME_PAGEMASK | 0x01)) == regs->AIV
|
|
)
|
|
{
|
|
/* Branch directly to the new instruction */
|
|
regs->ip = regs->aip + (vaddr - regs->AIV);
|
|
PTT_INF( "branch", vaddr, regs->AIV, regs->ip );
|
|
return;
|
|
}
|
|
|
|
/* Branch target is in another page: point the PSW to the target
|
|
instruction and force a new "regs->ip" value to get set by
|
|
forcing a full instruction fetch from the new target address.
|
|
*/
|
|
regs->psw.IA = vaddr; /* Point PSW to target instr */
|
|
regs->aie = INVALID_AIE; /* Force a fresh 'instfetch' */
|
|
|
|
PTT_INF( "branch", vaddr, regs->AIV, 0 );
|
|
PER_SB( regs, regs->psw.IA );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SuccessfulRelativeBranch */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( SuccessfulRelativeBranch )( REGS* regs, S64 offset )
|
|
{
|
|
/* Set BEAR to branch instruction. Note: for branch instructions
|
|
regs->ip is not updated to point to the next instruction and
|
|
thus is still pointing to the branch instruction itself.
|
|
*/
|
|
SET_BEAR_REG( regs, regs->ip );
|
|
|
|
/* Branch target still within same page as branch instruction? */
|
|
if (1
|
|
&& !regs->permode
|
|
&& !regs->execflag
|
|
&& offset > -4096
|
|
&& offset < +4096
|
|
&& (regs->ip + offset) >= regs->aip
|
|
&& (regs->ip + offset) < regs->aie
|
|
)
|
|
{
|
|
/* Branch directly to the new instruction */
|
|
regs->ip = regs->ip + offset;
|
|
PTT_INF( "rbranch <", regs->ip, offset, regs->aip );
|
|
return;
|
|
}
|
|
|
|
/* Branch target is in another page: point the PSW to the target
|
|
instruction and force a new "regs->ip" value to get set by
|
|
forcing a full instruction fetch from the new target address.
|
|
*/
|
|
PTT_INF( "rbranch >", regs->psw.IA, offset, regs->execflag );
|
|
|
|
/* Point PSW to target instruction */
|
|
if (!regs->execflag)
|
|
regs->psw.IA = PSW_IA_FROM_IP( regs, offset );
|
|
else
|
|
{
|
|
regs->psw.IA = regs->ET + offset;
|
|
regs->psw.IA &= ADDRESS_MAXWRAP( regs );
|
|
}
|
|
regs->aie = INVALID_AIE; /* Force a fresh 'instfetch' */
|
|
|
|
PTT_INF( "rbranch >", regs->psw.IA, offset, regs->execflag );
|
|
PER_SB( regs, regs->psw.IA );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* trace_program_interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void ARCH_DEP( trace_program_interrupt )( REGS* regs, int pcode, int ilc )
|
|
{
|
|
char sie_mode_str [ 10] = {0}; // maybe "SIE: "
|
|
char sie_debug_arch [ 32] = {0}; // "370", "390" or "900" if defined( SIE_DEBUG )
|
|
char txf_why [256] = {0}; // TXF "why" string if txf pgmint
|
|
char dxcstr [ 8] = {0}; // data exception code if PGM_DATA_EXCEPTION
|
|
// or PGM_VECTOR_PROCESSING_EXCEPTION
|
|
|
|
BYTE* ip; /* ptr to instr that program checked or NULL */
|
|
|
|
/* Just the low byte of program interrupt code itself please */
|
|
int code = (pcode & 0xFF);
|
|
|
|
/* If the program interrupt code is zero, or if it's not, if the
|
|
code isn't one they're interested in seeing (as determiend by
|
|
OSTAILOR/pgmtrace) AND instruction tracing is NOT active, then
|
|
there's nothing for us to do.
|
|
*/
|
|
if (0
|
|
|| !code
|
|
|| (1
|
|
&& !(sysblk.pgminttr & ((U64) 1 << ((code - 1) & 0x3F)))
|
|
&& !CPU_STEPPING_OR_TRACING( regs, ilc )
|
|
)
|
|
)
|
|
{
|
|
return; // (nothing to do; quick exit)
|
|
}
|
|
|
|
/*
|
|
First things first: backup the 'ip' by the 'ilc' value to point
|
|
to the actual instruction that actually program checked.
|
|
|
|
If 'instinvalid' is set it means an instruction fetch error
|
|
occurred so we shouldn't rely on the value of regs->ip.
|
|
|
|
Otherwise if the instruction that program checked (i.e. after
|
|
backing up 'ip' by 'ilc' amount) appears to be in the previous
|
|
mainstor page (meaning the instruction itself crossed a page
|
|
boundary), use the the copy of the instruction that was saved
|
|
in regs->inst for our instruction pointer instead.
|
|
|
|
If neither condition is true (the most common case) then we
|
|
simply use current regs->ip value backed up by the ilc amount.
|
|
*/
|
|
PTT_PGM( "tr PGM int", regs->ip, regs->aip, ilc );
|
|
ip =
|
|
(
|
|
/* Instruction fetch error? (least likely) */
|
|
regs->instinvalid ? NULL
|
|
|
|
/* Instruction crossed page boundary? (unlikely) */
|
|
: ((regs->ip - ilc) < regs->aip) ? regs->inst
|
|
|
|
/* Instruction still on same page (most likely) */
|
|
: (regs->ip - ilc)
|
|
);
|
|
PTT_PGM( "tr PGM int", ip, regs->aip, ilc );
|
|
|
|
#if defined( OPTION_FOOTPRINT_BUFFER )
|
|
if (!(sysblk.insttrace || sysblk.instbreak))
|
|
{
|
|
U32 n;
|
|
for (n = sysblk.footprptr[ regs->cpuad ] + 1;
|
|
n != sysblk.footprptr[ regs->cpuad ];
|
|
n++, n &= OPTION_FOOTPRINT_BUFFER - 1
|
|
)
|
|
{
|
|
ARCH_DEP( display_inst )(
|
|
&sysblk.footprregs[ regs->cpuad ][n],
|
|
sysblk.footprregs[ regs->cpuad ][n].inst );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Suppress LRA Special Operation Exception tracing if requested */
|
|
if (1
|
|
&& code == PGM_SPECIAL_OPERATION_EXCEPTION
|
|
&& ip && ip[0] == 0xB1 // LRA
|
|
&& sysblk.nolrasoe // suppression enabled
|
|
)
|
|
{
|
|
return; // (nothing to do; quick exit)
|
|
}
|
|
|
|
/* Trace this program interrupt... */
|
|
|
|
#if defined( _FEATURE_SIE )
|
|
if (SIE_MODE( regs ))
|
|
STRLCPY( sie_mode_str, "SIE: " );
|
|
#endif
|
|
|
|
#if defined( SIE_DEBUG )
|
|
STRLCPY( sie_debug_arch, QSTR( _GEN_ARCH ));
|
|
STRLCAT( sie_debug_arch, " " );
|
|
#endif
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
if (1
|
|
&& pcode & PGM_TXF_EVENT
|
|
&& regs->txf_why
|
|
)
|
|
txf_why_str( txf_why, sizeof( txf_why ), regs->txf_why );
|
|
#endif
|
|
|
|
if (code == PGM_DATA_EXCEPTION)
|
|
MSGBUF( dxcstr, " DXC=%2.2X", regs->dxc );
|
|
|
|
if (code == PGM_VECTOR_PROCESSING_EXCEPTION)
|
|
MSGBUF( dxcstr, " VXC=%2.2X", regs->dxc );
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0801( regs, pcode, ilc );
|
|
|
|
// "Processor %s%02X: %s%s %s interruption code %4.4X ilc %d%s%s"
|
|
WRMSG( HHC00801, "I",
|
|
PTYPSTR( regs->cpuad ), regs->cpuad,
|
|
sie_mode_str, sie_debug_arch,
|
|
PIC2Name( code ), pcode,
|
|
ilc, dxcstr, txf_why );
|
|
|
|
// HHC02324 "PSW=... INST=... OPCODE operands name"
|
|
ARCH_DEP( display_pgmint_inst )( regs, ip );
|
|
|
|
} /* end function ARCH_DEP( trace_program_interrupt ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* fix_program_interrupt_PSW */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int ARCH_DEP( fix_program_interrupt_PSW )( REGS* regs )
|
|
{
|
|
/* Determine the instruction length code (ilc).
|
|
|
|
The 'zeroilc' flag is set when the Instruction Length Code
|
|
should be reported as zero (such as when instruction-fetching
|
|
nullification PER option is set in CR9 or when the load PSW
|
|
instruction results in an invalid PSW being loaded).
|
|
|
|
The PSW 'ilc' value can also be specifically set to '0' when
|
|
a BALR, BASR or BASSM program checks during 'trace_br' call.
|
|
*/
|
|
int ilc =
|
|
(
|
|
/* If zeroilc flag is set, then we MUST use zero for the ilc */
|
|
regs->psw.zeroilc ? 0
|
|
|
|
/* Otherwise use either the ilc value set in the PSW or the
|
|
length of the EX/EXRL instruction is the instruction is
|
|
being executed.
|
|
*/
|
|
: REAL_ILC( regs )
|
|
);
|
|
PTT_PGM( "fxpiPSW ilc", 0, 0, ilc );
|
|
|
|
/* If our resulting ilc is still 0 but the zeroilc flag is NOT set,
|
|
then we're left with no choice but to GUESS the 'ilc' value
|
|
based on whether the instruction was being executed or not.
|
|
*/
|
|
if (regs->psw.ilc == 0 && !regs->psw.zeroilc)
|
|
{
|
|
ilc = (!regs->execflag ? 2 : (regs->exrl ? 6 : 4));
|
|
|
|
PTT_PGM( "fxpiPSW ilc", regs->ip, regs->psw.IA, ilc );
|
|
|
|
/* Now ADVANCE the 'ip' mainstor instruction pointer and
|
|
psw 'IA' instruction address by that ilc amount so that
|
|
the 'trace_program_interrupt' can then back them both up
|
|
by the same amount to point to the actual instruction
|
|
that actually program checked.
|
|
*/
|
|
regs->psw.ilc = ilc; // (guessed value)
|
|
regs->ip += ilc; // (so trace_program_interrupt can undo)
|
|
regs->psw.IA += ilc; // (so trace_program_interrupt can undo)
|
|
|
|
PTT_PGM( "fxpiPSW ilc", regs->ip, regs->psw.IA, ilc );
|
|
}
|
|
|
|
/* Return ilc value to be passed to 'trace_program_interrupt' */
|
|
|
|
PTT_PGM( "fxpiPSW ret", 0, 0, ilc );
|
|
return ilc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* program_interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
/* The purpose of this function is to store the current PSW into the */
|
|
/* Program interrupt old PSW field in low core followed by loading */
|
|
/* the Program interrupt new PSW and then jumping via regs->progjmp */
|
|
/* back to the run_cpu instruction execution loop to begin executing */
|
|
/* instructions at the Progran new PSW location. */
|
|
/*-------------------------------------------------------------------*/
|
|
void (ATTR_REGPARM(2) ARCH_DEP( program_interrupt ))( REGS* regs, int pcode )
|
|
{
|
|
PSA *psa; /* -> Prefixed storage area */
|
|
REGS *realregs; /* True regs structure */
|
|
RADR px; /* host real address of pfx */
|
|
int code; /* pcode without PER ind. */
|
|
int ilc; /* instruction length */
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* FIXME : SEE ISW20090110-1 */
|
|
void *zmoncode = NULL; /* mon call SIE intercept;
|
|
special reloc for z/Arch */
|
|
/* FIXME : zmoncode not being
|
|
initialized here raises a
|
|
potentially non-initialized
|
|
warning in GCC.. can't find
|
|
why. ISW 2009/02/04 */
|
|
#endif
|
|
#if defined( FEATURE_SIE )
|
|
int sie_ilc=0; /* SIE instruction length */
|
|
#endif
|
|
#if defined( _FEATURE_SIE )
|
|
bool intercept; /* False for virtual pgmint */
|
|
/* (True for host interrupt?)*/
|
|
#endif
|
|
|
|
/* If called with ghost registers (ie from hercules command
|
|
then ignore all interrupt handling and report the error
|
|
to the caller */
|
|
if (regs->ghostregs)
|
|
longjmp( regs->progjmp, pcode );
|
|
|
|
PTT_PGM("PGM", pcode, regs->TEA, regs->psw.IA );
|
|
|
|
/* program_interrupt() may be called with a shadow copy of the
|
|
regs structure, realregs is the pointer to the real structure
|
|
which must be used when loading/storing the psw, or backing up
|
|
the instruction address in case of nullification
|
|
*/
|
|
#if defined( _FEATURE_SIE )
|
|
realregs = SIE_MODE( regs )
|
|
? GUEST( sysblk.regs[ regs->cpuad ])
|
|
: HOST( sysblk.regs[ regs->cpuad ]);
|
|
#else
|
|
realregs = HOST( sysblk.regs[ regs->cpuad ]);
|
|
#endif
|
|
|
|
PTT_PGM( "PGM (r)h,g,a", realregs->host, realregs->guest, realregs->sie_active );
|
|
|
|
/* Prevent machine check when in (almost) interrupt loop */
|
|
realregs->instcount++;
|
|
UPDATE_SYSBLK_INSTCOUNT( 1 );
|
|
|
|
/* Release any locks */
|
|
if (IS_INTLOCK_HELD( realregs ))
|
|
RELEASE_INTLOCK( realregs );
|
|
|
|
/* Unlock the main storage lock if held */
|
|
if (sysblk.mainowner == realregs->cpuad)
|
|
RELEASE_MAINLOCK_UNCONDITIONAL( realregs );
|
|
|
|
/* Ensure psw.IA is set and aia invalidated */
|
|
INVALIDATE_AIA(realregs);
|
|
|
|
#if defined( FEATURE_SIE )
|
|
if (realregs->sie_active)
|
|
ARCH_DEP( invalidate_guest_aia )( GUEST( realregs ));
|
|
#endif
|
|
|
|
/* Fix PSW and get instruction length (ilc) */
|
|
ilc = ARCH_DEP( fix_program_interrupt_PSW )( realregs );
|
|
|
|
PTT_PGM( "PGM ilc", 0, 0, ilc );
|
|
|
|
#if defined( FEATURE_SIE )
|
|
if (realregs->sie_active)
|
|
{
|
|
sie_ilc = GUEST( realregs )->psw.zeroilc ? 0 : REAL_ILC( GUEST( realregs ));
|
|
if (GUEST( realregs )->psw.ilc == 0 && !GUEST( realregs )->psw.zeroilc)
|
|
{
|
|
sie_ilc = likely( !GUEST( realregs )->execflag) ? 2 : GUEST( realregs )->exrl ? 6 : 4;
|
|
GUEST( realregs )->psw.ilc = sie_ilc;
|
|
}
|
|
|
|
PTT_PGM( "PGM sie_ilc", 0, 0, sie_ilc );
|
|
}
|
|
#endif
|
|
|
|
/* Trace the program interrupt right away */
|
|
ARCH_DEP( trace_program_interrupt )( regs, pcode, ilc );
|
|
|
|
/* Remove PER indication from program interrupt code
|
|
such that interrupt code specific tests may be done.
|
|
The PER indication will be stored in the PER handling
|
|
code */
|
|
code = pcode & ~PGM_PER_EVENT;
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
|
|
/* If transaction is active, check if interrupt can be filtered */
|
|
if (realregs->txf_tnd)
|
|
{
|
|
/* Indicate TXF related program interrupt */
|
|
pcode |= PGM_TXF_EVENT;
|
|
|
|
/* Always reset the NTSTG indicator on any program interrupt */
|
|
regs->txf_NTSTG = false;
|
|
|
|
/* Save the program interrupt and data exception codes */
|
|
realregs->txf_piid = pcode;
|
|
realregs->txf_piid |= (ilc << 16);
|
|
|
|
realregs->txf_dxc_vxc =
|
|
(0
|
|
|| pcode == PGM_DATA_EXCEPTION
|
|
|| pcode == PGM_VECTOR_PROCESSING_EXCEPTION
|
|
)
|
|
? realregs->dxc : 0;
|
|
|
|
PTT_TXF( "TXF PIID", realregs->txf_piid, realregs->txf_dxc_vxc, 0 );
|
|
|
|
/* 'txf_do_pi_filtering' does not return for filterable
|
|
program interrupts. It returns only if the program
|
|
interrupt is unfilterable, and when it returns, the
|
|
active transaction has already been aborted.
|
|
*/
|
|
PTT_TXF( "TXF PROG?", (code & 0xFF), realregs->txf_contran, realregs->txf_tnd );
|
|
ARCH_DEP( txf_do_pi_filtering )( realregs, pcode );
|
|
|
|
if (realregs->txf_tnd ) // (sanity check)
|
|
CRASH(); // (sanity check)
|
|
|
|
PTT_TXF( "*TXF UPROG!", (code & 0xFF), 0, 0 );
|
|
|
|
/* Set flag for sie_exit */
|
|
realregs->txf_UPGM_abort = true;
|
|
}
|
|
else /* (no transaction is CURRENTLY active) */
|
|
{
|
|
/* While no transaction is CURRENTLY active, it's possible
|
|
that a previously active transaction was aborted BEFORE
|
|
we were called, so we need to check for that.
|
|
*/
|
|
if ((code & 0xFF) == PGM_TRANSACTION_CONSTRAINT_EXCEPTION)
|
|
{
|
|
/* Indicate TXF related program interrupt */
|
|
pcode |= PGM_TXF_EVENT;
|
|
|
|
PTT_TXF( "*TXF 218!", pcode, 0, 0 );
|
|
|
|
/* Set flag for sie_exit */
|
|
realregs->txf_UPGM_abort = true;
|
|
}
|
|
}
|
|
#endif /* defined( FEATURE_073_TRANSACT_EXEC_FACILITY ) */
|
|
|
|
/* Set 'execflag' to 0 in case EXecuted instruction program-checked */
|
|
|
|
PTT_PGM( "PGM execflag", realregs->execflag, realregs->sie_active, 0 );
|
|
realregs->execflag = 0;
|
|
|
|
#if defined( FEATURE_SIE )
|
|
if (realregs->sie_active)
|
|
GUEST( realregs )->execflag = 0;
|
|
#endif
|
|
PTT_PGM( "PGM execflag", realregs->execflag, realregs->sie_active, 0 );
|
|
|
|
/* If this is a concurrent PER event
|
|
then we must add the PER bit to the interrupts code */
|
|
if (OPEN_IC_PER (realregs ))
|
|
pcode |= PGM_PER_EVENT;
|
|
|
|
/* Perform serialization and checkpoint synchronization */
|
|
PERFORM_SERIALIZATION( realregs );
|
|
PERFORM_CHKPT_SYNC( realregs );
|
|
|
|
#if defined( FEATURE_SIE )
|
|
/* Host protection and addressing exceptions
|
|
must be reflected to the guest */
|
|
if (1
|
|
&& realregs->sie_active
|
|
&& (0
|
|
|| code == PGM_PROTECTION_EXCEPTION
|
|
|| code == PGM_ADDRESSING_EXCEPTION
|
|
|
|
#if defined( _FEATURE_MULTIPLE_CONTROLLED_DATA_SPACE )
|
|
|| code == PGM_ALET_SPECIFICATION_EXCEPTION
|
|
|| code == PGM_ALEN_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ALE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_EXTENDED_AUTHORITY_EXCEPTION
|
|
#endif
|
|
)
|
|
)
|
|
{
|
|
/* Pass this interrupt to the guest */
|
|
#if defined( SIE_DEBUG )
|
|
LOGMSG( "program_int() passing to guest code=%4.4X\n", pcode );
|
|
#endif
|
|
switch (GUEST( realregs )->arch_mode)
|
|
{
|
|
case ARCH_370_IDX: GUEST( realregs )->TEA_370 = realregs->TEA; break;
|
|
case ARCH_390_IDX: GUEST( realregs )->TEA_390 = realregs->TEA; break;
|
|
case ARCH_900_IDX: GUEST( realregs )->TEA_900 = realregs->TEA; break;
|
|
default: CRASH();
|
|
}
|
|
|
|
GUEST( realregs )->excarid = realregs->excarid;
|
|
GUEST( realregs )->opndrid = realregs->opndrid;
|
|
|
|
#if defined(_FEATURE_PROTECTION_INTERCEPTION_CONTROL)
|
|
GUEST( realregs )->hostint = 1;
|
|
#endif
|
|
GUEST( realregs )->program_interrupt( GUEST( realregs ), pcode );
|
|
}
|
|
#endif /* defined( FEATURE_SIE ) */
|
|
|
|
/* Back up the PSW for exceptions which cause nullification,
|
|
unless the exception occurred during instruction fetch
|
|
*/
|
|
PTT_PGM( "PGM psw.IA", realregs->psw.IA, realregs->instinvalid, ilc );
|
|
if (1
|
|
&& !realregs->instinvalid
|
|
&& (0
|
|
|| code == PGM_PAGE_TRANSLATION_EXCEPTION
|
|
|| code == PGM_SEGMENT_TRANSLATION_EXCEPTION
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
|| code == PGM_ASCE_TYPE_EXCEPTION
|
|
|| code == PGM_REGION_FIRST_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_SECOND_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_THIRD_TRANSLATION_EXCEPTION
|
|
#endif
|
|
|| code == PGM_TRACE_TABLE_EXCEPTION
|
|
|| code == PGM_AFX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ASX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LFX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LSX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LSTE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_EX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_PRIMARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_SECONDARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_ALEN_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ALE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_ASTE_VALIDITY_EXCEPTION
|
|
|| code == PGM_ASTE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_ASTE_INSTANCE_EXCEPTION
|
|
|| code == PGM_EXTENDED_AUTHORITY_EXCEPTION
|
|
|| code == PGM_STACK_FULL_EXCEPTION
|
|
|| code == PGM_STACK_EMPTY_EXCEPTION
|
|
|| code == PGM_STACK_SPECIFICATION_EXCEPTION
|
|
|| code == PGM_STACK_TYPE_EXCEPTION
|
|
|| code == PGM_STACK_OPERATION_EXCEPTION
|
|
|| code == PGM_VECTOR_OPERATION_EXCEPTION
|
|
)
|
|
)
|
|
{
|
|
realregs->psw.IA -= ilc;
|
|
realregs->psw.IA &= ADDRESS_MAXWRAP(realregs);
|
|
|
|
PTT_PGM( "PGM IA-ilc", realregs->psw.IA, realregs->instinvalid, ilc );
|
|
|
|
#if defined( FEATURE_SIE )
|
|
/* When in SIE mode the guest instruction
|
|
causing this host exception must also be nullified
|
|
*/
|
|
if (realregs->sie_active && !GUEST( realregs )->instinvalid)
|
|
ARCH_DEP( update_guest_psw_ia )( GUEST( realregs ), -sie_ilc );
|
|
#endif
|
|
}
|
|
|
|
/* The OLD PSW must be incremented
|
|
on the following exceptions during instfetch
|
|
*/
|
|
if (1
|
|
&& realregs->instinvalid
|
|
&& (0
|
|
|| code == PGM_PROTECTION_EXCEPTION
|
|
|| code == PGM_ADDRESSING_EXCEPTION
|
|
|| code == PGM_SPECIFICATION_EXCEPTION
|
|
|| code == PGM_TRANSLATION_SPECIFICATION_EXCEPTION
|
|
)
|
|
)
|
|
{
|
|
realregs->psw.IA += ilc;
|
|
realregs->psw.IA &= ADDRESS_MAXWRAP( realregs );
|
|
|
|
PTT_PGM( "PGM IA+ilc", realregs->psw.IA, realregs->instinvalid, ilc );
|
|
}
|
|
|
|
/* Store the interrupt code in the PSW */
|
|
realregs->psw.intcode = pcode;
|
|
|
|
/* Call debugger if active */
|
|
HDC2( debug_program_interrupt, regs, pcode );
|
|
|
|
realregs->instinvalid = 0;
|
|
PTT_PGM( "PGM inval=0", 0, 0, 0 );
|
|
|
|
#if defined( FEATURE_SIE )
|
|
|
|
/*---------------------------------------------------------*/
|
|
/* If this is a host exception in SIE state then leave SIE */
|
|
/*---------------------------------------------------------*/
|
|
if (realregs->sie_active)
|
|
{
|
|
PTT_PGM( "PGM >sie_exit", SIE_HOST_PGM_INT, 0, 0 );
|
|
ARCH_DEP( sie_exit )( realregs, SIE_HOST_PGM_INT );
|
|
}
|
|
#endif
|
|
|
|
/* Absolute address of prefix page */
|
|
px = realregs->PX;
|
|
|
|
/* If under SIE use translated to host absolute prefix */
|
|
#if defined( _FEATURE_SIE )
|
|
if (SIE_MODE( regs ))
|
|
px = regs->sie_px;
|
|
#endif
|
|
|
|
#if defined( _FEATURE_SIE )
|
|
/*---------------------------------------------------------------*/
|
|
/* If we're in SIE mode, then we need to determine whether */
|
|
/* we must, or must not, intercept this program interrupt, */
|
|
/* and by intercept, we mean pass it on to the SIE host so */
|
|
/* that it (not the guest!) can decide what action to take. */
|
|
/*---------------------------------------------------------------*/
|
|
if (0
|
|
|
|
/* If not in SIE mode, then we (duh!) must not intercept it */
|
|
|| !SIE_MODE( regs )
|
|
|
|
/* Interception is mandatory for the following exceptions,
|
|
so if ANY of the below conditions are false, then we MUST
|
|
intercept this program interrupt.
|
|
|
|
The below tests/checks are for "not this condition" where
|
|
the "condition" is the condition which we MUST intercept.
|
|
Thus if any of them fail, then we MUST intercept.
|
|
|
|
Only if ALL of them are true (only if no condition exists
|
|
that REQUIRES an intercept) should we then NOT intercept.
|
|
*/
|
|
|| (1
|
|
|
|
&& code != PGM_ADDRESSING_EXCEPTION
|
|
&& code != PGM_SPECIFICATION_EXCEPTION
|
|
&& code != PGM_SPECIAL_OPERATION_EXCEPTION
|
|
|
|
#if defined( FEATURE_S370_S390_VECTOR_FACILITY )
|
|
&& code != PGM_VECTOR_OPERATION_EXCEPTION
|
|
#endif
|
|
#ifdef FEATURE_BASIC_FP_EXTENSIONS
|
|
&& !(code == PGM_DATA_EXCEPTION && (regs->dxc == 1 || regs->dxc == 2) && (regs->CR(0) & CR0_AFP) && !(HOSTREGS->CR(0) & CR0_AFP))
|
|
#endif
|
|
&& !SIE_FEAT_BIT_ON( regs, IC0, PGMALL )
|
|
|
|
#if !defined( _FEATURE_PROTECTION_INTERCEPTION_CONTROL )
|
|
&& code != PGM_PROTECTION_EXCEPTION
|
|
#else
|
|
&& !(code == PGM_PROTECTION_EXCEPTION && (!SIE_FEAT_BIT_ON( regs, EC2, PROTEX ) || realregs->hostint ))
|
|
#endif
|
|
#if defined( _FEATURE_PER2 )
|
|
&& !((pcode & PGM_PER_EVENT) && SIE_FEAT_BIT_ON( regs, M, GPE ))
|
|
#endif
|
|
#if defined( FEATURE_MULTIPLE_CONTROLLED_DATA_SPACE )
|
|
&& !(code == PGM_ALEN_TRANSLATION_EXCEPTION && SIE_FEAT_BIT_ON( regs, MX, XC ))
|
|
&& !(code == PGM_ALE_SEQUENCE_EXCEPTION && SIE_FEAT_BIT_ON( regs, MX, XC ))
|
|
&& !(code == PGM_EXTENDED_AUTHORITY_EXCEPTION && SIE_FEAT_BIT_ON( regs, MX, XC ))
|
|
#endif
|
|
&& !(code == PGM_OPERATION_EXCEPTION && SIE_FEAT_BIT_ON( regs, IC0, OPEREX ))
|
|
&& !(code == PGM_PRIVILEGED_OPERATION_EXCEPTION && SIE_FEAT_BIT_ON( regs, IC0, PRIVOP ))
|
|
)
|
|
)
|
|
{
|
|
#endif /*defined(_FEATURE_SIE)*/
|
|
|
|
intercept = false;
|
|
PTT_PGM( "PGM !icept", intercept, 0, 0 );
|
|
|
|
/* Set the main storage reference and change bits */
|
|
ARCH_DEP( or_storage_key )( px, (STORKEY_REF | STORKEY_CHANGE) );
|
|
|
|
/* Point to PSA in main storage */
|
|
psa = (void*)(regs->mainstor + px);
|
|
|
|
#if defined( _FEATURE_SIE )
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/** FIXME : SEE ISW20090110-1 */
|
|
if (code == PGM_MONITOR_EVENT)
|
|
{
|
|
zmoncode = psa->moncode;
|
|
}
|
|
#endif
|
|
}
|
|
else /* The SIE host must deal with this program interrupt */
|
|
{
|
|
intercept = true;
|
|
PTT_PGM( "PGM icept", intercept, 0, 0 );
|
|
|
|
/* This is a guest interruption interception so point to
|
|
the interruption parm area in the state descriptor
|
|
rather then the PSA (except for Operation Exception)
|
|
*/
|
|
if (code != PGM_OPERATION_EXCEPTION)
|
|
{
|
|
psa = (void*)(HOSTREGS->mainstor + SIE_STATE(regs) + SIE_IP_PSA_OFFSET);
|
|
|
|
/* Set the main storage reference and change bits */
|
|
ARCH_DEP( or_storage_key )( SIE_STATE( regs ), (STORKEY_REF | STORKEY_CHANGE) );
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/** FIXME : SEE ISW20090110-1 */
|
|
if (code == PGM_MONITOR_EVENT)
|
|
{
|
|
PSA* _psa;
|
|
_psa = (void *)(HOSTREGS->mainstor + SIE_STATE( regs ) + SIE_II_PSA_OFFSET);
|
|
zmoncode = _psa->ioid;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* Point to PSA in main storage */
|
|
psa = (void*)(regs->mainstor + px);
|
|
|
|
/* Set the main storage reference and change bits */
|
|
ARCH_DEP( or_storage_key )( px, (STORKEY_REF | STORKEY_CHANGE) );
|
|
}
|
|
}
|
|
#endif /*defined(_FEATURE_SIE)*/
|
|
|
|
#if defined( _FEATURE_PER )
|
|
/* Handle PER or concurrent PER event */
|
|
|
|
/* Throw out Stor Alter PER if merged with nullified/suppressed rupt */
|
|
if (1
|
|
&& IS_IC_PER_SA( realregs )
|
|
&& !IS_IC_PER_STURA( realregs )
|
|
&& (realregs->ip[0] != 0x0E)
|
|
&& !(0
|
|
|| code == 0x00
|
|
|| code == PGM_SPECIFICATION_EXCEPTION
|
|
|| code == PGM_FIXED_POINT_OVERFLOW_EXCEPTION
|
|
|| code == PGM_DECIMAL_OVERFLOW_EXCEPTION
|
|
|| code == PGM_EXPONENT_OVERFLOW_EXCEPTION
|
|
|| code == PGM_EXPONENT_UNDERFLOW_EXCEPTION
|
|
|| code == PGM_SIGNIFICANCE_EXCEPTION
|
|
|| code == PGM_SPACE_SWITCH_EVENT
|
|
|| code == PGM_MONITOR_EVENT
|
|
)
|
|
)
|
|
{
|
|
OFF_IC_PER_SA( realregs );
|
|
}
|
|
|
|
if (OPEN_IC_PER( realregs ))
|
|
{
|
|
if (CPU_STEPPING_OR_TRACING( realregs, ilc ))
|
|
{
|
|
BYTE perc = IS_IC_PER( realregs ) >> 16;
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0802( regs,
|
|
(realregs->psw.IA - ilc) & ADDRESS_MAXWRAP( realregs ),
|
|
pcode, perc );
|
|
else
|
|
{
|
|
char percname[32];
|
|
perc2name( perc, percname, sizeof( percname ));
|
|
|
|
// "Processor %s%02X: PER event: code %4.4X perc %2.2X=%s addr "F_VADR
|
|
WRMSG( HHC00802, "I", PTYPSTR( regs->cpuad ), regs->cpuad,
|
|
pcode, perc, percname,
|
|
(realregs->psw.IA - ilc) & ADDRESS_MAXWRAP( realregs ));
|
|
}
|
|
}
|
|
|
|
realregs->perc |= OPEN_IC_PER( realregs ) >> ((32 - IC_CR9_SHIFT) - 16);
|
|
|
|
/* Positions 14 and 15 contain zeros
|
|
if a storage alteration event was not indicated
|
|
*/
|
|
if (0
|
|
|| !OPEN_IC_PER_SA( realregs )
|
|
|| OPEN_IC_PER_STURA( realregs )
|
|
)
|
|
realregs->perc &= 0xFFFC;
|
|
|
|
STORE_HW( psa->perint, realregs->perc );
|
|
STORE_W( psa->peradr, realregs->peradr );
|
|
|
|
if (IS_IC_PER_SA( realregs ) && ACCESS_REGISTER_MODE( &realregs->psw ))
|
|
psa->perarid = realregs->peraid;
|
|
|
|
#if defined( _FEATURE_SIE )
|
|
/* Reset PER pending indication */
|
|
if (!intercept)
|
|
OFF_IC_PER( realregs );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
pcode &= ~(PGM_PER_EVENT);
|
|
}
|
|
#endif /* defined( _FEATURE_PER ) */
|
|
|
|
#if defined( FEATURE_BCMODE )
|
|
/* For ECMODE, store extended interrupt information in PSA */
|
|
if (ECMODE( &realregs->psw ))
|
|
#endif
|
|
{
|
|
PTT_PGM( "PGM ec", 0, 0, 0 );
|
|
|
|
/* Store the program interrupt code at PSA+X'8C' */
|
|
psa->pgmint[0] = 0;
|
|
psa->pgmint[1] = ilc;
|
|
|
|
STORE_HW( psa->pgmint + 2, pcode );
|
|
|
|
/* Store the exception access identification at PSA+160 */
|
|
if (0
|
|
|| code == PGM_PAGE_TRANSLATION_EXCEPTION
|
|
|| code == PGM_SEGMENT_TRANSLATION_EXCEPTION
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
|| code == PGM_ASCE_TYPE_EXCEPTION
|
|
|| code == PGM_REGION_FIRST_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_SECOND_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_THIRD_TRANSLATION_EXCEPTION
|
|
#endif
|
|
|| code == PGM_ALEN_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ALE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_ASTE_VALIDITY_EXCEPTION
|
|
|| code == PGM_ASTE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_ASTE_INSTANCE_EXCEPTION
|
|
|| code == PGM_EXTENDED_AUTHORITY_EXCEPTION
|
|
|
|
#if defined( FEATURE_SUPPRESSION_ON_PROTECTION )
|
|
|| code == PGM_PROTECTION_EXCEPTION
|
|
#endif
|
|
)
|
|
{
|
|
psa->excarid = regs->excarid;
|
|
// FIXME: this conditional will ALWAYS be true!!
|
|
if (regs->TEA | TEA_MVPG)
|
|
psa->opndrid = regs->opndrid;
|
|
realregs->opndrid = 0;
|
|
}
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* Store the translation exception address at PSA+168 */
|
|
if (0
|
|
|| code == PGM_PAGE_TRANSLATION_EXCEPTION
|
|
|| code == PGM_SEGMENT_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ASCE_TYPE_EXCEPTION
|
|
|| code == PGM_REGION_FIRST_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_SECOND_TRANSLATION_EXCEPTION
|
|
|| code == PGM_REGION_THIRD_TRANSLATION_EXCEPTION
|
|
|
|
#if defined( FEATURE_SUPPRESSION_ON_PROTECTION )
|
|
|| code == PGM_PROTECTION_EXCEPTION
|
|
#endif
|
|
)
|
|
{
|
|
STORE_DW( psa->TEA_G, regs->TEA );
|
|
}
|
|
|
|
/* For z, store translation exception address at PSA+172 */
|
|
if (0
|
|
|| code == PGM_AFX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ASX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_PRIMARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_SECONDARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_SPACE_SWITCH_EVENT
|
|
|| code == PGM_LX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LFX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LSX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_LSTE_SEQUENCE_EXCEPTION
|
|
|| code == PGM_EX_TRANSLATION_EXCEPTION
|
|
)
|
|
{
|
|
STORE_FW( psa->TEA_L, regs->TEA );
|
|
}
|
|
|
|
#else /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
|
|
/* For 370/390, store translation exception address at PSA+144 */
|
|
if (0
|
|
|| code == PGM_PAGE_TRANSLATION_EXCEPTION
|
|
|| code == PGM_SEGMENT_TRANSLATION_EXCEPTION
|
|
|| code == PGM_AFX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_ASX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_PRIMARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_SECONDARY_AUTHORITY_EXCEPTION
|
|
|| code == PGM_SPACE_SWITCH_EVENT
|
|
|| code == PGM_LX_TRANSLATION_EXCEPTION
|
|
|| code == PGM_EX_TRANSLATION_EXCEPTION
|
|
|
|
#if defined( FEATURE_SUPPRESSION_ON_PROTECTION )
|
|
|| code == PGM_PROTECTION_EXCEPTION
|
|
#endif
|
|
)
|
|
{
|
|
STORE_FW( psa->tea, regs->TEA );
|
|
}
|
|
#endif /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
|
|
realregs->TEA = 0;
|
|
|
|
/* Store Data exception code in PSA */
|
|
if (code == PGM_DATA_EXCEPTION
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
|| code == PGM_VECTOR_PROCESSING_EXCEPTION
|
|
#endif /* !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) */
|
|
)
|
|
{
|
|
STORE_FW( psa->DXC, regs->dxc );
|
|
|
|
#if defined( FEATURE_BASIC_FP_EXTENSIONS )
|
|
/* Load data exception code into FPC register byte 2 */
|
|
if (regs->CR(0) & CR0_AFP)
|
|
{
|
|
regs->fpc &= ~(FPC_DXC);
|
|
regs->fpc |= ((regs->dxc << 8)) & FPC_DXC;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Store the monitor class and event code */
|
|
if (code == PGM_MONITOR_EVENT)
|
|
{
|
|
STORE_HW( psa->monclass, regs->monclass );
|
|
|
|
/* Store the monitor code word at PSA+156 */
|
|
/* or doubleword at PSA+176 */
|
|
/* ISW20090110-1 ZSIEMCFIX */
|
|
/* In the event of a z/Arch guest being */
|
|
/* intercepted during a succesful Monitor */
|
|
/* call, the monitor code is not stored */
|
|
/* at psa->moncode (which is beyond sie2bk->ip */
|
|
/* but rather at the same location as an */
|
|
/* I/O interrupt would store the SSID */
|
|
/* zmoncode points to this location */
|
|
/* **** FIXME **** FIXME *** FIXME *** */
|
|
/* ---- The determination of the location */
|
|
/* of the z/Sie Intercept moncode */
|
|
/* should be made more flexible */
|
|
/* and should be put somewhere in */
|
|
/* esa390.h */
|
|
/* **** FIXME **** FIXME *** FIXME *** */
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
STORE_DW( zmoncode, regs->MONCODE );
|
|
#else
|
|
STORE_W( psa->moncode, regs->MONCODE );
|
|
#endif
|
|
}
|
|
|
|
#if defined( FEATURE_PER3 )
|
|
/* Store the breaking event address register in the PSA */
|
|
STORE_W( psa->bea, regs->bear );
|
|
PTT_PGM( "PGM bear", regs->bear, 0, 0 );
|
|
#endif
|
|
|
|
} /* end if(ECMODE) */
|
|
|
|
#if defined( _FEATURE_PROTECTION_INTERCEPTION_CONTROL )
|
|
realregs->hostint = 0;
|
|
#endif
|
|
|
|
/* Normal (non-intercepted) program interrupt? */
|
|
#if defined( _FEATURE_SIE )
|
|
if (!intercept)
|
|
#endif
|
|
{
|
|
PSW pgmold, pgmnew;
|
|
int pgmintloop = 0;
|
|
int detect_pgmintloop = FACILITY_ENABLED( HERC_DETECT_PGMINTLOOP, realregs );
|
|
|
|
/* Store current PSW at PSA+X'28' or PSA+X'150' for ESAME */
|
|
ARCH_DEP( store_psw )( realregs, psa->pgmold );
|
|
|
|
/* Save program old psw */
|
|
if (detect_pgmintloop)
|
|
{
|
|
memcpy( &pgmold, &realregs->psw, sizeof( PSW ));
|
|
pgmold.cc = 0;
|
|
pgmold.intcode = 0;
|
|
pgmold.ilc = 0;
|
|
}
|
|
|
|
/* Load new PSW from PSA+X'68' or PSA+X'1D0' for ESAME */
|
|
if ((code = ARCH_DEP( load_psw )( realregs, psa->pgmnew )))
|
|
{
|
|
/* The load psw failed */
|
|
#if defined( _FEATURE_SIE )
|
|
if (SIE_MODE( realregs ))
|
|
{
|
|
PTT_PGM( "*PGM *lpsw", code, 0, 0 );
|
|
PTT_PGM( "PGM progjmp", pcode, 0, 0 );
|
|
longjmp( realregs->progjmp, pcode );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Invalid pgmnew: ==> program interrupt loop */
|
|
pgmintloop = detect_pgmintloop;
|
|
}
|
|
}
|
|
else if (detect_pgmintloop)
|
|
{
|
|
/* Save program new psw */
|
|
memcpy( &pgmnew, &realregs->psw, sizeof( PSW ));
|
|
pgmnew.cc = 0;
|
|
pgmnew.intcode = 0;
|
|
pgmnew.ilc = 0;
|
|
|
|
/* Adjust pgmold instruction address */
|
|
pgmold.ia.D -= ilc;
|
|
|
|
/* Check for program interrupt loop (old==new) */
|
|
if (memcmp( &pgmold, &pgmnew, sizeof( PSW )) == 0)
|
|
pgmintloop = 1;
|
|
}
|
|
|
|
if (pgmintloop)
|
|
{
|
|
char buf[64];
|
|
STR_PSW( realregs, buf );
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0803( realregs, buf );
|
|
|
|
// "Processor %s%02X: program interrupt loop PSW %s"
|
|
WRMSG( HHC00803, "I", PTYPSTR( realregs->cpuad ),
|
|
realregs->cpuad, buf );
|
|
|
|
OBTAIN_INTLOCK( realregs );
|
|
{
|
|
realregs->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT( realregs );
|
|
}
|
|
RELEASE_INTLOCK( realregs );
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
/* Normal non-intercepted program interrupt: return to */
|
|
/* either the run_cpu or run_sie loop and start executing */
|
|
/* instructions again, but at Program New PSW instead. */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
PTT_PGM( "PGM !icept", intercept, 0, 0 );
|
|
PTT_PGM( "PGM progjmp", SIE_NO_INTERCEPT, 0, 0 );
|
|
longjmp( realregs->progjmp, SIE_NO_INTERCEPT );
|
|
}
|
|
|
|
#if defined( _FEATURE_SIE )
|
|
/*---------------------------------------------------------------*/
|
|
/* We're in SIE mode and SIE host MUST intercept this program */
|
|
/* interrupt. Jump back to the run_sie loop with the interrupt */
|
|
/* code so it can break out of its instruction execution loop */
|
|
/* and exit from run_sie back to sie_exit so the interrupt can */
|
|
/* be passed on to the SIE host for handling. */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
PTT_PGM( "PGM icept", intercept, 0, 0 );
|
|
PTT_PGM( "PGM progjmp", pcode, 0, 0 );
|
|
longjmp( realregs->progjmp, pcode );
|
|
#endif
|
|
|
|
} /* end function ARCH_DEP( program_interrupt ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Load restart new PSW */
|
|
/*-------------------------------------------------------------------*/
|
|
static void ARCH_DEP(restart_interrupt) (REGS *regs)
|
|
{
|
|
int rc; /* Return code */
|
|
PSA *psa; /* -> Prefixed storage area */
|
|
|
|
PTT_INF("*RESTART",regs->cpuad,regs->cpustate,regs->psw.IA_L);
|
|
|
|
/* Set the main storage reference and change bits */
|
|
ARCH_DEP( or_storage_key )( regs->PX, (STORKEY_REF | STORKEY_CHANGE) );
|
|
|
|
/* Zeroize the interrupt code in the PSW */
|
|
regs->psw.intcode = 0;
|
|
|
|
/* Point to PSA in main storage */
|
|
psa = (PSA*)(regs->mainstor + regs->PX);
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
/* Abort any active transaction and then return back to here
|
|
to continue with restart interrupt processing */
|
|
if (regs->txf_tnd)
|
|
{
|
|
PTT_TXF( "*TXF MISC", 0, regs->txf_contran, regs->txf_tnd );
|
|
regs->txf_why |= TXF_WHY_RESTART_INT;
|
|
ABORT_TRANS( regs, ABORT_RETRY_RETURN, TAC_MISC );
|
|
}
|
|
#endif
|
|
/* Store current PSW at PSA+X'8' or PSA+X'120' for ESAME */
|
|
ARCH_DEP(store_psw) (regs, psa->RSTOLD);
|
|
|
|
/* Load new PSW from PSA+X'0' or PSA+X'1A0' for ESAME */
|
|
rc = ARCH_DEP(load_psw) (regs, psa->RSTNEW);
|
|
|
|
if ( rc == 0)
|
|
{
|
|
regs->opinterv = 0;
|
|
regs->cpustate = CPUSTATE_STARTED;
|
|
}
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
|
|
if ( rc )
|
|
regs->program_interrupt(regs, rc);
|
|
|
|
longjmp (regs->progjmp, SIE_INTERCEPT_RESTART);
|
|
} /* end function restart_interrupt */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform I/O interrupt if pending */
|
|
/* Note: The caller MUST hold the interrupt lock (sysblk.intlock) */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP(perform_io_interrupt) (REGS *regs)
|
|
{
|
|
int rc; /* Return code */
|
|
int icode; /* Intercept code */
|
|
PSA *psa; /* -> Prefixed storage area */
|
|
U32 ioparm; /* I/O interruption parameter*/
|
|
U32 ioid; /* I/O interruption address */
|
|
U32 iointid; /* I/O interruption ident */
|
|
RADR pfx; /* Prefix */
|
|
DBLWRD csw; /* CSW for S/370 channels */
|
|
DEVBLK *dev; /* dev presenting interrupt */
|
|
|
|
/* Test and clear pending I/O interrupt */
|
|
icode = ARCH_DEP( present_io_interrupt )( regs, &ioid, &ioparm, &iointid, csw, &dev );
|
|
|
|
/* Exit if no interrupt was presented */
|
|
if (icode == 0) return;
|
|
|
|
PTT_IO("*IOINT",ioid,ioparm,iointid);
|
|
|
|
#if defined(_FEATURE_IO_ASSIST)
|
|
if(SIE_MODE(regs) && icode != SIE_NO_INTERCEPT)
|
|
{
|
|
/* Point to SIE copy of PSA in state descriptor */
|
|
psa = (void*)(HOSTREGS->mainstor + SIE_STATE(regs) + SIE_II_PSA_OFFSET);
|
|
ARCH_DEP( or_storage_key )( SIE_STATE( regs ), (STORKEY_REF | STORKEY_CHANGE) );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Point to PSA in main storage */
|
|
pfx =
|
|
#if defined(_FEATURE_SIE)
|
|
SIE_MODE(regs) ? regs->sie_px :
|
|
#endif
|
|
regs->PX;
|
|
psa = (void*)(regs->mainstor + pfx);
|
|
ARCH_DEP( or_storage_key )( pfx, (STORKEY_REF | STORKEY_CHANGE) );
|
|
}
|
|
|
|
#ifdef FEATURE_S370_CHANNEL
|
|
/* CSW has already been stored at PSA+X'40' */
|
|
|
|
if (sysblk.arch_mode == ARCH_370_IDX &&
|
|
ECMODE(®s->psw))
|
|
{
|
|
/* For ECMODE, store the I/O device address at PSA+X'B8' */
|
|
STORE_FW(psa->ioid,
|
|
((((U32)psa->ioid[0] << 8) |
|
|
((U32)SSID_TO_LCSS(ioid >> 16) & 0x07)) << 16) |
|
|
(ioid & 0x0000FFFFUL));
|
|
}
|
|
else
|
|
{
|
|
/* Set the interrupt code to the I/O device address */
|
|
regs->psw.intcode = ioid;
|
|
}
|
|
|
|
/* Trace the I/O interrupt */
|
|
if (CPU_STEPPING_OR_TRACING( regs, 0 ) || dev->ccwtrace)
|
|
{
|
|
BYTE* csw = psa->csw;
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0804( regs, csw, ioid, SSID_TO_LCSS(ioid >> 16) & 0x07 );
|
|
else
|
|
{
|
|
// "Processor %s%02X: I/O interrupt code %1.1X:%4.4X CSW %2.2X...
|
|
WRMSG( HHC00804, "I", PTYPSTR( regs->cpuad ), regs->cpuad,
|
|
SSID_TO_LCSS( ioid >> 16 ) & 0x07, ioid,
|
|
csw[0], csw[1], csw[2], csw[3],
|
|
csw[4], csw[5], csw[6], csw[7] );
|
|
}
|
|
}
|
|
#endif /*FEATURE_S370_CHANNEL*/
|
|
|
|
#ifdef FEATURE_CHANNEL_SUBSYSTEM
|
|
/* Store X'0001' + subchannel number at PSA+X'B8' */
|
|
STORE_FW(psa->ioid, ioid);
|
|
|
|
/* Store the I/O interruption parameter at PSA+X'BC' */
|
|
STORE_FW(psa->ioparm, ioparm);
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) || defined( _FEATURE_IO_ASSIST )
|
|
/* Store the I/O interruption identification word at PSA+X'C0' */
|
|
STORE_FW(psa->iointid, iointid);
|
|
#endif
|
|
|
|
/* Trace the I/O interrupt */
|
|
if (CPU_STEPPING_OR_TRACING( regs, 0 ) || dev->ccwtrace)
|
|
{
|
|
if (sysblk.traceFILE)
|
|
{
|
|
if (regs->insttrace || dev->ccwtrace)
|
|
{
|
|
// (handles both HHC00805 and HHC00806)
|
|
tf_0806( regs, ioid, ioparm, iointid );
|
|
}
|
|
}
|
|
else // (!sysblk.traceFILE)
|
|
{
|
|
#if !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY ) && !defined( _FEATURE_IO_ASSIST )
|
|
// "Processor %s%02X: I/O interrupt code %8.8X parm %8.8X"
|
|
WRMSG (HHC00805, "I", PTYPSTR(regs->cpuad), regs->cpuad, ioid, ioparm);
|
|
#else
|
|
// "Processor %s%02X: I/O interrupt code %8.8X parm %8.8X id %8.8X"
|
|
WRMSG (HHC00806, "I", PTYPSTR(regs->cpuad), regs->cpuad, ioid, ioparm, iointid);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* FEATURE_CHANNEL_SUBSYSTEM */
|
|
|
|
#if defined( _FEATURE_IO_ASSIST )
|
|
if (icode == SIE_NO_INTERCEPT)
|
|
#endif
|
|
{
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
/* Abort any active transaction and then return back to here
|
|
to continue with I/O interrupt processing */
|
|
if (regs->txf_tnd)
|
|
{
|
|
PTT_TXF( "*TXF IO", 0, regs->txf_contran, regs->txf_tnd );
|
|
regs->txf_why |= TXF_WHY_IO_INT;
|
|
ABORT_TRANS( regs, ABORT_RETRY_RETURN, TAC_IO );
|
|
}
|
|
#endif
|
|
/* Store current PSW at PSA+X'38' or PSA+X'170' for ESAME */
|
|
ARCH_DEP(store_psw) ( regs, psa->iopold );
|
|
|
|
/* Load new PSW from PSA+X'78' or PSA+X'1F0' for ESAME */
|
|
rc = ARCH_DEP(load_psw) ( regs, psa->iopnew );
|
|
|
|
if ( rc )
|
|
{
|
|
RELEASE_INTLOCK(regs);
|
|
regs->program_interrupt (regs, rc);
|
|
}
|
|
}
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
|
|
longjmp(regs->progjmp, icode);
|
|
|
|
} /* end function perform_io_interrupt */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform Machine Check interrupt if pending */
|
|
/* Note: The caller MUST hold the interrupt lock (sysblk.intlock) */
|
|
/*-------------------------------------------------------------------*/
|
|
static void ARCH_DEP(perform_mck_interrupt) (REGS *regs)
|
|
{
|
|
int rc; /* Return code */
|
|
PSA *psa; /* -> Prefixed storage area */
|
|
U64 mcic; /* Mach.check interrupt code */
|
|
U32 xdmg; /* External damage code */
|
|
RADR fsta; /* Failing storage address */
|
|
|
|
/* Test and clear pending machine check interrupt */
|
|
rc = ARCH_DEP(present_mck_interrupt) (regs, &mcic, &xdmg, &fsta);
|
|
|
|
/* Exit if no machine check was presented */
|
|
if (rc == 0) return;
|
|
|
|
/* Set the main storage reference and change bits */
|
|
ARCH_DEP( or_storage_key )( regs->PX, (STORKEY_REF | STORKEY_CHANGE) );
|
|
|
|
/* Point to the PSA in main storage */
|
|
psa = (void*)(regs->mainstor + regs->PX);
|
|
|
|
/* Store registers in machine check save area */
|
|
ARCH_DEP(store_status) (regs, regs->PX);
|
|
|
|
#if !defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* Set the extended logout area to zeros */
|
|
memset(psa->storepsw, 0, 16);
|
|
#endif
|
|
|
|
/* Store the machine check interrupt code at PSA+232 */
|
|
STORE_DW(psa->mckint, mcic);
|
|
|
|
/* Trace the machine check interrupt */
|
|
if (CPU_STEPPING_OR_TRACING(regs, 0))
|
|
{
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0807( regs, mcic, fsta, xdmg );
|
|
else
|
|
// "Processor %s%02X: machine check code %16.16"PRIu64
|
|
WRMSG (HHC00807, "I", PTYPSTR(regs->cpuad), regs->cpuad, mcic);
|
|
}
|
|
|
|
/* Store the external damage code at PSA+244 */
|
|
STORE_FW(psa->xdmgcode, xdmg);
|
|
|
|
#if defined( FEATURE_001_ZARCH_INSTALLED_FACILITY )
|
|
/* Store the failing storage address at PSA+248 */
|
|
STORE_DW(psa->mcstorad, fsta);
|
|
#else
|
|
/* Store the failing storage address at PSA+248 */
|
|
STORE_FW(psa->mcstorad, fsta);
|
|
#endif
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
/* Abort any active transaction and then return back to here
|
|
to continue with machine check interrupt processing */
|
|
if (regs->txf_tnd)
|
|
{
|
|
PTT_TXF( "*TXF MCK", 0, regs->txf_contran, regs->txf_tnd );
|
|
regs->txf_why |= TXF_WHY_MCK_INT;
|
|
ABORT_TRANS( regs, ABORT_RETRY_RETURN, TAC_MCK );
|
|
}
|
|
#endif
|
|
/* Store current PSW at PSA+X'30' */
|
|
ARCH_DEP(store_psw) ( regs, psa->mckold );
|
|
|
|
/* Load new PSW from PSA+X'70' */
|
|
rc = ARCH_DEP(load_psw) ( regs, psa->mcknew );
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
|
|
if ( rc )
|
|
regs->program_interrupt (regs, rc);
|
|
|
|
longjmp (regs->progjmp, SIE_INTERCEPT_MCK);
|
|
} /* end function perform_mck_interrupt */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
void (ATTR_REGPARM(1) ARCH_DEP(process_interrupt))(REGS *regs)
|
|
{
|
|
/* Process PER program interrupts */
|
|
if( OPEN_IC_PER(regs) )
|
|
regs->program_interrupt (regs, PGM_PER_EVENT);
|
|
|
|
/* Obtain the interrupt lock */
|
|
OBTAIN_INTLOCK(regs);
|
|
OFF_IC_INTERRUPT(regs);
|
|
regs->breakortrace = (sysblk.instbreak || (sysblk.insttrace && regs->insttrace));
|
|
|
|
/* Ensure psw.IA is set and invalidate the aia */
|
|
INVALIDATE_AIA(regs);
|
|
|
|
/* Perform invalidation */
|
|
if (unlikely(regs->invalidate))
|
|
ARCH_DEP(invalidate_tlbe)(regs, regs->invalidate_main);
|
|
|
|
/* Take interrupts if CPU is not stopped */
|
|
if (likely(regs->cpustate == CPUSTATE_STARTED))
|
|
{
|
|
/* Process machine check interrupt */
|
|
if ( OPEN_IC_MCKPENDING(regs) )
|
|
{
|
|
PERFORM_SERIALIZATION (regs);
|
|
PERFORM_CHKPT_SYNC (regs);
|
|
ARCH_DEP (perform_mck_interrupt) (regs);
|
|
}
|
|
|
|
/* Process external interrupt */
|
|
if (1
|
|
&& OPEN_IC_EXTPENDING( regs )
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
/* Don't interrupt active transaction */
|
|
&& (0
|
|
|| regs->txf_tnd == 0
|
|
|| regs->txf_PPA < PPA_MUCH_HELP_THRESHOLD
|
|
)
|
|
#endif
|
|
)
|
|
{
|
|
PERFORM_SERIALIZATION( regs );
|
|
PERFORM_CHKPT_SYNC( regs );
|
|
ARCH_DEP( perform_external_interrupt )( regs );
|
|
}
|
|
|
|
/* Process I/O interrupt */
|
|
if (IS_IC_IOPENDING)
|
|
{
|
|
if (1
|
|
&& OPEN_IC_IOPENDING( regs )
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
/* Don't interrupt active transaction */
|
|
&& (0
|
|
|| regs->txf_tnd == 0
|
|
|| regs->txf_PPA < PPA_MUCH_HELP_THRESHOLD
|
|
)
|
|
#endif
|
|
)
|
|
{
|
|
PERFORM_SERIALIZATION( regs );
|
|
PERFORM_CHKPT_SYNC( regs );
|
|
ARCH_DEP( perform_io_interrupt )( regs );
|
|
}
|
|
else
|
|
WAKEUP_CPU_MASK(sysblk.waiting_mask);
|
|
}
|
|
} /*CPU_STARTED*/
|
|
|
|
/* If CPU is stopping, change status to stopped */
|
|
if (unlikely(regs->cpustate == CPUSTATE_STOPPING))
|
|
{
|
|
/* Change CPU status to stopped */
|
|
cpustate_stopping:
|
|
regs->opinterv = 0;
|
|
regs->cpustate = CPUSTATE_STOPPED;
|
|
|
|
/* Signal that the cpu is now stopped */
|
|
signal_condition( &sysblk.cpucond );
|
|
|
|
/* Thread exit (note - intlock still held) */
|
|
if (!regs->configured)
|
|
longjmp(regs->exitjmp, SIE_NO_INTERCEPT);
|
|
|
|
/* If initial CPU reset pending then perform reset */
|
|
if (regs->sigp_ini_reset)
|
|
{
|
|
PERFORM_SERIALIZATION (regs);
|
|
PERFORM_CHKPT_SYNC (regs);
|
|
ARCH_DEP (initial_cpu_reset) (regs);
|
|
RELEASE_INTLOCK(regs);
|
|
longjmp(regs->progjmp, SIE_NO_INTERCEPT);
|
|
}
|
|
|
|
/* If a CPU reset is pending then perform the reset */
|
|
if (regs->sigp_reset)
|
|
{
|
|
PERFORM_SERIALIZATION (regs);
|
|
PERFORM_CHKPT_SYNC (regs);
|
|
ARCH_DEP(cpu_reset) (regs);
|
|
RELEASE_INTLOCK(regs);
|
|
longjmp(regs->progjmp, SIE_NO_INTERCEPT);
|
|
}
|
|
|
|
/* Store status at absolute location 0 if requested */
|
|
if (IS_IC_STORSTAT(regs))
|
|
{
|
|
OFF_IC_STORSTAT(regs);
|
|
ARCH_DEP(store_status) (regs, 0);
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0808( regs );
|
|
|
|
// "Processor %s%02X: store status completed"
|
|
WRMSG( HHC00808, "I", PTYPSTR( regs->cpuad ), regs->cpuad );
|
|
|
|
/* ISW 20071102 : Do not return via longjmp here. */
|
|
/* process_interrupt needs to finish putting the */
|
|
/* CPU in its manual state */
|
|
/*
|
|
RELEASE_INTLOCK(regs);
|
|
longjmp(regs->progjmp, SIE_NO_INTERCEPT);
|
|
*/
|
|
}
|
|
} /*CPUSTATE_STOPPING*/
|
|
|
|
/* Perform restart interrupt if pending */
|
|
if ( IS_IC_RESTART(regs) )
|
|
{
|
|
PERFORM_SERIALIZATION (regs);
|
|
PERFORM_CHKPT_SYNC (regs);
|
|
OFF_IC_RESTART(regs);
|
|
ARCH_DEP(restart_interrupt) (regs);
|
|
} /* end if(restart) */
|
|
|
|
/* This is where a stopped CPU will wait */
|
|
if (unlikely(regs->cpustate == CPUSTATE_STOPPED))
|
|
{
|
|
S64 saved_timer = get_cpu_timer(regs);
|
|
regs->ints_state = IC_INITIAL_STATE;
|
|
sysblk.started_mask ^= regs->cpubit;
|
|
|
|
CPU_Wait(regs);
|
|
|
|
sysblk.started_mask |= regs->cpubit;
|
|
regs->ints_state |= sysblk.ints_state;
|
|
set_cpu_timer(regs,saved_timer);
|
|
|
|
ON_IC_INTERRUPT(regs);
|
|
|
|
/* Purge the lookaside buffers */
|
|
ARCH_DEP(purge_tlb) (regs);
|
|
#if defined(FEATURE_ACCESS_REGISTERS)
|
|
ARCH_DEP(purge_alb) (regs);
|
|
#endif /*defined(FEATURE_ACCESS_REGISTERS)*/
|
|
|
|
/* If the architecture mode has changed we must adapt */
|
|
if(sysblk.arch_mode != regs->arch_mode)
|
|
longjmp(regs->archjmp,SIE_NO_INTERCEPT);
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
longjmp(regs->progjmp, SIE_NO_INTERCEPT);
|
|
} /*CPUSTATE_STOPPED*/
|
|
|
|
/* Test for wait state */
|
|
if (WAITSTATE(®s->psw))
|
|
{
|
|
regs->waittod = host_tod();
|
|
|
|
/* Test for disabled wait PSW and issue message */
|
|
if (IS_IC_DISABLED_WAIT_PSW( regs ))
|
|
{
|
|
/* Don't log the disabled wait when OSTAILOR VM is active
|
|
unless it is the very last CPU in the configuration. */
|
|
if (0
|
|
|| !(sysblk.ostailor & OSTAILOR_VM)
|
|
|| !(sysblk.started_mask ^ regs->cpubit)
|
|
)
|
|
{
|
|
char buf[128];
|
|
STR_PSW( regs, buf );
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0809( regs, buf );
|
|
|
|
// "Processor %s%02X: disabled wait state %s"
|
|
WRMSG( HHC00809, "I", PTYPSTR( regs->cpuad ),
|
|
regs->cpuad, buf );
|
|
|
|
// "Processor %s%02X: processor %sstopped due to disabled wait"
|
|
WRMSG( HHC00826, "W", PTYPSTR( regs->cpuad ),
|
|
regs->cpuad, "auto-" );
|
|
}
|
|
regs->cpustate = CPUSTATE_STOPPING;
|
|
RELEASE_INTLOCK( regs );
|
|
longjmp( regs->progjmp, SIE_NO_INTERCEPT );
|
|
}
|
|
|
|
/* Indicate waiting and invoke CPU wait */
|
|
sysblk.waiting_mask |= regs->cpubit;
|
|
CPU_Wait(regs);
|
|
|
|
/* Turn off the waiting bit .
|
|
*
|
|
* Note: ANDing off of the CPU waiting bit, rather than using
|
|
* XOR, is required to handle the remote and rare case when the
|
|
* CPU is removed from the sysblk.waiting_mask while in
|
|
* wait_condition (intlock is NOT held; use of XOR incorrectly
|
|
* turns the CPU waiting bit back on).
|
|
*/
|
|
sysblk.waiting_mask &= ~(regs->cpubit);
|
|
|
|
/* Calculate the time we waited */
|
|
regs->waittime += host_tod() - regs->waittod;
|
|
regs->waittod = 0;
|
|
|
|
/* If late state change to stopping, go reprocess */
|
|
if (unlikely(regs->cpustate == CPUSTATE_STOPPING))
|
|
goto cpustate_stopping;
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
longjmp(regs->progjmp, SIE_NO_INTERCEPT);
|
|
} /* end if(wait) */
|
|
|
|
/* Release the interrupt lock */
|
|
RELEASE_INTLOCK(regs);
|
|
|
|
} /* process_interrupt */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Run CPU */
|
|
/*-------------------------------------------------------------------*/
|
|
REGS *ARCH_DEP(run_cpu) (int cpu, REGS *oldregs)
|
|
{
|
|
const INSTR_FUNC *current_opcode_table;
|
|
register REGS *regs;
|
|
BYTE *ip;
|
|
int i = 0;
|
|
int aswitch;
|
|
|
|
/* Assign new regs if not already assigned */
|
|
regs = sysblk.regs[cpu] ?
|
|
sysblk.regs[cpu] :
|
|
malloc_aligned( ROUND_UP( sizeof( REGS ), _4K ), _4K );
|
|
|
|
if (oldregs)
|
|
{
|
|
if (oldregs != regs)
|
|
{
|
|
TXF_FREEMAP( oldregs );
|
|
memcpy (regs, oldregs, sizeof(REGS));
|
|
free_aligned(oldregs);
|
|
regs->blkloc = CSWAP64((U64)((uintptr_t)regs));
|
|
HOSTREGS = regs;
|
|
if (GUESTREGS)
|
|
HOST(GUESTREGS) = regs;
|
|
sysblk.regs[cpu] = regs;
|
|
release_lock(&sysblk.cpulock[cpu]);
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0811( regs, get_arch_name( regs ));
|
|
// "Processor %s%02X: architecture mode %s"
|
|
WRMSG( HHC00811, "I", PTYPSTR( cpu ), cpu, get_arch_name( regs ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(regs, 0, sizeof(REGS));
|
|
|
|
if (cpu_init( cpu, regs, NULL ))
|
|
return NULL;
|
|
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0811( regs, get_arch_name( regs ));
|
|
|
|
// "Processor %s%02X: architecture mode %s"
|
|
WRMSG( HHC00811, "I", PTYPSTR( cpu ), cpu, get_arch_name( regs ));
|
|
|
|
#if defined( FEATURE_S370_S390_VECTOR_FACILITY )
|
|
if (regs->vf->online)
|
|
{
|
|
if (regs->insttrace && sysblk.traceFILE)
|
|
tf_0812( regs );
|
|
|
|
// "Processor %s%02X: vector facility online"
|
|
WRMSG( HHC00812, "I", PTYPSTR( cpu ), cpu );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
regs->program_interrupt = &ARCH_DEP(program_interrupt);
|
|
|
|
regs->breakortrace = (sysblk.instbreak || (sysblk.insttrace && regs->insttrace));
|
|
regs->ints_state |= sysblk.ints_state;
|
|
|
|
/* Establish longjmp destination for cpu thread exit */
|
|
if (setjmp(regs->exitjmp))
|
|
return cpu_uninit(cpu, regs);
|
|
|
|
/* Establish longjmp destination for architecture switch */
|
|
aswitch = setjmp(regs->archjmp);
|
|
|
|
/* Switch architecture mode if appropriate */
|
|
if(sysblk.arch_mode != regs->arch_mode)
|
|
{
|
|
PTT_INF("*SETARCH",regs->arch_mode,sysblk.arch_mode,cpu);
|
|
regs->arch_mode = sysblk.arch_mode;
|
|
|
|
/* Ensure CPU ID is accurate in case archmode changed */
|
|
setCpuIdregs( regs, -1, -1, -1, -1, true );
|
|
|
|
oldregs = malloc_aligned(sizeof(REGS), 4096);
|
|
if (oldregs)
|
|
{
|
|
memcpy(oldregs, regs, sizeof(REGS));
|
|
obtain_lock(&sysblk.cpulock[cpu]);
|
|
}
|
|
else
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)sizeof(REGS));
|
|
// "Processor %s%02X: error in function %s: %s"
|
|
WRMSG (HHC00813, "E", PTYPSTR(cpu), cpu, buf, strerror(errno));
|
|
cpu_uninit (cpu, regs);
|
|
}
|
|
return oldregs;
|
|
}
|
|
|
|
/* Initialize Facilities List */
|
|
init_cpu_facilities( regs );
|
|
|
|
/* Initialize Transactional-Execution Facility */
|
|
TXF_ALLOCMAP( regs );
|
|
|
|
/* Get pointer to primary opcode table */
|
|
current_opcode_table = regs->ARCH_DEP( runtime_opcode_xxxx );
|
|
|
|
/* Signal cpu has started */
|
|
if(!aswitch)
|
|
signal_condition (&sysblk.cpucond);
|
|
|
|
RELEASE_INTLOCK(regs);
|
|
|
|
/* Establish longjmp destination for program check or
|
|
RETURN_INTCHECK, or SIE_INTERCEPT, or longjmp, etc.
|
|
*/
|
|
if (setjmp( regs->progjmp ) && sysblk.ipled)
|
|
{
|
|
/* Our instruction execution loop further below didn't finish
|
|
due to a longjmp(progjmp) having been executed bringing us
|
|
to here, thereby causing the instruction counter to not be
|
|
properly updated. Thus, we need to update it here instead.
|
|
*/
|
|
regs->instcount += (i * 2);
|
|
UPDATE_SYSBLK_INSTCOUNT( (i * 2) );
|
|
|
|
/* Perform automatic instruction tracing if it's enabled */
|
|
DO_AUTOMATIC_TRACING();
|
|
}
|
|
|
|
/* Set `execflag' to 0 in case EXecuted instruction did a longjmp() */
|
|
regs->execflag = 0;
|
|
|
|
//--------------------------------------------------------------
|
|
// PROGRAMMING NOTE
|
|
//--------------------------------------------------------------
|
|
// The first 'fastest_no_txf_loop' loop below is used when the
|
|
// TXF facility is not enabled, and since facilities cannot be
|
|
// enabled or disabled once the guest system has been IPLed and
|
|
// started, it utilizes our original instruction execution loop
|
|
// which uses the 'EXECUTE_INSTRUCTION' and 'UNROLLED_EXECUTE'
|
|
// macros which do not have any TXF related code in them.
|
|
//
|
|
// The second and third loops below (the 'txf_facility_loop' and
|
|
// 'txf_slower_loop') are used when the TXF facility is enabled,
|
|
// requiring us to check whether or not a transaction is active
|
|
// or not after each instruction is executed.
|
|
//
|
|
// If no transaction is active, the normal 'EXECUTE_INSTRUCTION'
|
|
// and 'UNROLLED_EXECUTE' macros can be used, but a check for an
|
|
// active transaction still needs to be performed after each and
|
|
// every instruction (so we can know which loop we need to use).
|
|
//
|
|
// When a transaction is active, we use the third (slowest) loop
|
|
// called 'txf_slower_loop', using the 'TXF_EXECUTE_INSTRUCTION'
|
|
// and 'TXF_UNROLLED_EXECUTE' macros, which contain code that
|
|
// enforces certain Transaction-Exceution Facility constraints.
|
|
//--------------------------------------------------------------
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
if (FACILITY_ENABLED( 073_TRANSACT_EXEC, regs ))
|
|
if (regs->CR(0) & CR0_TXC)
|
|
goto txf_facility_loop;
|
|
#endif
|
|
|
|
fastest_no_txf_loop:
|
|
|
|
if (INTERRUPT_PENDING( regs ))
|
|
ARCH_DEP( process_interrupt )( regs );
|
|
|
|
enter_fastest_no_txf_loop:
|
|
|
|
ip = INSTRUCTION_FETCH( regs, 0 );
|
|
PROCESS_TRACE( regs, ip, enter_fastest_no_txf_loop );
|
|
EXECUTE_INSTRUCTION( current_opcode_table, ip, regs );
|
|
regs->instcount++;
|
|
UPDATE_SYSBLK_INSTCOUNT( 1 );
|
|
|
|
for (i=0; i < MAX_CPU_LOOPS/2; i++)
|
|
{
|
|
UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
}
|
|
regs->instcount += (i * 2);
|
|
UPDATE_SYSBLK_INSTCOUNT( (i * 2) );
|
|
|
|
/* Perform automatic instruction tracing if it's enabled */
|
|
DO_AUTOMATIC_TRACING();
|
|
goto fastest_no_txf_loop;
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
|
|
txf_facility_loop:
|
|
|
|
if (INTERRUPT_PENDING( regs ))
|
|
ARCH_DEP( process_interrupt )( regs );
|
|
|
|
if (regs->txf_tnd)
|
|
goto enter_txf_slower_loop;
|
|
|
|
enter_txf_faster_loop:
|
|
|
|
ip = INSTRUCTION_FETCH( regs, 0 );
|
|
PROCESS_TRACE( regs, ip, enter_txf_faster_loop );
|
|
EXECUTE_INSTRUCTION( current_opcode_table, ip, regs );
|
|
regs->instcount++;
|
|
UPDATE_SYSBLK_INSTCOUNT( 1 );
|
|
|
|
for (i=0; i < MAX_CPU_LOOPS/2; i++)
|
|
{
|
|
if (regs->txf_tnd)
|
|
break;
|
|
|
|
UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
|
|
if (regs->txf_tnd)
|
|
break;
|
|
|
|
UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
}
|
|
regs->instcount += (i * 2);
|
|
UPDATE_SYSBLK_INSTCOUNT( (i * 2) );
|
|
|
|
/* Perform automatic instruction tracing if it's enabled */
|
|
DO_AUTOMATIC_TRACING();
|
|
|
|
//txf_slower_loop:
|
|
|
|
if (INTERRUPT_PENDING( regs ))
|
|
ARCH_DEP( process_interrupt )( regs );
|
|
|
|
if (!regs->txf_tnd)
|
|
goto enter_txf_faster_loop;
|
|
|
|
enter_txf_slower_loop:
|
|
|
|
ip = INSTRUCTION_FETCH( regs, 0 );
|
|
PROCESS_TRACE( regs, ip, enter_txf_slower_loop );
|
|
TXF_EXECUTE_INSTRUCTION( current_opcode_table, ip, regs );
|
|
regs->instcount++;
|
|
UPDATE_SYSBLK_INSTCOUNT( 1 );
|
|
|
|
for (i=0; i < MAX_CPU_LOOPS/2; i++)
|
|
{
|
|
if (!regs->txf_tnd)
|
|
break;
|
|
|
|
TXF_UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
|
|
if (!regs->txf_tnd)
|
|
break;
|
|
|
|
TXF_UNROLLED_EXECUTE( current_opcode_table, regs );
|
|
}
|
|
regs->instcount += (i * 2);
|
|
UPDATE_SYSBLK_INSTCOUNT( (i * 2) );
|
|
|
|
/* Perform automatic instruction tracing if it's enabled */
|
|
DO_AUTOMATIC_TRACING();
|
|
goto txf_facility_loop;
|
|
|
|
#endif /* defined( FEATURE_073_TRANSACT_EXEC_FACILITY ) */
|
|
|
|
UNREACHABLE_CODE( return NULL );
|
|
|
|
} /* end function run_cpu */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process Trace */
|
|
/*-------------------------------------------------------------------*/
|
|
void ARCH_DEP( process_trace )( REGS* regs, BYTE* dest )
|
|
{
|
|
bool shouldtrace = false; /* true == Trace instruction */
|
|
|
|
/* Test for trace */
|
|
shouldtrace = CPU_TRACING( regs, 0 );
|
|
|
|
/* Test for step */
|
|
regs->stepping = CPU_STEPPING( regs, 0 );
|
|
|
|
if (regs->stepping)
|
|
{
|
|
if (sysblk.breakasid)
|
|
{
|
|
if (regs->CR_LHL(4) != sysblk.breakasid)
|
|
regs->stepping = false;
|
|
}
|
|
}
|
|
|
|
/* Display the instruction */
|
|
if (shouldtrace || regs->stepping)
|
|
ARCH_DEP( display_inst )( regs, dest );
|
|
|
|
/* Stop the CPU if instruction stepping */
|
|
if (regs->stepping)
|
|
{
|
|
REGS* hostregs = HOSTREGS;
|
|
S64 saved_timer[2] = {0};
|
|
|
|
OBTAIN_INTLOCK( hostregs );
|
|
{
|
|
hostregs->waittod = host_tod();
|
|
|
|
/* The CPU timer is not decremented for a CPU that is in
|
|
the manual state (e.g. stopped in single step mode) */
|
|
|
|
saved_timer[0] = get_cpu_timer( regs );
|
|
saved_timer[1] = get_cpu_timer( hostregs );
|
|
|
|
hostregs->cpustate = CPUSTATE_STOPPED;
|
|
sysblk.started_mask &= ~hostregs->cpubit;
|
|
hostregs->stepwait = 1;
|
|
sysblk.intowner = LOCK_OWNER_NONE;
|
|
|
|
while (hostregs->cpustate == CPUSTATE_STOPPED)
|
|
{
|
|
wait_condition( &hostregs->intcond, &sysblk.intlock );
|
|
}
|
|
|
|
sysblk.intowner = hostregs->cpuad;
|
|
hostregs->stepwait = 0;
|
|
sysblk.started_mask |= hostregs->cpubit;
|
|
|
|
set_cpu_timer( regs, saved_timer[0] );
|
|
set_cpu_timer( hostregs, saved_timer[1] );
|
|
|
|
hostregs->waittime += host_tod() - hostregs->waittod;
|
|
hostregs->waittod = 0;
|
|
}
|
|
RELEASE_INTLOCK( hostregs );
|
|
}
|
|
} /* process_trace */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if !defined( _GEN_ARCH )
|
|
|
|
#if defined( _ARCH_NUM_1 )
|
|
#define _GEN_ARCH _ARCH_NUM_1
|
|
#include "cpu.c"
|
|
#endif
|
|
|
|
#if defined( _ARCH_NUM_2 )
|
|
#undef _GEN_ARCH
|
|
#define _GEN_ARCH _ARCH_NUM_2
|
|
#include "cpu.c"
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* non-ARCH_DEP section: compiled only ONCE after last arch built */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Note: the last architecture has been built so the normal non- */
|
|
/* underscore FEATURE values are now #defined according to the */
|
|
/* LAST built architecture just built (usually zarch = 900). This */
|
|
/* means from this point onward (to the end of file) you should */
|
|
/* ONLY be testing the underscore _FEATURE values to see if the */
|
|
/* given feature was defined for *ANY* of the build architectures. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*********************************************************************/
|
|
/* IMPORTANT PROGRAMMING NOTE */
|
|
/*********************************************************************/
|
|
/* */
|
|
/* It is CRITICALLY IMPORTANT to not use any architecture dependent */
|
|
/* macros anywhere in any of your non-arch_dep functions. This means */
|
|
/* you CANNOT use GREG, RADR, VADR, etc. anywhere in your function, */
|
|
/* nor can you call "ARCH_DEP(func)(args)" anywhere in your code! */
|
|
/* */
|
|
/* Basically you MUST NOT use any architecture dependent macro that */
|
|
/* is #defined in the "feature.h" header. If you you need to use */
|
|
/* any of them, then your function MUST be an "ARCH_DEP" function */
|
|
/* that is placed within the ARCH_DEP section at the beginning of */
|
|
/* this module where it can be compiled multiple times, once for */
|
|
/* each of the supported architectures so the macro gets #defined */
|
|
/* to its proper value for the architecture. YOU HAVE BEEN WARNED. */
|
|
/* */
|
|
/*********************************************************************/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* run_cpu jump table -- used by the below "cpu_thread" function to */
|
|
/* call the proper ARCH_DEP(run_cpu) function for the architecture. */
|
|
/*-------------------------------------------------------------------*/
|
|
static REGS* (*run_cpu[ NUM_GEN_ARCHS ])( int cpu, REGS* oldregs ) =
|
|
{
|
|
#if defined( _ARCH_NUM_0 )
|
|
#if 370 == _ARCH_NUM_0
|
|
s370_run_cpu,
|
|
#elif 390 == _ARCH_NUM_0
|
|
s390_run_cpu,
|
|
#else // 900 == _ARCH_NUM_0
|
|
z900_run_cpu,
|
|
#endif
|
|
#endif
|
|
#if defined( _ARCH_NUM_1 )
|
|
#if 370 == _ARCH_NUM_1
|
|
s370_run_cpu,
|
|
#elif 390 == _ARCH_NUM_1
|
|
s390_run_cpu,
|
|
#else // 900 == _ARCH_NUM_1
|
|
z900_run_cpu,
|
|
#endif
|
|
#endif
|
|
#if defined( _ARCH_NUM_2 )
|
|
#if 370 == _ARCH_NUM_2
|
|
s370_run_cpu,
|
|
#elif 390 == _ARCH_NUM_2
|
|
s390_run_cpu,
|
|
#else // 900 == _ARCH_NUM_2
|
|
z900_run_cpu,
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CPU instruction execution thread */
|
|
/*-------------------------------------------------------------------*/
|
|
void *cpu_thread (void *ptr)
|
|
{
|
|
REGS *regs = NULL;
|
|
int cpu = *(int*)ptr;
|
|
char thread_name[16];
|
|
int rc;
|
|
|
|
OBTAIN_INTLOCK(NULL);
|
|
|
|
/* Increment number of CPUs online */
|
|
sysblk.cpus++;
|
|
|
|
/* Set hi CPU */
|
|
if (cpu >= sysblk.hicpu)
|
|
sysblk.hicpu = cpu + 1;
|
|
|
|
/* Start the TOD clock and CPU timer thread */
|
|
if (!sysblk.todtid)
|
|
{
|
|
rc = create_thread( &sysblk.todtid, DETACHED,
|
|
timer_thread, NULL, TIMER_THREAD_NAME );
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
RELEASE_INTLOCK( NULL );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Set CPU thread priority */
|
|
SET_THREAD_PRIORITY( sysblk.cpuprio, sysblk.qos_user_initiated );
|
|
|
|
/* Display thread started message on control panel */
|
|
|
|
MSGBUF( thread_name, "Processor %s%02X", PTYPSTR( cpu ), cpu );
|
|
LOG_THREAD_BEGIN( thread_name );
|
|
SET_THREAD_NAME( thread_name );
|
|
|
|
/* Execute the program in specified mode */
|
|
do {
|
|
regs = run_cpu[sysblk.arch_mode] (cpu, regs);
|
|
} while (regs);
|
|
|
|
/* Decrement number of CPUs online */
|
|
sysblk.cpus--;
|
|
|
|
/* Reset hi cpu */
|
|
if (cpu + 1 >= sysblk.hicpu)
|
|
{
|
|
int i;
|
|
for (i = sysblk.maxcpu - 1; i >= 0; i--)
|
|
if (IS_CPU_ONLINE(i))
|
|
break;
|
|
sysblk.hicpu = i + 1;
|
|
}
|
|
|
|
/* Signal cpu has terminated */
|
|
signal_condition (&sysblk.cpucond);
|
|
|
|
/* Display thread ended message on control panel */
|
|
LOG_THREAD_END( thread_name );
|
|
|
|
RELEASE_INTLOCK(NULL);
|
|
|
|
return NULL;
|
|
} /* end function cpu_thread */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize a CPU */
|
|
/*-------------------------------------------------------------------*/
|
|
int cpu_init (int cpu, REGS *regs, REGS *hostregs)
|
|
{
|
|
int i;
|
|
|
|
obtain_lock (&sysblk.cpulock[cpu]);
|
|
|
|
/* initialize eye-catchers */
|
|
{
|
|
char blknam[ sizeof( regs->blknam )];
|
|
MSGBUF( blknam, "%-4.4s_%s%02X", HDL_NAME_REGS, PTYPSTR( cpu ), cpu );
|
|
INIT_BLOCK_HEADER_TRAILER_WITH_CUSTOM_NAME( regs, REGS, blknam );
|
|
}
|
|
|
|
regs->cpuad = cpu;
|
|
regs->cpubit = CPU_BIT(cpu);
|
|
|
|
/* Save CPU creation time without epoch set, as epoch may change. When using
|
|
* the field, subtract the current epoch from any time being used in
|
|
* relation to the creation time to yield the correct result.
|
|
*/
|
|
if (sysblk.cpucreateTOD[cpu] == 0)
|
|
sysblk.cpucreateTOD[cpu] = host_tod(); /* tod_epoch is zero at this point */
|
|
|
|
regs->arch_mode = sysblk.arch_mode;
|
|
regs->mainstor = sysblk.mainstor;
|
|
regs->sysblk = &sysblk;
|
|
regs->storkeys = sysblk.storkeys;
|
|
regs->mainlim = sysblk.mainsize - 1;
|
|
regs->tod_epoch = get_tod_epoch();
|
|
|
|
/* Set initial CPU ID by REGS context. Note that this
|
|
must only be done AFTER regs->arch_mode has been set.
|
|
*/
|
|
setCpuIdregs( regs, -1, -1, -1, -1, true );
|
|
|
|
initialize_condition (®s->intcond);
|
|
regs->cpulock = &sysblk.cpulock[cpu];
|
|
|
|
#if defined( _FEATURE_S370_S390_VECTOR_FACILITY )
|
|
regs->vf = &sysblk.vf[cpu];
|
|
regs->vf->online = (cpu < sysblk.numvec);
|
|
#endif
|
|
initial_cpu_reset(regs);
|
|
|
|
/*****************************************************************/
|
|
/* Refer to the PROGRAMMING NOTE in hstructs.h */
|
|
/*****************************************************************/
|
|
/* */
|
|
/* If 'hostregs' passed as NULL, then 'regs' points to host regs.*/
|
|
/* Otherwise, 'regs' points to guest regs and 'hostregs' points */
|
|
/* to host regs. */
|
|
/* */
|
|
/* The flag 'guest' is only 1 in guest regs and 'host' is only 1 */
|
|
/* in host regs. 'hostregs' always points to host regs in both, */
|
|
/* and 'guestregs' always points to guest regs in both. */
|
|
/* */
|
|
/* HOWEVER, guest regs only exists when SIE is active/running. */
|
|
/* If SIE is NOT active/running, then 'guestregs' is NULL. */
|
|
/* */
|
|
/* 'sie_active' is only 1 in host regs, never in guest regs. */
|
|
/* 'sie_mode' is only 1 in guest regs, never in host regs. */
|
|
/* */
|
|
/*****************************************************************/
|
|
/* Refer to the PROGRAMMING NOTE in hstructs.h */
|
|
/*****************************************************************/
|
|
|
|
if (!hostregs)
|
|
{
|
|
/* regs points to host regs */
|
|
regs->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT(regs);
|
|
HOSTREGS = regs;
|
|
regs->host = 1;
|
|
sysblk.regs[cpu] = regs;
|
|
sysblk.config_mask |= regs->cpubit;
|
|
sysblk.started_mask |= regs->cpubit;
|
|
}
|
|
else // SIE mode
|
|
{
|
|
/* regs points to guest regs.
|
|
hostregs points to host regs.
|
|
*/
|
|
GUEST(hostregs) = regs;
|
|
HOSTREGS = hostregs;
|
|
GUESTREGS = regs;
|
|
regs->guest = 1;
|
|
regs->sie_mode = 1;
|
|
regs->opinterv = 0;
|
|
regs->cpustate = CPUSTATE_STARTED;
|
|
}
|
|
|
|
/* Initialize accelerated lookup fields */
|
|
switch (regs->arch_mode)
|
|
{
|
|
#if defined(_370)
|
|
case ARCH_370_IDX:
|
|
regs->CR_G( CR_ASD_REAL ) = TLB_REAL_ASD_L;
|
|
break;
|
|
#endif
|
|
#if defined(_390)
|
|
case ARCH_390_IDX:
|
|
regs->CR_G( CR_ASD_REAL ) = TLB_REAL_ASD_L;
|
|
break;
|
|
#endif
|
|
#if defined(_900)
|
|
case ARCH_900_IDX:
|
|
regs->CR_G( CR_ASD_REAL ) = TLB_REAL_ASD_G;
|
|
break;
|
|
#endif
|
|
default: CRASH();
|
|
}
|
|
|
|
for (i=0; i < 16; i++)
|
|
regs->AEA_AR( i ) = CR_ASD_REAL;
|
|
regs->AEA_AR( USE_INST_SPACE ) = CR_ASD_REAL;
|
|
regs->AEA_AR( USE_REAL_ADDR ) = CR_ASD_REAL;
|
|
regs->AEA_AR( USE_PRIMARY_SPACE ) = 1;
|
|
regs->AEA_AR( USE_SECONDARY_SPACE ) = 7;
|
|
regs->AEA_AR( USE_HOME_SPACE ) = 13;
|
|
|
|
/* Initialize opcode table pointers */
|
|
init_regs_runtime_opcode_pointers( regs );
|
|
|
|
regs->configured = 1;
|
|
|
|
release_lock (&sysblk.cpulock[cpu]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Uninitialize a CPU */
|
|
/*-------------------------------------------------------------------*/
|
|
static void *cpu_uninit (int cpu, REGS *regs)
|
|
{
|
|
int processHostRegs = (regs->host &&
|
|
regs == sysblk.regs[cpu]);
|
|
|
|
if (processHostRegs)
|
|
{
|
|
obtain_lock (&sysblk.cpulock[cpu]);
|
|
|
|
/* If pointing to guest REGS structure, free the guest REGS
|
|
* structure ONLY if it is not ourself, and set the guest REGS
|
|
* pointer to NULL;
|
|
*/
|
|
if (GUESTREGS)
|
|
GUESTREGS = (regs == GUESTREGS) ?
|
|
NULL : cpu_uninit( cpu, GUESTREGS );
|
|
}
|
|
|
|
destroy_condition(®s->intcond);
|
|
|
|
if (processHostRegs)
|
|
{
|
|
#if defined( _FEATURE_S370_S390_VECTOR_FACILITY )
|
|
/* Mark Vector Facility offline */
|
|
regs->vf->online = 0;
|
|
#endif
|
|
|
|
/* Remove CPU from all CPU bit masks */
|
|
sysblk.config_mask &= ~CPU_BIT(cpu);
|
|
sysblk.started_mask &= ~CPU_BIT(cpu);
|
|
sysblk.waiting_mask &= ~CPU_BIT(cpu);
|
|
sysblk.regs[cpu] = NULL;
|
|
sysblk.cpucreateTOD[cpu] = 0;
|
|
release_lock (&sysblk.cpulock[cpu]);
|
|
}
|
|
|
|
/* Free the REGS structure */
|
|
TXF_FREEMAP( regs );
|
|
free_aligned( regs );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* CPU Wait - Core wait routine for both CPU Wait and Stopped States */
|
|
/* */
|
|
/* Locks Held */
|
|
/* sysblk.intlock */
|
|
/*-------------------------------------------------------------------*/
|
|
static void CPU_Wait( REGS* regs )
|
|
{
|
|
/* Indicate we are giving up intlock */
|
|
sysblk.intowner = LOCK_OWNER_NONE;
|
|
|
|
/* Wait while SYNCHRONIZE_CPUS is in progress */
|
|
while (sysblk.syncing)
|
|
{
|
|
sysblk.sync_mask &= ~HOSTREGS->cpubit;
|
|
if (!sysblk.sync_mask)
|
|
signal_condition(&sysblk.all_synced_cond);
|
|
wait_condition (&sysblk.sync_done_cond, &sysblk.intlock);
|
|
}
|
|
|
|
/*
|
|
** Let test script know when a CPU either stops or loads a
|
|
** disabled wait state PSW, but NOT when a CPU simply loads
|
|
** an ENABLED wait PSW such as when it wishes to simply wait
|
|
** for an I/O, External or other type of interrupt.
|
|
**
|
|
** NOTE: we can't rely on just sysblk.started_mask since there
|
|
** is no guarantee the last CPU to have its sysblk.started_mask
|
|
** cleared will actually be the last CPU to reach this point.
|
|
*/
|
|
if (WAITSTATE( ®s->psw ) && !IS_IC_DISABLED_WAIT_PSW( regs ))
|
|
; /* enabled wait: do nothing */
|
|
else
|
|
{
|
|
obtain_lock( &sysblk.scrlock );
|
|
if (sysblk.scrtest)
|
|
{
|
|
sysblk.scrtest++;
|
|
broadcast_condition( &sysblk.scrcond );
|
|
}
|
|
release_lock( &sysblk.scrlock );
|
|
}
|
|
|
|
/* Wait for interrupt */
|
|
wait_condition (®s->intcond, &sysblk.intlock);
|
|
|
|
/* And we're the owner of intlock once again */
|
|
sysblk.intowner = regs->cpuad;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Copy program status word */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void copy_psw (REGS *regs, BYTE *addr)
|
|
{
|
|
REGS cregs;
|
|
int arch_mode;
|
|
|
|
memcpy(&cregs, regs, sysblk.regs_copy_len);
|
|
|
|
/* Use the architecture mode from SYSBLK if load indicator
|
|
shows IPL in process, otherwise use archmode from REGS */
|
|
if (cregs.loadstate)
|
|
{
|
|
arch_mode = sysblk.arch_mode;
|
|
}
|
|
else
|
|
{
|
|
arch_mode = cregs.arch_mode;
|
|
}
|
|
|
|
/* Call the appropriate store_psw routine based on archmode */
|
|
switch(arch_mode) {
|
|
#if defined(_370)
|
|
case ARCH_370_IDX:
|
|
s370_store_psw(&cregs, addr);
|
|
break;
|
|
#endif
|
|
#if defined(_390)
|
|
case ARCH_390_IDX:
|
|
s390_store_psw(&cregs, addr);
|
|
break;
|
|
#endif
|
|
#if defined(_900)
|
|
case ARCH_900_IDX:
|
|
z900_store_psw(&cregs, addr);
|
|
break;
|
|
#endif
|
|
default: CRASH();
|
|
}
|
|
} /* end function copy_psw */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Display program status word taking loadstate into consideration. */
|
|
/*-------------------------------------------------------------------*/
|
|
int display_psw( REGS* regs, char* buf, int buflen )
|
|
{
|
|
/* Use the architecture mode from SYSBLK if load indicator
|
|
shows IPL in process, otherwise use archmode from REGS */
|
|
int arch_mode = (regs->loadstate) ? sysblk.arch_mode
|
|
: regs->arch_mode;
|
|
return strlen( str_arch_psw( arch_mode, regs, buf, buflen ));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Print program status word into buffer for default REGS arch_mode */
|
|
/*-------------------------------------------------------------------*/
|
|
char* str_psw( REGS* regs, char* buf, int buflen )
|
|
{
|
|
return str_arch_psw( regs->arch_mode, regs, buf, buflen );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Print program status word into buffer for specified arch_mode */
|
|
/*-------------------------------------------------------------------*/
|
|
char* str_arch_psw( int arch_mode, REGS* regs, char* buf, int buflen )
|
|
{
|
|
QWORD qword = {0}; /* quadword work area */
|
|
|
|
copy_psw( regs, qword );
|
|
|
|
if (arch_mode != ARCH_900_IDX)
|
|
{
|
|
snprintf( buf, buflen,
|
|
"%2.2X%2.2X%2.2X%2.2X %2.2X%2.2X%2.2X%2.2X",
|
|
qword[0], qword[1], qword[2], qword[3],
|
|
qword[4], qword[5], qword[6], qword[7] );
|
|
}
|
|
else
|
|
{
|
|
snprintf( buf, buflen,
|
|
"%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X "
|
|
"%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X",
|
|
qword[ 0], qword[ 1], qword[ 2], qword[ 3],
|
|
qword[ 4], qword[ 5], qword[ 6], qword[ 7],
|
|
qword[ 8], qword[ 9], qword[10], qword[11],
|
|
qword[12], qword[13], qword[14], qword[15] );
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Automatic Tracing */
|
|
/*-------------------------------------------------------------------*/
|
|
void do_automatic_tracing()
|
|
{
|
|
static U64 inst_count; // (current sysblk.instcount)
|
|
static U64 missed_by; // (how far past trigger we went)
|
|
static U64 too_much; // (num extra instructions traced)
|
|
|
|
bool started = false, stopped = false;
|
|
int cpu;
|
|
|
|
/* Return immediately if automatic tracing not enabled or active */
|
|
//if (!sysblk.auto_trace_amt)
|
|
// return;
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
static U64 beg_count = 0; // (when auto-tracing began)
|
|
|
|
static U64 auto_trace_beg; // (instrcount to begin tracing)
|
|
static U64 auto_trace_amt; // (amt of instruction to trace)
|
|
|
|
static U64 traced_amt; // (amt instr. traced so far)
|
|
|
|
/* Check again under control of INTLOCK */
|
|
if (!sysblk.auto_trace_amt)
|
|
{
|
|
RELEASE_INTLOCK( NULL );
|
|
return;
|
|
}
|
|
|
|
auto_trace_beg = sysblk.auto_trace_beg;
|
|
auto_trace_amt = sysblk.auto_trace_amt;
|
|
inst_count = sysblk.instcount;
|
|
|
|
/* Should Automatic Tracing be started? */
|
|
if (1
|
|
&& !beg_count // (hasn't begun yet?)
|
|
&& inst_count >= auto_trace_beg // (but it should be?)
|
|
)
|
|
{
|
|
started = true; // (remember started)
|
|
beg_count = inst_count; // (when it was begun)
|
|
missed_by = (inst_count - auto_trace_beg);
|
|
sysblk.insttrace = true; // (activate tracing)
|
|
sysblk.auto_trace_beg = 0; // (prevent re-trigger)
|
|
SET_IC_TRACE; // (force interrupt)
|
|
}
|
|
|
|
/* Should Automatic Tracing be stopped? */
|
|
else if (1
|
|
&& beg_count
|
|
&& (traced_amt = (inst_count - beg_count)) >= auto_trace_amt
|
|
)
|
|
{
|
|
stopped = true; // (remember stopped)
|
|
beg_count = 0; // (reset for next time)
|
|
too_much = (traced_amt - auto_trace_amt);
|
|
sysblk.insttrace = false; // (deactivate tracing)
|
|
sysblk.auto_trace_amt = 0; // (prevent re-trigger)
|
|
SET_IC_TRACE; // (force interrupt)
|
|
}
|
|
|
|
/* Enable/disable CPU tracing based on overall trace status */
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (IS_CPU_ONLINE( cpu ))
|
|
sysblk.regs[ cpu ]->insttrace = sysblk.insttrace;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
if (started)
|
|
{
|
|
// "Automatic tracing started at instrcount %"PRIu64" (BEG+%"PRIu64")"
|
|
WRMSG( HHC02370, "I", inst_count, missed_by );
|
|
}
|
|
else if (stopped)
|
|
{
|
|
// "Automatic tracing stopped at instrcount %"PRIu64" (AMT+%"PRIu64")"
|
|
WRMSG( HHC02371, "I", inst_count, too_much );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* make_psw64 */
|
|
/*-------------------------------------------------------------------*/
|
|
U64 make_psw64( REGS* regs, int arch /*370/390/900*/, bool bc )
|
|
{
|
|
return do_make_psw64( ®s->psw, REAL_ILC( regs ), arch, bc );
|
|
}
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|