Files
org-hyperion-cules/machchk.c

561 lines
17 KiB
C

/* MACHCHK.C (c) Copyright Jan Jaeger, 2000-2012 */
/* ESA/390 Machine Check Functions */
/* */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2012 */
/*-------------------------------------------------------------------*/
/* The machine check function supports dynamic I/O configuration. */
/* When a subchannel is added/changed/deleted an ancillary */
/* channel report is made pending. This ancillary channel */
/* report can be read by the store channel report word I/O */
/* instruction. Changes to the availability will result in */
/* Messages IOS150I and IOS151I (device xxxx now/not available) */
/* Added Instruction processing damage machine check function such */
/* that abends/waits/loops in instructions will be reflected to the */
/* system running under hercules as a machine malfunction. This */
/* includes the machine check, checkstop, and malfunction alert */
/* external interrupt as defined in the architecture. - 6/8/01 *JJ */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#if !defined(_HENGINE_DLL_)
#define _HENGINE_DLL_
#endif
#if !defined(_MACHCHK_C_)
#define _MACHCHK_C_
#endif
#include "hercules.h"
#include "opcode.h"
#if !defined(_MACHCHK_C)
#define _MACHCHK_C
/*-------------------------------------------------------------------*/
/* Build a Channel Path Reset Channel Report */
/*-------------------------------------------------------------------*/
void build_chp_reset_chrpt( BYTE chpid, int solicited, int found )
{
U32 crw_erc, crwarray[8], crwcount=0;
/* Just return if shutting down */
if (sysblk.shutdown)
return;
chpid = ((U32)chpid) & CRW_RSID_MASK;
/* If a subchannel was found on this path and was reset. Ref:
SA22-7832 "Channel-Path-Reset-Function-Completion Signaling */
if (found)
crw_erc = CRW_ERC_INIT; /* Init'ed, parms unchanged */
else
crw_erc = CRW_ERC_RESET; /* Error, parms initialized */
/* Build the Channel Path Reset Channel Report */
crwarray[crwcount++] = (solicited ? CRW_SOL : 0) |
CRW_RSC_CHPID | CRW_AR | crw_erc | chpid;
/* Queue the Channel Report */
VERIFY( queue_channel_report( crwarray, crwcount ) == 0 );
}
/*-------------------------------------------------------------------*/
/* Build a device attach Channel Report */
/*-------------------------------------------------------------------*/
void build_attach_chrpt( DEVBLK *dev )
{
U32 ssid, subchan, crwarray[8], crwcount=0;
int devlock_obtained;
/* Just return if shutting down */
if (sysblk.shutdown)
return;
/* Retrieve Source IDs */
devlock_obtained = (try_obtain_lock( &dev->lock ) == 0);
{
ssid = ((U32)SSID_TO_LCSS( dev->ssid )) & CRW_RSID_MASK;
subchan = ((U32)dev->subchan) & CRW_RSID_MASK;
}
if (devlock_obtained)
release_lock( &dev->lock );
/* Build Subchannel Alert Channel Report */
crwarray[crwcount++] =
0
| (sysblk.mss ? CRW_CHAIN : 0)
| CRW_RSC_SUBCH
| CRW_AR
| CRW_ERC_ALERT
| subchan
;
if (sysblk.mss)
crwarray[crwcount++] =
0
| CRW_RSC_SUBCH
| CRW_AR
| CRW_ERC_ALERT
| (ssid << 8)
;
/* Queue the Channel Report(s) */
VERIFY( queue_channel_report( crwarray, crwcount ) == 0 );
}
/*-------------------------------------------------------------------*/
/* Build a device detach Channel Report */
/*-------------------------------------------------------------------*/
void build_detach_chrpt( DEVBLK *dev )
{
U32 ssid, subchan, crwarray[8], crwcount=0;
int devlock_obtained;
/* Just return if shutting down */
if (sysblk.shutdown)
return;
/* Retrieve Source IDs */
devlock_obtained = (try_obtain_lock( &dev->lock ) == 0);
{
ssid = ((U32)SSID_TO_LCSS( dev->ssid )) & CRW_RSID_MASK;
subchan = ((U32)dev->subchan) & CRW_RSID_MASK;
}
if (devlock_obtained)
release_lock( &dev->lock );
/* Build Subchannel Alert Channel Report */
crwarray[crwcount++] =
0
| (sysblk.mss ? CRW_CHAIN : 0)
| CRW_RSC_SUBCH
| CRW_AR
| CRW_ERC_ALERT
| subchan
;
if (sysblk.mss)
crwarray[crwcount++] =
0
| CRW_RSC_SUBCH
| CRW_AR
| CRW_ERC_ALERT
| (ssid << 8)
;
/* Queue the Channel Report */
VERIFY( queue_channel_report( crwarray, crwcount ) == 0 );
}
/*-------------------------------------------------------------------*/
/* Add a Channel Report to the queue */
/*-------------------------------------------------------------------*/
int queue_channel_report( U32* crwarray, U32 crwcount )
{
OBTAIN_CRWLOCK();
if ((sysblk.crwcount + crwcount) > sysblk.crwalloc)
{
/* Allocate larger queue */
U32 newalloc = sysblk.crwalloc + crwcount;
U32* newarray = malloc( newalloc * sizeof(U32));
if (!newarray)
{
/* Set overflow in last CRW */
if (sysblk.crwarray)
*(sysblk.crwarray + sysblk.crwcount - 1) |= CRW_OFLOW;
RELEASE_CRWLOCK();
return -1;
}
/* Copy existing queue to new queue */
if (sysblk.crwarray)
{
memcpy( newarray, sysblk.crwarray, sysblk.crwcount * sizeof(U32));
free( sysblk.crwarray );
}
/* Start using new queue */
sysblk.crwarray = newarray;
sysblk.crwalloc = newalloc;
}
/* Add the new CRWs to the queue */
memcpy( sysblk.crwarray + sysblk.crwcount, crwarray, crwcount * sizeof(U32));
sysblk.crwcount += crwcount;
RELEASE_CRWLOCK();
/* Signal waiting CPUs that a Channel Report is pending */
machine_check_crwpend();
return 0;
} /* end function queue_channel_report */
/*-------------------------------------------------------------------*/
/* Indicate CRW pending */
/*-------------------------------------------------------------------*/
void machine_check_crwpend()
{
/* Signal waiting CPUs that a Channel Report is pending */
OBTAIN_INTLOCK(NULL);
ON_IC_CHANRPT;
WAKEUP_CPUS_MASK (sysblk.waiting_mask);
RELEASE_INTLOCK(NULL);
} /* end function machine_check_crwpend */
/*-------------------------------------------------------------------*/
/* Return next Channel Report Word (CRW) */
/*-------------------------------------------------------------------*/
U32 get_next_channel_report_word( REGS* regs )
{
U32 crw = 0;
UNREFERENCED(regs);
OBTAIN_CRWLOCK();
ASSERT( sysblk.crwindex <= sysblk.crwcount );
ASSERT( sysblk.crwcount <= sysblk.crwalloc );
if (sysblk.crwcount)
{
if (sysblk.crwindex < sysblk.crwcount)
{
VERIFY((crw = *(sysblk.crwarray + sysblk.crwindex)) != 0);
sysblk.crwindex++; // (for next time)
}
else // (sysblk.crwindex >= sysblk.crwcount)
{
sysblk.crwindex = 0;
sysblk.crwcount = 0;
}
}
RELEASE_CRWLOCK();
return crw;
} /* end function get_next_channel_report_word */
#endif /*!defined(_MACHCHK_C)*/
/*-------------------------------------------------------------------*/
/* Present Machine Check Interrupt */
/* Input: */
/* regs Pointer to the CPU register context */
/* Output: */
/* mcic Machine check interrupt code */
/* xdmg External damage code */
/* fsta Failing storage address */
/* Return code: */
/* 0=No machine check, 1=Machine check presented */
/* */
/* Generic machine check function. At the momement the only */
/* supported machine check is the channel report. */
/* */
/* This routine must be called with the sysblk.intlock held */
/*-------------------------------------------------------------------*/
int ARCH_DEP(present_mck_interrupt)(REGS *regs, U64 *mcic, U32 *xdmg, RADR *fsta)
{
int rc = 0;
UNREFERENCED_370(regs);
UNREFERENCED_370(mcic);
UNREFERENCED_370(xdmg);
UNREFERENCED_370(fsta);
#ifdef FEATURE_CHANNEL_SUBSYSTEM
/* If there is a crw pending and we are enabled for the channel
report interrupt subclass then process the interrupt */
if( OPEN_IC_CHANRPT(regs) )
{
*mcic = MCIC_CP |
MCIC_WP |
MCIC_MS |
MCIC_PM |
MCIC_IA |
#ifdef FEATURE_HEXADECIMAL_FLOATING_POINT
MCIC_FP |
#endif /*FEATURE_HEXADECIMAL_FLOATING_POINT*/
MCIC_GR |
MCIC_CR |
MCIC_ST |
#ifdef FEATURE_ACCESS_REGISTERS
MCIC_AR |
#endif /*FEATURE_ACCESS_REGISTERS*/
#if defined(FEATURE_ESAME) && defined(FEATURE_EXTENDED_TOD_CLOCK)
MCIC_PR |
#endif /*defined(FEATURE_ESAME) && defined(FEATURE_EXTENDED_TOD_CLOCK)*/
#if defined(FEATURE_BINARY_FLOATING_POINT)
MCIC_XF |
#endif /*defined(FEATURE_BINARY_FLOATING_POINT)*/
MCIC_AP |
MCIC_CT |
MCIC_CC ;
*xdmg = 0;
*fsta = 0;
OFF_IC_CHANRPT;
rc = 1;
}
if(!IS_IC_CHANRPT)
#endif /*FEATURE_CHANNEL_SUBSYSTEM*/
OFF_IC_CHANRPT;
return rc;
} /* end function present_mck_interrupt */
void ARCH_DEP(sync_mck_interrupt) (REGS *regs)
{
int rc; /* Return code */
PSA *psa; /* -> Prefixed storage area */
U64 mcic = MCIC_P | /* Instruction processing damage */
MCIC_WP |
MCIC_MS |
MCIC_PM |
MCIC_IA |
#ifdef FEATURE_HEXADECIMAL_FLOATING_POINT
MCIC_FP |
#endif /*FEATURE_HEXADECIMAL_FLOATING_POINT*/
MCIC_GR |
MCIC_CR |
MCIC_ST |
#ifdef FEATURE_ACCESS_REGISTERS
MCIC_AR |
#endif /*FEATURE_ACCESS_REGISTERS*/
#if defined(FEATURE_ESAME) && defined(FEATURE_EXTENDED_TOD_CLOCK)
MCIC_PR |
#endif /*defined(FEATURE_ESAME) && defined(FEATURE_EXTENDED_TOD_CLOCK)*/
#if defined(FEATURE_BINARY_FLOATING_POINT)
MCIC_XF |
#endif /*defined(FEATURE_BINARY_FLOATING_POINT)*/
MCIC_CT |
MCIC_CC ;
U32 xdmg = 0;
RADR fsta = 0;
/* Release intlock if held */
if (regs->cpuad == sysblk.intowner)
RELEASE_INTLOCK(regs);
/* Release mainlock if held */
if (regs->cpuad == sysblk.mainowner)
RELEASE_MAINLOCK(regs);
/* Exit SIE when active */
#if defined(FEATURE_INTERPRETIVE_EXECUTION)
if(regs->sie_active)
ARCH_DEP(sie_exit) (regs, SIE_HOST_INTERRUPT);
#endif /*defined(FEATURE_INTERPRETIVE_EXECUTION)*/
/* Set the main storage reference and change bits */
STORAGE_KEY(regs->PX, regs) |= (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_ESAME)
// ZZ
/* 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 defined(_FEATURE_SIE)
WRMSG (HHC00824, "I", regs->sie_active ? "IE" : PTYPSTR(regs->cpuad),
regs->sie_active ? regs->guestregs->cpuad : regs->cpuad, mcic);
#else
WRMSG (HHC00824, "I", PTYPSTR(regs->cpuad), regs->cpuad, mcic);
#endif
/* Store the external damage code at PSA+244 */
STORE_FW(psa->xdmgcode, xdmg);
#if defined(FEATURE_ESAME)
/* Store the failing storage address at PSA+248 */
STORE_DW(psa->mcstorad, fsta);
#else /*!defined(FEATURE_ESAME)*/
/* Store the failing storage address at PSA+248 */
STORE_FW(psa->mcstorad, fsta);
#endif /*!defined(FEATURE_ESAME)*/
/* 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 );
if ( rc )
ARCH_DEP(program_interrupt) (regs, rc);
} /* end function sync_mck_interrupt */
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "machchk.c"
#endif
#if defined(_ARCHMODE3)
#undef _GEN_ARCH
#define _GEN_ARCH _ARCHMODE3
#include "machchk.c"
#endif
#if !defined(NO_SIGABEND_HANDLER)
void sigabend_handler (int signo)
{
REGS *regs = NULL;
TID tid;
int i;
tid = thread_id();
if( signo == SIGUSR2 )
{
DEVBLK *dev;
if (0
|| equal_threads( tid, sysblk.cnsltid )
|| equal_threads( tid, sysblk.socktid )
#if defined(OPTION_SHARED_DEVICES)
|| equal_threads( tid, sysblk.shrdtid )
#endif // defined(OPTION_SHARED_DEVICES)
|| equal_threads( tid, sysblk.httptid )
)
return;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if (0
|| equal_threads( dev->tid, tid )
#if defined(OPTION_SHARED_DEVICES)
|| equal_threads( dev->shrdtid, tid )
#endif // defined(OPTION_SHARED_DEVICES)
)
break;
if( dev == NULL)
{
if (!sysblk.shutdown)
WRMSG(HHC00825, "E");
}
else
if(dev->ccwtrace)
WRMSG(HHC00826, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return;
}
for (i = 0; i < sysblk.maxcpu; i++)
{
if ( equal_threads( sysblk.cputid[i], tid ) )
{
regs = sysblk.regs[i];
break;
}
}
if (regs == NULL)
{
signal(signo, SIG_DFL);
raise(signo);
return;
}
if(MACHMASK(&regs->psw))
{
#if defined(_FEATURE_SIE)
WRMSG(HHC00822, "I", regs->sie_active ? "IE" : PTYPSTR(regs->cpuad),
regs->sie_active ? regs->guestregs->cpuad : regs->cpuad,
strsignal(signo) );
#else /*!defined(_FEATURE_SIE)*/
WRMSG(HHC00822, "I", PTYPSTR(regs->cpuad), regs->cpuad, strsignal(signo));
#endif /*!defined(_FEATURE_SIE)*/
display_inst(
#if defined(_FEATURE_SIE)
regs->sie_active ? regs->guestregs :
#endif /*defined(_FEATURE_SIE)*/
regs,
#if defined(_FEATURE_SIE)
regs->sie_active ? regs->guestregs->ip :
#endif /*defined(_FEATURE_SIE)*/
regs->ip);
switch(regs->arch_mode) {
#if defined(_370)
case ARCH_370:
s370_sync_mck_interrupt(regs);
break;
#endif
#if defined(_390)
case ARCH_390:
s390_sync_mck_interrupt(regs);
break;
#endif
#if defined(_900)
case ARCH_900:
z900_sync_mck_interrupt(regs);
break;
#endif
}
}
else
{
#if defined(_FEATURE_SIE)
WRMSG(HHC00823, "I", regs->sie_active ? "IE" : PTYPSTR(regs->cpuad),
regs->sie_active ? regs->guestregs->cpuad : regs->cpuad,
strsignal(signo));
#else /*!defined(_FEATURE_SIE)*/
WRMSG(HHC00823, "I", PTYPSTR(regs->cpuad), regs->cpuad, strsignal(signo));
#endif /*!defined(_FEATURE_SIE)*/
display_inst(
#if defined(_FEATURE_SIE)
regs->sie_active ? regs->guestregs :
#endif /*defined(_FEATURE_SIE)*/
regs,
#if defined(_FEATURE_SIE)
regs->sie_active ? regs->guestregs->ip :
#endif /*defined(_FEATURE_SIE)*/
regs->ip);
regs->cpustate = CPUSTATE_STOPPING;
regs->checkstop = 1;
ON_IC_INTERRUPT(regs);
/* Notify other CPU's by means of a malfuction alert if possible */
if (!try_obtain_lock(&sysblk.sigplock))
{
if(!try_obtain_lock(&sysblk.intlock))
{
for (i = 0; i < sysblk.maxcpu; i++)
if (i != regs->cpuad && IS_CPU_ONLINE(i))
{
ON_IC_MALFALT(sysblk.regs[i]);
sysblk.regs[i]->malfcpu[regs->cpuad] = 1;
}
release_lock(&sysblk.intlock);
}
release_lock(&sysblk.sigplock);
}
}
longjmp (regs->progjmp, SIE_INTERCEPT_MCK);
}
#endif /*!defined(NO_SIGABEND_HANDLER)*/
#endif /*!defined(_GEN_ARCH)*/