mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 10:38:47 +02:00
5126 lines
158 KiB
C
5126 lines
158 KiB
C
/* HSCUTL.C (C) Copyright Ivan Warren & Others, 2003-2012 */
|
|
/* (C) Copyright TurboHercules, SAS 2010-2011 */
|
|
/* (C) and others 2011-2024 */
|
|
/* Hercules Platform Port & Misc Functions */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* HSCUTL.C -- Implementation of functions used in hercules that */
|
|
/* may be missing on some platform ports, or other convenient mis- */
|
|
/* laneous global utility functions. */
|
|
/*-------------------------------------------------------------------*/
|
|
/* (c) 2003-2006 Ivan Warren & Others -- Released under the Q Public */
|
|
/* License -- This file is portion of the HERCULES S/370, S/390 and */
|
|
/* z/Architecture emulator */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _HSCUTL_C_
|
|
#define _HUTIL_DLL_
|
|
|
|
#include "hercules.h"
|
|
|
|
#if defined(NEED_GETOPT_WRAPPER)
|
|
/*
|
|
GETOPT DYNAMIC LINKING KLUDGE...
|
|
|
|
For some odd reason, on some platforms (namely windows & possibly
|
|
Darwin) dynamically linking to the libc imports STATIC versions
|
|
of 'getopt' and 'getopt_long' into the link-edited shared library.
|
|
If any program then link edits against BOTH the shared library AND
|
|
libc, then the linker complains about 'getopt' and/or 'getopt_long'
|
|
being defined multiple times. In an effort to overcome this, I am
|
|
defining a stub version of 'getopt' and 'getop_long' that can be
|
|
called by loadable modules instead.
|
|
|
|
-- Ivan
|
|
|
|
|
|
** ZZ FIXME: the above needs to be re-verified to see whether it is
|
|
** still true or not. I suspect it may no longer be true due to the
|
|
** subtle bug fixed in the "_HC_CHECK_NEED_GETOPT_WRAPPER" m4 macro
|
|
** in the "./autoconf/hercules.m4" member. -- Fish, Feb 2005
|
|
|
|
*/
|
|
|
|
int herc_opterr=0;
|
|
char *herc_optarg=NULL;
|
|
int herc_optopt=0;
|
|
int herc_optind=1;
|
|
#if defined(NEED_GETOPT_OPTRESET)
|
|
int herc_optreset=0;
|
|
#endif
|
|
|
|
DLL_EXPORT int herc_getopt(int ac,char * const av[],const char *opt)
|
|
{
|
|
int rc;
|
|
#if defined(NEED_GETOPT_OPTRESET)
|
|
optreset=herc_optreset;
|
|
#endif
|
|
rc=getopt(ac,av,opt);
|
|
#if defined(NEED_GETOPT_OPTRESET)
|
|
herc_optreset=optreset;
|
|
#endif
|
|
herc_optarg=optarg;
|
|
herc_optind=optind;
|
|
herc_optopt=optind;
|
|
herc_opterr=optind;
|
|
return(rc);
|
|
}
|
|
|
|
#if defined(HAVE_GETOPT_LONG)
|
|
struct option; // (fwd ref)
|
|
int getopt_long (int, char *const *, const char *, const struct option *, int *);
|
|
struct option; // (fwd ref)
|
|
DLL_EXPORT int herc_getopt_long(int ac,
|
|
char * const av[],
|
|
const char *opt,
|
|
const struct option *lo,
|
|
int *li)
|
|
{
|
|
int rc;
|
|
#if defined(NEED_GETOPT_OPTRESET)
|
|
optreset=herc_optreset;
|
|
#endif
|
|
optind=herc_optind;
|
|
rc=getopt_long(ac,av,opt,lo,li);
|
|
#if defined(NEED_GETOPT_OPTRESET)
|
|
herc_optreset=optreset;
|
|
#endif
|
|
herc_optarg=optarg;
|
|
herc_optind=optind;
|
|
herc_optopt=optind;
|
|
herc_opterr=optind;
|
|
return(rc);
|
|
}
|
|
#endif /* HAVE_GETOPT_LONG */
|
|
|
|
#endif /* NEED_GETOPT_WRAPPER */
|
|
|
|
#if !defined(WIN32) && !defined(HAVE_STRERROR_R)
|
|
static LOCK strerror_lock;
|
|
DLL_EXPORT void strerror_r_init(void)
|
|
{
|
|
initialize_lock(&strerror_lock);
|
|
}
|
|
DLL_EXPORT int strerror_r(int err,char *bfr,size_t sz)
|
|
{
|
|
char *wbfr;
|
|
obtain_lock(&strerror_lock);
|
|
wbfr=strerror(err);
|
|
if(wbfr==NULL || (int)wbfr==-1)
|
|
{
|
|
release_lock(&strerror_lock);
|
|
return(-1);
|
|
}
|
|
if(sz<=strlen(wbfr))
|
|
{
|
|
errno=ERANGE;
|
|
release_lock(&strerror_lock);
|
|
return(-1);
|
|
}
|
|
strncpy(bfr,wbfr,sz);
|
|
release_lock(&strerror_lock);
|
|
return(0);
|
|
}
|
|
#endif // !defined(HAVE_STRERROR_R)
|
|
|
|
#if !defined(HAVE_STRLCPY)
|
|
/* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */
|
|
|
|
/* ** NOTE ** returns 'size_t' and NOT 'char*' like strncpy! */
|
|
/*
|
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#if defined(LIBC_SCCS) && !defined(lint)
|
|
static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $";
|
|
#endif /* LIBC_SCCS and not lint */
|
|
|
|
/*
|
|
* Copy src to string dst of size siz. At most siz-1 characters
|
|
* will be copied. Always NUL terminates (unless siz == 0).
|
|
* Returns strlen(src); if retval >= siz, truncation occurred.
|
|
*/
|
|
|
|
/* ** NOTE ** returns 'size_t' and NOT 'char*' like strncpy! */
|
|
|
|
DLL_EXPORT size_t
|
|
strlcpy(char *dst, const char *src, size_t siz)
|
|
{
|
|
register char *d = dst;
|
|
register const char *s = src;
|
|
register size_t n = siz;
|
|
|
|
/* Copy as many bytes as will fit */
|
|
if (n != 0 && --n != 0) {
|
|
do {
|
|
if ((*d++ = *s++) == 0)
|
|
break;
|
|
} while (--n != 0);
|
|
}
|
|
|
|
/* Not enough room in dst, add NUL and traverse rest of src */
|
|
if (n == 0) {
|
|
if (siz != 0)
|
|
*d = '\0'; /* NUL-terminate dst */
|
|
while (*s++)
|
|
;
|
|
}
|
|
|
|
return(s - src - 1); /* count does not include NUL */
|
|
}
|
|
#endif // !defined(HAVE_STRLCPY)
|
|
|
|
#if !defined(HAVE_STRLCAT)
|
|
/* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */
|
|
|
|
/* ** NOTE ** returns 'size_t' and NOT 'char*' like strncpy! */
|
|
/*
|
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#if defined(LIBC_SCCS) && !defined(lint)
|
|
static char *rcsid = "$OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $";
|
|
#endif /* LIBC_SCCS and not lint */
|
|
|
|
/*
|
|
* Appends src to string dst of size siz (unlike strncat, siz is the
|
|
* full size of dst, not space left). At most siz-1 characters
|
|
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
|
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
|
* If retval >= siz, truncation occurred.
|
|
*/
|
|
|
|
/* ** NOTE ** returns 'size_t' and NOT 'char*' like strncat! */
|
|
|
|
DLL_EXPORT size_t
|
|
strlcat(char *dst, const char *src, size_t siz)
|
|
{
|
|
register char *d = dst;
|
|
register const char *s = src;
|
|
register size_t n = siz;
|
|
size_t dlen;
|
|
|
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
|
while (n-- != 0 && *d != '\0')
|
|
d++;
|
|
dlen = d - dst;
|
|
n = siz - dlen;
|
|
|
|
if (n == 0)
|
|
return(dlen + strlen(s));
|
|
while (*s != '\0') {
|
|
if (n != 1) {
|
|
*d++ = *s;
|
|
n--;
|
|
}
|
|
s++;
|
|
}
|
|
*d = '\0';
|
|
|
|
return(dlen + (s - src)); /* count does not include NUL */
|
|
}
|
|
#endif // !defined(HAVE_STRLCAT)
|
|
|
|
#if !defined( HAVE_VASPRINTF )
|
|
|
|
DLL_EXPORT int
|
|
vasprintf(char **strp, const char *fmt, va_list ap)
|
|
{
|
|
va_list vcopy;
|
|
int expectedLen, actualLen;
|
|
size_t bufCount;
|
|
char *buf;
|
|
|
|
va_copy( vcopy, ap );
|
|
#if defined(_MSVC_) /* See w32_vsnprintf documentation */
|
|
expectedLen = _vscprintf( fmt, vcopy );
|
|
#else
|
|
expectedLen = vsnprintf( NULL, 0, fmt, vcopy );
|
|
#endif
|
|
va_end(vcopy);
|
|
if (expectedLen < 0)
|
|
{
|
|
*strp = NULL;
|
|
return -1;
|
|
}
|
|
|
|
bufCount = ((size_t)expectedLen) + 1;
|
|
if ((buf = (char *)malloc( bufCount )) == NULL)
|
|
{
|
|
*strp = NULL;
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
#if defined(_MSVC_)
|
|
actualLen = _vsnprintf_s( buf, bufCount, bufCount - 1, fmt, ap );
|
|
#else
|
|
actualLen = vsprintf( buf, fmt, ap );
|
|
#endif
|
|
if (actualLen != expectedLen)
|
|
{
|
|
free(buf);
|
|
|
|
*strp = NULL;
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
*strp = buf;
|
|
return expectedLen;
|
|
}
|
|
|
|
#endif // !defined(HAVE_VASPRINTF)
|
|
|
|
/* The following structures are defined herein because they are private structures */
|
|
/* that MUST be opaque to the outside world */
|
|
typedef struct _SYMBOL_TOKEN
|
|
{
|
|
char *var;
|
|
char *val;
|
|
} SYMBOL_TOKEN;
|
|
|
|
#define SYMBOL_TABLE_INCREMENT 256
|
|
#define SYMBOL_BUFFER_GROWTH 256
|
|
#define MAX_SYMBOL_SIZE 31
|
|
#define SYMBOL_QUAL_1 '$'
|
|
#define SYMBOL_QUAL_2 '('
|
|
#define SYMBOL_QUAL_3 ')'
|
|
|
|
static SYMBOL_TOKEN **symbols=NULL;
|
|
static int symbol_count=0;
|
|
static int symbol_max=0;
|
|
|
|
/* This function retrieves or allocates a new SYMBOL_TOKEN */
|
|
static SYMBOL_TOKEN *get_symbol_token(const char *sym, int alloc)
|
|
{
|
|
SYMBOL_TOKEN *tok;
|
|
int i;
|
|
|
|
for(i=0;i<symbol_count;i++)
|
|
{
|
|
tok=symbols[i];
|
|
if(tok==NULL)
|
|
{
|
|
continue;
|
|
}
|
|
#if defined( CASELESS_SYMBOLS )
|
|
if(strcasecmp(symbols[i]->var,sym)==0)
|
|
#else
|
|
if(strcmp(symbols[i]->var,sym)==0)
|
|
#endif
|
|
{
|
|
return(symbols[i]);
|
|
}
|
|
}
|
|
if(!alloc)
|
|
{
|
|
return(NULL);
|
|
}
|
|
if(symbol_count>=symbol_max)
|
|
{
|
|
symbol_max+=SYMBOL_TABLE_INCREMENT;
|
|
if(symbols==NULL)
|
|
{
|
|
symbols=malloc(sizeof(SYMBOL_TOKEN *)*symbol_max);
|
|
if(symbols==NULL)
|
|
{
|
|
symbol_max=0;
|
|
symbol_count=0;
|
|
return(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
symbols=realloc(symbols,sizeof(SYMBOL_TOKEN *)*symbol_max);
|
|
if(symbols==NULL)
|
|
{
|
|
symbol_max=0;
|
|
symbol_count=0;
|
|
return(NULL);
|
|
}
|
|
}
|
|
}
|
|
tok=malloc(sizeof(SYMBOL_TOKEN));
|
|
if(tok==NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
tok->var=malloc(MAX_SYMBOL_SIZE+1);
|
|
if(tok->var==NULL)
|
|
{
|
|
free(tok);
|
|
return(NULL);
|
|
}
|
|
strncpy(tok->var,sym,MAX_SYMBOL_SIZE);
|
|
tok->var[MAX_SYMBOL_SIZE]=0; /* Ensure null termination */
|
|
|
|
tok->val=NULL;
|
|
symbols[symbol_count]=tok;
|
|
symbol_count++;
|
|
return(tok);
|
|
}
|
|
|
|
DLL_EXPORT void del_symbol(const char *sym)
|
|
{
|
|
SYMBOL_TOKEN *tok;
|
|
int i;
|
|
|
|
for(i=0;i<symbol_count;i++)
|
|
{
|
|
tok=symbols[i];
|
|
if(tok==NULL)
|
|
{
|
|
continue;
|
|
}
|
|
#if defined( CASELESS_SYMBOLS )
|
|
if(strcasecmp(symbols[i]->var,sym)==0)
|
|
#else
|
|
if(strcmp(symbols[i]->var,sym)==0)
|
|
#endif
|
|
{
|
|
if ( tok->val != NULL ) free(tok->val);
|
|
if ( tok->var != NULL ) free(tok->var);
|
|
free(tok);
|
|
symbols[i] = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
DLL_EXPORT void set_symbol(const char *sym, const char *value)
|
|
{
|
|
SYMBOL_TOKEN *tok;
|
|
size_t size;
|
|
|
|
if ( sym == NULL || value == NULL || strlen(sym) == 0 )
|
|
return;
|
|
|
|
tok=get_symbol_token(sym,1);
|
|
if(tok==NULL)
|
|
{
|
|
return;
|
|
}
|
|
if(tok->val!=NULL)
|
|
{
|
|
free(tok->val);
|
|
}
|
|
size = strlen(value)+1;
|
|
tok->val=malloc(size);
|
|
if(tok->val==NULL)
|
|
{
|
|
return;
|
|
}
|
|
strlcpy(tok->val,value,size);
|
|
return;
|
|
}
|
|
|
|
DLL_EXPORT const char* get_symbol( const char* sym )
|
|
{
|
|
SYMBOL_TOKEN* tok;
|
|
char buf[ MAX_ENVVAR_LEN ];
|
|
|
|
if (CMD( sym, DATE, 4 ))
|
|
{
|
|
// Rebuild new value each time date/time symbol is retrieved
|
|
time_t raw_tt;
|
|
time( &raw_tt ); // YYYYMMDD
|
|
strftime( buf, sizeof( buf ) - 1, "%Y%m%d", localtime( &raw_tt ));
|
|
set_symbol( sym = "DATE", buf );
|
|
}
|
|
else if (CMD( sym, TIME, 4 ))
|
|
{
|
|
// Rebuild new value each time date/time symbol is retrieved
|
|
time_t raw_tt;
|
|
time( &raw_tt ); // HHMMSS
|
|
strftime( buf, sizeof( buf ) - 1, "%H%M%S", localtime( &raw_tt ));
|
|
set_symbol( sym = "TIME", buf );
|
|
}
|
|
|
|
if (!(tok = get_symbol_token( sym, 0 )))
|
|
{
|
|
// Add this environment variable to our DEFSYM pool
|
|
const char* val = getenv( sym );
|
|
MSGBUF( buf, "%s", val ? val : "" );
|
|
set_symbol( sym, buf );
|
|
// (now try again; should succeed this time)
|
|
tok = get_symbol_token( sym, 0 );
|
|
}
|
|
return tok->val;
|
|
}
|
|
|
|
DLL_EXPORT char *resolve_symbol_string(const char *text)
|
|
{
|
|
char buf[MAX_PATH*4]; /* Statement buffer */
|
|
char dflt[MAX_PATH*4]; /* temp location for default */
|
|
int c; /* Character work area */
|
|
int i = 0; /* Position in the input */
|
|
int stmtlen = 0; /* Statement length */
|
|
int inc_dollar = -1; /* >=0 Ndx of dollar */
|
|
int inc_lbrace = -1; /* >=0 Ndx of lbrace + 1 */
|
|
int inc_colon = -1; /* >=0 Ndx of colon */
|
|
int inc_equals = -1; /* >=0 Ndx of equals */
|
|
int lstarted; /* Indicate if non-whitespace*/
|
|
char *inc_envvar; /* ->Environment variable */
|
|
|
|
char symt = 0; /* Character work area */
|
|
|
|
if( strstr( text, "$(" ) == NULL && strstr( text, "${" ) == NULL )
|
|
{
|
|
/* Malloc anyway - the caller will free() */
|
|
return( strdup( text ) );
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
while(1)
|
|
{
|
|
for (i = 0, stmtlen = 0, lstarted = 0; ;i++)
|
|
{
|
|
c = text[i];
|
|
|
|
/* Ignore nulls and carriage returns */
|
|
if (c == '\0' ) break;
|
|
|
|
/* Check if it is a white space and no other character yet */
|
|
if(!lstarted && isspace((unsigned char)c)) continue;
|
|
lstarted=1;
|
|
|
|
/* Check that statement does not overflow buffer */
|
|
if (stmtlen >= (int)(sizeof(buf) - 1))
|
|
{
|
|
WRMSG( HHC01418, "E" );
|
|
return( strdup(text) );
|
|
}
|
|
|
|
/* inc_dollar already processed? */
|
|
if (inc_dollar >= 0)
|
|
{
|
|
/* Left brace already processed? */
|
|
if (inc_lbrace >= 0)
|
|
{
|
|
/* End of variable spec? */
|
|
if ( c == symt )
|
|
{
|
|
/* Terminate it */
|
|
buf[stmtlen] = '\0';
|
|
|
|
/* Terminate var name if we have a inc_colon specifier */
|
|
if (inc_colon >= 0)
|
|
{
|
|
buf[inc_colon] = '\0';
|
|
}
|
|
|
|
/* Terminate var name if we have a default value */
|
|
if (inc_equals >= 0)
|
|
{
|
|
buf[inc_equals] = '\0';
|
|
}
|
|
|
|
/* Reset statement index to start of variable */
|
|
stmtlen = inc_dollar;
|
|
|
|
/* Get variable value */
|
|
inc_envvar = (char *)get_symbol (&buf[inc_lbrace]);
|
|
|
|
/* Variable unset? */
|
|
if (inc_envvar == NULL || strlen(inc_envvar) == 0 )
|
|
{
|
|
/* Substitute default if specified */
|
|
if (inc_equals >= 0)
|
|
{
|
|
memset(dflt, 0, sizeof(dflt));
|
|
STRLCPY( dflt, &buf[inc_equals+1] );
|
|
inc_envvar = dflt;
|
|
}
|
|
}
|
|
|
|
/* Have a value? (environment or default) */
|
|
if (inc_envvar != NULL)
|
|
{
|
|
/* Check that statement does not overflow buffer */
|
|
if (stmtlen+strlen(inc_envvar) >= sizeof(buf) - 1)
|
|
{
|
|
WRMSG( HHC01418, "E" );
|
|
return( strdup( text ) );
|
|
}
|
|
|
|
/* Copy to buffer and update index */
|
|
stmtlen += snprintf( &buf[stmtlen],
|
|
(sizeof(buf) - stmtlen) - 1,
|
|
"%s", inc_envvar );
|
|
}
|
|
memset(&buf[stmtlen], 0, (sizeof(buf) - stmtlen));
|
|
|
|
/* Reset indexes */
|
|
inc_equals = -1;
|
|
inc_colon = -1;
|
|
inc_lbrace = -1;
|
|
inc_dollar = -1;
|
|
continue;
|
|
}
|
|
else if (c == ':' && inc_colon < 0 && inc_equals < 0)
|
|
{
|
|
/* Remember possible start of default specifier */
|
|
inc_colon = stmtlen;
|
|
}
|
|
else if (c == '=' && inc_equals < 0)
|
|
{
|
|
/* Remember possible start of default specifier */
|
|
inc_equals = stmtlen;
|
|
}
|
|
}
|
|
else // (inc_lbrace < 0)
|
|
{
|
|
/* Remember start of variable name */
|
|
|
|
if ( c == '(' )
|
|
{
|
|
symt = ')' ;
|
|
inc_lbrace = stmtlen + 1;
|
|
}
|
|
else if ( c == '{' )
|
|
{
|
|
symt = '}' ;
|
|
inc_lbrace = stmtlen + 1;
|
|
}
|
|
else
|
|
{
|
|
/* Reset inc_dollar specifier if immediately following
|
|
character is not a left brace */
|
|
inc_dollar = -1;
|
|
}
|
|
}
|
|
}
|
|
else // (inc_dollar < 0)
|
|
{
|
|
/* Enter variable substitution state */
|
|
if (c == '$')
|
|
{
|
|
inc_dollar = stmtlen;
|
|
}
|
|
}
|
|
|
|
/* Append character to buffer */
|
|
buf[stmtlen++] = c;
|
|
|
|
} /* end for(stmtlen) */
|
|
|
|
break;
|
|
}
|
|
|
|
return (strdup(buf));
|
|
}
|
|
|
|
/* (called by defsym panel command) */
|
|
DLL_EXPORT void list_all_symbols()
|
|
{
|
|
SYMBOL_TOKEN* tok; int i, k;
|
|
char* p;
|
|
|
|
for (i=0; i < symbol_count; i++)
|
|
{
|
|
if (!(tok = symbols[i]))
|
|
continue;
|
|
|
|
p = tok->val ? tok->val : "";
|
|
|
|
if ((k = strlen( p )) <= 80)
|
|
{
|
|
// "Symbol %-12s %-80s"
|
|
WRMSG( HHC02199, "I", tok->var, p );
|
|
}
|
|
else // (show first 80, then another 80, etc, until all is displayed)
|
|
{
|
|
bool did2199;
|
|
char buf80[ 80+1 ];
|
|
|
|
for (did2199 = false; k > 0; p += 80, k -= 80)
|
|
{
|
|
STRLCPY( buf80, p ); // (grab next chunk)
|
|
|
|
if (!did2199)
|
|
{
|
|
// "Symbol %-12s %-80s"
|
|
WRMSG( HHC02199, "I", tok->var, buf80 );
|
|
did2199 = true;
|
|
}
|
|
else
|
|
{
|
|
// " %-80s"
|
|
WRMSG( HHC02196, "I", buf80 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Hercules microsecond sleep */
|
|
DLL_EXPORT int herc_usleep( useconds_t usecs, const char* file, int line )
|
|
{
|
|
int rc, save_errno = 0, eintr_retrys = 0;
|
|
|
|
struct timespec usecs_left;
|
|
usecs_left.tv_nsec = usecs * 1000L;
|
|
usecs_left.tv_sec = usecs_left.tv_nsec / ONE_BILLION;
|
|
usecs_left.tv_nsec %= ONE_BILLION;
|
|
|
|
while (1
|
|
&& (rc = nanosleep( &usecs_left, &usecs_left ) != 0)
|
|
&& (save_errno = errno) == EINTR
|
|
&& (0
|
|
|| usecs_left.tv_sec != 0
|
|
|| usecs_left.tv_nsec > (USLEEP_MIN * 1000)
|
|
)
|
|
)
|
|
++eintr_retrys; // (keep retrying until done or min reached)
|
|
|
|
if (0
|
|
|| rc != 0
|
|
|| eintr_retrys > NANOSLEEP_EINTR_RETRY_WARNING_TRESHOLD
|
|
)
|
|
{
|
|
char fnc[128], msg[128];
|
|
|
|
MSGBUF( fnc, "USLEEP() at %s(%d)",
|
|
TRIMLOC( file ), line );
|
|
|
|
if (save_errno != EINTR)
|
|
{
|
|
MSGBUF( msg, "rc=%d, errno=%d: %s",
|
|
rc, save_errno, strerror( save_errno ));
|
|
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", fnc, msg );
|
|
errno = save_errno;
|
|
}
|
|
|
|
if (eintr_retrys > NANOSLEEP_EINTR_RETRY_WARNING_TRESHOLD)
|
|
{
|
|
MSGBUF( msg, "rc=%d, EINTR retrys count=%d",
|
|
rc, eintr_retrys );
|
|
|
|
// "Warning in function %s: %s"
|
|
WRMSG( HHC00092, "W", fnc, msg );
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Subtract 'beg_timeval' from 'end_timeval' yielding 'dif_timeval' */
|
|
/* Return code: success == 0, error == -1 (difference was negative) */
|
|
|
|
DLL_EXPORT int timeval_subtract
|
|
(
|
|
struct timeval *beg_timeval,
|
|
struct timeval *end_timeval,
|
|
struct timeval *dif_timeval
|
|
)
|
|
{
|
|
struct timeval begtime;
|
|
struct timeval endtime;
|
|
ASSERT ( beg_timeval -> tv_sec >= 0 && beg_timeval -> tv_usec >= 0 );
|
|
ASSERT ( end_timeval -> tv_sec >= 0 && end_timeval -> tv_usec >= 0 );
|
|
|
|
memcpy(&begtime,beg_timeval,sizeof(struct timeval));
|
|
memcpy(&endtime,end_timeval,sizeof(struct timeval));
|
|
|
|
dif_timeval->tv_sec = endtime.tv_sec - begtime.tv_sec;
|
|
|
|
if (endtime.tv_usec >= begtime.tv_usec)
|
|
{
|
|
dif_timeval->tv_usec = endtime.tv_usec - begtime.tv_usec;
|
|
}
|
|
else
|
|
{
|
|
dif_timeval->tv_sec--;
|
|
dif_timeval->tv_usec = (endtime.tv_usec + 1000000) - begtime.tv_usec;
|
|
}
|
|
|
|
return ((dif_timeval->tv_sec < 0 || dif_timeval->tv_usec < 0) ? -1 : 0);
|
|
}
|
|
|
|
/* Add 'dif_timeval' to 'accum_timeval' (use to accumulate elapsed times) */
|
|
/* Return code: success == 0, error == -1 (accum_timeval result negative) */
|
|
|
|
DLL_EXPORT int timeval_add
|
|
(
|
|
struct timeval *dif_timeval,
|
|
struct timeval *accum_timeval
|
|
)
|
|
{
|
|
ASSERT ( dif_timeval -> tv_sec >= 0 && dif_timeval -> tv_usec >= 0 );
|
|
ASSERT ( accum_timeval -> tv_sec >= 0 && accum_timeval -> tv_usec >= 0 );
|
|
|
|
accum_timeval->tv_sec += dif_timeval->tv_sec;
|
|
accum_timeval->tv_usec += dif_timeval->tv_usec;
|
|
|
|
if (accum_timeval->tv_usec >= 1000000)
|
|
{
|
|
int usec = accum_timeval->tv_usec / 1000000;
|
|
accum_timeval->tv_sec += usec;
|
|
accum_timeval->tv_usec -= usec * 1000000;
|
|
}
|
|
|
|
return ((accum_timeval->tv_sec < 0 || accum_timeval->tv_usec < 0) ? -1 : 0);
|
|
}
|
|
|
|
/*
|
|
Easier to use timed_wait_condition that waits for the specified
|
|
relative amount of time without you having to build an absolute
|
|
timeout time yourself. Use the "timed_wait_condition_relative_usecs"
|
|
macro to call it.
|
|
*/
|
|
DLL_EXPORT int timed_wait_condition_relative_usecs_impl
|
|
(
|
|
COND* pCOND, // ptr to condition to wait on
|
|
LOCK* pLOCK, // ptr to controlling lock (must be held!)
|
|
U32 usecs, // max #of microseconds to wait
|
|
struct timeval* pTV, // [OPTIONAL] ptr to tod value (may be NULL)
|
|
const char* loc // (location)
|
|
)
|
|
{
|
|
struct timespec timeout_timespec;
|
|
struct timeval now;
|
|
|
|
if (!pTV)
|
|
{
|
|
pTV = &now;
|
|
gettimeofday( pTV, NULL );
|
|
}
|
|
|
|
timeout_timespec.tv_sec = pTV->tv_sec + ( usecs / 1000000 );
|
|
timeout_timespec.tv_nsec = pTV->tv_usec + ( usecs % 1000000 );
|
|
|
|
if ( timeout_timespec.tv_nsec >= 1000000 )
|
|
{
|
|
timeout_timespec.tv_sec += timeout_timespec.tv_nsec / 1000000;
|
|
timeout_timespec.tv_nsec %= 1000000;
|
|
}
|
|
|
|
timeout_timespec.tv_nsec *= 1000;
|
|
// return timed_wait_condition( pCOND, pLOCK, &timeout_timespec );
|
|
return hthread_timed_wait_condition( pCOND, pLOCK, &timeout_timespec, loc );
|
|
}
|
|
|
|
/*********************************************************************
|
|
The following couple of Hercules 'utility' functions may be defined
|
|
elsewhere depending on which host platform we're being built for...
|
|
For Windows builds (e.g. MingW32), the functionality for the below
|
|
functions is defined in 'w32util.c'. For other host build platforms
|
|
(e.g. Linux, Apple, etc), the functionality for the below functions
|
|
is defined right here in 'hscutil.c'...
|
|
*********************************************************************/
|
|
|
|
#if !defined(_MSVC_)
|
|
|
|
/* THIS module (hscutil.c) is to provide the below functionality.. */
|
|
|
|
/*
|
|
Returns outpath as a host filesystem compatible filename path.
|
|
This is a Cygwin-to-MSVC transitional period helper function.
|
|
On non-Windows platforms it simply copies inpath to outpath.
|
|
On Windows it converts inpath of the form "/cygdrive/x/foo.bar"
|
|
to outpath in the form "x:/foo.bar" for Windows compatibility.
|
|
*/
|
|
DLL_EXPORT char *hostpath( char *outpath, const char *inpath, size_t buffsize )
|
|
{
|
|
if (inpath && outpath && buffsize > 1)
|
|
strlcpy( outpath, inpath, buffsize );
|
|
else if (outpath && buffsize)
|
|
*outpath = 0;
|
|
return outpath;
|
|
}
|
|
|
|
/* Poor man's "fcntl( fd, F_GETFL )"... */
|
|
/* (only returns access-mode flags and not any others) */
|
|
DLL_EXPORT int get_file_accmode_flags( int fd )
|
|
{
|
|
int flags = fcntl( fd, F_GETFL );
|
|
return ( flags & O_ACCMODE );
|
|
}
|
|
|
|
/* Set socket to blocking or non-blocking mode... */
|
|
DLL_EXPORT int socket_set_blocking_mode( int sfd, int blocking_mode )
|
|
{
|
|
int flags = fcntl( sfd, F_GETFL );
|
|
|
|
if ( blocking_mode )
|
|
flags &= ~O_NONBLOCK;
|
|
else
|
|
flags |= O_NONBLOCK;
|
|
|
|
return fcntl( sfd, F_SETFL, flags );
|
|
}
|
|
|
|
/* Initialize/Deinitialize sockets package... */
|
|
int socket_init ( void ) { return 0; }
|
|
DLL_EXPORT int socket_deinit ( void ) { return 0; }
|
|
|
|
/* Determine whether a file descriptor is a socket or not... */
|
|
/* (returns 1==true if it's a socket, 0==false otherwise) */
|
|
DLL_EXPORT int socket_is_socket( int sfd )
|
|
{
|
|
struct stat st;
|
|
return ( fstat( sfd, &st ) == 0 && S_ISSOCK( st.st_mode ) );
|
|
}
|
|
|
|
/* Set the SO_KEEPALIVE option and timeout values for a
|
|
socket connection to detect when client disconnects.
|
|
Returns 0==success, +1==warning, -1==failure
|
|
(*) Warning failure means function only partially
|
|
succeeded (not all requested values were set)
|
|
*/
|
|
int set_socket_keepalive( int sfd,
|
|
int idle_time,
|
|
int probe_interval,
|
|
int probe_count )
|
|
{
|
|
#if !defined( HAVE_BASIC_KEEPALIVE )
|
|
|
|
UNREFERENCED( sfd );
|
|
UNREFERENCED( idle_time );
|
|
UNREFERENCED( probe_interval );
|
|
UNREFERENCED( probe_count );
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
|
|
#else // basic, partial or full: must attempt setting keepalive
|
|
|
|
int idle, intv, cnt;
|
|
int rc, l_tcp, optval, succeeded = 0, tried = 0;
|
|
struct protoent * tcpproto;
|
|
|
|
/* Retrieve TCP protocol value (mostly for FreeBSD portability) */
|
|
tcpproto = getprotobyname("tcp");
|
|
if (!tcpproto)
|
|
{
|
|
tcpproto = getprotobyname("TCP");
|
|
if (!tcpproto)
|
|
{
|
|
WRMSG( HHC02219, "E", "getprotobyname(\"tcp\")", strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
}
|
|
l_tcp = tcpproto->p_proto;
|
|
|
|
optval = 1;
|
|
#if defined( HAVE_DECL_SO_KEEPALIVE ) && HAVE_DECL_SO_KEEPALIVE
|
|
tried++;
|
|
rc = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
|
if (rc)
|
|
{
|
|
WRMSG( HHC02219, "E", "setsockopt(SO_KEEPALIVE)", strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
else
|
|
succeeded++;
|
|
#endif
|
|
|
|
optval = idle_time;
|
|
#if defined( HAVE_DECL_TCP_KEEPALIVE ) && HAVE_DECL_TCP_KEEPALIVE
|
|
tried++;
|
|
rc = setsockopt(sfd, l_tcp, TCP_KEEPALIVE, &optval, sizeof(optval));
|
|
if (rc)
|
|
WRMSG( HHC02219, "E", "setsockopt(TCP_KEEPALIVE)", strerror( HSO_errno ));
|
|
else
|
|
{
|
|
succeeded++;
|
|
idle = idle_time;
|
|
}
|
|
#endif
|
|
|
|
/* Only try setting individual keepalive values if full or partial
|
|
keepalive is supported. Otherwise if we have only basic keepalive
|
|
then don't bother even trying
|
|
*/
|
|
#if defined( HAVE_FULL_KEEPALIVE ) || defined( HAVE_PARTIAL_KEEPALIVE )
|
|
|
|
optval = idle_time;
|
|
#if defined( HAVE_DECL_TCP_KEEPIDLE ) && HAVE_DECL_TCP_KEEPIDLE
|
|
tried++;
|
|
rc = setsockopt(sfd, l_tcp, TCP_KEEPIDLE, &optval, sizeof(optval));
|
|
if (rc)
|
|
WRMSG( HHC02219, "E", "setsockopt(TCP_KEEPIDLE)", strerror( HSO_errno ));
|
|
else
|
|
{
|
|
succeeded++;
|
|
idle = idle_time;
|
|
}
|
|
#endif
|
|
|
|
optval = probe_interval;
|
|
#if defined( HAVE_DECL_TCP_KEEPINTVL ) && HAVE_DECL_TCP_KEEPINTVL
|
|
tried++;
|
|
rc = setsockopt(sfd, l_tcp, TCP_KEEPINTVL, &optval, sizeof(optval));
|
|
if (rc)
|
|
WRMSG( HHC02219, "E", "setsockopt(TCP_KEEPINTVL)", strerror( HSO_errno ));
|
|
else
|
|
{
|
|
succeeded++;
|
|
intv = probe_interval;
|
|
}
|
|
#endif
|
|
|
|
optval = probe_count;
|
|
#if defined( HAVE_DECL_TCP_KEEPCNT ) && HAVE_DECL_TCP_KEEPCNT
|
|
tried++;
|
|
rc = setsockopt(sfd, l_tcp, TCP_KEEPCNT, &optval, sizeof(optval));
|
|
if (rc)
|
|
WRMSG( HHC02219, "E", "setsockopt(TCP_KEEPCNT)", strerror( HSO_errno ));
|
|
else
|
|
{
|
|
succeeded++;
|
|
cnt = probe_count;
|
|
}
|
|
#endif
|
|
|
|
#endif // defined( HAVE_FULL_KEEPALIVE ) || defined( HAVE_PARTIAL_KEEPALIVE )
|
|
|
|
/* Retrieve values in use, ignoring any error */
|
|
get_socket_keepalive( sfd, &idle, &intv, & cnt );
|
|
|
|
/* Determine return code: did everything succeed and,
|
|
more importantly, were all of our values accepted?
|
|
Did the values we tried setting (if any) "stick"?
|
|
*/
|
|
if (succeeded >= tried)
|
|
rc = (idle != idle_time || intv != probe_interval || cnt != probe_count) ? +1 : 0;
|
|
else
|
|
rc = (succeeded <= 0 ? -1 : +1);
|
|
|
|
return rc;
|
|
|
|
#endif // (KEEPALIVE)
|
|
}
|
|
|
|
/* Function to retrieve keepalive values. 0==success, -1=failure */
|
|
int get_socket_keepalive( int sfd, int* idle_time, int* probe_interval,
|
|
int* probe_count )
|
|
{
|
|
#if defined( HAVE_FULL_KEEPALIVE ) || defined( HAVE_PARTIAL_KEEPALIVE )
|
|
struct protoent* tcpproto;
|
|
int optval, l_tcp;
|
|
socklen_t optlen = sizeof( optval );
|
|
|
|
/* Retrieve TCP protocol value (mostly for FreeBSD portability) */
|
|
tcpproto = getprotobyname("tcp");
|
|
if (!tcpproto)
|
|
{
|
|
tcpproto = getprotobyname("TCP");
|
|
if (!tcpproto)
|
|
{
|
|
WRMSG( HHC02219, "E", "getprotobyname(\"tcp\")", strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
}
|
|
l_tcp = tcpproto->p_proto;
|
|
#else
|
|
UNREFERENCED( sfd );
|
|
#endif // HAVE_FULL_KEEPALIVE || HAVE_PARTIAL_KEEPALIVE
|
|
|
|
/* Default values */
|
|
*idle_time = sysblk.kaidle;
|
|
*probe_interval = sysblk.kaintv;
|
|
*probe_count = sysblk.kacnt;
|
|
|
|
#if defined( HAVE_FULL_KEEPALIVE ) || defined( HAVE_PARTIAL_KEEPALIVE )
|
|
/* Retrieve the actual values from the system */
|
|
#if defined( HAVE_DECL_TCP_KEEPIDLE ) && HAVE_DECL_TCP_KEEPIDLE
|
|
if (getsockopt( sfd, l_tcp, TCP_KEEPIDLE, &optval, &optlen ) >= 0) *idle_time = optval;
|
|
#endif
|
|
#if defined( HAVE_DECL_TCP_KEEPINTVL ) && HAVE_DECL_TCP_KEEPINTVL
|
|
if (getsockopt( sfd, l_tcp, TCP_KEEPINTVL, &optval, &optlen ) >= 0) *probe_interval = optval;
|
|
#endif
|
|
#if defined( HAVE_DECL_TCP_KEEPCNT ) && HAVE_DECL_TCP_KEEPCNT
|
|
if (getsockopt( sfd, l_tcp, TCP_KEEPCNT, &optval, &optlen ) >= 0) *probe_count = optval;
|
|
#endif
|
|
#endif // HAVE_FULL_KEEPALIVE || HAVE_PARTIAL_KEEPALIVE
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Hercules low-level file open...
|
|
DLL_EXPORT int hopen( const char* path, int oflag, ... )
|
|
{
|
|
int pmode = 0;
|
|
if (oflag & O_CREAT)
|
|
{
|
|
va_list vargs;
|
|
va_start( vargs, oflag );
|
|
pmode = va_arg( vargs, int );
|
|
va_end( vargs );
|
|
}
|
|
return open( path, oflag, pmode );
|
|
}
|
|
|
|
/* Determine whether process is running "elevated" or not */
|
|
/* (returns 1==true running elevated, 0==false otherwise) */
|
|
bool are_elevated()
|
|
{
|
|
return (sysblk.euid == 0) ? true : false;
|
|
}
|
|
|
|
#endif // !defined(_MSVC_)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*******************************************/
|
|
/* Read/Write to socket functions */
|
|
/*******************************************/
|
|
DLL_EXPORT int hgetc(int s)
|
|
{
|
|
char c;
|
|
int rc;
|
|
rc=recv(s,&c,1,0);
|
|
if(rc<1)
|
|
{
|
|
return EOF;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
DLL_EXPORT char * hgets(char *b,size_t c,int s)
|
|
{
|
|
size_t ix=0;
|
|
while(ix<c)
|
|
{
|
|
b[ix]=hgetc(s);
|
|
if ((signed char)b[ix] == EOF)
|
|
{
|
|
return NULL;
|
|
}
|
|
b[ix+1]=0;
|
|
if(b[ix]=='\n')
|
|
{
|
|
return(b);
|
|
}
|
|
ix++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
DLL_EXPORT int hwrite(int s,const char *bfr,size_t sz)
|
|
{
|
|
return send(s,bfr,(int)sz,0); /* (int) cast is for _WIN64 */
|
|
}
|
|
|
|
DLL_EXPORT int hprintf(int s,char *fmt,...)
|
|
{
|
|
va_list vl;
|
|
int rc;
|
|
char *bfr = NULL;
|
|
|
|
va_start( vl, fmt );
|
|
rc = vasprintf( &bfr, fmt, vl );
|
|
va_end( vl );
|
|
if (rc < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
rc = hwrite(s, bfr, rc);
|
|
free(bfr);
|
|
return rc;
|
|
}
|
|
|
|
/* Hercules page-aligned calloc/free */
|
|
|
|
DLL_EXPORT void* hpcalloc( BYTE type, size_t size )
|
|
{
|
|
/* PROGRAMMING NOTE: we presume the host page size
|
|
will never be smaller than the guest page size */
|
|
|
|
void* ptr = NULL; /* (aligned) */
|
|
void* p = NULL; /* (unaligned) */
|
|
size_t bytes = 0;
|
|
size_t pagesize = HPAGESIZE();
|
|
uintptr_t alignmask = ~(pagesize-1);
|
|
|
|
/* Need extra room for pointer and pagesize alignment */
|
|
bytes = size + sizeof(void*) + pagesize - 1;
|
|
|
|
/* Get memory already pre-initialized to binary zeroes */
|
|
if ((p = calloc( bytes, 1 )) != NULL)
|
|
{
|
|
/* Round up to the next host pagesize boundary
|
|
being careful there is room for our pointer */
|
|
ptr = (void*)(((uintptr_t) p + sizeof(void*) + pagesize - 1) & alignmask);
|
|
|
|
/* Save original ptr for hpcfree() just before
|
|
page-aligned ptr we'll be returning to them */
|
|
*((void**)((uintptr_t) ptr - sizeof(void*))) = p;
|
|
|
|
/* Indicate the storage has already been cleared */
|
|
if (HPC_MAINSTOR == type) sysblk.main_clear = 1;
|
|
if (HPC_XPNDSTOR == type) sysblk.xpnd_clear = 1;
|
|
}
|
|
return ptr; /* Return page-aligned allocation */
|
|
}
|
|
|
|
DLL_EXPORT void hpcfree( BYTE type, void* ptr )
|
|
{
|
|
/* Retrieve the original ptr that hpcalloc() saved
|
|
immediately preceding the original allocation */
|
|
void* p = *(void**)((uintptr_t)ptr - sizeof(void*));
|
|
|
|
/* Free the original calloc() allocated memory */
|
|
free( p );
|
|
|
|
/* Indicate guest storage is no longer cleared */
|
|
if (HPC_MAINSTOR == type) sysblk.main_clear = 0;
|
|
if (HPC_XPNDSTOR == type) sysblk.xpnd_clear = 0;
|
|
}
|
|
|
|
/* Posix 1003.1e capabilities support */
|
|
|
|
#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H) && defined(OPTION_CAPABILITIES)
|
|
/*-------------------------------------------------------------------*/
|
|
/* DROP root privileges but retain a capability */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int drop_privileges(int capa)
|
|
{
|
|
uid_t uid;
|
|
gid_t gid;
|
|
cap_t c;
|
|
int rc;
|
|
int failed;
|
|
cap_value_t v;
|
|
int have_capt;
|
|
|
|
/* If *real* userid is root, no need to do all this */
|
|
uid=getuid();
|
|
if(!uid) return 0;
|
|
|
|
failed=1;
|
|
have_capt=0;
|
|
do
|
|
{
|
|
c=cap_init();
|
|
if(!c) break;
|
|
have_capt=1;
|
|
v=capa;
|
|
rc=cap_set_flag(c,CAP_EFFECTIVE,1,&v,CAP_SET);
|
|
if(rc<0) break;
|
|
rc=cap_set_flag(c,CAP_INHERITABLE,1,&v,CAP_SET);
|
|
if(rc<0) break;
|
|
rc=cap_set_flag(c,CAP_PERMITTED,1,&v,CAP_SET);
|
|
if(rc<0) break;
|
|
rc=cap_set_proc(c);
|
|
if(rc<0) break;
|
|
rc=prctl(PR_SET_KEEPCAPS,1);
|
|
if(rc<0) break;
|
|
failed=0;
|
|
} while(0);
|
|
gid=getgid();
|
|
setregid(gid,gid);
|
|
setreuid(uid,uid);
|
|
if(!failed)
|
|
{
|
|
rc=cap_set_proc(c);
|
|
if(rc<0) failed=1;
|
|
}
|
|
|
|
if(have_capt)
|
|
cap_free(c);
|
|
|
|
return failed;
|
|
}
|
|
/*-------------------------------------------------------------------*/
|
|
/* DROP all capabilities */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int drop_all_caps(void)
|
|
{
|
|
uid_t uid;
|
|
cap_t c;
|
|
int rc;
|
|
int failed;
|
|
int have_capt;
|
|
|
|
/* If *real* userid is root, no need to do all this */
|
|
uid=getuid();
|
|
if(!uid) return 0;
|
|
|
|
failed=1;
|
|
have_capt=0;
|
|
do
|
|
{
|
|
c=cap_from_text("all-eip");
|
|
if(!c) break;
|
|
have_capt=1;
|
|
rc=cap_set_proc(c);
|
|
if(rc<0) break;
|
|
failed=0;
|
|
} while(0);
|
|
|
|
if(have_capt)
|
|
cap_free(c);
|
|
|
|
return failed;
|
|
}
|
|
#endif /* defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H) && defined(OPTION_CAPABILITIES) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Trim path information from __FILE__ macro value */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* trimloc( const char* loc )
|
|
{
|
|
/*
|
|
** The __FILE__ macro expands to a path by which the preprocessor
|
|
** opened the file, not the short name specified in "#include" or
|
|
** as the input file name argument. For example, __FILE__ might
|
|
** expand to "/usr/local/include/myheader.h", not "myheader.h".
|
|
** The following compensates for this condition by returning just
|
|
** the base name.
|
|
**
|
|
** PROGRAMMING NOTE: we cannot use the basename() function here
|
|
** because:
|
|
**
|
|
**
|
|
** "The basename() function may modify the string
|
|
** pointed to by path, and may return a pointer to
|
|
** internal storage. The returned pointer might be
|
|
** invalidated or the storage might be overwritten
|
|
** by a subsequent call to basename()."
|
|
**
|
|
** "The basename() function need not be thread-safe."
|
|
**
|
|
**
|
|
** The below implementation avoids both issues by just returning
|
|
** a pointer indexed into the current string constant.
|
|
*/
|
|
const char* p = strrchr( loc, '\\' ); /* Windows */
|
|
if (!p) p = strrchr( loc, '/' ); /* non-Windows */
|
|
if (p)
|
|
loc = p+1;
|
|
return loc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Format TIMEVAL to printable value: "YYYY-MM-DD HH:MM:SS.uuuuuu", */
|
|
/* being exactly 26 characters long (27 bytes with null terminator). */
|
|
/* pTV points to the TIMEVAL to be formatted. If NULL is passed then */
|
|
/* the current time of day returned by a call to 'gettimeofday' is */
|
|
/* used instead. buf must point to a char work buffer where the time */
|
|
/* is formatted into and must not be NULL. bufsz is the size of buf */
|
|
/* and must be >= 2. If successful then the value of buf is returned */
|
|
/* and is always zero terminated. If an error occurs or an invalid */
|
|
/* parameter is passed then NULL is returned instead. */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT char* FormatTIMEVAL( const TIMEVAL* pTV, char* buf, int bufsz )
|
|
{
|
|
struct timeval tv;
|
|
struct tm* pTM;
|
|
time_t todsecs;
|
|
if (!buf || bufsz < 2)
|
|
return NULL;
|
|
if (!pTV)
|
|
{
|
|
gettimeofday( &tv, NULL );
|
|
pTV = &tv;
|
|
}
|
|
todsecs = pTV->tv_sec;
|
|
pTM = localtime( &todsecs );
|
|
strftime( buf, bufsz, "%Y-%m-%d %H:%M:%S", pTM );
|
|
if (bufsz > 20)
|
|
snprintf( &buf[19], bufsz-19, ".%06d", (int)pTV->tv_usec );
|
|
buf[ bufsz-1 ] = 0;
|
|
return buf;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* fmt_memsize helper */
|
|
/*-------------------------------------------------------------------*/
|
|
static char* _fmt_memsize( const U64 memsize, const u_int n,
|
|
char* buf, const size_t bufsz )
|
|
{
|
|
/* Format storage in 2**(10*n) values at the highest integral
|
|
* integer boundary.
|
|
*
|
|
* Mainframe memory and DASD amounts are reported in 2**(10*n)
|
|
* values, (x_iB international format, and shown as x_ or x_B,
|
|
* when x >= 1024; x when x < 1024). Open Systems and Windows
|
|
* report memory in the same format, but report DASD storage in
|
|
* 10**(3*n) values. (Thank you, various marketing groups and
|
|
* international standards committees...)
|
|
*
|
|
* For Hercules, mainframe oriented reporting characteristics
|
|
* will be formatted and shown as x_, when x >= 1024, and as x
|
|
* when x < 1024. Reporting of Open Systems and Windows specifics
|
|
* should follow the international format, shown as x_iB,
|
|
* when x >= 1024, and x or xB when x < 1024.
|
|
*
|
|
* Reporting is done at the highest integral boundary.
|
|
*/
|
|
//------------------------------------------------------------------
|
|
// The 'n' value passed to us determines which suffix we start with
|
|
//------------------------------------------------------------------
|
|
const char suffix[9] = {0x00, 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
|
|
U64 mem = memsize;
|
|
u_int i = 0;
|
|
|
|
if (mem)
|
|
for (i = n;
|
|
i < _countof( suffix ) && !(mem & 0x03FF);
|
|
mem >>= 10, ++i);
|
|
|
|
snprintf( buf, bufsz, "%"PRIu64"%c", mem, suffix[ i ]);
|
|
return buf;
|
|
}
|
|
|
|
DLL_EXPORT char* fmt_memsize ( const U64 memsize, char* buf, const size_t bufsz ) { return _fmt_memsize( memsize, 0, buf, bufsz ); }
|
|
DLL_EXPORT char* fmt_memsize_KB( const U64 memsizeKB, char* buf, const size_t bufsz ) { return _fmt_memsize( memsizeKB, 1, buf, bufsz ); }
|
|
DLL_EXPORT char* fmt_memsize_MB( const U64 memsizeMB, char* buf, const size_t bufsz ) { return _fmt_memsize( memsizeMB, 2, buf, bufsz ); }
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Pretty format S64 value with thousand separators. Returns length. */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT size_t fmt_S64( char dst[32], S64 num )
|
|
{
|
|
char src[32];
|
|
char* p_src = src;
|
|
char* p_dst = dst;
|
|
|
|
const char separator = ','; // (FIXME)
|
|
int num_len, commas;
|
|
|
|
num_len = snprintf( src, sizeof( src ), "%"PRId64, num );
|
|
|
|
if (*p_src == '-')
|
|
{
|
|
*p_dst++ = *p_src++;
|
|
num_len--;
|
|
}
|
|
|
|
for (commas = 2 - num_len % 3;
|
|
*p_src;
|
|
commas = (commas + 1) % 3)
|
|
{
|
|
*p_dst++ = *p_src++;
|
|
if (commas == 1) {
|
|
*p_dst++ = separator;
|
|
}
|
|
}
|
|
|
|
*--p_dst = 0;
|
|
|
|
return (size_t) (p_dst - dst);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Standard Utility Initialization */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Performs standard utility initialization as follows: determines */
|
|
/* the program name (i.e. just the name without the .ext) from the */
|
|
/* passed argv[0] or default name value, displays the program name, */
|
|
/* description, version, copyright and build date values, tells the */
|
|
/* debugger what name to assign to the main thread (Windows only), */
|
|
/* initializes the global 'extgui' flag, initializes SYSBLK "detach" */
|
|
/* and "join" create_thread attributes (the remainder of SYSBLK is */
|
|
/* set to low values), initializes the translation codepage to the */
|
|
/* system default, and lastly, initializes the HOSTINFO structure. */
|
|
/* (but it doesn't necessarily do all that in that order of course) */
|
|
/* */
|
|
/* The program name (without .ext) is optionally returned in *pgm */
|
|
/* which must be freed by the caller when it is no longer needed. */
|
|
/* Pass NULL for 'pgm' if you don't care to know your own name. */
|
|
/* */
|
|
/* The return value is the new argc value which may not be the same */
|
|
/* as the value that was passed due to external gui initialization. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
int initialize_utility( int argc, char* argv[],
|
|
char* defpgm,
|
|
char* desc,
|
|
char** pgm )
|
|
{
|
|
char* exename; /* Executable name with .ext */
|
|
char* nameonly; /* Exename without any .ext */
|
|
char* strtok_str; /* Work (strtok_r context) */
|
|
char namedesc[256]; /* Message build work area */
|
|
|
|
setvbuf( stderr, NULL, _IONBF, 0 );
|
|
setvbuf( stdout, NULL, _IONBF, 0 );
|
|
|
|
/* If the last argument is "EXTERNALGUI" it means we're running
|
|
under the control of an external GUI. Utilities need to know
|
|
this so they can react differently than in command-line mode. */
|
|
if (argc >= 1 && strncmp( argv[argc-1], "EXTERNALGUI", 11 ) == 0)
|
|
{
|
|
extgui = 1; /* Tell utility to send progress msgs to GUI */
|
|
|
|
/* Remove the "EXTERNALGUI" argument from the command-line */
|
|
argv[argc-1] = NULL;
|
|
argc--;
|
|
}
|
|
|
|
if (argc < 1)
|
|
exename = strdup( defpgm );
|
|
else
|
|
{
|
|
if (strlen( argv[0] ) == 0)
|
|
exename = strdup( defpgm );
|
|
else
|
|
{
|
|
char path[ MAX_PATH ];
|
|
#if defined( _MSVC_ )
|
|
GetModuleFileName( NULL, path, MAX_PATH );
|
|
#else
|
|
STRLCPY( path, argv[0] );
|
|
#endif
|
|
exename = strdup( basename( path ));
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Initialize a bunch of other stuff...
|
|
**
|
|
** Note that initialization MUST occur in the following order:
|
|
** SYSBLK first, then hthreads (since it updates SYSBLK), then
|
|
** locks, etc, since they require hthreads.
|
|
*/
|
|
memset( &sysblk, 0, sizeof( SYSBLK )); // (must be first)
|
|
hthreads_internal_init(); // (must be second)
|
|
SET_THREAD_NAME( exename ); // (then other stuff)
|
|
sysblk.msglvl = DEFAULT_MLVL;
|
|
sysblk.sysgroup = DEFAULT_SYSGROUP;
|
|
|
|
if (extgui)
|
|
{
|
|
sysblk.gui_verstr = "";
|
|
sysblk.gui_vernum = 0;
|
|
initialize_lock( &sysblk.gui_msglock );
|
|
}
|
|
|
|
initialize_detach_attr( DETACHED );
|
|
initialize_join_attr( JOINABLE );
|
|
initialize_lock( &sysblk.dasdcache_lock );
|
|
initialize_lock( &sysblk.scrlock );
|
|
initialize_condition( &sysblk.scrcond );
|
|
|
|
set_codepage( NULL );
|
|
init_hostinfo( &hostinfo );
|
|
|
|
/* Seed the pseudo-random number generator */
|
|
init_random();
|
|
|
|
strtok_str = NULL;
|
|
if (!(nameonly = strtok_r( exename, ".", &strtok_str )))
|
|
nameonly = exename;
|
|
|
|
/* PROGRAMMING NOTE: we cannot free "exename" until we're
|
|
done using "nameonly" since nameonly now points to it! */
|
|
|
|
if (pgm)
|
|
*pgm = strdup( nameonly );
|
|
|
|
/* Format the program identification message */
|
|
MSGBUF( namedesc, "%s - %s", nameonly, desc );
|
|
|
|
/* Now it's safe to discard exename */
|
|
free( exename );
|
|
|
|
/* Display version, copyright, and build date */
|
|
init_sysblk_version_str_arrays( namedesc );
|
|
if (defpgm && strcasecmp(defpgm, "hercifc") == 0)
|
|
display_version( stderr, 0, NULL );
|
|
else
|
|
display_version( stdout, 0, NULL );
|
|
|
|
return argc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Reverse the bits in a BYTE */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static unsigned char rev_nib_bits_tab[16] =
|
|
{
|
|
// entry #1=0001==>1000, entry #7=0111==>1110, etc...
|
|
|
|
// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
|
|
0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E,
|
|
|
|
// 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
|
|
0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F
|
|
};
|
|
/* PROGRAMMING NOTE: while using a direct index into a 256 entry table
|
|
might be more efficient it also takes much more scrutiny (careful
|
|
visual inspection) of all 256 individual table entries to ensure
|
|
they're all correct and is harder to see what's going on. Using a
|
|
simple 16 entry table on the other hand (that only swaps 4 bits)
|
|
not only makes it easier to see what's actually going on but also
|
|
is much easier to visually verify all table entries are correct
|
|
(and isn't significantly less inefficient when the number of bytes
|
|
you are reversing is relatively small, which is usually the case)
|
|
*/
|
|
DLL_EXPORT BYTE reverse_bits( BYTE b )
|
|
{
|
|
/* Reverse the top/bottom nibbles separately,
|
|
then swap the two nibbles with each other.
|
|
*/
|
|
return
|
|
(
|
|
rev_nib_bits_tab[ b & 0x0F ] << 4
|
|
|
|
|
rev_nib_bits_tab[ b >> 4 ]
|
|
);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Format printer cctape information */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void FormatCCTAPE( char* buf, size_t buflen,
|
|
int lpi, int lpp,
|
|
U16* cctape )
|
|
{
|
|
size_t len, curlen = 0, newlen;
|
|
int line, chnum, chans;
|
|
U16 mask;
|
|
char onechan[8], allchans[48], chanlist[64];
|
|
|
|
/* Get started */
|
|
snprintf( buf, buflen, "lpi=%d lpp=%d cctape=(", lpi, lpp );
|
|
curlen = strlen( buf );
|
|
|
|
/* Format: "cctape=(lll=cc,lll=(cc,cc),...)" */
|
|
for (line=0; line < lpp; line++)
|
|
{
|
|
/* Skip lines with no channels defined */
|
|
if (!cctape[line])
|
|
continue;
|
|
|
|
/* Format channel stop(s) for this line */
|
|
for (chans=0, allchans[0]=0, chnum=1, mask=0x8000;
|
|
chnum <= 12; mask >>= 1, chnum++)
|
|
{
|
|
if (cctape[ line ] & mask)
|
|
{
|
|
MSGBUF( onechan, "%d", chnum );
|
|
if (chans++)
|
|
strlcat( allchans, ",", _countof( allchans ));
|
|
strlcat( allchans, onechan, _countof( allchans ));
|
|
}
|
|
}
|
|
|
|
/* Surround this line's channels with parens if needed */
|
|
MSGBUF( chanlist, "%d=%s%s%s,",
|
|
line+1,
|
|
chans > 1 ? "(" : "",
|
|
allchans,
|
|
chans > 1 ? ")" : "" );
|
|
|
|
len = strlen( chanlist );
|
|
|
|
/* Truncate and return if out of buffer space */
|
|
if ((newlen = curlen + len) >= (buflen - 5))
|
|
{
|
|
strlcat( buf, ", ...", buflen );
|
|
return;
|
|
}
|
|
|
|
/* Append formatted channel stop(s) to output */
|
|
strlcat( buf, chanlist, buflen );
|
|
curlen = newlen;
|
|
}
|
|
|
|
/* No channel stops at all? */
|
|
if (buf[ curlen-1 ] == '(')
|
|
buf[ curlen-1 ] = '0';
|
|
else
|
|
/* Change ending comma to closing paren instead */
|
|
buf[ curlen-1 ] = ')';
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Format printer FCB information */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void FormatFCB( char* buf, size_t buflen,
|
|
int index, int lpi, int lpp,
|
|
int* fcb )
|
|
{
|
|
int line;
|
|
size_t curlen, newlen;
|
|
char sep, chan[16];
|
|
|
|
/* Get started */
|
|
snprintf( buf, buflen, "index=%d lpi=%d lpp=%d fcb",
|
|
index, lpi, lpp );
|
|
curlen = strlen( buf );
|
|
sep = '=';
|
|
|
|
/* Format the "fcb=" values... */
|
|
for (line=1; line <= lpp; line++)
|
|
{
|
|
/* Skip lines with no channel stop defined */
|
|
if (!fcb[line])
|
|
continue;
|
|
|
|
/* Format channel stop */
|
|
MSGBUF( chan, "%c%d:%d", sep, fcb[line], line );
|
|
sep = ',';
|
|
|
|
/* Truncate and return if out of buffer space */
|
|
if ((newlen = curlen + strlen( chan )) >= (buflen - 5))
|
|
{
|
|
strlcat( buf, ", ...", buflen );
|
|
return;
|
|
}
|
|
|
|
/* Append formatted channel stop to output */
|
|
strlcat( buf, chan, buflen );
|
|
curlen = newlen;
|
|
}
|
|
|
|
/* No channel stops at all? */
|
|
if (sep == '=')
|
|
strlcat( buf, "=0", buflen );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Count number of tokens in a string */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int tkcount( const char* str, const char* delims )
|
|
{
|
|
char* w; // (work copy of passed str)
|
|
char* p; // (work ptr)
|
|
int k; // (number of tokens counted)
|
|
|
|
/* Make private copy of string, keep tokenizing it until no more
|
|
tokens, counting as we go, then free temporary work string. */
|
|
w = strdup( str );
|
|
for (k=0, p=strtok( w, delims ); p; k++, p=strtok( NULL, delims ));
|
|
free( w );
|
|
return k;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Remove leading chars from a string and return str */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT char* ltrim( char* str, const char* dlm ) // (Left trim)
|
|
{
|
|
char* p1; // (points to first non-dlm)
|
|
char* p2; // (work for shifting to remove leading dlm's)
|
|
const char* d; // (iterates through dlm's)
|
|
|
|
/* Skip leading dlm's to locate first non-dlm character */
|
|
for (p1=str, d=dlm; *p1 && *d; )
|
|
{
|
|
if (*p1 == *d) // (is this one of the delimiters?)
|
|
p1++, d=dlm; // (yes, get past it and reset scan)
|
|
else
|
|
d++; // (no, check next delimiter if any)
|
|
}
|
|
|
|
/* Shift left remaining chars left via p2 */
|
|
for (p2=str; *p1; *p2++ = *p1++);
|
|
|
|
/* Terminate string */
|
|
*p2 = 0;
|
|
|
|
return str;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Remove trailing chars from a string and return str */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT char* rtrim( char* str, const char* dlm ) // (right trim)
|
|
{
|
|
char* p1; // (work ptr for iterating backward thru string)
|
|
const char* d; // (work ptr for iterating through delimiters)
|
|
|
|
/* Point to last char of str */
|
|
p1 = str + strlen( str ) - 1;
|
|
|
|
/* Replace all trailing dlm's with nulls */
|
|
for (d=dlm; p1 >= str && *d; )
|
|
{
|
|
if (*p1 == *d) // (is this one of the delimiters?)
|
|
*p1-- = 0, d=dlm; // (yes, remove it and reset scan)
|
|
else
|
|
d++; // (no, check next delimiter if any)
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Remove leading and trailing chars from a string and return str */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT char* trim( char* str, const char* dlm ) // (trim both)
|
|
{
|
|
return rtrim( ltrim( str, dlm ), dlm );
|
|
}
|
|
|
|
#if defined( HAVE_PTHREAD_SETNAME_NP ) // !defined( _MSVC_ ) implied
|
|
/*-------------------------------------------------------------------*/
|
|
/* Set thread name (nonstandard GNU extension) */
|
|
/* (note: retcode is error code, NOT errno) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int nix_set_thread_name( pthread_t tid, const char* name )
|
|
{
|
|
int rc = 0;
|
|
char threadname[16];
|
|
|
|
if (!name) return 0; /* (ignore premature calls) */
|
|
STRLCPY( threadname, name );
|
|
|
|
/* Some platforms (e.g. Mac) can only set name of current thread */
|
|
|
|
#if defined( PTHREAD_SET_NAME_ONLY )
|
|
if (!pthread_equal( tid, pthread_self()))
|
|
return EINVAL;
|
|
rc = pthread_setname_np( threadname );
|
|
#elif defined( PTHREAD_SET_NAME_3ARGS )
|
|
rc = pthread_setname_np( tid, "%s", threadname );
|
|
#else
|
|
rc = pthread_setname_np( tid, threadname );
|
|
#endif
|
|
|
|
/* Ignore any error; it either worked or it didn't */
|
|
return rc;
|
|
}
|
|
|
|
#endif // !defined( _MSVC_ )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to parse an argument string. The string that is passed */
|
|
/* is modified in-place by inserting null characters at the end of */
|
|
/* each argument found. The returned array of argument pointers */
|
|
/* then points to each argument found in the original string. Any */
|
|
/* argument (except the first one) that begins with '#' character */
|
|
/* is considered a line comment and causes early termination of */
|
|
/* parsing and is not included in the argument count. If the first */
|
|
/* argument begins with a '#' character, it is treated as a command */
|
|
/* and parsing continues as normal, thus allowing comments to be */
|
|
/* processed as commands. Any argument beginning with a double quote */
|
|
/* or single apostrophe causes all characters up to the next quote */
|
|
/* or apostrophe to be included as part of that argument. The quotes */
|
|
/* and/or apostrophes themselves are not considered to be a part of */
|
|
/* the argument itself and are replaced with nulls. */
|
|
/* p Points to string to be parsed. */
|
|
/* maxargc Maximum allowable number of arguments. (Prevents */
|
|
/* overflowing the pargv array) */
|
|
/* pargv Pointer to buffer for argument pointer array. */
|
|
/* pargc Pointer to number of arguments integer result. */
|
|
/* Returns number of arguments found. (same value as at *pargc) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int parse_args( char* p, int maxargc, char** pargv, int* pargc )
|
|
{
|
|
*pargc = 0;
|
|
*pargv = NULL;
|
|
|
|
while (*p && *pargc < maxargc)
|
|
{
|
|
while (*p && isspace((unsigned char)*p))
|
|
{
|
|
p++;
|
|
}
|
|
if (!*p)
|
|
{
|
|
break; /* find start of arg */
|
|
}
|
|
|
|
if (*p == '#' && *pargc)
|
|
{
|
|
break; /* stop when line comment reached */
|
|
}
|
|
|
|
/* Count new arg */
|
|
*pargv = p;
|
|
++*pargc;
|
|
|
|
while (*p && !isspace((unsigned char)*p) && *p != '\"' && *p != '\'')
|
|
{
|
|
p++;
|
|
}
|
|
if (!*p)
|
|
{
|
|
break; /* find end of arg */
|
|
}
|
|
|
|
if (*p == '\"' || *p == '\'')
|
|
{
|
|
char delim = *p;
|
|
if (p == *pargv)
|
|
{
|
|
*pargv = p+1;
|
|
}
|
|
do {} while (*++p && *p != delim);
|
|
if (!*p)
|
|
{
|
|
break; /* find end of quoted string */
|
|
}
|
|
}
|
|
|
|
*p++ = 0; /* mark end of arg */
|
|
pargv++; /* next arg ptr */
|
|
}
|
|
|
|
return *pargc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Ensure all calls to "rand()" return a hopefully unique value */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void init_random()
|
|
{
|
|
struct timeval tv = {0};
|
|
gettimeofday( &tv, NULL );
|
|
srand( (U32) tv.tv_usec );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if string is numeric or hexadecimal */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool is_numeric( const char* str )
|
|
{
|
|
return is_numeric_l( str, strlen( str ));
|
|
}
|
|
DLL_EXPORT bool is_numeric_l( const char* str, size_t len )
|
|
{
|
|
size_t i;
|
|
for (i=0; i < len; i++)
|
|
if (!(str[i] >= '0' && str[i] <= '9'))
|
|
return false;
|
|
return len ? true : false; // (empty string is not numeric)
|
|
}
|
|
DLL_EXPORT bool is_hex( const char* str )
|
|
{
|
|
return is_hex_l( str, strlen( str ));
|
|
}
|
|
DLL_EXPORT bool is_hex_l( const char* str, size_t len )
|
|
{
|
|
size_t i;
|
|
for (i=0; i < len; i++)
|
|
if (!(0
|
|
|| (str[i] >= '0' && str[i] <= '9')
|
|
|| (str[i] >= 'a' && str[i] <= 'f')
|
|
|| (str[i] >= 'A' && str[i] <= 'F')
|
|
))
|
|
return false;
|
|
return len ? true : false; // (empty string is not hex)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutines to convert strings to upper or lower case */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void string_to_upper( char* source )
|
|
{
|
|
int i;
|
|
for (i=0; source[i]; i++)
|
|
source[i] = toupper( (unsigned char)source[i] );
|
|
}
|
|
DLL_EXPORT void string_to_lower( char* source )
|
|
{
|
|
int i;
|
|
for (i=0; source[i]; i++)
|
|
source[i] = tolower( (unsigned char)source[i] );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to convert a string to EBCDIC and pad with blanks */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void convert_to_ebcdic( BYTE* dest, int len, const char* source )
|
|
{
|
|
int i;
|
|
for (i=0; i < len && source[i]; i++)
|
|
dest[i] = host_to_guest( source[i] );
|
|
while (i < len)
|
|
dest[i++] = 0x40;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to convert an EBCDIC string to an ASCIIZ string. */
|
|
/* Removes trailing blanks and adds a terminating null. */
|
|
/* Returns the length of the ASCII string excluding terminating null */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int make_asciiz( char* dest, int destlen, BYTE* src, int srclen )
|
|
{
|
|
int len;
|
|
for (len=0; len < srclen && len < destlen-1; len++)
|
|
dest[len] = guest_to_host( src[len] );
|
|
while (len > 0 && dest[len-1] == SPACE)
|
|
len--;
|
|
dest[len] = 0;
|
|
return len;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to convert DEVBLK halt or clear type to string */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* str_HOC( int hoc )
|
|
{
|
|
static const char* hoc_str[6] =
|
|
{
|
|
// Must match DEVBLK "hoc" value!
|
|
|
|
"(none)",
|
|
"HSCH",
|
|
"CSCH",
|
|
"HIO or HDV",
|
|
"HALT",
|
|
"Device Reset"
|
|
};
|
|
if (hoc >= 0 && hoc < (int) _countof( hoc_str ))
|
|
return hoc_str[ hoc ];
|
|
return "???";
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* idx_snprintf */
|
|
/* Fix for "Potential snprintf buffer overflow" #457 */
|
|
/*-------------------------------------------------------------------*/
|
|
/* This function is a replacement for code that builds a message */
|
|
/* piecemeal (a little bit at a time) via a series of snprint calls */
|
|
/* indexing each time a little bit further into the message buffer */
|
|
/* until the entire message is built: */
|
|
/* */
|
|
/* char buf[64]; */
|
|
/* size_t bufl = sizeof( buf ); */
|
|
/* int n = 0; */
|
|
/* */
|
|
/* n = snprintf( buf, bufl, "%part1", part1 ); */
|
|
/* n += snprintf( buf+n, bufl-n, "%part2", part2 ); */
|
|
/* n += snprintf( buf+n, bufl-n, "%part3", part3 ); */
|
|
/* ... */
|
|
/* n += snprintf( buf+n, bufl-n, "%lastpart", lastpart ); */
|
|
/* */
|
|
/* WRMSG( HHCnnnn, "I", buf ); */
|
|
/* */
|
|
/* The problem with the above code is there is no check to ensure */
|
|
/* a buffer overflow does not occur due to trying to format more */
|
|
/* data than the buffer can hold. */
|
|
/* */
|
|
/* The idx_snprintf function accomplishes the same thing but without */
|
|
/* overflowing the buffer. It checks to ensure the idx buffer offset */
|
|
/* value never exceeds the size of the buffer. */
|
|
/* */
|
|
/* The return value is the same as what snprintf returns, BUT THE */
|
|
/* BUFFER ADDRESS AND SIZE PASSED TO THE FUNCTION is the *original* */
|
|
/* address and size of the buffer (i.e. the raw buf and bufl value */
|
|
/* but NOT offset by the index): */
|
|
/* */
|
|
/* char buf[64]; */
|
|
/* size_t bufl = sizeof( buf ); */
|
|
/* int n = 0; */
|
|
/* */
|
|
/* n = idx_snprintf( n, buf, bufl, "%part1", part1 ); */
|
|
/* n += idx_snprintf( n, buf, bufl, "%part2", part2 ); */
|
|
/* n += idx_snprintf( n, buf, bufl, "%part3", part3 ); */
|
|
/* ... */
|
|
/* n += idx_snprintf( n, buf, bufl, "%lastpart", lastpart ); */
|
|
/* */
|
|
/* WRMSG( HHCnnnn, "I", buf ); */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int idx_snprintf( int idx, char* buffer, size_t bufsiz, const char* fmt, ... )
|
|
{
|
|
int rc;
|
|
va_list vargs;
|
|
|
|
if (idx < 0 || !bufsiz || (size_t)idx >= bufsiz)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "W", "idx_snprintf", "OVERFLOW (bufsiz too small)" );
|
|
idx = (int)(bufsiz ? bufsiz-1 : 0); /* (prevent buffer overflow) */
|
|
}
|
|
|
|
va_start( vargs, fmt );
|
|
rc = vsnprintf( buffer+idx, bufsiz-idx, fmt, vargs );
|
|
va_end( vargs );
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Elbrus e2k
|
|
*-------------------------------------------------------------------*/
|
|
#if defined(__e2k__) && defined(__LCC__)
|
|
|
|
volatile char __e2k_lockflags[__E2K_LOCKFLAGS_SIZE];
|
|
|
|
int __cmpxchg16_e2k(volatile char *lockflag, U64 *old1, U64 *old2, U64 new1,
|
|
U64 new2, volatile void *ptr)
|
|
{
|
|
// returns 0 == success, 1 otherwise
|
|
int result;
|
|
|
|
while (__atomic_test_and_set(lockflag, __ATOMIC_ACQUIRE) == 1)
|
|
;
|
|
|
|
if (*old1 == *(U64 *)ptr && *old2 == *((U64 *)ptr + 1)) {
|
|
*(U64 *)ptr = new1;
|
|
*((U64 *)ptr + 1) = new2;
|
|
result = 0;
|
|
} else {
|
|
*old1 = *((U64 *)ptr);
|
|
*old2 = *((U64 *)ptr + 1);
|
|
result = 1;
|
|
}
|
|
|
|
__atomic_clear(lockflag, __ATOMIC_RELEASE);
|
|
|
|
return result;
|
|
}
|
|
#endif /* e2k && LCC */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Processor types table and associated query functions */
|
|
/*-------------------------------------------------------------------*/
|
|
struct PTYPTAB
|
|
{
|
|
const BYTE ptyp; // 1-byte processor type (service.h)
|
|
const char* shortname; // 2-character short name (Hercules)
|
|
const char* longname; // 16-character long name (diag 224)
|
|
};
|
|
typedef struct PTYPTAB PTYPTAB;
|
|
|
|
static PTYPTAB ptypes[] =
|
|
{
|
|
{ SCCB_PTYP_CP, "CP", "CP " }, // 0
|
|
{ SCCB_PTYP_UNKNOWN, "??", " " }, // 1 (unknown == blanks)
|
|
{ SCCB_PTYP_ZAAP, "AP", "ZAAP " }, // 2
|
|
{ SCCB_PTYP_IFL, "IL", "IFL " }, // 3
|
|
{ SCCB_PTYP_ICF, "CF", "ICF " }, // 4
|
|
{ SCCB_PTYP_ZIIP, "IP", "ZIIP " }, // 5
|
|
};
|
|
|
|
DLL_EXPORT const char* ptyp2long( BYTE ptyp )
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < _countof( ptypes ); i++)
|
|
if (ptypes[i].ptyp == ptyp)
|
|
return ptypes[i].longname;
|
|
return " "; // 16 blanks
|
|
}
|
|
|
|
DLL_EXPORT const char* ptyp2short( BYTE ptyp )
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < _countof( ptypes ); i++)
|
|
if (ptypes[i].ptyp == ptyp)
|
|
return ptypes[i].shortname;
|
|
return "??";
|
|
}
|
|
|
|
DLL_EXPORT BYTE short2ptyp( const char* shortname )
|
|
{
|
|
unsigned int i;
|
|
for (i=0; i < _countof( ptypes ); i++)
|
|
if (strcasecmp( ptypes[i].shortname, shortname ) == 0)
|
|
return ptypes[i].ptyp;
|
|
return SCCB_PTYP_UNKNOWN;
|
|
}
|
|
|
|
DLL_EXPORT U64 do_make_psw64( PSW* psw, BYTE real_ilc, int arch /*370/390/900*/, bool bc )
|
|
{
|
|
/* Return first/only 64-bit DWORD of the PSW -- IN HOST FORMAT!
|
|
|
|
Caller is responsible for doing the STORE_DW on the returned
|
|
value, which does a CSWAP to place it into guest storage in
|
|
proper big endian format. The 900 mode caller (i.e. z/Arch)
|
|
is also responsible for doing the store of the second DWORD
|
|
of the 16-byte z/Arch PSW = the 64-bit instruction address.
|
|
*/
|
|
|
|
BYTE b0, b1, b2, b3, b4;
|
|
U16 b23;
|
|
U32 b567, b4567;
|
|
U64 psw64 = 0;
|
|
|
|
switch (arch)
|
|
{
|
|
case 370:
|
|
|
|
if (bc) {
|
|
// 370 BC-mode
|
|
//
|
|
// +---------------+---+-----+------+---------------+----
|
|
// | channel masks | E | key | 0MWP | interupt code | ..
|
|
// +---------------+---+-----+------+---------------+----
|
|
// 0 7 8 12 16 31
|
|
|
|
// ---+-----+----+------+------------------------------+
|
|
// ..| ilc | cc | mask | instruction address |
|
|
// ---+-----+----+------+------------------------------+
|
|
// 32 34 36 40 63
|
|
|
|
b0 = psw->sysmask;
|
|
|
|
b1 = 0
|
|
| psw->pkey
|
|
| psw->states
|
|
;
|
|
|
|
b23 = psw->intcode;
|
|
|
|
b4 = 0
|
|
| ((real_ilc >> 1) << 6) // CAREFUL! Herc's ilc value
|
|
// is the ACTUAL length (2,
|
|
// 4 or 6), but the value in
|
|
// the PSW is either 1,2,3!)
|
|
| (psw->cc << 4)
|
|
| psw->progmask
|
|
;
|
|
|
|
b567 = psw->IA_L;
|
|
|
|
if (!psw->zeroilc)
|
|
b567 &= AMASK24;
|
|
|
|
psw64 = 0
|
|
| ( (U64) b0 << (64-(1*8)) )
|
|
| ( (U64) b1 << (64-(2*8)) )
|
|
| ( (U64) b23 << (64-(4*8)) )
|
|
| ( (U64) b4 << (64-(5*8)) )
|
|
| ( (U64) b567 << (64-(8*8)) )
|
|
;
|
|
break;
|
|
}
|
|
|
|
/* Not 370 BC-mode = 370 EC-mode. Fall through to the 390 case,
|
|
which handles both ESA/390 mode and S/370 EC-mode PSWs too.
|
|
|
|
The below special "FALLTHRU" comment lets GCC know that we are
|
|
purposely falling through to the next switch case and is needed
|
|
in order to suppress the warning that GCC would otherwise issue.
|
|
*/
|
|
/* FALLTHRU */
|
|
|
|
case 390:
|
|
// 370 EC-mode
|
|
//
|
|
// +------+------+-----+------+----+----+------+----------+---
|
|
// | 0R00 | 0TIE | key | 1MWP | S0 | cc | mask | 00000000 | ..
|
|
// +------+------+-----+------+----+----+------+----------+---
|
|
// 0 4 8 12 16 18 20 24
|
|
|
|
// ---+----------+-------------------------------------------+
|
|
// ..| 00000000 | instruction address | (370)
|
|
// ---+----------+-------------------------------------------+
|
|
// 32 40 63
|
|
//
|
|
// ESA/390
|
|
//
|
|
// +------+------+-----+------+----+----+------+----------+---
|
|
// | 0R00 | 0TIE | key | 1MWP | AS | cc | mask | 00000000 | ..
|
|
// +------+------+-----+------+----+----+------+----------+---
|
|
// 0 4 8 12 16 18 20 24
|
|
//
|
|
// ---+---+--------------------------------------------------+
|
|
// ..| A | instruction address | (390)
|
|
// ---+---+--------------------------------------------------+
|
|
// 32 33 63
|
|
|
|
b0 = psw->sysmask;
|
|
|
|
b1 = 0
|
|
| psw->pkey
|
|
| psw->states
|
|
;
|
|
|
|
b2 = 0
|
|
| psw->asc // (S0 or AS)
|
|
| (psw->cc << 4)
|
|
| psw->progmask
|
|
;
|
|
|
|
b3 = psw->zerobyte;
|
|
|
|
b4567 = psw->IA_L;
|
|
|
|
if (!psw->zeroilc)
|
|
b4567 &= psw->amode ? AMASK31 : AMASK24;
|
|
|
|
if (psw->amode)
|
|
b4567 |= 0x80000000;
|
|
|
|
psw64 = 0
|
|
| ( (U64) b0 << (64-(1*8)) )
|
|
| ( (U64) b1 << (64-(2*8)) )
|
|
| ( (U64) b2 << (64-(3*8)) )
|
|
| ( (U64) b3 << (64-(4*8)) )
|
|
| ( (U64) b4567 << (64-(8*8)) )
|
|
;
|
|
break;
|
|
|
|
case 900:
|
|
// z/Architecture
|
|
//
|
|
// +------+------+-----+------+----+----+------+-----------+---
|
|
// | 0R00 | 0TIE | key | 0MWP | AS | cc | mask | 0000 000E | ..
|
|
// +------+------+-----+------+----+----+------+-----------+---
|
|
// 0 4 8 12 16 18 20 24 31
|
|
//
|
|
// ---+---+---------------------------------------------------+
|
|
// ..| B | 0000000000000000000000000000000000000000000000000 |
|
|
// ---+---+---------------------------------------------------+
|
|
// 32 33 63
|
|
|
|
b0 = psw->sysmask;
|
|
|
|
b1 = 0
|
|
| psw->pkey
|
|
| psw->states
|
|
;
|
|
|
|
b2 = 0
|
|
| psw->asc
|
|
| (psw->cc << 4)
|
|
| psw->progmask
|
|
;
|
|
|
|
b3 = psw->zerobyte;
|
|
|
|
if (psw->amode64)
|
|
b3 |= 0x01;
|
|
|
|
b4567 = psw->zeroword;
|
|
|
|
if (psw->amode)
|
|
b4567 |= 0x80000000;
|
|
|
|
psw64 = 0
|
|
| ( (U64) b0 << (64-(1*8)) )
|
|
| ( (U64) b1 << (64-(2*8)) )
|
|
| ( (U64) b2 << (64-(3*8)) )
|
|
| ( (U64) b3 << (64-(4*8)) )
|
|
| ( (U64) b4567 << (64-(8*8)) )
|
|
;
|
|
break;
|
|
|
|
default: // LOGIC ERROR!
|
|
CRASH(); // LOGIC ERROR!
|
|
}
|
|
return psw64;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return Program Interrupt Name */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* PIC2Name( int pcode )
|
|
{
|
|
static const char* pgmintname[] =
|
|
{
|
|
/* 01 */ "Operation exception",
|
|
/* 02 */ "Privileged-operation exception",
|
|
/* 03 */ "Execute exception",
|
|
/* 04 */ "Protection exception",
|
|
/* 05 */ "Addressing exception",
|
|
/* 06 */ "Specification exception",
|
|
/* 07 */ "Data exception",
|
|
/* 08 */ "Fixed-point-overflow exception",
|
|
/* 09 */ "Fixed-point-divide exception",
|
|
/* 0A */ "Decimal-overflow exception",
|
|
/* 0B */ "Decimal-divide exception",
|
|
/* 0C */ "HFP-exponent-overflow exception",
|
|
/* 0D */ "HFP-exponent-underflow exception",
|
|
/* 0E */ "HFP-significance exception",
|
|
/* 0F */ "HFP-floating-point-divide exception",
|
|
/* 10 */ "Segment-translation exception",
|
|
/* 11 */ "Page-translation exception",
|
|
/* 12 */ "Translation-specification exception",
|
|
/* 13 */ "Special-operation exception",
|
|
/* 14 */ "Pseudo-page-fault exception",
|
|
/* 15 */ "Operand exception",
|
|
/* 16 */ "Trace-table exception",
|
|
/* 17 */ "ASN-translation exception",
|
|
/* 18 */ "Transaction constraint exception",
|
|
/* 19 */ "Vector/Crypto operation exception",
|
|
/* 1A */ "Page state exception",
|
|
/* 1B */ "Vector processing exception",
|
|
/* 1C */ "Space-switch event",
|
|
/* 1D */ "Square-root exception",
|
|
/* 1E */ "Unnormalized-operand exception",
|
|
/* 1F */ "PC-translation specification exception",
|
|
/* 20 */ "AFX-translation exception",
|
|
/* 21 */ "ASX-translation exception",
|
|
/* 22 */ "LX-translation exception",
|
|
/* 23 */ "EX-translation exception",
|
|
/* 24 */ "Primary-authority exception",
|
|
/* 25 */ "Secondary-authority exception",
|
|
/* 26 */ /* "Page-fault-assist exception", */
|
|
/* 26 */ "LFX-translation exception",
|
|
/* 27 */ /* "Control-switch exception", */
|
|
/* 27 */ "LSX-translation exception",
|
|
/* 28 */ "ALET-specification exception",
|
|
/* 29 */ "ALEN-translation exception",
|
|
/* 2A */ "ALE-sequence exception",
|
|
/* 2B */ "ASTE-validity exception",
|
|
/* 2C */ "ASTE-sequence exception",
|
|
/* 2D */ "Extended-authority exception",
|
|
/* 2E */ "LSTE-sequence exception",
|
|
/* 2F */ "ASTE-instance exception",
|
|
/* 30 */ "Stack-full exception",
|
|
/* 31 */ "Stack-empty exception",
|
|
/* 32 */ "Stack-specification exception",
|
|
/* 33 */ "Stack-type exception",
|
|
/* 34 */ "Stack-operation exception",
|
|
/* 35 */ "Unassigned exception",
|
|
/* 36 */ "Unassigned exception",
|
|
/* 37 */ "Unassigned exception",
|
|
/* 38 */ "ASCE-type exception",
|
|
/* 39 */ "Region-first-translation exception",
|
|
/* 3A */ "Region-second-translation exception",
|
|
/* 3B */ "Region-third-translation exception",
|
|
/* 3C */ "Unassigned exception",
|
|
/* 3D */ "Unassigned exception",
|
|
/* 3E */ "Unassigned exception",
|
|
/* 3F */ "Unassigned exception",
|
|
/* 40 */ "Monitor event"
|
|
};
|
|
|
|
int ndx, code = (pcode & 0xFF);
|
|
|
|
if (code == 0x80)
|
|
return "PER event";
|
|
|
|
if (code < 1 || code > (int) _countof( pgmintname ))
|
|
return "Unassigned exception";
|
|
|
|
ndx = ((code - 1) & 0x3F);
|
|
|
|
return (ndx >= 0 && ndx < (int) _countof( pgmintname )) ?
|
|
pgmintname[ ndx ] : "Unassigned exception";
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return SIGP Order Name */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* order2name( BYTE order )
|
|
{
|
|
static const char *ordername[] =
|
|
{
|
|
/* 0x00 */ "Unassigned",
|
|
/* 0x01 SIGP_SENSE */ "Sense",
|
|
/* 0x02 SIGP_EXTCALL */ "External call",
|
|
/* 0x03 SIGP_EMERGENCY */ "Emergency signal",
|
|
/* 0x04 SIGP_START */ "Start",
|
|
/* 0x05 SIGP_STOP */ "Stop",
|
|
/* 0x06 SIGP_RESTART */ "Restart",
|
|
/* 0x07 SIGP_IPR */ "Initial program reset",
|
|
/* 0x08 SIGP_PR */ "Program reset",
|
|
/* 0x09 SIGP_STOPSTORE */ "Stop and store status",
|
|
/* 0x0A SIGP_IMPL */ "Initial microprogram load",
|
|
/* 0x0B SIGP_INITRESET */ "Initial CPU reset",
|
|
/* 0x0C SIGP_RESET */ "CPU reset",
|
|
/* 0x0D SIGP_SETPREFIX */ "Set prefix",
|
|
/* 0x0E SIGP_STORE */ "Store status",
|
|
/* 0x0F */ "Unassigned",
|
|
/* 0x10 */ "Unassigned",
|
|
/* 0x11 SIGP_STOREX */ "Store extended status at address",
|
|
/* 0x12 SIGP_SETARCH */ "Set architecture mode",
|
|
/* 0x13 SIGP_COND_EMERGENCY */ "Conditional emergency",
|
|
/* 0x14 */ "Unassigned",
|
|
/* 0x15 SIGP_SENSE_RUNNING_STATE */ "Sense running state"
|
|
};
|
|
return (order >= _countof( ordername )) ?
|
|
"Unassigned" : ordername[ order ];
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return PER Event name(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* perc2name( BYTE perc, char* buf, size_t bufsiz )
|
|
{ /*
|
|
Hex Bit PER Event
|
|
80 0 Successful-branching
|
|
40 1 Instruction-fetching
|
|
20 2 Storage-alteration
|
|
10 3 Storage-key-alteration
|
|
08 4 Store-using-real-address
|
|
04 5 Zero-address-detection
|
|
02 6 Transaction-end
|
|
01 7 Instruction-fetching nullification (PER-3) */
|
|
|
|
const char* name;
|
|
|
|
switch (perc)
|
|
{
|
|
/*-----------------------------------------------------------*/
|
|
/* Basic single-bit events... */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
case 0x80:
|
|
{
|
|
name = "BRANCH";
|
|
break;
|
|
}
|
|
|
|
case 0x40:
|
|
{
|
|
name = "IFETCH";
|
|
break;
|
|
}
|
|
|
|
case 0x20:
|
|
{
|
|
name = "STOR";
|
|
break;
|
|
}
|
|
|
|
case 0x10:
|
|
{
|
|
/* NOTE: docs say: "A zero is stored in bit position 3 of
|
|
locations 150-151", so this SHOULDN'T(?) occur, but if
|
|
for some reason it does, let's return an indication.
|
|
*/
|
|
name = "SKEY";
|
|
break;
|
|
}
|
|
|
|
#if 0 // (probably illegal? i.e. should never occur? See 0x28 below!)
|
|
|
|
case 0x08:
|
|
{
|
|
name = "STURA";
|
|
break;
|
|
}
|
|
#endif
|
|
case 0x04:
|
|
{
|
|
name = "ZAD";
|
|
break;
|
|
}
|
|
|
|
case 0x02:
|
|
{
|
|
name = "TEND";
|
|
break;
|
|
}
|
|
|
|
case 0x01:
|
|
{
|
|
name = "IFNULL";
|
|
break;
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
/* Exceptions to the rule requiring special handling */
|
|
/*-----------------------------------------------------------*/
|
|
|
|
case 0x24:
|
|
{
|
|
/* "... when ... a storage-alteration ... event is re-
|
|
cognized concurrently with a zero-address-detection
|
|
event, only the storage alteration ... event is in-
|
|
dicated." (Not sure whether that means ONLY the 0x20
|
|
storage alteration bit is set, or whether the "event"
|
|
should simply be TREATED AS a storage event, so to
|
|
be safe, we'll code for it.)
|
|
*/
|
|
name = "STOR";
|
|
break;
|
|
}
|
|
|
|
case 0x28:
|
|
{
|
|
/* "... while ones in bit positions 2 and 4 indicate a
|
|
store-using-real-address event."
|
|
*/
|
|
name = "STURA";
|
|
break;
|
|
}
|
|
|
|
#if 0 // (probably illegal? i.e. should never occur?)
|
|
|
|
case 0x2C:
|
|
{
|
|
/* "... when ... store-using-real-address event is re-
|
|
cognized concurrently with a zero-address-detection
|
|
event, only the ... store-using-real-address event
|
|
is indicated."
|
|
*/
|
|
name = "STURA";
|
|
break;
|
|
}
|
|
#endif
|
|
case 0x41:
|
|
{
|
|
/* "When a program interruption occurs for a PER instruc-
|
|
tion fetching nullification event, bits 1 and 7 are set
|
|
to one in the PER code. No other PER events are concur-
|
|
rently indicated.
|
|
*/
|
|
name = "IFETCH+IFNULL";
|
|
break;
|
|
}
|
|
|
|
case 0x42:
|
|
{
|
|
/* "If an instruction-fetching basic event coincides with
|
|
the transaction-end event, bit 1 is also set to one
|
|
in the PER code."
|
|
*/
|
|
name = "IFETCH+TEND";
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
name = "UNKNOWN!";
|
|
break;
|
|
}
|
|
}
|
|
|
|
strlcpy( buf, name, bufsiz );
|
|
return buf;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Format Operation-Request Block (ORB) for display */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT const char* FormatORB( ORB* orb, char* buf, size_t bufsz )
|
|
{
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
if (bufsz)
|
|
*buf = 0;
|
|
|
|
if (bufsz <= 1 || !orb)
|
|
return buf;
|
|
|
|
snprintf( buf, bufsz,
|
|
|
|
"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]
|
|
);
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Determine if running on a big endian system or not */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool are_big_endian()
|
|
{
|
|
static union
|
|
{
|
|
uint32_t ui32;
|
|
char b[4];
|
|
}
|
|
test = {0x01020304};
|
|
return (0x01 == test.b[0]);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* NON-Windows implementation of "IsDebuggerPresent()" */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if !defined( _MSVC_ ) // Linux, etc..
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#if defined( __APPLE__ ) || defined( __FreeBSD__ )
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#if defined( __FreeBSD__ )
|
|
#include <sys/user.h>
|
|
#include <libutil.h>
|
|
#endif
|
|
|
|
static bool IsDebuggerPresent()
|
|
{
|
|
#if defined( __APPLE__ )
|
|
|
|
// macOS
|
|
|
|
int mib[4];
|
|
struct kinfo_proc info;
|
|
size_t size = sizeof( info );
|
|
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROC;
|
|
mib[2] = KERN_PROC_PID;
|
|
mib[3] = getpid();
|
|
|
|
sysctl( mib, 4, &info, &size, NULL, 0 );
|
|
|
|
return ((info.kp_proc.p_flag & P_TRACED) != 0);
|
|
|
|
#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ )
|
|
|
|
// BSD
|
|
|
|
int mib[4];
|
|
struct kinfo_proc info;
|
|
size_t size = sizeof( info );
|
|
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROC;
|
|
mib[2] = KERN_PROC_PID;
|
|
mib[3] = getpid();
|
|
|
|
sysctl( mib, 4, &info, &size, NULL, 0 );
|
|
|
|
return ((info.ki_flag & P_TRACED) != 0);
|
|
|
|
#elif defined( __linux__ )
|
|
|
|
// Linux
|
|
|
|
FILE* f;
|
|
char buf[ 256 ];
|
|
int tracer_pid;
|
|
bool debugger_is_present = false;
|
|
|
|
snprintf( buf, sizeof( buf ), "/proc/%d/status", getpid() );
|
|
|
|
if ((f = fopen( buf, "r" )))
|
|
{
|
|
while (fgets( buf, sizeof( buf ), f ))
|
|
{
|
|
if (strncmp( buf, "TracerPid:", 10 ) == 0)
|
|
{
|
|
tracer_pid = atoi( &buf[10] );
|
|
|
|
if (tracer_pid != 0)
|
|
{
|
|
debugger_is_present = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose( f );
|
|
}
|
|
|
|
return debugger_is_present;
|
|
|
|
#endif
|
|
|
|
} // IsDebuggerPresent
|
|
|
|
#endif // !MSVC
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Determine if running under the control of a debugger */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT bool check_if_debugger_is_present()
|
|
{
|
|
return (sysblk.is_debugger_present = IsDebuggerPresent() ? true : false);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Format and send a status message to HercGUI */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT void send2gui( const char* pszFormat, ... )
|
|
{
|
|
if (extgui)
|
|
{
|
|
va_list vl;
|
|
int rc;
|
|
char* msg = NULL;
|
|
|
|
// Format the message...
|
|
va_start( vl, pszFormat );
|
|
rc = vasprintf( &msg, pszFormat, vl );
|
|
va_end( vl );
|
|
|
|
// CRASH if invalid function arguments or Out of Memory
|
|
// CRASH if unable to successfully format a message
|
|
if (rc < 0)
|
|
{
|
|
CRASH(); // (WTF?!)
|
|
return;
|
|
}
|
|
|
|
// Send the message to HercGUI...
|
|
|
|
LOG_H_to_G( msg ); // (Herc <--> stream debugging)
|
|
|
|
obtain_lock( &sysblk.gui_msglock );
|
|
{
|
|
fprintf( stderr, "%s", msg );
|
|
fflush( stderr );
|
|
}
|
|
release_lock( &sysblk.gui_msglock );
|
|
|
|
free( msg );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* convert a version string to a number or vice-versa */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT U32 verstr2num( const char* verstr )
|
|
{
|
|
U32 numvers, major = 0, minor = 0, bugfix = 0;
|
|
sscanf( verstr, "%u.%u.%u", &major, &minor, &bugfix );
|
|
|
|
MINMAX( major, 0, 999 );
|
|
MINMAX( minor, 0, 999 );
|
|
MINMAX( bugfix, 0, 999 );
|
|
|
|
numvers = (major * 1000000) + (minor * 1000) + (bugfix * 1);
|
|
return numvers;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
DLL_EXPORT char* vernum2str( U32 vernum, char* verstr )
|
|
{
|
|
U32 major, minor, bugfix;
|
|
|
|
major = (vernum / 1000000); vernum -= (major * 1000000);
|
|
minor = (vernum / 1000); vernum -= (minor * 1000);
|
|
bugfix = (vernum / 1);
|
|
|
|
snprintf( verstr, VERNUM2STR_BUFFSIZE, "%u.%u.%u"
|
|
, major
|
|
, minor
|
|
, bugfix
|
|
);
|
|
|
|
return verstr;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
/** **/
|
|
/** TraceFile support **/
|
|
/** **/
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Determine if endian swaps are going to be needed or not */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool tf_are_swaps_needed( TFSYS* sys )
|
|
{
|
|
return
|
|
(0
|
|
|| (1
|
|
&& are_big_endian() // (we are big endian)
|
|
&& sys->bigend == 0 // (but file is little endian)
|
|
)
|
|
|| (1
|
|
&& !are_big_endian() // (we are little endian)
|
|
&& sys->bigend == 1 // (but file is big endian)
|
|
)
|
|
);
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// (helper function to call correct ARCH_DEP function)
|
|
//---------------------------------------------------------------------
|
|
static int tf_virt_to_real( U64* raptr, int* siptr, U64 vaddr, int arn, REGS* regs, int acctype )
|
|
{
|
|
switch (regs->arch_mode)
|
|
{
|
|
#if defined( _370 )
|
|
case ARCH_370_IDX:
|
|
return sysblk.s370_vtr( raptr, siptr, vaddr, arn, regs, acctype );
|
|
#endif
|
|
#if defined( _390 )
|
|
case ARCH_390_IDX:
|
|
return sysblk.s390_vtr( raptr, siptr, vaddr, arn, regs, acctype );
|
|
#endif
|
|
#if defined( _900 )
|
|
case ARCH_900_IDX:
|
|
return sysblk.z900_vtr( raptr, siptr, vaddr, arn, regs, acctype );
|
|
#endif
|
|
default: CRASH();
|
|
UNREACHABLE_CODE( return 0 );
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// (helper function to call correct ARCH_DEP function)
|
|
//---------------------------------------------------------------------
|
|
static BYTE tf_get_storage_key( REGS* regs, U64 abs )
|
|
{
|
|
switch (regs->arch_mode)
|
|
{
|
|
#if defined( _370 )
|
|
case ARCH_370_IDX:
|
|
return sysblk.s370_gsk( abs );
|
|
#endif
|
|
#if defined( _390 )
|
|
case ARCH_390_IDX:
|
|
return sysblk.s390_gsk( abs );
|
|
#endif
|
|
#if defined( _900 )
|
|
case ARCH_900_IDX:
|
|
return sysblk.z900_gsk( abs );
|
|
#endif
|
|
default: CRASH();
|
|
UNREACHABLE_CODE( return 0 );
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// (helper function to call correct ARCH_DEP function)
|
|
//---------------------------------------------------------------------
|
|
static void tf_store_int_timer( REGS* regs, U64 raddr )
|
|
{
|
|
switch (regs->arch_mode)
|
|
{
|
|
#if defined( _370 )
|
|
case ARCH_370_IDX:
|
|
if (ITIMER_ACCESS( raddr, 16 ))
|
|
sysblk.s370_sit( regs );
|
|
return;
|
|
#endif
|
|
#if defined( _390 )
|
|
case ARCH_390_IDX:
|
|
// 390 doesn't have interval timers
|
|
return;
|
|
#endif
|
|
#if defined( _900 )
|
|
case ARCH_900_IDX:
|
|
// 900 doesn't have interval timers
|
|
return;
|
|
#endif
|
|
default: CRASH();
|
|
UNREACHABLE_CODE( return );
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// (helper function to call correct ARCH_DEP function)
|
|
//---------------------------------------------------------------------
|
|
static U64 tf_apply_prefixing( U64 radr, REGS* regs )
|
|
{
|
|
switch (regs->arch_mode)
|
|
{
|
|
#if defined( _370 )
|
|
case ARCH_370_IDX:
|
|
return APPLY_370_PREFIXING( radr, regs->PX_370 );
|
|
#endif
|
|
#if defined( _390 )
|
|
case ARCH_390_IDX:
|
|
return APPLY_390_PREFIXING( radr, regs->PX_390 );
|
|
#endif
|
|
#if defined( _900 )
|
|
case ARCH_900_IDX:
|
|
return APPLY_900_PREFIXING( radr, regs->PX_900 );
|
|
#endif
|
|
default: CRASH();
|
|
UNREACHABLE_CODE( return 0 );
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* TFSYS record */
|
|
/*-------------------------------------------------------------------*/
|
|
static const size_t tfsys_size = sizeof( TFSYS );
|
|
static TFSYS tfsys = {0};
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write starting TFSYS record */
|
|
/*-------------------------------------------------------------------*/
|
|
static bool tf_write_initial_TFSYS_locked( TIMEVAL* tod )
|
|
{
|
|
size_t written;
|
|
bool ret = true;
|
|
|
|
/* Initialize TFSYS record */
|
|
|
|
tfsys.ffmt[0] = '%';
|
|
tfsys.ffmt[1] = 'T';
|
|
tfsys.ffmt[2] = 'F';
|
|
tfsys.ffmt[3] = TF_FMT;
|
|
|
|
tfsys.bigend = are_big_endian() ? 1 : 0;
|
|
tfsys.engs = MAX_CPU_ENGS;
|
|
tfsys.archnum0 = _ARCH_NUM_0;
|
|
tfsys.archnum1 = _ARCH_NUM_1;
|
|
tfsys.archnum2 = _ARCH_NUM_2;
|
|
|
|
memcpy( &tfsys.beg_tod, tod, sizeof( tfsys.beg_tod ));
|
|
memset( &tfsys.end_tod, 0, sizeof( tfsys.end_tod ));
|
|
|
|
STRLCPY( tfsys.version, *sysblk.vers_info );
|
|
memcpy( &tfsys.ptyp[0], &sysblk.ptyp[0], MAX_CPU_ENGS );
|
|
|
|
if ((written = fwrite( &tfsys, 1, tfsys_size,
|
|
sysblk.traceFILE )) < tfsys_size)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fwrite()", strerror( errno ));
|
|
ret = false;
|
|
}
|
|
|
|
sysblk.curtracesize += written;
|
|
|
|
// "Trace file tracing begun..."
|
|
WRMSG( HHC02383, "I" );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close TraceFile */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void tf_close_locked()
|
|
{
|
|
/* Quick exit if already closed */
|
|
if (!sysblk.traceFILE)
|
|
return;
|
|
|
|
if (fseek( sysblk.traceFILE, 0, SEEK_SET ) != 0)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fseek()", strerror( errno ));
|
|
}
|
|
else if (fwrite( &tfsys, 1, tfsys_size, sysblk.traceFILE ) < tfsys_size)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fwrite()", strerror( errno ));
|
|
}
|
|
|
|
/* Now close the file */
|
|
VERIFY( 0 == fclose( sysblk.traceFILE ));
|
|
sysblk.traceFILE = NULL;
|
|
sysblk.curtracesize = 0;
|
|
}
|
|
|
|
DLL_EXPORT void tf_close( void* notused )
|
|
{
|
|
UNREFERENCED( notused );
|
|
|
|
OBTAIN_TRACEFILE_LOCK();
|
|
{
|
|
tf_close_locked();
|
|
}
|
|
RELEASE_TRACEFILE_LOCK();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write TraceFile record */
|
|
/*-------------------------------------------------------------------*/
|
|
static bool tf_write( REGS* regs, void* rec, U16 curr, U16 msgnum )
|
|
{
|
|
static size_t bufsize = 0;
|
|
static U16 prev = 0;
|
|
|
|
TFHDR* hdr;
|
|
size_t written;
|
|
TIMEVAL tod;
|
|
TID tid;
|
|
bool nostop;
|
|
bool auto_closed = false; // (true when MAX= reached)
|
|
bool ret = true;
|
|
|
|
OBTAIN_TRACEFILE_LOCK();
|
|
{
|
|
/* Quick exit if tracefile tracing isn't active */
|
|
if (!sysblk.traceFILE)
|
|
{
|
|
RELEASE_TRACEFILE_LOCK();
|
|
return true;
|
|
}
|
|
|
|
gettimeofday( (TIMEVAL*) &tod, NULL );
|
|
|
|
/* Write TFSYS record if first time */
|
|
if (!sysblk.curtracesize)
|
|
{
|
|
if (!tf_write_initial_TFSYS_locked( &tod ))
|
|
{
|
|
// (error message already issued)
|
|
RELEASE_TRACEFILE_LOCK();
|
|
return false;
|
|
}
|
|
|
|
bufsize = tf_MAX_RECSIZE();
|
|
prev = 0;
|
|
|
|
/* Register shutdown function so close updates TFSYS */
|
|
hdl_addshut( "tf_close", tf_close, NULL );
|
|
}
|
|
|
|
/* Copy caller's entire record into our buffer */
|
|
if ((size_t)curr > bufsize) CRASH();
|
|
hdr = (TFHDR*) sysblk.tracefilebuff;
|
|
memcpy( hdr, rec, curr );
|
|
|
|
/* Finish building their header */
|
|
hdr->prev = prev;
|
|
hdr->curr = curr;
|
|
hdr->cpuad = regs ? regs->cpuad : 0xFFFF;
|
|
hdr->msgnum = msgnum;
|
|
hdr->arch_mode = regs ? regs->arch_mode : 0xFF;
|
|
hdr->tod = tod;
|
|
tid = hthread_self();
|
|
hdr->tidnum = TID_CAST( tid );
|
|
get_thread_name( tid, hdr->thrdname );
|
|
|
|
/* Update the TFSYS record's end time too */
|
|
memcpy( &tfsys.end_tod, &tod, sizeof( tfsys.end_tod ));
|
|
|
|
/* Write caller's entire record to the trace file */
|
|
if ((written = fwrite( hdr, 1, curr,
|
|
sysblk.traceFILE )) < curr)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00075, "E", "fwrite()", strerror( errno ));
|
|
ret = false;
|
|
}
|
|
|
|
/* Update variables for next time */
|
|
sysblk.curtracesize += written;
|
|
prev = curr;
|
|
|
|
/* Count total trace records */
|
|
if (hdr->cpuad == 0xFFFF)
|
|
tfsys.tot_dev++;
|
|
else
|
|
{
|
|
if (hdr->msgnum == 2324)
|
|
tfsys.tot_ins++;
|
|
}
|
|
|
|
/* Stop tracing when MAX size exceeded */
|
|
if (sysblk.curtracesize > sysblk.maxtracesize)
|
|
{
|
|
tf_close_locked();
|
|
auto_closed = true; // (handled further below)
|
|
nostop = sysblk.tfnostop ? true : false;
|
|
}
|
|
}
|
|
RELEASE_TRACEFILE_LOCK();
|
|
|
|
/* If file automatically closed due to MAX= being reached... */
|
|
if (auto_closed)
|
|
{
|
|
/* Stop all instruction tracing too unless asked not to */
|
|
if (nostop)
|
|
{
|
|
// "Trace file MAX= reached; file closed, tracing %s"
|
|
WRMSG( HHC02379, "I", "continues" );
|
|
}
|
|
else
|
|
{
|
|
tf_autostop();
|
|
|
|
// "Trace file MAX= reached; file closed, tracing %s"
|
|
WRMSG( HHC02379, "I", "auto-stopped" );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Automatically stop all tracing
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_autostop()
|
|
{
|
|
bool bWasActive;
|
|
|
|
/* Stop all tracing */
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
DEVBLK* dev;
|
|
int cpu;
|
|
|
|
/* Stop overall instruction tracing */
|
|
bWasActive = sysblk.insttrace ? true : false;
|
|
sysblk.insttrace = false;
|
|
|
|
/* Stop instruction tracing for all CPUs */
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (IS_CPU_ONLINE( cpu ))
|
|
{
|
|
bWasActive = bWasActive || sysblk.regs[ cpu ]->insttrace;
|
|
sysblk.regs[ cpu ]->insttrace = false;
|
|
}
|
|
}
|
|
|
|
/* Stop ORB/CCW/KEY tracing for all devices */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
{
|
|
bWasActive = bWasActive || dev->ccwtrace || dev->orbtrace || dev->ckdkeytrace;
|
|
dev->ccwtrace = false;
|
|
dev->orbtrace = false;
|
|
dev->ckdkeytrace = false;
|
|
}
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
return bWasActive;
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Search key
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0423( DEVBLK* dev, size_t kl, BYTE* key )
|
|
{
|
|
TF00423 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.kl = (BYTE) kl;
|
|
memcpy( rec.key, key, kl );
|
|
return tf_write( NULL, &rec, sizeof( TF00423 ), 423 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cur trk
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0424( DEVBLK* dev, S32 trk )
|
|
{
|
|
TF00424 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
rec.bufcur = dev->bufcur;
|
|
return tf_write( NULL, &rec, sizeof( TF00424 ), 424 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Updating track
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0425( DEVBLK* dev )
|
|
{
|
|
TF00425 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.bufcur = dev->bufcur;
|
|
return tf_write( NULL, &rec, sizeof( TF00425 ), 425 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cache hit
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0426( DEVBLK* dev, S32 trk, S32 i )
|
|
{
|
|
TF00426 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
rec.idx = i;
|
|
return tf_write( NULL, &rec, sizeof( TF00426 ), 426 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Unavailable cache
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0427( DEVBLK* dev, S32 trk )
|
|
{
|
|
TF00427 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
return tf_write( NULL, &rec, sizeof( TF00427 ), 427 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cache miss
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0428( DEVBLK* dev, S32 trk, S32 o )
|
|
{
|
|
TF00428 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
rec.idx = o;
|
|
return tf_write( NULL, &rec, sizeof( TF00428 ), 428 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Offset
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0429( DEVBLK* dev, S32 trk, S32 fnum )
|
|
{
|
|
TF00429 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
rec.offset = dev->ckdtrkoff;
|
|
rec.len = dev->ckdtrksz;
|
|
rec.fnum = fnum;
|
|
return tf_write( NULL, &rec, sizeof( TF00429 ), 429 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Trkhdr
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0430( DEVBLK* dev, S32 trk )
|
|
{
|
|
TF00430 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.trk = trk;
|
|
memcpy( rec.buf, dev->buf, sizeof( rec.buf ));
|
|
return tf_write( NULL, &rec, sizeof( TF00430 ), 430 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Seeking
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0431( DEVBLK* dev, int cyl, int head )
|
|
{
|
|
TF00431 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.cyl = cyl;
|
|
rec.head = head;
|
|
return tf_write( NULL, &rec, sizeof( TF00431 ), 431 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// MT advance error
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0432( DEVBLK* dev )
|
|
{
|
|
TF00432 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.count = dev->ckdlcount;
|
|
rec.mask = dev->ckdfmask;
|
|
return tf_write( NULL, &rec, sizeof( TF00432 ), 432 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// MT advance
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0433( DEVBLK* dev, int cyl, int head )
|
|
{
|
|
TF00433 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.cyl = cyl;
|
|
rec.head = head;
|
|
return tf_write( NULL, &rec, sizeof( TF00433 ), 433 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Read count orientation
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0434( DEVBLK* dev )
|
|
{
|
|
TF00434 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.orient = (BYTE) dev->ckdorient;
|
|
return tf_write( NULL, &rec, sizeof( TF00434 ), 434 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cyl head record kl dl
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0435( DEVBLK* dev )
|
|
{
|
|
TF00435 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.dl = dev->ckdcurdl;
|
|
rec.cyl = dev->ckdcurcyl;
|
|
rec.head = dev->ckdcurhead;
|
|
rec.record = dev->ckdcurrec;
|
|
rec.kl = dev->ckdcurkl;
|
|
rec.offset = dev->ckdtrkof;
|
|
return tf_write( NULL, &rec, sizeof( TF00435 ), 435 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Read key
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0436( DEVBLK* dev )
|
|
{
|
|
TF00436 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.kl = dev->ckdcurkl;
|
|
return tf_write( NULL, &rec, sizeof( TF00436 ), 436 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Read data
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0437( DEVBLK* dev )
|
|
{
|
|
TF00437 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.dl = dev->ckdcurdl;
|
|
return tf_write( NULL, &rec, sizeof( TF00437 ), 437 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Write cyl head record kl dl
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0438( DEVBLK* dev, BYTE recnum, BYTE keylen, U16 datalen )
|
|
{
|
|
TF00438 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.datalen = datalen;
|
|
rec.recnum = recnum;
|
|
rec.keylen = keylen;
|
|
rec.cyl = dev->ckdcurcyl;
|
|
rec.head = dev->ckdcurhead;
|
|
return tf_write( NULL, &rec, sizeof( TF00438 ), 438 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Set track overflow flag
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0439( DEVBLK* dev, BYTE recnum )
|
|
{
|
|
TF00439 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.recnum = recnum;
|
|
rec.cyl = dev->ckdcurcyl;
|
|
rec.head = dev->ckdcurhead;
|
|
return tf_write( NULL, &rec, sizeof( TF00439 ), 439 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Update cyl head record kl dl
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0440( DEVBLK* dev )
|
|
{
|
|
TF00440 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.cyl = dev->ckdcurcyl;
|
|
rec.head = dev->ckdcurhead;
|
|
rec.recnum = dev->ckdcurrec;
|
|
rec.keylen = dev->ckdcurkl;
|
|
rec.datalen = dev->ckdcurdl;
|
|
|
|
return tf_write( NULL, &rec, sizeof( TF00440 ), 440 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Update cyl head record dl
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0441( DEVBLK* dev )
|
|
{
|
|
TF00441 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.cyl = dev->ckdcurcyl;
|
|
rec.head = dev->ckdcurhead;
|
|
rec.recnum = dev->ckdcurrec;
|
|
rec.datalen = dev->ckdcurdl;
|
|
return tf_write( NULL, &rec, sizeof( TF00441 ), 441 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Set file mask
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0442( DEVBLK* dev )
|
|
{
|
|
TF00442 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.mask = dev->ckdfmask;
|
|
return tf_write( NULL, &rec, sizeof( TF00442 ), 442 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cache hit
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0516( DEVBLK* dev, S32 blkgrp, S32 i )
|
|
{
|
|
TF00516 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.blkgrp = blkgrp;
|
|
rec.idx = i;
|
|
return tf_write( NULL, &rec, sizeof( TF00516 ), 516 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Unavailable cache
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0517( DEVBLK* dev, S32 blkgrp )
|
|
{
|
|
TF00517 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.blkgrp = blkgrp;
|
|
return tf_write( NULL, &rec, sizeof( TF00517 ), 517 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Cache miss
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0518( DEVBLK* dev, S32 blkgrp, S32 o )
|
|
{
|
|
TF00518 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.blkgrp = blkgrp;
|
|
rec.idx = o;
|
|
return tf_write( NULL, &rec, sizeof( TF00518 ), 518 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Offset len
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0519( DEVBLK* dev, S32 blkgrp, U64 offset, S32 len )
|
|
{
|
|
TF00519 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.blkgrp = blkgrp;
|
|
rec.offset = offset;
|
|
rec.len = len;
|
|
return tf_write( NULL, &rec, sizeof( TF00519 ), 519 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Positioning
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0520( DEVBLK* dev )
|
|
{
|
|
TF00520 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
STRLCPY( rec.filename, TRIMLOC( dev->filename ));
|
|
rec.rba = dev->fbarba;
|
|
return tf_write( NULL, &rec, sizeof( TF00520 ), 520 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Wait State PSW
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0800( REGS* regs )
|
|
{
|
|
TF00800 rec;
|
|
memcpy( &rec.psw, ®s->psw, sizeof( rec.psw ));
|
|
rec.psw.ilc = REAL_ILC( regs );
|
|
rec.amode64 = rec.psw.amode64;
|
|
rec.amode = rec.psw.amode;
|
|
rec.zeroilc = rec.psw.zeroilc;
|
|
return tf_write( regs, &rec, sizeof( TF00800 ), 800 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Program Interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0801( REGS* regs, U16 pcode, BYTE ilc )
|
|
{
|
|
TF00801 rec;
|
|
rec.sie =
|
|
#if defined( _FEATURE_SIE )
|
|
SIE_MODE( regs );
|
|
#else
|
|
false;
|
|
#endif
|
|
rec.ilc = ilc;
|
|
rec.pcode = pcode;
|
|
rec.why = (pcode & PGM_TXF_EVENT) ? regs->txf_why : 0;
|
|
rec.dxc = ((pcode & 0xFF) == PGM_DATA_EXCEPTION) ? regs->dxc : 0;
|
|
return tf_write( regs, &rec, sizeof( TF00801 ), 801 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// PER event
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0802( REGS* regs, U64 ia, U32 pcode, U16 perc )
|
|
{
|
|
TF00802 rec;
|
|
rec.ia = ia;
|
|
rec.pcode = pcode;
|
|
rec.perc = perc;
|
|
return tf_write( regs, &rec, sizeof( TF00802 ), 802 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Program interrupt loop
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0803( REGS* regs, const char* str )
|
|
{
|
|
TF00803 rec;
|
|
STRLCPY( rec.str, str );
|
|
return tf_write( regs, &rec, sizeof( TF00803 ), 803 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// I/O interrupt (S/370)
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0804( REGS* regs, BYTE* csw, U16 ioid, BYTE lcss )
|
|
{
|
|
TF00804 rec;
|
|
rec.ioid = ioid;
|
|
rec.rhdr.lcss = lcss;
|
|
memcpy( rec.csw, csw, sizeof( rec.csw ));
|
|
return tf_write( regs, &rec, sizeof( TF00804 ), 804 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// I/O Interrupt
|
|
// (handles both HHC00805 and HHC00806)
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0806( REGS* regs, U32 ioid, U32 ioparm, U32 iointid )
|
|
{
|
|
TF00806 rec;
|
|
rec.ioid = ioid;
|
|
rec.ioparm = ioparm;
|
|
rec.iointid = iointid;
|
|
return tf_write( regs, &rec, sizeof( TF00806 ), 806 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Machine Check Interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0807( REGS* regs, U64 mcic, U64 fsta, U32 xdmg )
|
|
{
|
|
TF00807 rec;
|
|
rec.mcic = mcic;
|
|
rec.xdmg = xdmg;
|
|
rec.fsta = fsta;
|
|
return tf_write( regs, &rec, sizeof( TF00807 ), 807 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Store Status
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0808( REGS* regs )
|
|
{
|
|
TF00808 rec;
|
|
return tf_write( regs, &rec, sizeof( TF00808 ), 808 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Disabled Wait State
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0809( REGS* regs, const char* str)
|
|
{
|
|
TF00809 rec;
|
|
STRLCPY( rec.str, str );
|
|
return tf_write( regs, &rec, sizeof( TF00809 ), 809 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Architecture mode
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0811( REGS* regs, const char* archname )
|
|
{
|
|
TF00811 rec;
|
|
STRLCPY( rec.archname, archname );
|
|
return tf_write( regs, &rec, sizeof( TF00811 ), 811 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Vector facility online (370/390)
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0812( REGS* regs )
|
|
{
|
|
TF00812 rec;
|
|
return tf_write( regs, &rec, sizeof( TF00812 ), 812 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Signal Processor
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0814( REGS* regs, BYTE order, BYTE cc, U16 cpad, U32 status, U64 parm, BYTE got_status )
|
|
{
|
|
TF00814 rec;
|
|
rec.order = order;
|
|
rec.cc = cc;
|
|
rec.cpad = cpad;
|
|
rec.status = status;
|
|
rec.parm = parm;
|
|
rec.got_status = got_status;
|
|
return tf_write( regs, &rec, sizeof( TF00814 ), 814 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// External Interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0840( REGS* regs, U16 icode )
|
|
{
|
|
TF00840 rec;
|
|
rec.icode = icode;
|
|
rec.cpu_timer = (EXT_CPU_TIMER_INTERRUPT == rec.icode)
|
|
? sysblk.gct( regs ) : 0;
|
|
return tf_write( regs, &rec, sizeof( TF00840 ), 840 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Block I/O Interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0844( REGS* regs )
|
|
{
|
|
TF00844 rec;
|
|
rec.rhdr.devnum = sysblk.biodev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( sysblk.biodev->ssid );
|
|
rec.servcode = sysblk.servcode;
|
|
rec.bioparm = sysblk.bioparm;
|
|
rec.biostat = sysblk.biostat;
|
|
rec.biosubcd = sysblk.biosubcd;
|
|
return tf_write( regs, &rec, sizeof( TF00844 ), 844 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Block I/O External interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0845( REGS* regs )
|
|
{
|
|
TF00845 rec;
|
|
rec.bioparm = sysblk.bioparm;
|
|
rec.biosubcd = sysblk.biosubcd;
|
|
return tf_write( regs, &rec, sizeof( TF00845 ), 845 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Service Signal External Interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_0846( REGS* regs )
|
|
{
|
|
TF00846 rec;
|
|
rec.servparm = sysblk.servparm;
|
|
return tf_write( regs, &rec, sizeof( TF00846 ), 846 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Halt subchannel
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1300( DEVBLK* dev, BYTE cc )
|
|
{
|
|
TF01300 rec;
|
|
rec.cc = cc;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01300 ), 1300 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// IDAW/MIDAW
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1301( const DEVBLK* dev, const U64 addr, const U16 count,
|
|
BYTE* data, BYTE amt, const BYTE flag, const BYTE type )
|
|
{
|
|
TF01301 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
rec.amt = amt;
|
|
rec.addr = addr;
|
|
rec.count = count;
|
|
rec.type = type;
|
|
rec.mflag = flag;
|
|
rec.code = dev->code;
|
|
memcpy( rec.data, data, amt );
|
|
return tf_write( NULL, &rec, sizeof( TF01301 ), 1301 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Attention signaled
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1304( DEVBLK* dev )
|
|
{
|
|
TF01304 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01304 ), 1304 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Attention
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1305( DEVBLK* dev )
|
|
{
|
|
TF01305 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01305 ), 1305 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Initial status interrupt
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1306( DEVBLK* dev )
|
|
{
|
|
TF01306 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01306 ), 1306 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Attention completed
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1307( DEVBLK* dev )
|
|
{
|
|
TF01307 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01307 ), 1307 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Clear completed
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1308( DEVBLK* dev )
|
|
{
|
|
TF01308 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01308 ), 1308 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Halt completed
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1309( DEVBLK* dev )
|
|
{
|
|
TF01309 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01309 ), 1309 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Suspended
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1310( DEVBLK* dev )
|
|
{
|
|
TF01310 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01310 ), 1310 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Resumed
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1311( DEVBLK* dev )
|
|
{
|
|
TF01311 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01311 ), 1311 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// I/O stat
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1312( DEVBLK* dev, BYTE us, BYTE cs, BYTE amt, U32 resid, BYTE* data )
|
|
{
|
|
TF01312 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
rec.unitstat = us;
|
|
rec.chanstat = cs;
|
|
rec.amt = amt;
|
|
rec.residual = resid;
|
|
memcpy( rec.data, data, amt );
|
|
return tf_write( NULL, &rec, sizeof( TF01312 ), 1312 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Sense
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1313( DEVBLK* dev )
|
|
{
|
|
TF01313 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
memcpy( rec.sense, dev->sense, sizeof( rec.sense ));
|
|
if (dev->sns)
|
|
dev->sns( dev, rec.sns, sizeof( rec.sns ));
|
|
else
|
|
rec.sns[0] = 0;
|
|
return tf_write( NULL, &rec, sizeof( TF01313 ), 1313 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// CCW
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1315( const DEVBLK* dev, const BYTE* ccw,
|
|
const U32 addr, const U16 count, BYTE* data, BYTE amt )
|
|
{
|
|
TF01315 rec;
|
|
ASSERT( amt > 0 ); // (sanity check)
|
|
ASSERT( count > 0 ); // (sanity check)
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.sysg = (dev == sysblk.sysgdev);
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
rec.amt = MIN( amt, sizeof( rec.data )); // (no buffer o'flows!)
|
|
rec.addr = addr;
|
|
rec.count = count;
|
|
memcpy( rec.ccw, ccw, sizeof( rec.ccw ));
|
|
memcpy( rec.data, data, rec.amt );
|
|
|
|
return tf_write( NULL, &rec, sizeof( TF01315 ), 1315 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// CSW (370)
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1316( const DEVBLK* dev, const BYTE csw[] )
|
|
{
|
|
TF01316 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
memcpy( rec.csw, csw, sizeof( rec.csw ));
|
|
return tf_write( NULL, &rec, sizeof( TF01316 ), 1316 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// SCSW
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1317( const DEVBLK* dev, const SCSW scsw )
|
|
{
|
|
TF01317 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
memcpy( &rec.scsw, &scsw, sizeof( rec.scsw ));
|
|
return tf_write( NULL, &rec, sizeof( TF01317 ), 1317 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// TEST I/O
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1318( DEVBLK* dev, BYTE cc )
|
|
{
|
|
TF01318 rec;
|
|
rec.cc = cc;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01318 ), 1318 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// S/370 start I/O conversion started
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1320( DEVBLK* dev )
|
|
{
|
|
TF01320 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01320 ), 1320 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// S/370 start I/O conversion success
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1321( DEVBLK* dev )
|
|
{
|
|
TF01321 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01321 ), 1321 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Halt I/O
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1329( DEVBLK* dev )
|
|
{
|
|
TF01329 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01329 ), 1329 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// HIO modification
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1330( DEVBLK* dev )
|
|
{
|
|
TF01330 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01330 ), 1330 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Clear subchannel
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1331( DEVBLK* dev )
|
|
{
|
|
TF01331 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01331 ), 1331 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Halt subchannel
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1332( DEVBLK* dev )
|
|
{
|
|
TF01332 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01332 ), 1332 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Resume subchannel
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1333( DEVBLK* dev, BYTE cc )
|
|
{
|
|
TF01333 rec;
|
|
rec.cc = cc;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
return tf_write( NULL, &rec, sizeof( TF01333 ), 1333 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// ORB
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1334( DEVBLK* dev, ORB* orb )
|
|
{
|
|
TF01334 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
memcpy( &rec.orb, orb, sizeof( rec.orb ));
|
|
return tf_write( NULL, &rec, sizeof( TF01334 ), 1334 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Startio cc=2
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_1336( DEVBLK* dev )
|
|
{
|
|
TF01336 rec;
|
|
rec.rhdr.devnum = dev->devnum;
|
|
rec.rhdr.lcss = SSID_TO_LCSS( dev->ssid );
|
|
rec.busy = dev->busy;
|
|
rec.startpending = dev->startpending;
|
|
return tf_write( NULL, &rec, sizeof( TF01336 ), 1336 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// General Registers
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2269( REGS* regs, BYTE* inst )
|
|
{
|
|
TF02269 rec;
|
|
memcpy( rec.gr, regs->gr, sizeof( rec.gr ));
|
|
rec.ifetch_error = inst ? false : true;
|
|
rec.sie =
|
|
#if defined( _FEATURE_SIE )
|
|
SIE_MODE( regs );
|
|
#else
|
|
false;
|
|
#endif
|
|
return tf_write( regs, &rec, sizeof( TF02269 ), 2269 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Floating Point Registers
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2270( REGS* regs )
|
|
{
|
|
TF02270 rec;
|
|
int i;
|
|
for (i=0; i < 16; ++i)
|
|
memcpy( &rec.fpr[i], ®s->FPR_L(i), sizeof( rec.fpr[0] ));
|
|
rec.afp = (regs->CR(0) & CR0_AFP) ? true : false;
|
|
return tf_write( regs, &rec, sizeof( TF02270 ), 2270 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Control Registers
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2271( REGS* regs )
|
|
{
|
|
TF02271 rec;
|
|
memcpy( rec.cr, ®s->cr_struct[1], sizeof( rec.cr ));
|
|
return tf_write( regs, &rec, sizeof( TF02271 ), 2271 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Access Registers
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2272( REGS* regs )
|
|
{
|
|
TF02272 rec;
|
|
memcpy( rec.ar, regs->ar, sizeof( rec.ar ));
|
|
return tf_write( regs, &rec, sizeof( TF02272 ), 2272 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Floating Point Control Register
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2276( REGS* regs )
|
|
{
|
|
TF02276 rec;
|
|
rec.fpc = regs->fpc;
|
|
rec.afp = (regs->CR(0) & CR0_AFP) ? true : false;
|
|
return tf_write( regs, &rec, sizeof( TF02276 ), 2276 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Vector Registers
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2266( REGS* regs )
|
|
{
|
|
TF02266 rec;
|
|
memcpy( rec.vfp, regs->vfp, sizeof( rec.vfp ));
|
|
return tf_write( regs, &rec, sizeof( TF02266 ), 2266 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Primary Instruction Trace
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2324( REGS* regs, BYTE* inst )
|
|
{
|
|
TF02324 rec;
|
|
rec.sie =
|
|
#if defined( _FEATURE_SIE )
|
|
SIE_MODE( regs );
|
|
#else
|
|
false;
|
|
#endif
|
|
rec.ilc = ILC( inst[0] ); // (value in regs not always right)
|
|
memcpy( &rec.psw, ®s->psw, sizeof( rec.psw ));
|
|
memcpy( rec.inst, inst, rec.ilc );
|
|
rec.amode64 = rec.psw.amode64;
|
|
rec.amode = rec.psw.amode;
|
|
rec.zeroilc = rec.psw.zeroilc;
|
|
return tf_write( regs, &rec, sizeof( TF02324 ), 2324 );
|
|
}
|
|
|
|
//---------------------------------------------------------------------
|
|
// Instruction Storage Operand helper
|
|
//---------------------------------------------------------------------
|
|
|
|
PUSH_GCC_WARNINGS()
|
|
DISABLE_GCC_WARNING( "-Waddress-of-packed-member" )
|
|
|
|
static void tf_2326_op( REGS* regs, TFOP* op, BYTE opcode1, BYTE opcode2, int b, bool isop2 )
|
|
{
|
|
if (b >= 0)
|
|
{
|
|
int ar, stid;
|
|
|
|
/* Determine if operand should be treated as a real address */
|
|
if (0
|
|
|| REAL_MODE( ®s->psw )
|
|
|| (isop2 && IS_REAL_ADDR_OP( opcode1, opcode2 ))
|
|
)
|
|
ar = USE_REAL_ADDR;
|
|
else
|
|
ar = b;
|
|
|
|
/* Get the real address for this virtual address */
|
|
if ((op->xcode = tf_virt_to_real( &op->raddr, &stid,
|
|
op->vaddr, ar, regs, ACCTYPE_HW )) == 0)
|
|
{
|
|
U64 pagesize, bytemask, abs;
|
|
size_t amt = sizeof( op->stor );
|
|
|
|
/* Convert real address to absolute address */
|
|
abs = tf_apply_prefixing( op->raddr, regs );
|
|
|
|
/* Determine page size and byte mask */
|
|
if (regs->arch_mode == ARCH_370_IDX)
|
|
{
|
|
pagesize = STORAGE_KEY_2K_PAGESIZE;
|
|
bytemask = STORAGE_KEY_2K_BYTEMASK;
|
|
}
|
|
else
|
|
{
|
|
pagesize = STORAGE_KEY_4K_PAGESIZE;
|
|
bytemask = STORAGE_KEY_4K_BYTEMASK;
|
|
}
|
|
|
|
/* If virtual storage, only save the data that's
|
|
on this one page (i.e. to the end of the page) */
|
|
if (ar != USE_REAL_ADDR)
|
|
{
|
|
amt = pagesize - (abs & bytemask);
|
|
|
|
if (amt > sizeof( op->stor ))
|
|
amt = sizeof( op->stor );
|
|
}
|
|
|
|
/* Update Interval Timer beforehand, if needed */
|
|
#if defined( _FEATURE_INTERVAL_TIMER )
|
|
tf_store_int_timer( regs, op->raddr );
|
|
#endif
|
|
/* Save as much operand data as possible */
|
|
memcpy( op->stor, regs->mainstor + abs, amt );
|
|
|
|
/* Save the amount we saved and its storage key */
|
|
op->amt = (BYTE) amt; // (how much we saved)
|
|
op->skey = tf_get_storage_key( regs, abs );
|
|
}
|
|
}
|
|
}
|
|
|
|
POP_GCC_WARNINGS()
|
|
|
|
//---------------------------------------------------------------------
|
|
// Instruction Storage
|
|
//---------------------------------------------------------------------
|
|
DLL_EXPORT bool tf_2326( REGS* regs, TF02326* tf2326, BYTE opcode1, BYTE opcode2,
|
|
int b1, int b2 )
|
|
{
|
|
if (tf2326->valid)
|
|
{
|
|
tf2326->real_mode = REAL_MODE( ®s->psw );
|
|
tf2326->b1 = b1;
|
|
tf2326->b2 = b2;
|
|
tf_2326_op( regs, &tf2326->op1, opcode1, opcode2, b1, false );
|
|
tf_2326_op( regs, &tf2326->op2, opcode1, opcode2, b2, true );
|
|
}
|
|
return tf_write( regs, tf2326, sizeof( TF02326 ), 2326 );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return maximum TraceFile record size */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT size_t tf_MAX_RECSIZE()
|
|
{
|
|
size_t
|
|
max_recsize = sizeof( TFSYS );
|
|
|
|
// Device tracing
|
|
|
|
if (max_recsize < sizeof( TF00423 ))
|
|
max_recsize = sizeof( TF00423 );
|
|
|
|
if (max_recsize < sizeof( TF00424 ))
|
|
max_recsize = sizeof( TF00424 );
|
|
|
|
if (max_recsize < sizeof( TF00425 ))
|
|
max_recsize = sizeof( TF00425 );
|
|
|
|
if (max_recsize < sizeof( TF00426 ))
|
|
max_recsize = sizeof( TF00426 );
|
|
|
|
if (max_recsize < sizeof( TF00427 ))
|
|
max_recsize = sizeof( TF00427 );
|
|
|
|
if (max_recsize < sizeof( TF00428 ))
|
|
max_recsize = sizeof( TF00428 );
|
|
|
|
if (max_recsize < sizeof( TF00429 ))
|
|
max_recsize = sizeof( TF00429 );
|
|
|
|
if (max_recsize < sizeof( TF00430 ))
|
|
max_recsize = sizeof( TF00430 );
|
|
|
|
if (max_recsize < sizeof( TF00431 ))
|
|
max_recsize = sizeof( TF00431 );
|
|
|
|
if (max_recsize < sizeof( TF00432 ))
|
|
max_recsize = sizeof( TF00432 );
|
|
|
|
if (max_recsize < sizeof( TF00433 ))
|
|
max_recsize = sizeof( TF00433 );
|
|
|
|
if (max_recsize < sizeof( TF00434 ))
|
|
max_recsize = sizeof( TF00434 );
|
|
|
|
if (max_recsize < sizeof( TF00435 ))
|
|
max_recsize = sizeof( TF00435 );
|
|
|
|
if (max_recsize < sizeof( TF00436 ))
|
|
max_recsize = sizeof( TF00436 );
|
|
|
|
if (max_recsize < sizeof( TF00437 ))
|
|
max_recsize = sizeof( TF00437 );
|
|
|
|
if (max_recsize < sizeof( TF00438 ))
|
|
max_recsize = sizeof( TF00438 );
|
|
|
|
if (max_recsize < sizeof( TF00439 ))
|
|
max_recsize = sizeof( TF00439 );
|
|
|
|
if (max_recsize < sizeof( TF00440 ))
|
|
max_recsize = sizeof( TF00440 );
|
|
|
|
if (max_recsize < sizeof( TF00441 ))
|
|
max_recsize = sizeof( TF00441 );
|
|
|
|
if (max_recsize < sizeof( TF00442 ))
|
|
max_recsize = sizeof( TF00442 );
|
|
|
|
if (max_recsize < sizeof( TF00516 ))
|
|
max_recsize = sizeof( TF00516 );
|
|
|
|
if (max_recsize < sizeof( TF00517 ))
|
|
max_recsize = sizeof( TF00517 );
|
|
|
|
if (max_recsize < sizeof( TF00518 ))
|
|
max_recsize = sizeof( TF00518 );
|
|
|
|
if (max_recsize < sizeof( TF00519 ))
|
|
max_recsize = sizeof( TF00519 );
|
|
|
|
if (max_recsize < sizeof( TF00520 ))
|
|
max_recsize = sizeof( TF00520 );
|
|
|
|
// Instruction tracing
|
|
|
|
if (max_recsize < sizeof( TF00800 ))
|
|
max_recsize = sizeof( TF00800 );
|
|
|
|
if (max_recsize < sizeof( TF00801 ))
|
|
max_recsize = sizeof( TF00801 );
|
|
|
|
if (max_recsize < sizeof( TF00802 ))
|
|
max_recsize = sizeof( TF00802 );
|
|
|
|
if (max_recsize < sizeof( TF00803 ))
|
|
max_recsize = sizeof( TF00803 );
|
|
|
|
if (max_recsize < sizeof( TF00804 ))
|
|
max_recsize = sizeof( TF00804 );
|
|
|
|
if (max_recsize < sizeof( TF00806 )) // (handles both HHC00805 and HHC00806)
|
|
max_recsize = sizeof( TF00806 );
|
|
|
|
if (max_recsize < sizeof( TF00807 ))
|
|
max_recsize = sizeof( TF00807 );
|
|
|
|
if (max_recsize < sizeof( TF00808 ))
|
|
max_recsize = sizeof( TF00808 );
|
|
|
|
if (max_recsize < sizeof( TF00809 ))
|
|
max_recsize = sizeof( TF00809 );
|
|
|
|
if (max_recsize < sizeof( TF00811 ))
|
|
max_recsize = sizeof( TF00811 );
|
|
|
|
if (max_recsize < sizeof( TF00812 ))
|
|
max_recsize = sizeof( TF00812 );
|
|
|
|
if (max_recsize < sizeof( TF00814 ))
|
|
max_recsize = sizeof( TF00814 );
|
|
|
|
if (max_recsize < sizeof( TF00840 ))
|
|
max_recsize = sizeof( TF00840 );
|
|
|
|
if (max_recsize < sizeof( TF00844 ))
|
|
max_recsize = sizeof( TF00844 );
|
|
|
|
if (max_recsize < sizeof( TF00845 ))
|
|
max_recsize = sizeof( TF00845 );
|
|
|
|
if (max_recsize < sizeof( TF00846 ))
|
|
max_recsize = sizeof( TF00846 );
|
|
|
|
// Device tracing
|
|
|
|
if (max_recsize < sizeof( TF01300 ))
|
|
max_recsize = sizeof( TF01300 );
|
|
|
|
if (max_recsize < sizeof( TF01301 ))
|
|
max_recsize = sizeof( TF01301 );
|
|
|
|
if (max_recsize < sizeof( TF01304 ))
|
|
max_recsize = sizeof( TF01304 );
|
|
|
|
if (max_recsize < sizeof( TF01305 ))
|
|
max_recsize = sizeof( TF01305 );
|
|
|
|
if (max_recsize < sizeof( TF01306 ))
|
|
max_recsize = sizeof( TF01306 );
|
|
|
|
if (max_recsize < sizeof( TF01307 ))
|
|
max_recsize = sizeof( TF01307 );
|
|
|
|
if (max_recsize < sizeof( TF01308 ))
|
|
max_recsize = sizeof( TF01308 );
|
|
|
|
if (max_recsize < sizeof( TF01309 ))
|
|
max_recsize = sizeof( TF01309 );
|
|
|
|
if (max_recsize < sizeof( TF01310 ))
|
|
max_recsize = sizeof( TF01310 );
|
|
|
|
if (max_recsize < sizeof( TF01311 ))
|
|
max_recsize = sizeof( TF01311 );
|
|
|
|
if (max_recsize < sizeof( TF01312 ))
|
|
max_recsize = sizeof( TF01312 );
|
|
|
|
if (max_recsize < sizeof( TF01313 ))
|
|
max_recsize = sizeof( TF01313 );
|
|
|
|
if (max_recsize < sizeof( TF01315 ))
|
|
max_recsize = sizeof( TF01315 );
|
|
|
|
if (max_recsize < sizeof( TF01316 ))
|
|
max_recsize = sizeof( TF01316 );
|
|
|
|
if (max_recsize < sizeof( TF01317 ))
|
|
max_recsize = sizeof( TF01317 );
|
|
|
|
if (max_recsize < sizeof( TF01318 ))
|
|
max_recsize = sizeof( TF01318 );
|
|
|
|
if (max_recsize < sizeof( TF01320 ))
|
|
max_recsize = sizeof( TF01320 );
|
|
|
|
if (max_recsize < sizeof( TF01321 ))
|
|
max_recsize = sizeof( TF01321 );
|
|
|
|
if (max_recsize < sizeof( TF01329 ))
|
|
max_recsize = sizeof( TF01329 );
|
|
|
|
if (max_recsize < sizeof( TF01330 ))
|
|
max_recsize = sizeof( TF01330 );
|
|
|
|
if (max_recsize < sizeof( TF01331 ))
|
|
max_recsize = sizeof( TF01331 );
|
|
|
|
if (max_recsize < sizeof( TF01332 ))
|
|
max_recsize = sizeof( TF01332 );
|
|
|
|
if (max_recsize < sizeof( TF01333 ))
|
|
max_recsize = sizeof( TF01333 );
|
|
|
|
if (max_recsize < sizeof( TF01334 ))
|
|
max_recsize = sizeof( TF01334 );
|
|
|
|
if (max_recsize < sizeof( TF01336 ))
|
|
max_recsize = sizeof( TF01336 );
|
|
|
|
// Instruction tracing
|
|
|
|
if (max_recsize < sizeof( TF02269 ))
|
|
max_recsize = sizeof( TF02269 );
|
|
|
|
if (max_recsize < sizeof( TF02270 ))
|
|
max_recsize = sizeof( TF02270 );
|
|
|
|
if (max_recsize < sizeof( TF02271 ))
|
|
max_recsize = sizeof( TF02271 );
|
|
|
|
if (max_recsize < sizeof( TF02272 ))
|
|
max_recsize = sizeof( TF02272 );
|
|
|
|
if (max_recsize < sizeof( TF02276 ))
|
|
max_recsize = sizeof( TF02276 );
|
|
|
|
if (max_recsize < sizeof( TF02266 ))
|
|
max_recsize = sizeof( TF02266 );
|
|
|
|
if (max_recsize < sizeof( TF02324))
|
|
max_recsize = sizeof( TF02324 );
|
|
|
|
if (max_recsize < sizeof( TF02326))
|
|
max_recsize = sizeof( TF02326 );
|
|
|
|
return
|
|
max_recsize;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Do unconditional Endian swap of TFSYS record */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void tf_swap_sys( TFSYS* sys )
|
|
{
|
|
sys->bigend = !sys->bigend;
|
|
sys->beg_tod.tv_sec = SWAP32( sys->beg_tod.tv_sec );
|
|
sys->beg_tod.tv_usec = SWAP32( sys->beg_tod.tv_usec );
|
|
sys->end_tod.tv_sec = SWAP32( sys->end_tod.tv_sec );
|
|
sys->end_tod.tv_usec = SWAP32( sys->end_tod.tv_usec );
|
|
sys->tot_ins = SWAP64( sys->tot_ins );
|
|
sys->tot_dev = SWAP64( sys->tot_dev );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Do unconditional Endian swap of TFHDR record header */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void tf_swap_hdr( BYTE sys_ffmt, TFHDR* hdr )
|
|
{
|
|
hdr->prev = SWAP16( hdr->prev );
|
|
hdr->curr = SWAP16( hdr->curr );
|
|
hdr->cpuad = SWAP16( hdr->cpuad );
|
|
hdr->msgnum = SWAP16( hdr->msgnum );
|
|
hdr->devnum = SWAP16( hdr->devnum );
|
|
hdr->tod.tv_sec = SWAP32( hdr->tod.tv_sec );
|
|
hdr->tod.tv_usec = SWAP32( hdr->tod.tv_usec );
|
|
|
|
// Format-2...
|
|
|
|
if (sys_ffmt >= TF_FMT2)
|
|
hdr->tidnum = SWAP64( hdr->tidnum );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Do unconditional Endian swap of Trace File record fields */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* PROGRAMMING NOTE: the TFHDR* passed to us by TFPRINT, from our */
|
|
/* point of view, appears to point to a "new" (current) format trace */
|
|
/* record with both the header fields as well as the rec fields all */
|
|
/* properly aligned. */
|
|
/* */
|
|
/* When TFSWAP calls us however, it passes an adjusted TFHDR pointer */
|
|
/* wherein only the actual rec fields are aligned properly. This is */
|
|
/* safe to do since we don't ever access any of the header fields */
|
|
/* anyway. We only mess with the actual rec fields. */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void tf_swap_rec( TFHDR* hdr, U16 msgnum )
|
|
{
|
|
switch (msgnum)
|
|
{
|
|
case 423:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 424:
|
|
{
|
|
TF00424* rec = (TF00424*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
rec->bufcur = SWAP32( rec->bufcur );
|
|
}
|
|
break;
|
|
|
|
case 425:
|
|
{
|
|
TF00425* rec = (TF00425*) hdr;
|
|
rec->bufcur = SWAP32( rec->bufcur );
|
|
}
|
|
break;
|
|
|
|
case 426:
|
|
{
|
|
TF00426* rec = (TF00426*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
rec->idx = SWAP32( rec->idx );
|
|
}
|
|
break;
|
|
|
|
case 427:
|
|
{
|
|
TF00427* rec = (TF00427*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
}
|
|
break;
|
|
|
|
case 428:
|
|
{
|
|
TF00428* rec = (TF00428*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
rec->idx = SWAP32( rec->idx );
|
|
}
|
|
break;
|
|
|
|
case 429:
|
|
{
|
|
TF00429* rec = (TF00429*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
rec->fnum = SWAP32( rec->fnum );
|
|
rec->len = SWAP32( rec->len );
|
|
rec->offset = SWAP64( rec->offset );
|
|
}
|
|
break;
|
|
|
|
case 430:
|
|
{
|
|
TF00430* rec = (TF00430*) hdr;
|
|
rec->trk = SWAP32( rec->trk );
|
|
}
|
|
break;
|
|
|
|
case 431:
|
|
{
|
|
TF00431* rec = (TF00431*) hdr;
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
}
|
|
break;
|
|
|
|
case 432:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 433:
|
|
{
|
|
TF00433* rec = (TF00433*) hdr;
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
}
|
|
break;
|
|
|
|
case 434:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 435:
|
|
{
|
|
TF00435* rec = (TF00435*) hdr;
|
|
rec->dl = SWAP16( rec->dl );
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
rec->record = SWAP32( rec->record );
|
|
rec->kl = SWAP32( rec->kl );
|
|
rec->offset = SWAP32( rec->offset );
|
|
}
|
|
break;
|
|
|
|
case 436:
|
|
{
|
|
TF00436* rec = (TF00436*) hdr;
|
|
rec->kl = SWAP32( rec->kl );
|
|
}
|
|
break;
|
|
|
|
case 437:
|
|
{
|
|
TF00437* rec = (TF00437*) hdr;
|
|
rec->dl = SWAP16( rec->dl );
|
|
}
|
|
break;
|
|
|
|
case 438:
|
|
{
|
|
TF00438* rec = (TF00438*) hdr;
|
|
rec->datalen = SWAP16( rec->datalen );
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
}
|
|
break;
|
|
|
|
case 439:
|
|
{
|
|
TF00439* rec = (TF00439*) hdr;
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
}
|
|
break;
|
|
|
|
case 440:
|
|
{
|
|
TF00440* rec = (TF00440*) hdr;
|
|
rec->datalen = SWAP16( rec->datalen );
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
rec->recnum = SWAP32( rec->recnum );
|
|
rec->keylen = SWAP32( rec->keylen );
|
|
}
|
|
break;
|
|
|
|
case 441:
|
|
{
|
|
TF00441* rec = (TF00441*) hdr;
|
|
rec->datalen = SWAP16( rec->datalen );
|
|
rec->cyl = SWAP32( rec->cyl );
|
|
rec->head = SWAP32( rec->head );
|
|
rec->recnum = SWAP32( rec->recnum );
|
|
}
|
|
break;
|
|
|
|
case 442:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 516:
|
|
{
|
|
TF00516* rec = (TF00516*) hdr;
|
|
rec->blkgrp = SWAP32( rec->blkgrp );
|
|
rec->idx = SWAP32( rec->idx );
|
|
}
|
|
break;
|
|
|
|
case 517:
|
|
{
|
|
TF00517* rec = (TF00517*) hdr;
|
|
rec->blkgrp = SWAP32( rec->blkgrp );
|
|
}
|
|
break;
|
|
|
|
case 518:
|
|
{
|
|
TF00518* rec = (TF00518*) hdr;
|
|
rec->blkgrp = SWAP32( rec->blkgrp );
|
|
rec->idx = SWAP32( rec->idx );
|
|
}
|
|
break;
|
|
|
|
case 519:
|
|
{
|
|
TF00519* rec = (TF00519*) hdr;
|
|
rec->blkgrp = SWAP32( rec->blkgrp );
|
|
rec->len = SWAP32( rec->len );
|
|
rec->offset = SWAP64( rec->offset );
|
|
}
|
|
break;
|
|
|
|
case 520:
|
|
{
|
|
TF00520* rec = (TF00520*) hdr;
|
|
rec->rba = SWAP64( rec->rba );
|
|
}
|
|
break;
|
|
|
|
case 800:
|
|
{
|
|
TF00800* rec = (TF00800*) hdr;
|
|
|
|
rec->psw.intcode = SWAP16( rec->psw.intcode );
|
|
rec->psw.zeroword = SWAP32( rec->psw.zeroword );
|
|
rec->psw.ia.D = SWAP64( rec->psw.ia.D );
|
|
rec->psw.amask.D = SWAP64( rec->psw.amask.D );
|
|
|
|
rec->psw.amode64 = rec->amode64 ? 1 : 0;
|
|
rec->psw.amode = rec->amode ? 1 : 0;
|
|
rec->psw.zeroilc = rec->zeroilc ? 1 : 0;
|
|
}
|
|
break;
|
|
|
|
case 801:
|
|
{
|
|
TF00801* rec = (TF00801*) hdr;
|
|
rec->pcode = SWAP16( rec->pcode );
|
|
rec->dxc = SWAP32( rec->dxc );
|
|
rec->why = SWAP32( rec->why );
|
|
}
|
|
break;
|
|
|
|
case 802:
|
|
{
|
|
TF00802* rec = (TF00802*) hdr;
|
|
rec->perc = SWAP16( rec->perc );
|
|
rec->pcode = SWAP32( rec->pcode );
|
|
rec->ia = SWAP64( rec->ia );
|
|
}
|
|
break;
|
|
|
|
case 803:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 804:
|
|
{
|
|
TF00804* rec = (TF00804*) hdr;
|
|
rec->ioid = SWAP16( rec->ioid );
|
|
}
|
|
break;
|
|
|
|
case 806: // (handles both HHC00805 and HHC00806)
|
|
{
|
|
TF00806* rec = (TF00806*) hdr;
|
|
rec->ioid = SWAP32( rec->ioid );
|
|
rec->ioparm = SWAP32( rec->ioparm );
|
|
rec->iointid = SWAP32( rec->iointid );
|
|
}
|
|
break;
|
|
|
|
case 807:
|
|
{
|
|
TF00807* rec = (TF00807*) hdr;
|
|
rec->xdmg = SWAP32( rec->xdmg );
|
|
rec->mcic = SWAP64( rec->mcic );
|
|
rec->fsta = SWAP64( rec->fsta );
|
|
}
|
|
break;
|
|
|
|
case 808:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 809:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 811:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 812:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 814:
|
|
{
|
|
TF00814* rec = (TF00814*) hdr;
|
|
rec->cpad = SWAP16( rec->cpad );
|
|
rec->status = SWAP32( rec->status );
|
|
rec->parm = SWAP64( rec->parm );
|
|
}
|
|
break;
|
|
|
|
case 840:
|
|
{
|
|
TF00840* rec = (TF00840*) hdr;
|
|
rec->icode = SWAP16( rec->icode );
|
|
rec->cpu_timer = SWAP64( rec->cpu_timer );
|
|
}
|
|
break;
|
|
|
|
case 844:
|
|
{
|
|
TF00844* rec = (TF00844*) hdr;
|
|
rec->servcode = SWAP16( rec->servcode );
|
|
rec->bioparm = SWAP64( rec->bioparm );
|
|
}
|
|
break;
|
|
|
|
case 845:
|
|
{
|
|
TF00845* rec = (TF00845*) hdr;
|
|
rec->bioparm = SWAP64( rec->bioparm );
|
|
}
|
|
break;
|
|
|
|
case 846:
|
|
{
|
|
TF00846* rec = (TF00846*) hdr;
|
|
rec->servparm = SWAP32( rec->servparm );
|
|
}
|
|
break;
|
|
|
|
case 1300:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1301:
|
|
{
|
|
TF01301* rec = (TF01301*) hdr;
|
|
rec->count = SWAP16( rec->count );
|
|
rec->addr = SWAP64( rec->addr );
|
|
}
|
|
break;
|
|
|
|
case 1304:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1305:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1306:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1307:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1308:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1309:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1310:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1311:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1312:
|
|
{
|
|
TF01312* rec = (TF01312*) hdr;
|
|
rec->residual = SWAP32( rec->residual );
|
|
}
|
|
break;
|
|
|
|
case 1313:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1315:
|
|
{
|
|
TF01315* rec = (TF01315*) hdr;
|
|
rec->count = SWAP16( rec->count );
|
|
rec->addr = SWAP32( rec->addr );
|
|
}
|
|
break;
|
|
|
|
case 1316:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1317:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1318:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1320:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1321:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1329:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1330:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1331:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1332:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1333:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1334:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 1336:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 2266:
|
|
{
|
|
// (nothing to swap!)
|
|
}
|
|
break;
|
|
|
|
case 2269:
|
|
{
|
|
TF02269* rec = (TF02269*) hdr;
|
|
int i;
|
|
for (i=0; i < 16; ++i)
|
|
rec->gr[i].D = SWAP64( rec->gr[i].D );
|
|
}
|
|
break;
|
|
|
|
case 2270:
|
|
{
|
|
TF02270* rec = (TF02270*) hdr;
|
|
int i;
|
|
for (i=0; i < 16; ++i)
|
|
rec->fpr[i].D = SWAP64( rec->fpr[i].D );
|
|
}
|
|
break;
|
|
|
|
case 2271:
|
|
{
|
|
TF02271* rec = (TF02271*) hdr;
|
|
int i;
|
|
for (i=0; i < 16; ++i)
|
|
rec->cr[i].D = SWAP64( rec->cr[i].D );
|
|
}
|
|
break;
|
|
|
|
case 2272:
|
|
{
|
|
TF02272* rec = (TF02272*) hdr;
|
|
int i;
|
|
for (i=0; i < 16; ++i)
|
|
rec->ar[i] = SWAP32( rec->ar[i] );
|
|
}
|
|
break;
|
|
|
|
case 2276:
|
|
{
|
|
TF02276* rec = (TF02276*) hdr;
|
|
rec->fpc = SWAP32( rec->fpc );
|
|
}
|
|
break;
|
|
|
|
case 2324:
|
|
{
|
|
TF02324* rec = (TF02324*) hdr;
|
|
|
|
rec->psw.intcode = SWAP16( rec->psw.intcode );
|
|
rec->psw.zeroword = SWAP32( rec->psw.zeroword );
|
|
rec->psw.ia.D = SWAP64( rec->psw.ia.D );
|
|
rec->psw.amask.D = SWAP64( rec->psw.amask.D );
|
|
|
|
rec->psw.amode64 = rec->amode64 ? 1 : 0;
|
|
rec->psw.amode = rec->amode ? 1 : 0;
|
|
rec->psw.zeroilc = rec->zeroilc ? 1 : 0;
|
|
}
|
|
break;
|
|
|
|
case 2326:
|
|
{
|
|
TF02326* rec = (TF02326*) hdr;
|
|
TFOP* op;
|
|
|
|
rec->b1 = SWAP16( rec->b1 );
|
|
rec->b2 = SWAP16( rec->b2 );
|
|
|
|
op = &rec->op1;
|
|
|
|
op->xcode = SWAP16( op->xcode );
|
|
op->vaddr = SWAP64( op->vaddr );
|
|
op->raddr = SWAP64( op->raddr );
|
|
|
|
op = &rec->op2;
|
|
|
|
op->xcode = SWAP16( op->xcode );
|
|
op->vaddr = SWAP64( op->vaddr );
|
|
op->raddr = SWAP64( op->raddr );
|
|
}
|
|
break;
|
|
|
|
default: CRASH(); UNREACHABLE_CODE( return );
|
|
}
|
|
}
|
|
|
|
/*********************************************************************/
|
|
/* END OF TraceFile support */
|
|
/*********************************************************************/
|