Files
org-hyperion-cules/hscmisc.c
2015-01-03 05:07:47 -08:00

2820 lines
91 KiB
C

/* HSCMISC.C (c) Copyright Roger Bowler, 1999-2012 */
/* (c) Copyright Jan Jaeger, 1999-2012 */
/* Miscellaneous System Command Routines */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
#include "hstdinc.h"
#if !defined(_HENGINE_DLL_)
#define _HENGINE_DLL_
#endif
#if !defined(_HSCMISC_C_)
#define _HSCMISC_C_
#endif
#include "hercules.h"
#include "devtype.h"
#include "opcode.h"
#include "inline.h"
#include "hconsole.h"
#include "esa390io.h"
#include "hexdumpe.h"
#define DISPLAY_INSTRUCTION_OPERANDS
#if !defined(_HSCMISC_C)
#define _HSCMISC_C
/*-------------------------------------------------------------------*/
/* System Shutdown Processing */
/*-------------------------------------------------------------------*/
/* The following 'sigq' functions are responsible for ensuring all of
the CPUs are stopped ("quiesced") before continuing with Hercules
shutdown processing, and should NEVER be called directly. Instead,
they are called by the main 'do_shutdown' (or 'do_shutdown_wait')
function(s) (defined further below) as needed and/or appropriate.
*/
static int wait_sigq_pending = 0;
static int is_wait_sigq_pending()
{
int pending;
OBTAIN_INTLOCK(NULL);
pending = wait_sigq_pending;
RELEASE_INTLOCK(NULL);
return pending;
}
static void wait_sigq_resp()
{
int pending, i;
/* Wait for all CPU's to stop */
do
{
OBTAIN_INTLOCK(NULL);
wait_sigq_pending = 0;
for (i = 0; i < sysblk.maxcpu; i++)
if (IS_CPU_ONLINE(i)
&& sysblk.regs[i]->cpustate != CPUSTATE_STOPPED)
wait_sigq_pending = 1;
pending = wait_sigq_pending;
RELEASE_INTLOCK(NULL);
if(pending)
SLEEP(1);
}
while(is_wait_sigq_pending());
}
static void cancel_wait_sigq()
{
OBTAIN_INTLOCK(NULL);
wait_sigq_pending = 0;
RELEASE_INTLOCK(NULL);
}
/* do_shutdown_now
This is the main shutdown processing function. It is NEVER called
directly, but is instead ONLY called by either the 'do_shutdown'
or 'do_shutdown_wait' functions after all CPUs have been stopped.
It is responsible for releasing the device configuration and then
calling the Hercules Dynamic Loader "hdl_shut" function to invoke
all registered "Hercules at-exit/termination functions" (similar
to 'atexit' but unique to Hercules) (to perform any other needed
miscellaneous shutdown related processing).
Only after the above three tasks have been completed (stopping the
CPUs, releasing the device configuration, calling registered term-
ination routines/functions) can Hercules then be safely terminated.
Note too that, *technically*, this function *should* wait for *all*
other threads to finish terminating first before either exiting or
returning back to the caller, but we don't currently enforce that
(since that's *really* what hdl_adsc + hdl_shut is designed for!).
At the moment, as long as the three previously mentioned three most
important shutdown tasks have been completed (stop cpus, release
device config, call term funcs), then we consider the brunt of our
shutdown processing to be completed and thus exit (or return back
to the caller to let them exit instead). If there happen to be any
threads still running when that happens, they will be automatically
terminated by the operating sytem as normal when a process exits.
SO... If there are any threads that must be terminated completely
and cleanly before Hercules can safely terminate, then you better
add code to this function to ENSURE that your thread is terminated
properly! (and/or add a call to 'hdl_adsc' at the appropriate place
in the startup sequence). For this purpose, the use of "join_thread"
is *strongly* encouraged as it *ensures* that your thread will not
continue until the thread in question has completely exited first.
*/
static void do_shutdown_now()
{
WRMSG(HHC01420, "I");
ASSERT( !sysblk.shutfini ); // (sanity check)
sysblk.shutfini = FALSE; // (shutdown NOT finished yet)
sysblk.shutdown = TRUE; // (system shutdown initiated)
/* Wakeup I/O subsystem to start I/O subsystem shutdown */
{
int n;
for (n = 0; sysblk.devtnbr && n < 100; ++n)
{
signal_condition(&sysblk.ioqcond);
usleep(10000);
}
}
//#if defined(_MSVC_)
WRMSG(HHC01423, "I");
hdl_shut();
WRMSG(HHC01424, "I");
//#endif
/*
logmsg("Terminating threads\n");
{
// (none we really care about at the moment...)
}
logmsg("Threads terminations complete\n");
*/
WRMSG(HHC01425, "I");
sysblk.shutfini = TRUE; // (shutdown is now complete)
// PROGRAMMING NOTE
// If we're NOT in "daemon_mode" (i.e. panel_display in control),
// -OR- if a daemon_task DOES exist, then THEY are in control of
// shutdown; THEY are responsible for exiting the system whenever
// THEY feel it's proper to do so (by simply returning back to the
// caller thereby allowing 'main' to return back to the operating
// system).
// OTHEWRWISE we ARE in "daemon_mode", but a daemon_task does NOT
// exist, which means the main thread (tail end of 'impl.c') is
// stuck in a loop reading log messages and writing them to the
// logfile, so we need to do the exiting here since it obviously
// cannot.
if ( sysblk.daemon_mode
#if defined(OPTION_DYNAMIC_LOAD)
&& !daemon_task
#endif /*defined(OPTION_DYNAMIC_LOAD)*/
)
{
#ifdef _MSVC_
socket_deinit();
#endif
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( sysblk.emsg & EMSG_TEXT )
fprintf(stdout, HHC01412);
else
#endif
fprintf(stdout, MSG(HHC01412, "I"));
fflush(stdout);
exit(0);
}
}
/* do_shutdown_wait
This function simply waits for the CPUs to stop and then calls
the above do_shutdown_now function to perform the actual shutdown
(which releases the device configuration, etc)
*/
static void* do_shutdown_wait(void* arg)
{
UNREFERENCED( arg );
WRMSG(HHC01426, "I");
wait_sigq_resp();
do_shutdown_now();
return NULL;
}
/* ***** do_shutdown *****
This is the main system shutdown function, and the ONLY function
that should EVER be called to shut the system down. It calls one
or more of the above static helper functions as needed.
*/
void do_shutdown()
{
TID tid;
if ( sysblk.shutimmed )
do_shutdown_now();
else
{
if(is_wait_sigq_pending())
cancel_wait_sigq();
else
if(can_signal_quiesce() && !signal_quiesce(0,0))
create_thread(&tid, DETACHED, do_shutdown_wait,
NULL, "do_shutdown_wait");
else
do_shutdown_now();
}
}
/*-------------------------------------------------------------------*/
/* The following 2 routines display an array of 32/64 registers */
/* 1st parameter is the register type (GR, CR, AR, etc..) */
/* 2nd parameter is the CPU Address involved */
/* 3rd parameter is an array of 32/64 bit regs */
/* NOTE : 32 bit regs are displayed 4 by 4, while 64 bit regs are */
/* displayed 2 by 2. Change the modulo if to change this */
/* behaviour. */
/* These routines are intended to be invoked by display_regs, */
/* display_cregs and display_aregs */
/* Ivan Warren 2005/11/07 */
/*-------------------------------------------------------------------*/
static int display_regs32(char *hdr,U16 cpuad,U32 *r,int numcpus,char *buf,int buflen,char *msghdr)
{
int i;
int len=0;
for(i=0;i<16;i++)
{
if(!(i%4))
{
if(i)
{
len+=snprintf(buf+len, buflen-len-1, "%s", "\n");
}
len+=snprintf(buf+len, buflen-len-1, "%s", msghdr);
if(numcpus>1)
{
len+=snprintf(buf+len,buflen-len-1,"%s%02X: ", PTYPSTR(cpuad), cpuad);
}
}
if(i%4)
{
len+=snprintf(buf+len,buflen-len-1,"%s", " ");
}
len+=snprintf(buf+len,buflen-len-1,"%s%2.2d=%8.8"I32_FMT"X",hdr,i,r[i]);
}
len+=snprintf(buf+len,buflen-len-1,"%s","\n");
return(len);
}
#if defined(_900)
static int display_regs64(char *hdr,U16 cpuad,U64 *r,int numcpus,char *buf,int buflen,char *msghdr)
{
int i;
int rpl;
int len=0;
if(numcpus>1 && !(sysblk.insttrace || sysblk.inststep) )
{
rpl=2;
}
else
{
rpl=4;
}
for(i=0;i<16;i++)
{
if(!(i%rpl))
{
if(i)
{
len+=snprintf(buf+len,buflen-len-1,"%s", "\n");
}
len+=snprintf(buf+len,buflen-len-1, "%s", msghdr);
if(numcpus>1)
{
len+=snprintf(buf+len,buflen-len-1,"%s%02X: ", PTYPSTR(cpuad), cpuad);
}
}
if(i%rpl)
{
len+=snprintf(buf+len,buflen-len-1,"%s"," ");
}
len+=snprintf(buf+len,buflen-len-1,"%s%1.1X=%16.16"I64_FMT"X",hdr,i,r[i]);
}
len+=snprintf(buf+len,buflen-len-1,"%s","\n");
return(len);
}
#endif
/*-------------------------------------------------------------------*/
/* Display registers for the instruction display */
/*-------------------------------------------------------------------*/
int display_inst_regs (REGS *regs, BYTE *inst, BYTE opcode, char *buf, int buflen, char *hdr)
{
int len=0;
/* Display the general purpose registers */
if (!(opcode == 0xB3 || (opcode >= 0x20 && opcode <= 0x3F))
|| (opcode == 0xB3 && (
(inst[1] >= 0x80 && inst[1] <= 0xCF)
|| (inst[1] >= 0xE1 && inst[1] <= 0xFE)
)))
{
len += display_regs (regs, buf + len, buflen - len - 1, hdr);
if (sysblk.showregsfirst)
len += snprintf(buf + len, buflen - len - 1, "\n");
}
/* Display control registers if appropriate */
if (!REAL_MODE(&regs->psw) || opcode == 0xB2)
{
len += display_cregs (regs, buf + len, buflen - len - 1, hdr);
if (sysblk.showregsfirst)
len += snprintf(buf + len, buflen - len - 1, "\n");
}
/* Display access registers if appropriate */
if (!REAL_MODE(&regs->psw) && ACCESS_REGISTER_MODE(&regs->psw))
{
len += display_aregs (regs, buf + len, buflen - len - 1, hdr);
if (sysblk.showregsfirst)
len += snprintf(buf + len, buflen - len - 1, "\n");
}
/* Display floating-point registers if appropriate */
if (opcode == 0xB3 || opcode == 0xED
|| (opcode >= 0x20 && opcode <= 0x3F)
|| (opcode >= 0x60 && opcode <= 0x70)
|| (opcode >= 0x78 && opcode <= 0x7F)
|| (opcode == 0xB2 && inst[1] == 0x2D) /*DXR*/
|| (opcode == 0xB2 && inst[1] == 0x44) /*SQDR*/
|| (opcode == 0xB2 && inst[1] == 0x45) /*SQER*/
)
{
len += display_fregs (regs, buf + len, buflen - len - 1, hdr);
if (sysblk.showregsfirst)
len += snprintf(buf + len, buflen - len - 1, "\n");
}
return(len);
}
/*-------------------------------------------------------------------*/
/* Display general purpose registers */
/*-------------------------------------------------------------------*/
int display_regs (REGS *regs, char *buf, int buflen, char *hdr)
{
int i;
U32 gprs[16];
#if defined(_900)
U64 ggprs[16];
#endif
#if defined(_900)
if(regs->arch_mode != ARCH_900)
{
#endif
for(i=0;i<16;i++)
{
gprs[i]=regs->GR_L(i);
}
return(display_regs32("GR",regs->cpuad,gprs,sysblk.cpus,buf,buflen,hdr));
#if defined(_900)
}
else
{
for(i=0;i<16;i++)
{
ggprs[i]=regs->GR_G(i);
}
return(display_regs64("R",regs->cpuad,ggprs,sysblk.cpus,buf,buflen,hdr));
}
#endif
} /* end function display_regs */
/*-------------------------------------------------------------------*/
/* Display control registers */
/*-------------------------------------------------------------------*/
int display_cregs (REGS *regs, char *buf, int buflen, char *hdr)
{
int i;
U32 crs[16];
#if defined(_900)
U64 gcrs[16];
#endif
#if defined(_900)
if(regs->arch_mode != ARCH_900)
{
#endif
for(i=0;i<16;i++)
{
crs[i]=regs->CR_L(i);
}
return(display_regs32("CR",regs->cpuad,crs,sysblk.cpus,buf,buflen,hdr));
#if defined(_900)
}
else
{
for(i=0;i<16;i++)
{
gcrs[i]=regs->CR_G(i);
}
return(display_regs64("C",regs->cpuad,gcrs,sysblk.cpus,buf,buflen,hdr));
}
#endif
} /* end function display_cregs */
/*-------------------------------------------------------------------*/
/* Display access registers */
/*-------------------------------------------------------------------*/
int display_aregs (REGS *regs, char *buf, int buflen, char *hdr)
{
int i;
U32 ars[16];
for(i=0;i<16;i++)
{
ars[i]=regs->AR(i);
}
return(display_regs32("AR",regs->cpuad,ars,sysblk.cpus,buf,buflen,hdr));
} /* end function display_aregs */
/*-------------------------------------------------------------------*/
/* Display floating point registers */
/*-------------------------------------------------------------------*/
int display_fregs (REGS *regs, char *buf, int buflen, char *hdr)
{
char cpustr[32] = "";
if(sysblk.cpus>1)
MSGBUF(cpustr, "%s%s%02X: ", hdr, PTYPSTR(regs->cpuad), regs->cpuad);
else
MSGBUF(cpustr, "%s", hdr);
if(regs->CR(0) & CR0_AFP)
return(snprintf(buf,buflen-1,
"%sFPR0=%8.8X%8.8X FPR2=%8.8X%8.8X\n"
"%sFPR1=%8.8X%8.8X FPR3=%8.8X%8.8X\n"
"%sFPR4=%8.8X%8.8X FPR6=%8.8X%8.8X\n"
"%sFPR5=%8.8X%8.8X FPR7=%8.8X%8.8X\n"
"%sFPR8=%8.8X%8.8X FP10=%8.8X%8.8X\n"
"%sFPR9=%8.8X%8.8X FP11=%8.8X%8.8X\n"
"%sFP12=%8.8X%8.8X FP14=%8.8X%8.8X\n"
"%sFP13=%8.8X%8.8X FP15=%8.8X%8.8X\n"
,cpustr, regs->fpr[0], regs->fpr[1], regs->fpr[4], regs->fpr[5]
,cpustr, regs->fpr[2], regs->fpr[3], regs->fpr[6], regs->fpr[7]
,cpustr, regs->fpr[8], regs->fpr[9], regs->fpr[12], regs->fpr[13]
,cpustr, regs->fpr[10], regs->fpr[11], regs->fpr[14], regs->fpr[15]
,cpustr, regs->fpr[16], regs->fpr[17], regs->fpr[20], regs->fpr[21]
,cpustr, regs->fpr[18], regs->fpr[19], regs->fpr[22], regs->fpr[23]
,cpustr, regs->fpr[24], regs->fpr[25], regs->fpr[28], regs->fpr[29]
,cpustr, regs->fpr[26], regs->fpr[27], regs->fpr[30], regs->fpr[31]
));
else
return(snprintf(buf,buflen-1,
"%sFPR0=%8.8X%8.8X FPR2=%8.8X%8.8X\n"
"%sFPR4=%8.8X%8.8X FPR6=%8.8X%8.8X\n"
,cpustr, regs->fpr[0], regs->fpr[1], regs->fpr[2], regs->fpr[3]
,cpustr, regs->fpr[4], regs->fpr[5], regs->fpr[6], regs->fpr[7]
));
} /* end function display_fregs */
/*-------------------------------------------------------------------*/
/* Display subchannel */
/*-------------------------------------------------------------------*/
int display_subchannel (DEVBLK *dev, char *buf, int buflen, char *hdr)
{
static const char* status_type[3] = {"Device Status ",
"Unit Status ",
"Subchannel Status"};
struct BITS { U8 b7:1; U8 b6:1; U8 b5:1; U8 b4:1; U8 b3:1; U8 b2:1; U8 b1:1; U8 b0:1; };
union ByteToBits { struct BITS b; U8 status; } u;
int len = 0;
len+=snprintf(buf+len,buflen-len-1,
"%s%1d:%04X D/T%04X\n",
hdr, SSID_TO_LCSS(dev->ssid), dev->devnum, dev->devtype);
if (ARCH_370 == sysblk.arch_mode)
{
len+=snprintf(buf+len,buflen-len-1,
"%s CSW Flags:%2.2X CCW:%2.2X%2.2X%2.2X Flags\n"
"%s US:%2.2X CS:%2.2X Count:%2.2X%2.2X (Key) Subchannel key %1.1X\n"
"%s (S) Suspend control %1.1X\n"
"%s (L) Extended format %1.1X\n"
"%s Subchannel Internal Management (CC) Deferred condition code %1.1X\n",
hdr, dev->scsw.flag0,
dev->scsw.ccwaddr[1], dev->scsw.ccwaddr[2], dev->scsw.ccwaddr[3],
hdr, dev->scsw.unitstat, dev->scsw.chanstat,
dev->scsw.count[0], dev->scsw.count[1],
(dev->scsw.flag0 & SCSW0_KEY) >> 4,
hdr, (dev->scsw.flag0 & SCSW0_S) >> 3,
hdr, (dev->scsw.flag0 & SCSW0_L) >> 2,
hdr, (dev->scsw.flag0 & SCSW0_CC));
}
len+=snprintf(buf+len,buflen-len-1,
"%s Subchannel Number[%04X]\n"
"%s Path Management Control Word (PMCW)\n"
"%s IntParm:%2.2X%2.2X%2.2X%2.2X\n"
"%s Flags:%2.2X%2.2X Dev:%2.2X%2.2X\n"
"%s LPM:%2.2X PNOM:%2.2X LPUM:%2.2X PIM:%2.2X\n"
"%s MBI:%2.2X%2.2X POM:%2.2X PAM:%2.2X\n"
"%s CHPID 0:%2.2X 1:%2.2X 2:%2.2X 3:%2.2X\n"
"%s 4:%2.2X 5:%2.2X 6:%2.2X 7:%2.2X\n"
"%s Misc:%2.2X%2.2X%2.2X%2.2X\n",
hdr, dev->subchan,
hdr,
hdr, dev->pmcw.intparm[0], dev->pmcw.intparm[1],
dev->pmcw.intparm[2], dev->pmcw.intparm[3],
hdr, dev->pmcw.flag4, dev->pmcw.flag5,
dev->pmcw.devnum[0], dev->pmcw.devnum[1],
hdr, dev->pmcw.lpm, dev->pmcw.pnom, dev->pmcw.lpum, dev->pmcw.pim,
hdr, dev->pmcw.mbi[0], dev->pmcw.mbi[1],
dev->pmcw.pom, dev->pmcw.pam,
hdr, dev->pmcw.chpid[0], dev->pmcw.chpid[1],
dev->pmcw.chpid[2], dev->pmcw.chpid[3],
hdr, dev->pmcw.chpid[4], dev->pmcw.chpid[5],
dev->pmcw.chpid[6], dev->pmcw.chpid[7],
hdr,dev->pmcw.zone, dev->pmcw.flag25,
dev->pmcw.flag26, dev->pmcw.flag27);
len+=snprintf(buf+len,buflen-len-1,
"%s Subchannel Status Word (SCSW)\n"
"%s Flags: %2.2X%2.2X Subchan Ctl: %2.2X%2.2X (FC) Function Control\n"
"%s CCW: %2.2X%2.2X%2.2X%2.2X Start %1.1X\n"
"%s DS: %2.2X SS: %2.2X Count: %2.2X%2.2X Halt %1.1X\n"
"%s Clear %1.1X\n"
"%s Flags (AC) Activity Control\n"
"%s (Key) Subchannel key %1.1X Resume pending %1.1X\n"
"%s (S) Suspend control %1.1X Start pending %1.1X\n"
"%s (L) Extended format %1.1X Halt pending %1.1X\n"
"%s (CC) Deferred condition code %1.1X Clear pending %1.1X\n"
"%s (F) CCW-format control %1.1X Subchannel active %1.1X\n"
"%s (P) Prefetch control %1.1X Device active %1.1X\n"
"%s (I) Initial-status control %1.1X Suspended %1.1X\n"
"%s (A) Address-limit control %1.1X (SC) Status Control\n"
"%s (U) Suppress-suspend int. %1.1X Alert %1.1X\n"
"%s Subchannel Control Intermediate %1.1X\n"
"%s (Z) Zero condition code %1.1X Primary %1.1X\n"
"%s (E) Extended control (ECW) %1.1X Secondary %1.1X\n"
"%s (N) Path not operational %1.1X Status pending %1.1X\n"
"%s (Q) QDIO active %1.1X\n",
hdr,
hdr, dev->scsw.flag0, dev->scsw.flag1, dev->scsw.flag2, dev->scsw.flag3,
hdr, dev->scsw.ccwaddr[0], dev->scsw.ccwaddr[1],
dev->scsw.ccwaddr[2], dev->scsw.ccwaddr[3],
(dev->scsw.flag2 & SCSW2_FC_START) >> 6,
hdr, dev->scsw.unitstat, dev->scsw.chanstat,
dev->scsw.count[0], dev->scsw.count[1],
(dev->scsw.flag2 & SCSW2_FC_HALT) >> 5,
hdr, (dev->scsw.flag2 & SCSW2_FC_CLEAR) >> 4,
hdr,
hdr, (dev->scsw.flag0 & SCSW0_KEY) >> 4,
(dev->scsw.flag2 & SCSW2_AC_RESUM) >> 3,
hdr, (dev->scsw.flag0 & SCSW0_S) >> 3,
(dev->scsw.flag2 & SCSW2_AC_START) >> 2,
hdr, (dev->scsw.flag0 & SCSW0_L) >> 2,
(dev->scsw.flag2 & SCSW2_AC_HALT) >> 1,
hdr, (dev->scsw.flag0 & SCSW0_CC),
(dev->scsw.flag2 & SCSW2_AC_CLEAR),
hdr, (dev->scsw.flag1 & SCSW1_F) >> 7,
(dev->scsw.flag3 & SCSW3_AC_SCHAC) >> 7,
hdr, (dev->scsw.flag1 & SCSW1_P) >> 6,
(dev->scsw.flag3 & SCSW3_AC_DEVAC) >> 6,
hdr, (dev->scsw.flag1 & SCSW1_I) >> 5,
(dev->scsw.flag3 & SCSW3_AC_SUSP) >> 5,
hdr, (dev->scsw.flag1 & SCSW1_A) >> 4,
hdr, (dev->scsw.flag1 & SCSW1_U) >> 3,
(dev->scsw.flag3 & SCSW3_SC_ALERT) >> 4,
hdr, (dev->scsw.flag3 & SCSW3_SC_INTER) >> 3,
hdr, (dev->scsw.flag1 & SCSW1_Z) >> 2,
(dev->scsw.flag3 & SCSW3_SC_PRI) >> 2,
hdr, (dev->scsw.flag1 & SCSW1_E) >> 1,
(dev->scsw.flag3 & SCSW3_SC_SEC) >> 1,
hdr, (dev->scsw.flag1 & SCSW1_N),
(dev->scsw.flag3 & SCSW3_SC_PEND),
hdr, (dev->scsw.flag2 & SCSW2_Q) >> 7);
u.status = (U8)dev->scsw.unitstat;
len+=snprintf(buf+len,buflen-len-1,
"%s %s %s%s%s%s%s%s%s%s%s\n",
hdr, status_type[(sysblk.arch_mode == ARCH_370)],
u.status == 0 ? "is Normal" : "",
u.b.b0 ? "Attention " : "",
u.b.b1 ? "SM " : "",
u.b.b2 ? "CUE " : "",
u.b.b3 ? "Busy " : "",
u.b.b4 ? "CE " : "",
u.b.b5 ? "DE " : "",
u.b.b6 ? "UC " : "",
u.b.b7 ? "UE " : "");
u.status = (U8)dev->scsw.chanstat;
len+=snprintf(buf+len,buflen-len-1,
"%s %s %s%s%s%s%s%s%s%s%s\n",
hdr, status_type[2],
u.status == 0 ? "is Normal" : "",
u.b.b0 ? "PCI " : "",
u.b.b1 ? "IL " : "",
u.b.b2 ? "PC " : "",
u.b.b3 ? "ProtC " : "",
u.b.b4 ? "CDC " : "",
u.b.b5 ? "CCC " : "",
u.b.b6 ? "ICC " : "",
u.b.b7 ? "CC " : "");
// PROGRAMMING NOTE: the following ugliness is needed
// because 'snprintf' is a macro on Windows builds and
// you obviously can't use the preprocessor to select
// the arguments to be passed to a preprocessor macro.
#if defined( OPTION_SHARED_DEVICES )
#define BUSYSHAREABLELINE_PATTERN "%s busy %1.1X shareable %1.1X\n"
#define BUSYSHAREABLELINE_VALUE hdr, dev->busy, dev->shareable,
#else // !defined( OPTION_SHARED_DEVICES )
#define BUSYSHAREABLELINE_PATTERN "%s busy %1.1X\n"
#define BUSYSHAREABLELINE_VALUE hdr, dev->busy,
#endif // defined( OPTION_SHARED_DEVICES )
len+=snprintf(buf+len,buflen-len-1,
"%s DEVBLK Status\n"
BUSYSHAREABLELINE_PATTERN
"%s suspended %1.1X console %1.1X rlen3270 %5d\n"
"%s pending %1.1X connected %1.1X\n"
"%s pcipending %1.1X readpending %1.1X\n"
"%s attnpending %1.1X connecting %1.1X\n"
"%s startpending %1.1X localhost %1.1X\n"
"%s resumesuspended %1.1X reserved %1.1X\n"
"%s tschpending %1.1X locked %1.1X\n",
hdr,
BUSYSHAREABLELINE_VALUE
hdr, dev->suspended, dev->console, dev->rlen3270,
hdr, dev->pending, dev->connected,
hdr, dev->pcipending, dev->readpending,
hdr, dev->attnpending, dev->connecting,
hdr, dev->startpending, dev->localhost,
hdr, dev->resumesuspended, dev->reserved,
hdr, dev->tschpending, test_lock(&dev->lock) ? 1 : 0);
return(len);
} /* end function display_subchannel */
/*-------------------------------------------------------------------*/
/* Parse a storage range or storage alteration operand */
/* */
/* Valid formats for a storage range operand are: */
/* startaddr */
/* startaddr-endaddr */
/* startaddr.length */
/* where startaddr, endaddr, and length are hexadecimal values. */
/* */
/* Valid format for a storage alteration operand is: */
/* startaddr=hexstring (up to 32 pairs of digits) */
/* */
/* Return values: */
/* 0 = operand contains valid storage range display syntax; */
/* start/end of range is returned in saddr and eaddr */
/* >0 = operand contains valid storage alteration syntax; */
/* return value is number of bytes to be altered; */
/* start/end/value are returned in saddr, eaddr, newval */
/* -1 = error message issued */
/*-------------------------------------------------------------------*/
static int parse_range (char *operand, U64 maxadr, U64 *sadrp,
U64 *eadrp, BYTE *newval)
{
U64 opnd1, opnd2; /* Address/length operands */
U64 saddr, eaddr; /* Range start/end addresses */
int rc; /* Return code */
int n; /* Number of bytes altered */
int h1, h2; /* Hexadecimal digits */
char *s; /* Alteration value pointer */
BYTE delim; /* Operand delimiter */
BYTE c; /* Character work area */
rc = sscanf(operand, "%"I64_FMT"x%c%"I64_FMT"x%c",
&opnd1, &delim, &opnd2, &c);
/* Process storage alteration operand */
if (rc > 2 && delim == '=' && newval)
{
s = strchr (operand, '=');
for (n = 0; n < 32;)
{
h1 = *(++s);
if (h1 == '\0' || h1 == '#' ) break;
if (h1 == SPACE || h1 == '\t') continue;
h1 = toupper(h1);
h2 = *(++s);
h2 = toupper(h2);
h1 = (h1 >= '0' && h1 <= '9') ? h1 - '0' :
(h1 >= 'A' && h1 <= 'F') ? h1 - 'A' + 10 : -1;
h2 = (h2 >= '0' && h2 <= '9') ? h2 - '0' :
(h2 >= 'A' && h2 <= 'F') ? h2 - 'A' + 10 : -1;
if (h1 < 0 || h2 < 0 || n >= 32)
{
WRMSG(HHC02205, "E", s, "");
return -1;
}
newval[n++] = (h1 << 4) | h2;
} /* end for(n) */
saddr = opnd1;
eaddr = saddr + n - 1;
}
else
{
/* Process storage range operand */
saddr = opnd1;
if (rc == 1)
{
/* If only starting address is specified, default to
64 byte display, or less if near end of storage */
eaddr = saddr + 0x3F;
if (eaddr > maxadr) eaddr = maxadr;
}
else
{
/* Ending address or length is specified */
if (rc != 3 || !(delim == '-' || delim == '.'))
{
WRMSG(HHC02205, "E", operand, "");
return -1;
}
eaddr = (delim == '.') ? saddr + opnd2 - 1 : opnd2;
}
/* Set n=0 to indicate storage display only */
n = 0;
}
/* Check for valid range */
if (saddr > maxadr || eaddr > maxadr || eaddr < saddr)
{
WRMSG(HHC02205, "E", operand, ": invalid range");
return -1;
}
/* Return start/end addresses and number of bytes altered */
*sadrp = saddr;
*eadrp = eaddr;
return n;
} /* end function parse_range */
/*-------------------------------------------------------------------*/
/* get_connected_client return IP address and hostname of the */
/* client that is connected to this device */
/*-------------------------------------------------------------------*/
void get_connected_client (DEVBLK* dev, char** pclientip, char** pclientname)
{
*pclientip = NULL;
*pclientname = NULL;
obtain_lock (&dev->lock);
if (dev->bs /* if device is a socket device, */
&& dev->fd != -1) /* and a client is connected to it */
{
*pclientip = strdup(dev->bs->clientip);
*pclientname = strdup(dev->bs->clientname);
}
release_lock (&dev->lock);
}
/*-------------------------------------------------------------------*/
/* Return the address of a regs structure to be used for address */
/* translation. This address should be freed by the caller. */
/*-------------------------------------------------------------------*/
static REGS *copy_regs (REGS *regs)
{
REGS *newregs, *hostregs;
size_t size;
size = (SIE_MODE(regs) || SIE_ACTIVE(regs)) ? 2*sizeof(REGS) : sizeof(REGS);
newregs = malloc_aligned(size, 4096);
if (newregs == NULL)
{
char buf[64];
MSGBUF(buf, "malloc(%d)", (int)size);
WRMSG(HHC00075, "E", buf, strerror(errno));
return NULL;
}
/* Perform partial copy and clear the TLB */
memcpy(newregs, regs, sysblk.regs_copy_len);
memset(&newregs->tlb.vaddr, 0, TLBN * sizeof(DW));
newregs->ghostregs = 1;
newregs->hostregs = newregs;
newregs->guestregs = NULL;
newregs->sie_active=0;
/* Copy host regs if in SIE mode */
/* newregs is a SIE Guest REGS */
if(SIE_MODE(newregs))
{
hostregs = newregs + 1;
memcpy(hostregs, regs->hostregs, sysblk.regs_copy_len);
memset(&hostregs->tlb.vaddr, 0, TLBN * sizeof(DW));
hostregs->ghostregs = 1;
hostregs->hostregs = hostregs;
hostregs->guestregs = newregs;
newregs->hostregs = hostregs;
newregs->guestregs = newregs;
}
return newregs;
}
/*-------------------------------------------------------------------*/
/* Format Channel Report Word (CRW) for display */
/*-------------------------------------------------------------------*/
const char* FormatCRW( U32 crw, char* buf, size_t bufsz )
{
static const char* rsctab[] =
{
"0",
"1",
"MONIT",
"SUBCH",
"CHPID",
"5",
"6",
"7",
"8",
"CAF",
"10",
"CSS",
};
static const BYTE numrsc = _countof( rsctab );
static const char* erctab[] =
{
"NULL",
"AVAIL",
"INIT",
"TEMP",
"ALERT",
"ABORT",
"ERROR",
"RESET",
"MODFY",
"9",
"RSTRD",
};
static const BYTE numerc = _countof( erctab );
if (!buf)
return NULL;
if (bufsz)
*buf = 0;
if (bufsz <= 1)
return buf;
if (crw)
{
U32 flags = (U32) ( crw & CRW_FLAGS_MASK );
BYTE erc = (BYTE) ( ( crw & CRW_ERC_MASK ) >> 16 );
BYTE rsc = (BYTE) ( ( crw & CRW_RSC_MASK ) >> 24 );
U16 rsid = (U16) ( crw & CRW_RSID_MASK );
size_t n;
n = (size_t) snprintf( buf, bufsz - 1,
"RSC:%d=%s, ERC:%d=%s, RSID:%d=0x%4.4X Flags:%s%s%s%s%s%s%s"
, rsc
, rsc < numrsc ? rsctab[ rsc ] : "???"
, erc
, erc < numerc ? erctab[ erc ] : "???"
, rsid
, rsid
, ( flags & CRW_FLAGS_MASK ) ? "" : "0"
, ( flags & 0x80000000 ) ? "0x80000000," : ""
, ( flags & CRW_SOL ) ? "SOL," : ""
, ( flags & CRW_OFLOW ) ? "OFLOW," : ""
, ( flags & CRW_CHAIN ) ? "CHAIN," : ""
, ( flags & CRW_AR ) ? "AR," : ""
, ( flags & 0x00400000 ) ? "0x00400000," : ""
);
if (n < bufsz)
*(buf + n) = 0; // (null terminate)
if (n > 0 &&
*(buf + n - 1) == ',') // (remove trailing comma)
*(buf + n - 1) = 0; // (remove trailing comma)
}
else
strlcpy( buf, "(end)", bufsz ); // (end of channel report)
return buf;
}
/*-------------------------------------------------------------------*/
/* Format Operation-Request Block (ORB) for display */
/*-------------------------------------------------------------------*/
const char* FormatORB( ORB* orb, char* buf, size_t bufsz )
{
size_t n;
if (!buf)
return NULL;
if (bufsz)
*buf = 0;
if (bufsz <= 1 || !orb)
return buf;
n = (size_t) snprintf( buf, bufsz - 1,
"IntP:%2.2X%2.2X%2.2X%2.2X Key:%d LPM:%2.2X "
"Flags:%X%2.2X%2.2X %c%c%c%c%c%c%c%c %c%c%c%c %c%c%c %cCW:%2.2X%2.2X%2.2X%2.2X"
, orb->intparm[0], orb->intparm[1], orb->intparm[2], orb->intparm[3]
, (orb->flag4 & ORB4_KEY) >> 4
, orb->lpm
, (orb->flag4 & ~ORB4_KEY)
, orb->flag5
, orb->flag7
, ( orb->flag4 & ORB4_S ) ? 'S' : '.'
, ( orb->flag4 & ORB4_C ) ? 'C' : '.'
, ( orb->flag4 & ORB4_M ) ? 'M' : '.'
, ( orb->flag4 & ORB4_Y ) ? 'Y' : '.'
, ( orb->flag5 & ORB5_F ) ? 'F' : '.'
, ( orb->flag5 & ORB5_P ) ? 'P' : '.'
, ( orb->flag5 & ORB5_I ) ? 'I' : '.'
, ( orb->flag5 & ORB5_A ) ? 'A' : '.'
, ( orb->flag5 & ORB5_U ) ? 'U' : '.'
, ( orb->flag5 & ORB5_B ) ? 'B' : '.'
, ( orb->flag5 & ORB5_H ) ? 'H' : '.'
, ( orb->flag5 & ORB5_T ) ? 'T' : '.'
, ( orb->flag7 & ORB7_L ) ? 'L' : '.'
, ( orb->flag7 & ORB7_D ) ? 'D' : '.'
, ( orb->flag7 & ORB7_X ) ? 'X' : '.'
, ( orb->flag5 & ORB5_B ) ? 'T' : 'C' // (TCW or CCW)
, orb->ccwaddr[0], orb->ccwaddr[1], orb->ccwaddr[2], orb->ccwaddr[3]
);
if (n < bufsz)
*(buf + n) = 0; // (null terminate)
return buf;
}
/*-------------------------------------------------------------------*/
/* Format ESW's Subchannel Logout information for display */
/*-------------------------------------------------------------------*/
const char* FormatSCL( ESW* esw, char* buf, size_t bufsz )
{
size_t n;
static const char* sa[] =
{
"00",
"RD",
"WR",
"BW",
};
static const char* tc[] =
{
"HA",
"ST",
"CL",
"11",
};
if (!buf)
return NULL;
if (bufsz)
*buf = 0;
if (bufsz <= 1 || !esw)
return buf;
n = (size_t) snprintf( buf, bufsz - 1,
"ESF:%c%c%c%c%c%c%c%c%s FVF:%c%c%c%c%c LPUM:%2.2X SA:%s TC:%s Flgs:%c%c%c SC=%d"
, ( esw->scl0 & 0x80 ) ? '0' : '.'
, ( esw->scl0 & SCL0_ESF_KEY ) ? 'K' : '.'
, ( esw->scl0 & SCL0_ESF_MBPGK ) ? 'G' : '.'
, ( esw->scl0 & SCL0_ESF_MBDCK ) ? 'D' : '.'
, ( esw->scl0 & SCL0_ESF_MBPTK ) ? 'P' : '.'
, ( esw->scl0 & SCL0_ESF_CCWCK ) ? 'C' : '.'
, ( esw->scl0 & SCL0_ESF_IDACK ) ? 'I' : '.'
, ( esw->scl0 & 0x01 ) ? '7' : '.'
, ( esw->scl2 & SCL2_R ) ? " (R)" : ""
, ( esw->scl2 & SCL2_FVF_LPUM ) ? 'L' : '.'
, ( esw->scl2 & SCL2_FVF_TC ) ? 'T' : '.'
, ( esw->scl2 & SCL2_FVF_SC ) ? 'S' : '.'
, ( esw->scl2 & SCL2_FVF_USTAT ) ? 'D' : '.'
, ( esw->scl2 & SCL2_FVF_CCWAD ) ? 'C' : '.'
, esw->lpum
, sa[ esw->scl2 & SCL2_SA ]
, tc[ (esw->scl3 & SCL3_TC) >> 6 ]
, ( esw->scl3 & SCL3_D ) ? 'D' : '.'
, ( esw->scl3 & SCL3_E ) ? 'E' : '.'
, ( esw->scl3 & SCL3_A ) ? 'A' : '.'
, ( esw->scl3 & SCL3_SC )
);
if (n < bufsz)
*(buf + n) = 0; // (null terminate)
return buf;
}
/*-------------------------------------------------------------------*/
/* Format ESW's Extended-Report Word (ERW) for display */
/*-------------------------------------------------------------------*/
const char* FormatERW( ESW* esw, char* buf, size_t bufsz )
{
size_t n;
if (!buf)
return NULL;
if (bufsz)
*buf = 0;
if (bufsz <= 1 || !esw)
return buf;
n = (size_t) snprintf( buf, bufsz - 1,
"Flags:%c%c%c%c%c%c%c%c %c%c SCNT:%d"
, ( esw->erw0 & ERW0_RSV ) ? '0' : '.'
, ( esw->erw0 & ERW0_L ) ? 'L' : '.'
, ( esw->erw0 & ERW0_E ) ? 'E' : '.'
, ( esw->erw0 & ERW0_A ) ? 'A' : '.'
, ( esw->erw0 & ERW0_P ) ? 'P' : '.'
, ( esw->erw0 & ERW0_T ) ? 'T' : '.'
, ( esw->erw0 & ERW0_F ) ? 'F' : '.'
, ( esw->erw0 & ERW0_S ) ? 'S' : '.'
, ( esw->erw1 & ERW1_C ) ? 'C' : '.'
, ( esw->erw1 & ERW1_R ) ? 'R' : '.'
, ( esw->erw1 & ERW1_SCNT )
);
if (n < bufsz)
*(buf + n) = 0; // (null terminate)
return buf;
}
/*-------------------------------------------------------------------*/
/* Format Extended-Status Word (ESW) for display */
/*-------------------------------------------------------------------*/
const char* FormatESW( ESW* esw, char* buf, size_t bufsz )
{
char scl[64]; /* Subchannel Logout */
char erw[64]; /* Extended-Report Word */
size_t n;
if (!buf)
return NULL;
if (bufsz)
*buf = 0;
if (bufsz <= 1 || !esw)
return buf;
n = (size_t) snprintf( buf, bufsz - 1,
"SCL = %s, ERW = %s"
, FormatSCL( esw, scl, _countof( scl ))
, FormatERW( esw, erw, _countof( erw ))
);
if (n < bufsz)
*(buf + n) = 0; // (null terminate)
return buf;
}
/*-------------------------------------------------------------------*/
/* Format SDC (Self Describing Component) information */
/*-------------------------------------------------------------------*/
static BYTE
sdcchar(BYTE c)
{
/* This suberfuge resolved a compiler bug that leads to a slew */
/* of warnings about c possibly being undefined. */
c = guest_to_host( c );
return isgraph(c) ? c : '?';
}
const char* FormatSDC( SDC* sdc, char* buf, size_t bufsz )
{
size_t n;
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !sdc)
return buf;
#define SDCCHAR(fld, n) sdcchar(sdc->fld[n])
n = (size_t) snprintf( buf, bufsz-1,
"SDC: type/model:%c%c%c%c%c%c-%c%c%c mfg:%c%c%c plant:%c%c seq/serial:%c%c%c%c%c%c%c%c%c%c%c%c\n"
, SDCCHAR(type,0),SDCCHAR(type,1),SDCCHAR(type,2),SDCCHAR(type,3),SDCCHAR(type,4),SDCCHAR(type,5)
, SDCCHAR(model,0),SDCCHAR(model,1),SDCCHAR(model,2)
, SDCCHAR(mfr,0),SDCCHAR(mfr,1),SDCCHAR(mfr,2)
, SDCCHAR(plant,0),SDCCHAR(plant,1)
, SDCCHAR(serial,0),SDCCHAR(serial,1),SDCCHAR(serial,2),SDCCHAR(serial,3),SDCCHAR(serial,4),SDCCHAR(serial,5)
, SDCCHAR(serial,6),SDCCHAR(serial,7),SDCCHAR(serial,8),SDCCHAR(serial,9),SDCCHAR(serial,10),SDCCHAR(serial,11)
);
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* NEQ (Node-Element Qualifier) type table */
/*-------------------------------------------------------------------*/
static const char* NED_NEQ_type[] =
{
"UNUSED", "NEQ", "GENEQ", "NED",
};
/*-------------------------------------------------------------------*/
/* Format NED (Node-Element Descriptor) */
/*-------------------------------------------------------------------*/
const char* FormatNED( NED* ned, char* buf, size_t bufsz )
{
size_t n;
const char* typ;
char bad_typ[4];
char sdc_info[256];
static const char* sn_ind[] = { "NEXT", "UNIQUE", "NODE", "CODE3" };
static const char* ned_type[] = { "UNSPEC", "DEVICE", "CTLUNIT" };
static const char* dev_class[] =
{
"UNKNOWN",
"DASD",
"TAPE",
"READER",
"PUNCH",
"PRINTER",
"COMM",
"DISPLAY",
"CONSOLE",
"CTCA",
"SWITCH",
"PROTO",
};
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !ned)
return buf;
if (ned->type < _countof( ned_type ))
typ = ned_type[ ned->type ];
else
{
snprintf( bad_typ, sizeof(bad_typ)-1, "%u", ned->type );
bad_typ[3] = 0;
typ = bad_typ;
}
if (ned->type == NED_TYP_DEVICE)
{
const char* cls;
char bad_class[4];
if (ned->cls < _countof( dev_class ))
cls = dev_class[ ned->cls ];
else
{
snprintf( bad_class, sizeof(bad_class)-1, "%u", ned->cls );
bad_class[3] = 0;
cls = bad_class;
}
n = (size_t) snprintf( buf, bufsz-1,
"NED:%s%styp:%s cls:%s lvl:%s sn:%s tag:%02X%02X\n %s"
, (ned->flags & 0x20) ? "*" : " "
, (ned->flags & 0x01) ? "(EMULATED) " : ""
, typ
, cls
, (ned->lvl & 0x01) ? "UNRELATED" : "RELATED"
, sn_ind[ (ned->flags >> 3) & 0x03 ]
, ned->tag[0], ned->tag[1]
, FormatSDC( &ned->info, sdc_info, sizeof(sdc_info))
);
}
else
{
n = (size_t) snprintf( buf, bufsz-1,
"NED:%s%styp:%s lvl:%s sn:%s tag:%02X%02X\n %s"
, (ned->flags & 0x20) ? "*" : " "
, (ned->flags & 0x01) ? "(EMULATED) " : ""
, typ
, (ned->lvl & 0x01) ? "UNRELATED" : "RELATED"
, sn_ind[ (ned->flags >> 3) & 0x03 ]
, ned->tag[0], ned->tag[1]
, FormatSDC( &ned->info, sdc_info, sizeof(sdc_info))
);
}
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* Format NEQ (Node-Element Qualifier) */
/*-------------------------------------------------------------------*/
const char* FormatNEQ( NEQ* neq, char* buf, size_t bufsz )
{
size_t n;
BYTE* byte = (BYTE*) neq;
U16 iid;
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !neq)
return buf;
iid = fetch_hw( &neq->iid );
n = (size_t) snprintf( buf, bufsz-1,
"NEQ: typ:%s IID:%02X%02X DDTO:%u\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
, NED_NEQ_type[ neq->flags >> 6 ]
, (BYTE)(iid >> 8), (BYTE)(iid & 0xFF)
, neq->ddto
, byte[ 0],byte[ 1],byte[ 2],byte[ 3], byte[ 4],byte[ 5],byte[ 6],byte[ 7]
, byte[ 8],byte[ 9],byte[10],byte[11], byte[12],byte[13],byte[14],byte[15]
, byte[16],byte[17],byte[18],byte[19], byte[20],byte[21],byte[22],byte[23]
, byte[24],byte[25],byte[26],byte[27], byte[28],byte[29],byte[30],byte[31]
);
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* Helper function to format data as just individual BYTES */
/*-------------------------------------------------------------------*/
static void FormatBytes( BYTE* data, int len, char* buf, size_t bufsz )
{
char temp[4];
int i;
for (i=0; i < len; ++i)
{
if (i == 4)
strlcat( buf, " ", bufsz );
MSGBUF( temp, "%02X", data[i] );
strlcat( buf, temp, bufsz );
}
}
/*-------------------------------------------------------------------*/
/* Helper function to remove trailing blanks including CRLF */
/*-------------------------------------------------------------------*/
static void TrimEnd( char* buf )
{
size_t n; char* p;
for (n = strlen(buf), p = buf+n-1; p > buf && isspace((BYTE)*p); --p, --n)
; // (nop)
p[1] = 0;
}
/*-------------------------------------------------------------------*/
/* Format RCD (Read Configuration Data) response */
/*-------------------------------------------------------------------*/
DLL_EXPORT const char* FormatRCD( BYTE* rcd, int len, char* buf, size_t bufsz )
{
size_t n;
char temp[256];
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !rcd || !len)
return buf;
for (; len > 0; rcd += sizeof(NED), len -= sizeof(NED))
{
if (len < (int)sizeof(NED))
{
FormatBytes( rcd, len, buf, bufsz );
break;
}
switch (rcd[0] >> 6)
{
case FIELD_IS_NEQ:
case FIELD_IS_GENEQ:
FormatNEQ( (NEQ*)rcd, temp, sizeof(temp)-1);
break;
case FIELD_IS_NED:
FormatNED( (NED*)rcd, temp, sizeof(temp)-1);
break;
case FIELD_IS_UNUSED:
n = (size_t) snprintf( temp, sizeof(temp)-1, "n/a\n" );
if (n < sizeof(temp))
temp[n] = 0;
break;
}
strlcat( buf, temp, bufsz );
}
TrimEnd( buf );
return buf;
}
/*-------------------------------------------------------------------*/
/* Format ND (Node Descriptor) */
/*-------------------------------------------------------------------*/
const char* FormatND( ND* nd, char* buf, size_t bufsz )
{
size_t n;
const char* val;
const char* cls;
const char* by3;
const char* typ;
char bad_cls[4];
char sdc_info[256];
static const char* css_class[] = { "UNKNOWN", "CHPATH", "CTCA" };
static const char* val_type[] =
{
"VALID", "UNSURE", "INVALID", "3", "4", "5", "6", "7",
};
static const char* dev_class[] =
{
"UNKNOWN",
"DASD",
"TAPE",
"READER",
"PUNCH",
"PRINTER",
"COMM",
"DISPLAY",
"CONSOLE",
"CTCA",
"SWITCH",
"PROTO",
};
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !nd)
return buf;
val = val_type[ nd->flags >> 5 ];
switch (nd->flags >> 5)
{
case ND_VAL_VALID:
case ND_VAL_UNSURE:
cls = NULL;
if (nd->flags & 0x01)
{
typ = "CSS";
by3 = "CHPID";
if (nd->cls < _countof( css_class ))
cls = css_class[ nd->cls ];
}
else
{
typ = "DEV";
by3 = (nd->cls == ND_DEV_PROTO) ? "LINK" : "BYTE3";
if (nd->cls < _countof( dev_class ))
cls = dev_class[ nd->cls ];
}
if (!cls)
{
snprintf( bad_cls, sizeof(bad_cls)-1, "%u", nd->cls );
bad_cls[3] = 0;
cls = bad_cls;
}
n = (size_t) snprintf( buf, bufsz-1,
"ND: val:%s typ:%s cls:%s %s:%02X tag:%02X%02X\n %s"
, val
, typ
, cls
, by3, nd->ua
, nd->tag[0], nd->tag[1]
, FormatSDC( &nd->info, sdc_info, sizeof(sdc_info))
);
break;
case ND_VAL_INVALID:
n = (size_t) snprintf( buf, bufsz-1, "ND: val:INVALID\n" );
break;
default:
n = (size_t) snprintf( buf, bufsz-1, "ND: val:%u (invalid)\n",
(int)(nd->flags >> 5) );
break;
}
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* Format NQ (Node Qualifier) */
/*-------------------------------------------------------------------*/
const char* FormatNQ( NQ* nq, char* buf, size_t bufsz )
{
size_t n;
BYTE* byte = (BYTE*) nq;
static const char* type[] =
{
"IIL", "MODEP", "2", "3", "4", "5", "6", "7",
};
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !nq)
return buf;
n = (size_t) snprintf( buf, bufsz-1,
"NQ: %02X%02X%02X%02X %02X%02X%02X%02X (typ:%s)\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
" %02X%02X%02X%02X %02X%02X%02X%02X\n"
, byte[ 0],byte[ 1],byte[ 2],byte[ 3], byte[ 4],byte[ 5],byte[ 6],byte[ 7]
, type[ nq->flags >> 5 ]
, byte[ 8],byte[ 9],byte[10],byte[11], byte[12],byte[13],byte[14],byte[15]
, byte[16],byte[17],byte[18],byte[19], byte[20],byte[21],byte[22],byte[23]
, byte[24],byte[25],byte[26],byte[27], byte[28],byte[29],byte[30],byte[31]
);
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* Format RNI (Read Node Identifier) response */
/*-------------------------------------------------------------------*/
DLL_EXPORT const char* FormatRNI( BYTE* rni, int len, char* buf, size_t bufsz )
{
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !rni || !len)
return buf;
if (len >= (int)sizeof(ND))
{
char work[256];
register ND* nd = (ND*) rni;
FormatND( nd, work, sizeof(work)-1);
strlcat( buf, work, bufsz );
len -= sizeof(ND);
rni += sizeof(ND);
if (len >= (int)sizeof(NQ))
{
register NQ* nq = (NQ*) rni;
FormatNQ( nq, work, sizeof(work)-1);
strlcat( buf, work, bufsz );
len -= sizeof(NQ);
rni += sizeof(NQ);
if (len)
FormatBytes( rni, len, buf, bufsz );
}
else
FormatBytes( rni, len, buf, bufsz );
}
else
FormatBytes( rni, len, buf, bufsz );
TrimEnd( buf );
return buf;
}
/*-------------------------------------------------------------------*/
/* Format CIW (Command Information Word) */
/*-------------------------------------------------------------------*/
const char* FormatCIW( BYTE* ciw, char* buf, size_t bufsz )
{
size_t n;
static const char* type[] =
{
"RCD", "SII", "RNI", "3 ", "4 ", "5 ", "6 ", "7 ",
"8 ", "9 ", "10 ", "11 ", "12 ", "13 ", "14 ", "15 ",
};
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !ciw)
return buf;
if ((ciw[0] & 0xC0) == 0x40)
{
n = (size_t) snprintf( buf, bufsz-1,
"CIW: %02X%02X%02X%02X typ:%s op:%02X len:%u\n"
, ciw[0], ciw[1], ciw[2], ciw[3]
, type[ ciw[0] & 0x0F ]
, ciw[1]
, fetch_hw( ciw+2 )
);
}
else
{
n = (size_t) snprintf( buf, bufsz-1,
"CIW: %02X%02X%02X%02X not a CIW\n"
, ciw[0]
, ciw[1]
, ciw[2]
, ciw[3]
);
}
if (n < bufsz)
buf[n] = 0;
return buf;
}
/*-------------------------------------------------------------------*/
/* Format SID (Sense ID) response */
/*-------------------------------------------------------------------*/
DLL_EXPORT const char* FormatSID( BYTE* ciw, int len, char* buf, size_t bufsz )
{
size_t n;
char temp[128];
if (!buf)
return NULL;
if (bufsz)
buf[0] = 0;
if (bufsz <= 1 || !ciw || !len)
return buf;
if (len < 8)
FormatBytes( ciw, len, buf, bufsz );
else
{
n = (size_t) snprintf( buf, bufsz-1,
"%02X CU=%02X%02X-%02X DEV=%02X%02X-%02X %02X\n"
, ciw[0]
, ciw[1], ciw[2], ciw[3]
, ciw[4], ciw[5], ciw[6]
, ciw[7]
);
if (n < bufsz)
buf[n] = 0;
ciw += 8;
len -= 8;
for (; len >= 4; ciw += 4, len -= 4)
{
FormatCIW( ciw, temp, sizeof(temp)-1);
strlcat( buf, temp, bufsz );
}
if (len)
FormatBytes( ciw, len, buf, bufsz );
TrimEnd( buf );
}
return buf;
}
#endif /*!defined(_HSCMISC_C)*/
#define RANGE_LIMIT _64_KILOBYTE
#define LIMIT_RANGE( _s, _e, _n ) \
do { \
if ((_e) > (_n) && ((_e) - (_n) + 1) > (_s)) \
(_e) = ((_s) + (_n) - 1); \
} while(0)
/*-------------------------------------------------------------------*/
/* Convert virtual address to absolute address */
/* */
/* Input: */
/* vaddr Virtual address to be translated */
/* arn Access register number */
/* regs CPU register context */
/* acctype Type of access (ACCTYPE_INSTFETCH, ACCTYPE_READ, */
/* or ACCTYPE_LRA) */
/* Output: */
/* raptr Points to word in which abs address is returned */
/* siptr Points to word to receive indication of which */
/* STD or ASCE was used to perform the translation */
/* Return value: */
/* 0=translation successful, non-zero=exception code */
/* Note: */
/* To avoid unwanted alteration of the CPU register context */
/* during translation (for example, the TEA will be updated */
/* if a translation exception occurs), the translation is */
/* performed using a temporary copy of the CPU registers. */
/*-------------------------------------------------------------------*/
static U16 ARCH_DEP(virt_to_abs) (RADR *raptr, int *siptr,
VADR vaddr, int arn, REGS *regs, int acctype)
{
int icode;
if( !(icode = setjmp(regs->progjmp)) )
{
int temp_arn = arn; // bypass longjmp clobber warning
if (acctype == ACCTYPE_INSTFETCH)
temp_arn = USE_INST_SPACE;
if (SIE_MODE(regs))
memcpy(regs->hostregs->progjmp, regs->progjmp,
sizeof(jmp_buf));
ARCH_DEP(logical_to_main) (vaddr, temp_arn, regs, acctype, 0);
}
*siptr = regs->dat.stid;
*raptr = regs->hostregs->dat.raddr;
return icode;
} /* end function virt_to_abs */
/*-------------------------------------------------------------------*/
/* Display real storage (up to 16 bytes, or until end of page) */
/* Prefixes display by Rxxxxx: if draflag is 1 */
/* Returns number of characters placed in display buffer */
/*-------------------------------------------------------------------*/
static int ARCH_DEP(display_real) (REGS *regs, RADR raddr, char *buf, size_t bufl,
int draflag, char *hdr)
{
RADR aaddr; /* Absolute storage address */
int i, j; /* Loop counters */
int n = 0; /* Number of bytes in buffer */
char hbuf[64]; /* Hexadecimal buffer */
BYTE cbuf[17]; /* Character buffer */
BYTE c; /* Character work area */
#if defined(FEATURE_INTERVAL_TIMER)
if(ITIMER_ACCESS(raddr,16))
ARCH_DEP(store_int_timer)(regs);
#endif
n = snprintf(buf, bufl-1, "%s", hdr);
if (draflag)
{
n += snprintf (buf+n, bufl-n-1, "R:"F_RADR":", raddr);
}
aaddr = APPLY_PREFIXING (raddr, regs->PX);
if (regs->mainlim == 0 || aaddr > regs->mainlim)
{
n += snprintf (buf+n, bufl-n-1, "%s", " Real address is not valid");
return n;
}
n += snprintf (buf+n, bufl-n-1, "K:%2.2X=", STORAGE_KEY(aaddr, regs));
memset (hbuf, SPACE, sizeof(hbuf));
memset (cbuf, SPACE, sizeof(cbuf));
for (i = 0, j = 0; i < 16; i++)
{
c = regs->mainstor[aaddr++];
j += snprintf (hbuf+j, sizeof(hbuf)-j, "%2.2X", c);
if ((aaddr & 0x3) == 0x0) hbuf[j++] = SPACE;
c = guest_to_host(c);
if (!isprint(c)) c = '.';
cbuf[i] = c;
if ((aaddr & PAGEFRAME_BYTEMASK) == 0x000) break;
} /* end for(i) */
n += snprintf (buf+n, bufl-n-1, "%36.36s %16.16s", hbuf, cbuf);
return n;
} /* end function display_real */
/*-------------------------------------------------------------------*/
/* Display virtual storage (up to 16 bytes, or until end of page) */
/* Returns number of characters placed in display buffer */
/*-------------------------------------------------------------------*/
static int ARCH_DEP(display_virt) (REGS *regs, VADR vaddr, char *buf, size_t bufl,
int ar, int acctype, char *hdr, U16* xcode)
{
RADR raddr; /* Real address */
int n; /* Number of bytes in buffer */
int stid; /* Segment table indication */
n = snprintf (buf, bufl-1, "%s%c:"F_VADR":", hdr,
ar == USE_REAL_ADDR ? 'R' : 'V', vaddr);
*xcode = ARCH_DEP(virt_to_abs) (&raddr, &stid,
vaddr, ar, regs, acctype);
if (*xcode == 0)
{
n += ARCH_DEP(display_real) (regs, raddr, buf+n, bufl-n, 0, "");
}
else
n += snprintf (buf+n,bufl-n-1," Translation exception %4.4hX",*xcode);
return n;
} /* end function display_virt */
/*-------------------------------------------------------------------*/
/* Hexdump real storage page */
/*-------------------------------------------------------------------*/
/* */
/* regs CPU register context */
/* raddr Real address of start of page to be dumped */
/* adr Cosmetic address of start of page */
/* offset Offset from start of page where to begin dumping */
/* amt Number of bytes to dump */
/* real 1 = alter_display_real call, 0 = alter_display_virt */
/* wid Width of addresses in bits (32 or 64) */
/* */
/* Message number HHC02290 used if real == 1, otherwise HHC02291. */
/* raddr must be page aligned. offset must be < pagesize. amt must */
/* be <= pagesize - offset. Results printed directly via logmsg. */
/* Returns 0 on success, -1 = error otherwise. */
/*-------------------------------------------------------------------*/
static int ARCH_DEP( dump_real_page )( REGS *regs, RADR raddr, RADR adr,
size_t offset, size_t amt,
BYTE real, BYTE wid )
{
char* msgnum; /* "HHC02290" or "HHC02291" */
char* dumpdata; /* pointer to data to be dumped */
char* dumpbuf = NULL; /* pointer to hexdump buffer */
char pfx[64]; /* string prefixed to each line */
RADR aaddr; /* absolute addresses (work) */
msgnum = real ? "HHC02290" : "HHC02291";
if (0
|| raddr & PAGEFRAME_BYTEMASK /* not page aligned */
|| adr & PAGEFRAME_BYTEMASK /* not page aligned */
|| offset >= PAGEFRAME_PAGESIZE /* offset >= pagesize */
|| amt > (PAGEFRAME_PAGESIZE - offset)/* more than 1 page */
|| (wid != 32 && wid != 64) /* invalid address width */
)
{
// "Error in function %s: %s"
WRMSG( HHC02219, "E", "dump_real_page()", "invalid parameters" );
return -1;
}
/* Flush interval timer value to storage */
ITIMER_SYNC( raddr + offset, amt, regs );
/* Get absolute address of page */
aaddr = APPLY_PREFIXING( raddr, regs->PX );
if (aaddr > regs->mainlim)
{
MSGBUF( pfx, "R:"F_RADR" Addressing exception", raddr );
if (real)
WRMSG( HHC02290, "I", pfx );
else
WRMSG( HHC02291, "I", pfx );
return -1;
}
/* Format string each dump line should be prefixed with */
MSGBUF( pfx, "%sI %c:", msgnum, real ? 'R' : 'V' );
/* Point to first byte of actual real storage to be dumped */
dumpdata = (char*) regs->mainstor + aaddr + offset;
/* Adjust cosmetic starting address of first line of dump */
adr += offset; /* exact cosmetic start address */
adr &= ~0xF; /* align to 16-byte boundary */
offset &= 0xF; /* offset must be < (bpg * gpl) */
/* Use hexdump to format 16-byte aligned dump of real storage... */
hexdumpew /* afterwards dumpbuf --> dump */
(
pfx, /* string prefixed to each line */
&dumpbuf, /* ptr to hexdump buffer pointer */
/* (if NULL hexdump will malloc) */
dumpdata, /* pointer to data to be dumped */
offset, /* bytes to skip on first line */
amt, /* amount of data to be dumped */
adr, /* cosmetic dump address of data */
wid, /* width of dump address in bits */
4, /* bpg value (bytes per group) */
4 /* gpl value (groups per line) */
);
/* Check for internal hexdumpew error */
if (!dumpbuf)
{
// "Error in function %s: %s"
WRMSG( HHC02219, "E", "dump_real_page()", "hexdumpew failed" );
return -1;
}
/* Display the dump and free the buffer hexdump malloc'ed for us */
logmsg( "%s", dumpbuf );
free( dumpbuf );
return 0;
} /* end function dump_real_page */
/*-------------------------------------------------------------------*/
/* Disassemble real */
/*-------------------------------------------------------------------*/
static void ARCH_DEP(disasm_stor) (REGS *regs, int argc, char *argv[], char *cmdline)
{
char* opnd; /* Range/alteration operand */
U64 saddr, eaddr; /* Range start/end addresses */
U64 maxadr; /* Highest real storage addr */
RADR raddr; /* Real storage address */
RADR aaddr; /* Absolute storage address */
int stid = -1; /* How translation was done */
int len; /* Number of bytes to alter */
int ilc; /* Instruction length counter*/
BYTE inst[6]; /* Storage alteration value */
BYTE opcode; /* Instruction opcode */
U16 xcode; /* Exception code */
char type; /* Address space type */
char buf[512]; /* MSGBUF work buffer */
UNREFERENCED(cmdline);
/* We require only one operand */
if (argc != 1)
{
// "Missing or invalid argument(s)"
WRMSG( HHC17000, "E" );
return;
}
/* Parse optional address-space prefix */
opnd = argv[0];
type = toupper( *opnd );
if (0
|| type == 'R'
|| type == 'V'
|| type == 'P'
|| type == 'H'
)
opnd++;
else
type = REAL_MODE( &regs->psw ) ? 'R' : 'V';
/* Set limit for address range */
#if defined(FEATURE_ESAME)
maxadr = 0xFFFFFFFFFFFFFFFFULL;
#else /*!defined(FEATURE_ESAME)*/
maxadr = 0x7FFFFFFF;
#endif /*!defined(FEATURE_ESAME)*/
/* Parse the range or alteration operand */
len = parse_range (opnd, maxadr, &saddr, &eaddr, NULL);
if (len < 0) return;
if (regs->mainlim == 0)
{
WRMSG(HHC02289, "I", "Real address is not valid");
return;
}
/* Limit the amount to be displayed to a reasonable value */
LIMIT_RANGE( saddr, eaddr, RANGE_LIMIT );
/* Display real storage */
while (saddr <= eaddr)
{
if(type == 'R')
raddr = saddr;
else
{
/* Convert virtual address to real address */
if((xcode = ARCH_DEP(virt_to_abs) (&raddr, &stid, saddr, 0, regs, ACCTYPE_INSTFETCH) ))
{
MSGBUF( buf, "R:"F_RADR" Storage not accessible code = %4.4X", saddr, xcode );
WRMSG( HHC02289, "I", buf );
return;
}
}
/* Convert real address to absolute address */
aaddr = APPLY_PREFIXING (raddr, regs->PX);
if (aaddr > regs->mainlim)
{
MSGBUF( buf, "R:"F_RADR" Addressing exception", raddr );
WRMSG( HHC02289, "I", buf );
return;
}
/* Determine opcode and check for addressing exception */
opcode = regs->mainstor[aaddr];
ilc = ILC(opcode);
if (aaddr + ilc > regs->mainlim)
{
MSGBUF( buf, "R:"F_RADR" Addressing exception", aaddr );
WRMSG( HHC02289, "I", buf );
return;
}
/* Copy instruction to work area and hex print it */
memcpy(inst, regs->mainstor + aaddr, ilc);
len = sprintf(buf, "%c:"F_RADR" %2.2X%2.2X",
stid == TEA_ST_PRIMARY ? 'P' :
stid == TEA_ST_HOME ? 'H' :
stid == TEA_ST_SECNDRY ? 'S' : 'R',
raddr, inst[0], inst[1]);
if(ilc > 2)
{
len += snprintf(buf + len, sizeof(buf)-len-1, "%2.2X%2.2X", inst[2], inst[3]);
if(ilc > 4)
len += snprintf(buf + len, sizeof(buf)-len-1, "%2.2X%2.2X ", inst[4], inst[5]);
else
len += snprintf(buf + len, sizeof(buf)-len-1, " ");
}
else
len += snprintf(buf + len, sizeof(buf)-len-1, " ");
/* Disassemble the instruction and display the results */
DISASM_INSTRUCTION(inst, buf + len);
WRMSG(HHC02289, "I", buf);
/* Go on to the next instruction */
saddr += ilc;
} /* end while (saddr <= eaddr) */
} /* end function disasm_stor */
/*-------------------------------------------------------------------*/
/* Process real storage alter/display command */
/*-------------------------------------------------------------------*/
static void ARCH_DEP(alter_display_real) (REGS *regs, int argc, char *argv[], char *cmdline)
{
char* opnd; /* range/alteration operand */
U64 saddr, eaddr; /* Range start/end addresses */
U64 maxadr; /* Highest real storage addr */
RADR raddr; /* Real storage address */
RADR aaddr; /* Absolute storage address */
size_t totamt; /* Total amount to be dumped */
int len; /* Number of bytes to alter */
int i; /* Loop counter */
BYTE newval[32]; /* Storage alteration value */
char buf[64]; /* MSGBUF work buffer */
UNREFERENCED(argc);
UNREFERENCED(argv);
opnd = cmdline+1;
/* Set limit for address range */
#if defined(FEATURE_ESAME)
maxadr = 0xFFFFFFFFFFFFFFFFULL;
#else /*!defined(FEATURE_ESAME)*/
maxadr = 0x7FFFFFFF;
#endif /*!defined(FEATURE_ESAME)*/
/* Parse the range or alteration operand */
len = parse_range (opnd, maxadr, &saddr, &eaddr, newval);
if (len < 0) return;
raddr = saddr;
if (regs->mainlim == 0)
{
MSGBUF( buf, "R:"F_RADR" Real address is not valid", saddr );
WRMSG( HHC02290, "I", buf );
return;
}
/* Alter real storage */
if (len > 0)
{
for (i = 0; i < len && raddr+i <= regs->mainlim; i++)
{
aaddr = raddr + i;
aaddr = APPLY_PREFIXING (aaddr, regs->PX);
/* Validate real address */
if (aaddr > regs->mainlim)
{
MSGBUF( buf, "R:"F_RADR" Addressing exception", aaddr );
WRMSG( HHC02290, "I", buf );
return;
}
regs->mainstor[aaddr] = newval[i];
STORAGE_KEY(aaddr, regs) |= (STORKEY_REF | STORKEY_CHANGE);
} /* end for(i) */
}
/* Limit the amount to be displayed to a reasonable value */
LIMIT_RANGE( saddr, eaddr, RANGE_LIMIT );
/* Display real storage */
if ((totamt = (eaddr - saddr) + 1) > 0)
{
RADR pageadr = saddr & PAGEFRAME_PAGEMASK;
size_t pageoff = saddr - pageadr;
size_t pageamt = PAGEFRAME_PAGESIZE - pageoff;
BYTE addrwid = (ARCH_900 == sysblk.arch_mode) ? 64: 32;
if (pageamt > totamt)
pageamt = totamt;
/* Dump requested real storage one whole page at a time */
for (;;)
{
/* Check for addressing exception */
if (APPLY_PREFIXING( pageadr, regs->PX ) > regs->mainlim)
{
MSGBUF( buf, "R:"F_RADR" Addressing exception", pageadr );
WRMSG( HHC02290, "I", buf );
break;
}
/* Display address and storage key */
MSGBUF( buf, "R:"F_RADR" K:%2.2X", pageadr, STORAGE_KEY( pageadr, regs ));
WRMSG( HHC02290, "I", buf );
/* Now hexdump that entire page */
VERIFY( ARCH_DEP( dump_real_page )( regs, pageadr, pageadr, pageoff, pageamt, 1, addrwid ) == 0);
/* Check if we're done */
if (!(totamt -= pageamt))
break;
/* Go on to the next page */
pageoff = 0; // (from now on)
pageamt = PAGEFRAME_PAGESIZE;
pageadr += PAGEFRAME_PAGESIZE;
if (pageamt > totamt)
pageamt = totamt;
}
}
} /* end function alter_display_real */
/*-------------------------------------------------------------------*/
/* Process virtual storage alter/display command */
/*-------------------------------------------------------------------*/
static void ARCH_DEP(alter_display_virt) (REGS *regs, int argc, char *argv[], char *cmdline)
{
char* opnd; /* range/alteration operand */
U64 saddr, eaddr; /* Range start/end addresses */
U64 maxadr; /* Highest virt storage addr */
VADR vaddr; /* Virtual storage address */
RADR raddr; /* Real storage address */
RADR aaddr; /* Absolute storage address */
int stid; /* Segment table indication */
int len; /* Number of bytes to alter */
int i; /* Loop counter */
int arn = 0; /* Access register number */
U16 xcode; /* Exception code */
BYTE newval[32]; /* Storage alteration value */
char buf[512]; /* Message buffer */
char type; /* optional addr-space type */
size_t totamt; /* Total amount to be dumped */
UNREFERENCED(argc);
UNREFERENCED(argv);
/* Parse optional address-space prefix */
opnd = cmdline+1;
type = toupper( *opnd );
if (1
&& type != 'P'
&& type != 'S'
&& type != 'H'
)
arn = 0;
else
{
switch (type)
{
case 'P': arn = USE_PRIMARY_SPACE; break;
case 'S': arn = USE_SECONDARY_SPACE; break;
case 'H': arn = USE_HOME_SPACE; break;
}
opnd++;
}
/* Set limit for address range */
#if defined(FEATURE_ESAME)
maxadr = 0xFFFFFFFFFFFFFFFFULL;
#else /*!defined(FEATURE_ESAME)*/
maxadr = 0x7FFFFFFF;
#endif /*!defined(FEATURE_ESAME)*/
/* Parse the range or alteration operand */
len = parse_range (opnd, maxadr, &saddr, &eaddr, newval);
if (len < 0) return;
if (regs->mainlim == 0)
{
WRMSG(HHC02291, "I", "Real address is not valid");
return;
}
vaddr = saddr;
/* Alter virtual storage */
if (len > 0
&& ARCH_DEP(virt_to_abs) (&raddr, &stid, vaddr, arn,
regs, ACCTYPE_LRA) == 0
&& ARCH_DEP(virt_to_abs) (&raddr, &stid, eaddr, arn,
regs, ACCTYPE_LRA) == 0)
{
for (i = 0; i < len && raddr+i <= regs->mainlim; i++)
{
ARCH_DEP(virt_to_abs) (&raddr, &stid, vaddr+i, arn,
regs, ACCTYPE_LRA);
aaddr = APPLY_PREFIXING (raddr, regs->PX);
/* Validate real address */
if (aaddr > regs->mainlim)
{
WRMSG(HHC02291, "I", "Addressing exception");
return;
}
regs->mainstor[aaddr] = newval[i];
STORAGE_KEY(aaddr, regs) |= (STORKEY_REF | STORKEY_CHANGE);
} /* end for(i) */
}
/* Limit the amount to be displayed to a reasonable value */
LIMIT_RANGE( saddr, eaddr, RANGE_LIMIT );
/* Display virtual storage */
if ((totamt = (eaddr - saddr) + 1) > 0)
{
RADR pageadr = saddr & PAGEFRAME_PAGEMASK;
size_t pageoff = saddr - pageadr;
size_t pageamt = PAGEFRAME_PAGESIZE - pageoff;
BYTE addrwid = (ARCH_900 == sysblk.arch_mode) ? 64: 32;
BYTE real = (USE_REAL_ADDR == arn);
char trans[16]; // (address translation mode)
if (pageamt > totamt)
pageamt = totamt;
/* Dump requested virtual storage one whole page at a time */
for (;;)
{
/* Convert virtual address to real address */
xcode = ARCH_DEP( virt_to_abs )( &raddr, &stid, pageadr, arn, regs, ACCTYPE_LRA );
/* Check for addressing exception */
if (APPLY_PREFIXING( raddr, regs->PX ) > regs->mainlim)
{
MSGBUF( buf, "R:"F_RADR" Addressing exception", raddr );
WRMSG( HHC02291, "I", buf );
break;
}
/* Show how virtual address was translated */
if (REAL_MODE( &regs->psw )) MSGBUF( trans, "%s", "(dat off)" );
else if (stid == TEA_ST_PRIMARY) MSGBUF( trans, "%s", "(primary)" );
else if (stid == TEA_ST_SECNDRY) MSGBUF( trans, "%s", "(secondary)" );
else if (stid == TEA_ST_HOME) MSGBUF( trans, "%s", "(home)" );
else MSGBUF( trans, "(AR%2.2d)", arn );
if (xcode == 0)
MSGBUF( buf, "R:"F_RADR" K:%2.2X %s", raddr, STORAGE_KEY( raddr, regs ), trans );
else
MSGBUF( buf, "V:"F_RADR" Translation exception %4.4hX %s", pageadr, xcode, trans );
WRMSG( HHC02291, "I", buf );
/* Now hexdump that entire page */
if (xcode == 0)
VERIFY( ARCH_DEP( dump_real_page )( regs, raddr, pageadr, pageoff, pageamt, real, addrwid ) == 0);
/* Check if we're done */
if (!(totamt -= pageamt))
break;
/* Go on to the next page */
pageoff = 0; // (from now on)
pageamt = PAGEFRAME_PAGESIZE;
pageadr += PAGEFRAME_PAGESIZE;
if (pageamt > totamt)
pageamt = totamt;
}
}
} /* end function alter_display_virt */
/*-------------------------------------------------------------------*/
/* Display instruction */
/*-------------------------------------------------------------------*/
void ARCH_DEP(display_inst) (REGS *iregs, BYTE *inst)
{
QWORD qword; /* Doubleword work area */
BYTE opcode; /* Instruction operation code*/
int ilc; /* Instruction length */
#ifdef DISPLAY_INSTRUCTION_OPERANDS
int b1=-1, b2=-1, x1; /* Register numbers */
U16 xcode = 0; /* Exception code */
VADR addr1 = 0, addr2 = 0; /* Operand addresses */
#endif /*DISPLAY_INSTRUCTION_OPERANDS*/
char buf[2048]; /* Message buffer */
char buf2[512];
int n; /* Number of bytes in buffer */
REGS *regs; /* Copied regs */
/* Ensure storage exists to attempt the display */
if (iregs->mainlim == 0)
{
WRMSG(HHC02267, "I", "Real address is not valid");
return;
}
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n = snprintf(buf, sizeof(buf)-1, "HHC02267I ");
else
#endif
{
n = 0;
buf[0] = '\0';
}
if (iregs->ghostregs)
regs = iregs;
else if ((regs = copy_regs(iregs)) == NULL)
return;
#if defined(_FEATURE_SIE)
if(SIE_MODE (regs))
n += snprintf (buf + n, sizeof(buf)-n-1, "SIE: ");
#endif /*defined(_FEATURE_SIE)*/
#if 0
#if _GEN_ARCH == 370
n += snprintf (buf + n, sizeof(buf)-n-1, "S/370 ");
#elif _GEN_ARCH == 390
n += snprintf (buf + n, sizeof(buf)-n-1, "ESA/390 ");
#else
n += snprintf (buf + n, sizeof(buf)-n-1, "z/Arch ");
#endif
#endif
/* Display the PSW */
memset (qword, 0, sizeof(qword));
copy_psw (regs, qword);
if ( sysblk.cpus > 1 )
n += snprintf (buf + n, sizeof(buf)-n-1, "%s%02X: ", PTYPSTR(regs->cpuad), regs->cpuad);
n += snprintf (buf + n, sizeof(buf)-n-1,
"PSW=%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]);
#if defined(FEATURE_ESAME)
n += snprintf (buf + n, sizeof(buf)-n-1,
"%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X ",
qword[8], qword[9], qword[10], qword[11],
qword[12], qword[13], qword[14], qword[15]);
#endif /*defined(FEATURE_ESAME)*/
/* Exit if instruction is not valid */
if (inst == NULL)
{
n += snprintf(buf + n, sizeof(buf)-n-1, "Instruction fetch error\n");
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n += display_regs (regs, buf + n, sizeof(buf)-n-1, "HHC02267I ");
else
#endif
n += display_regs (regs, buf + n, sizeof(buf)-n-1, "");
if (!iregs->ghostregs)
free_aligned( regs );
writemsg(__FILE__, __LINE__, __FUNCTION__, 0, MLVL(ANY), "", "%s", buf);
return;
}
/* Extract the opcode and determine the instruction length */
opcode = inst[0];
ilc = ILC(opcode);
/* Show registers associated with the instruction */
if (sysblk.showregsfirst)
{
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n += display_inst_regs (regs, inst, opcode, buf + n, sizeof(buf)-n, "HHC02267I ");
else
#endif
n += display_inst_regs (regs, inst, opcode, buf + n, sizeof(buf)-n, "");
}
/* Display the instruction */
n += snprintf (buf + n, sizeof(buf)-n-1, "INST=%2.2X%2.2X", inst[0], inst[1]);
if (ilc > 2) n += snprintf (buf + n, sizeof(buf)-n-1, "%2.2X%2.2X", inst[2], inst[3]);
if (ilc > 4) n += snprintf (buf + n, sizeof(buf)-n-1, "%2.2X%2.2X", inst[4], inst[5]);
n += snprintf (buf + n, sizeof(buf)-n-1, " %s", (ilc<4) ? " " : (ilc<6) ? " " : "");
n += DISASM_INSTRUCTION(inst, buf + n);
n += snprintf (buf + n, sizeof(buf)-n-1, "\n");
#ifdef DISPLAY_INSTRUCTION_OPERANDS
/* Process the first storage operand */
if (ilc > 2
&& opcode != 0x84 && opcode != 0x85
&& opcode != 0xA5 && opcode != 0xA7
&& opcode != 0xB3
&& opcode != 0xC0 && opcode != 0xC4 && opcode != 0xC6
&& opcode != 0xEC)
{
/* Calculate the effective address of the first operand */
b1 = inst[2] >> 4;
addr1 = ((inst[2] & 0x0F) << 8) | inst[3];
if (b1 != 0)
{
addr1 += regs->GR(b1);
addr1 &= ADDRESS_MAXWRAP(regs);
}
/* Apply indexing for RX/RXE/RXF instructions */
if ((opcode >= 0x40 && opcode <= 0x7F) || opcode == 0xB1
|| opcode == 0xE3 || opcode == 0xED)
{
x1 = inst[1] & 0x0F;
if (x1 != 0)
{
addr1 += regs->GR(x1);
addr1 &= ADDRESS_MAXWRAP(regs);
}
}
}
/* Process the second storage operand */
if (ilc > 4
&& opcode != 0xC0 && opcode != 0xC4 && opcode != 0xC6
&& opcode != 0xE3 && opcode != 0xEB
&& opcode != 0xEC && opcode != 0xED)
{
/* Calculate the effective address of the second operand */
b2 = inst[4] >> 4;
addr2 = ((inst[4] & 0x0F) << 8) | inst[5];
if (b2 != 0)
{
addr2 += regs->GR(b2);
addr2 &= ADDRESS_MAXWRAP(regs);
}
}
/* Calculate the operand addresses for MVCL(E) and CLCL(E) */
if (opcode == 0x0E || opcode == 0x0F
|| opcode == 0xA8 || opcode == 0xA9)
{
b1 = inst[1] >> 4;
addr1 = regs->GR(b1) & ADDRESS_MAXWRAP(regs);
b2 = inst[1] & 0x0F;
addr2 = regs->GR(b2) & ADDRESS_MAXWRAP(regs);
}
/* Calculate the operand addresses for RRE instructions */
if ((opcode == 0xB2 &&
((inst[1] >= 0x20 && inst[1] <= 0x2F)
|| (inst[1] >= 0x40 && inst[1] <= 0x6F)
|| (inst[1] >= 0xA0 && inst[1] <= 0xAF)))
|| opcode == 0xB9)
{
b1 = inst[3] >> 4;
addr1 = regs->GR(b1) & ADDRESS_MAXWRAP(regs);
b2 = inst[3] & 0x0F;
if (inst[1] >= 0x29 && inst[1] <= 0x2C)
addr2 = regs->GR(b2) & ADDRESS_MAXWRAP_E(regs);
else
if (inst[1] >= 0x29 && inst[1] <= 0x2C)
addr2 = regs->GR(b2) & ADDRESS_MAXWRAP(regs);
else
addr2 = regs->GR(b2) & ADDRESS_MAXWRAP(regs);
}
/* Calculate the operand address for RIL_A instructions */
if ((opcode == 0xC0 &&
((inst[1] & 0x0F) == 0x00
|| (inst[1] & 0x0F) == 0x04
|| (inst[1] & 0x0F) == 0x05))
|| opcode == 0xC4
|| opcode == 0xC6)
{
S64 offset = 2LL*(S32)(fetch_fw(inst+2));
addr1 = (likely(!regs->execflag)) ?
PSW_IA(regs, offset) : \
(regs->ET + offset) & ADDRESS_MAXWRAP(regs);
b1 = 0;
}
/* Display storage at first storage operand location */
if (b1 >= 0)
{
if(REAL_MODE(&regs->psw))
ARCH_DEP(display_virt) (regs, addr1, buf2, sizeof(buf2), USE_REAL_ADDR,
ACCTYPE_READ, "", &xcode);
else
ARCH_DEP(display_virt) (regs, addr1, buf2, sizeof(buf2), b1,
(opcode == 0x44
#if defined(FEATURE_EXECUTE_EXTENSIONS_FACILITY)
|| (opcode == 0xc6 && !(inst[1] & 0x0f))
#endif /*defined(FEATURE_EXECUTE_EXTENSIONS_FACILITY)*/
? ACCTYPE_INSTFETCH :
opcode == 0xB1 ? ACCTYPE_LRA :
ACCTYPE_READ),"", &xcode);
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n += snprintf(buf+n, sizeof(buf)-n-1, "HHC02267I ");
#endif
if ( sysblk.cpus > 1 )
{
n += snprintf(buf + n, sizeof(buf)-n-1, "%s%02X: ",
PTYPSTR(regs->cpuad), regs->cpuad );
}
n += snprintf(buf+n, sizeof(buf)-n-1, "%s\n", buf2);
}
/* Display storage at second storage operand location */
if (b2 >= 0)
{
if(
(REAL_MODE(&regs->psw)
|| (opcode == 0xB2 && inst[1] == 0x4B) /*LURA*/
|| (opcode == 0xB2 && inst[1] == 0x46) /*STURA*/
|| (opcode == 0xB9 && inst[1] == 0x05) /*LURAG*/
|| (opcode == 0xB9 && inst[1] == 0x25))) /*STURG*/
ARCH_DEP(display_virt) (regs, addr2, buf2, sizeof(buf2), USE_REAL_ADDR,
ACCTYPE_READ, "", &xcode);
else
ARCH_DEP(display_virt) (regs, addr2, buf2, sizeof(buf2), b2,
ACCTYPE_READ, "", &xcode);
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n += snprintf(buf+n, sizeof(buf)-n-1, "HHC02267I ");
#endif
if ( sysblk.cpus > 1 )
{
n += snprintf(buf + n, sizeof(buf)-n-1, "%s%02X: ",
PTYPSTR(regs->cpuad), regs->cpuad );
}
n += snprintf(buf + n, sizeof(buf)-n-1, "%s\n", buf2);
}
#endif /*DISPLAY_INSTRUCTION_OPERANDS*/
/* Show registers associated with the instruction */
if (!sysblk.showregsfirst && !sysblk.showregsnone)
{
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( !(sysblk.emsg & EMSG_TEXT) )
n += display_inst_regs (regs, inst, opcode, buf + n, sizeof(buf)-n, "HHC02267I ");
else
#endif
n += display_inst_regs (regs, inst, opcode, buf + n, sizeof(buf)-n, "");
}
if (!iregs->ghostregs)
free_aligned( regs );
writemsg(__FILE__, __LINE__, __FUNCTION__, 0, MLVL(ANY), "", "%s", buf);
} /* end function display_inst */
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "hscmisc.c"
#endif
#if defined(_ARCHMODE3)
#undef _GEN_ARCH
#define _GEN_ARCH _ARCHMODE3
#include "hscmisc.c"
#endif
/*-------------------------------------------------------------------*/
/* Wrappers for architecture-dependent functions */
/*-------------------------------------------------------------------*/
void alter_display_real (REGS *regs, int argc, char *argv[], char *cmdline)
{
switch(sysblk.arch_mode) {
#if defined(_370)
case ARCH_370:
s370_alter_display_real (regs, argc, argv, cmdline); break;
#endif
#if defined(_390)
case ARCH_390:
s390_alter_display_real (regs, argc, argv, cmdline); break;
#endif
#if defined(_900)
case ARCH_900:
z900_alter_display_real (regs, argc, argv, cmdline); break;
#endif
}
} /* end function alter_display_real */
void alter_display_virt (REGS *iregs, int argc, char *argv[], char *cmdline)
{
REGS *regs;
if (iregs->ghostregs)
regs = iregs;
else if ((regs = copy_regs(iregs)) == NULL)
return;
switch(sysblk.arch_mode) {
#if defined(_370)
case ARCH_370:
s370_alter_display_virt (regs, argc, argv, cmdline); break;
#endif
#if defined(_390)
case ARCH_390:
s390_alter_display_virt (regs, argc, argv, cmdline); break;
#endif
#if defined(_900)
case ARCH_900:
z900_alter_display_virt (regs, argc, argv, cmdline); break;
#endif
}
if (!iregs->ghostregs)
free_aligned( regs );
} /* end function alter_display_virt */
void display_inst(REGS *iregs, BYTE *inst)
{
REGS *regs;
if (iregs->ghostregs)
regs = iregs;
else if ((regs = copy_regs(iregs)) == NULL)
return;
switch(regs->arch_mode) {
#if defined(_370)
case ARCH_370:
s370_display_inst(regs,inst);
break;
#endif
#if defined(_390)
case ARCH_390:
s390_display_inst(regs,inst);
break;
#endif
#if defined(_900)
case ARCH_900:
z900_display_inst(regs,inst);
break;
#endif
}
if (!iregs->ghostregs)
free_aligned( regs );
}
void disasm_stor(REGS *iregs, int argc, char *argv[], char *cmdline)
{
REGS *regs;
if (iregs->ghostregs)
regs = iregs;
else if ((regs = copy_regs(iregs)) == NULL)
return;
switch(regs->arch_mode) {
#if defined(_370)
case ARCH_370:
s370_disasm_stor(regs, argc, argv, cmdline);
break;
#endif
#if defined(_390)
case ARCH_390:
s390_disasm_stor(regs, argc, argv, cmdline);
break;
#endif
#if defined(_900)
case ARCH_900:
z900_disasm_stor(regs, argc, argv, cmdline);
break;
#endif
}
if (!iregs->ghostregs)
free_aligned( regs );
}
/*-------------------------------------------------------------------*/
/* Execute a Unix or Windows command */
/* Returns the system command status code */
/* look at popen for this in the future */
/*-------------------------------------------------------------------*/
int herc_system (char* command)
{
#if HOW_TO_IMPLEMENT_SH_COMMAND == USE_ANSI_SYSTEM_API_FOR_SH_COMMAND
return system(command);
#elif HOW_TO_IMPLEMENT_SH_COMMAND == USE_W32_POOR_MANS_FORK
#define SHELL_CMD_SHIM_PGM "conspawn "
int rc = (int)(strlen(SHELL_CMD_SHIM_PGM) + strlen(command) + 1);
char* pszNewCommandLine = malloc( rc );
strlcpy( pszNewCommandLine, SHELL_CMD_SHIM_PGM, rc );
strlcat( pszNewCommandLine, command, rc );
rc = w32_poor_mans_fork( pszNewCommandLine, NULL );
free( pszNewCommandLine );
return rc;
#elif HOW_TO_IMPLEMENT_SH_COMMAND == USE_FORK_API_FOR_SH_COMMAND
extern char **environ;
int pid, status;
if (command == 0)
return 1;
pid = fork();
if (pid == -1)
return -1;
if (pid == 0)
{
char *argv[4];
/* Redirect stderr (screen) to hercules log task */
dup2(STDOUT_FILENO, STDERR_FILENO);
/* Drop ROOT authority (saved uid) */
SETMODE(TERM);
DROP_ALL_CAPS();
argv[0] = "sh";
argv[1] = "-c";
argv[2] = command;
argv[3] = 0;
execve("/bin/sh", argv, environ);
_exit(127);
}
do
{
if (waitpid(pid, &status, 0) == -1)
{
if (errno != EINTR)
return -1;
} else
return status;
} while(1);
#else
#error 'HOW_TO_IMPLEMENT_SH_COMMAND' not #defined correctly
#endif
} /* end function herc_system */
#endif /*!defined(_GEN_ARCH)*/