mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 02:29:51 +02:00
6ff24aa680
* ctc_lcs.c: ensure pszTUNDevice is free'd 'pszTUNDevice = strdup( DEF_NETDEV );' is definitely lost. pszOATFilename check included to follow convention. * cckddasd.c: ensure dasdsfn is free'd 1. ckd_dasd_init_handler invokes cckd_sf_parse_sfn 2. ckd_dasd_init_handler invokes ckd64_dasd_init_handler 3. ckd64_dasd_init_handler invokes cckd_sf_parse_sfn Leaving previous dasdsfn dangling. Given how 'dasdsfn' is managed (e.g., in attach_device and dasd_close_device), it may be more elegant to compare the previous dasdsfn to the current pathname and throwing a warning if they are different. * config.c: ensure prev_buf is free'd * panel.c: zero prev_psw To avoid instances of memcmp'ing random stack data with curr_psw. * control.c: zero newregs tlb ARCH_DEP( maddr_l ) may reference tlb.asb and tlb.common - ensure newregs in program_return does not reference garbage stack data. * cckddasd64.c: zero L2_pad To avoid feeding random stack data to syscalls later on (e.g., write in cckd64_write). * panel.c: ensure messages are null delimited Uses of string.h functions and regexec assume the string is null delimited. This may not be true with some _PANMSG msg uses. * ecpsvm.c: ensure buffer is null delimited * refactor: CPU_BITMAP formatting When CPU_BITMAP is __uint128_t and F_CPU_BITMAP is '"%16.16"PRIX64', random/clobbered stack data will be referenced in the associated libc formatting routines used by fwritemsg due to the different stack alignment requirements of the variadic arguments. * refactor: reorganize history_init panel_command may be invoked (e.g., 'LPARNUM BASIC') prior to the panel display thread being initialized. This can lead to history_add being invoked prior to history_init (causing dangling pointers). * cckddasd64: zero the entire free block to avoid writing garbage to underlying file Per MemorySanitizer * w32util.c: set_thread_name buffer overflow Ensures correct nNumberOfArguments argument on x64. Updated the struct definition to match the Microsoft documentation: https://learn.microsoft.com/en-us/visualstudio/debugger/tips-for-debugging-threads SetThreadDescription was introduced in Windows 10 version 1607, so Kernel32.dll could technically be probed to see if that export exists.
2306 lines
73 KiB
C
2306 lines
73 KiB
C
/* CONFIG.C (C) Copyright Jan Jaeger, 2000-2012 */
|
|
/* (C) and others 2013-2023 */
|
|
/* Device and Storage configuration functions */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* The original configuration builder is now called bldcfg.c */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
DISABLE_GCC_UNUSED_FUNCTION_WARNING;
|
|
|
|
#define _CONFIG_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "chsc.h"
|
|
#include "cckddasd.h"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ARCH_DEP section: compiled multiple times, once for each arch. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
// (we have no ARCH_DEP code in this module)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if !defined( _GEN_ARCH )
|
|
|
|
#if defined( _ARCH_NUM_1 )
|
|
#define _GEN_ARCH _ARCH_NUM_1
|
|
#include "config.c"
|
|
#undef _GEN_ARCH
|
|
#endif
|
|
|
|
#if defined( _ARCH_NUM_2 )
|
|
#define _GEN_ARCH _ARCH_NUM_2
|
|
#include "config.c"
|
|
#undef _GEN_ARCH
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* (delineates ARCH_DEP from non-arch_dep) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* non-ARCH_DEP section: compiled only ONCE after last arch built */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Note: the last architecture has been built so the normal non- */
|
|
/* underscore FEATURE values are now #defined according to the */
|
|
/* LAST built architecture just built (usually zarch = 900). This */
|
|
/* means from this point onward (to the end of file) you should */
|
|
/* ONLY be testing the underscore _FEATURE values to see if the */
|
|
/* given feature was defined for *ANY* of the build architectures. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*********************************************************************/
|
|
/* IMPORTANT PROGRAMMING NOTE */
|
|
/*********************************************************************/
|
|
/* */
|
|
/* It is CRITICALLY IMPORTANT to not use any architecture dependent */
|
|
/* macros anywhere in any of your non-arch_dep functions. This means */
|
|
/* you CANNOT use GREG, RADR, VADR, etc. anywhere in your function, */
|
|
/* nor can you call "ARCH_DEP(func)(args)" anywhere in your code! */
|
|
/* */
|
|
/* Basically you MUST NOT use any architecture dependent macro that */
|
|
/* is #defined in the "feature.h" header. If you need to use */
|
|
/* any of them, then your function MUST be an "ARCH_DEP" function */
|
|
/* that is placed within the ARCH_DEP section at the beginning of */
|
|
/* this module where it can be compiled multiple times, once for */
|
|
/* each of the supported architectures so the macro gets #defined */
|
|
/* to its proper value for the architecture. YOU HAVE BEEN WARNED. */
|
|
/* */
|
|
/*********************************************************************/
|
|
|
|
#if defined( HAVE_MLOCKALL )
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_memlock - lock or unlock storage */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_memlock( int flags )
|
|
{
|
|
int rc;
|
|
|
|
if(flags)
|
|
rc = mlockall( MCL_CURRENT | MCL_FUTURE );
|
|
else
|
|
rc = munlockall();
|
|
|
|
return rc ? errno : 0;
|
|
}
|
|
#endif /*defined(HAVE_MLOCKALL)*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_region_reloc */
|
|
/*-------------------------------------------------------------------*/
|
|
static void configure_region_reloc()
|
|
{
|
|
DEVBLK* dev;
|
|
int i;
|
|
|
|
#if defined(_FEATURE_REGION_RELOCATE)
|
|
|
|
/* Initialize base zone storage view (SIE compat) */
|
|
for (i=0; i < FEATURE_SIE_MAXZONES; i++)
|
|
{
|
|
sysblk.zpb[i].mso = 0;
|
|
sysblk.zpb[i].msl = (sysblk.mainsize - 1) >> SHIFT_MEGABYTE;
|
|
|
|
if (sysblk.xpndsize)
|
|
{
|
|
sysblk.zpb[i].eso = 0;
|
|
sysblk.zpb[i].esl = ((size_t)sysblk.xpndsize * XSTORE_PAGESIZE - 1) >> SHIFT_MEGABYTE;
|
|
}
|
|
else
|
|
{
|
|
sysblk.zpb[i].eso = -1;
|
|
sysblk.zpb[i].esl = -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Relocate storage for all devices */
|
|
for (dev = sysblk.firstdev; dev; dev = dev->nextdev)
|
|
{
|
|
dev->mainstor = sysblk.mainstor;
|
|
dev->storkeys = sysblk.storkeys;
|
|
dev->mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
|
|
}
|
|
|
|
/* Relocate storage for all online cpus */
|
|
for (i=0; i < sysblk.maxcpu; i++)
|
|
if (IS_CPU_ONLINE( i ))
|
|
{
|
|
sysblk.regs[i]->storkeys = sysblk.storkeys;
|
|
sysblk.regs[i]->mainstor = sysblk.mainstor;
|
|
sysblk.regs[i]->mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_memfree - adjust minimum emulator free storage amount */
|
|
/*-------------------------------------------------------------------*/
|
|
static U64 config_mfree = (200 << SHIFT_MEGABYTE);
|
|
|
|
int configure_memfree( int mfree )
|
|
{
|
|
if (mfree < 0)
|
|
return config_mfree >> SHIFT_MEGABYTE;
|
|
|
|
config_mfree = ((U64)mfree) << SHIFT_MEGABYTE;
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* adjust_mainsize -- range check MAINSIZE by architecture */
|
|
/*-------------------------------------------------------------------*/
|
|
U64 adjust_mainsize( int archnum, U64 mainsize )
|
|
{
|
|
static const U64 minmax_mainsize[ NUM_GEN_ARCHS ][2] =
|
|
{
|
|
#if defined( _ARCH_NUM_0 )
|
|
#if 370 == _ARCH_NUM_0
|
|
{ MIN_370_MAINSIZE_BYTES, // (MIN_ARCH_MAINSIZE_BYTES)
|
|
MAX_370_MAINSIZE_BYTES }, // (MAX_ARCH_MAINSIZE_BYTES)
|
|
|
|
#elif 390 == _ARCH_NUM_0
|
|
{ MIN_390_MAINSIZE_BYTES, // etc...
|
|
MAX_390_MAINSIZE_BYTES }, // etc...
|
|
|
|
#else // 900 == _ARCH_NUM_0
|
|
{ MIN_900_MAINSIZE_BYTES, // etc...
|
|
MAX_900_MAINSIZE_BYTES }, // etc...
|
|
#endif
|
|
#endif
|
|
#if defined( _ARCH_NUM_1 )
|
|
#if 370 == _ARCH_NUM_1
|
|
{ MIN_370_MAINSIZE_BYTES, // etc...
|
|
MAX_370_MAINSIZE_BYTES }, // etc...
|
|
|
|
#elif 390 == _ARCH_NUM_1
|
|
{ MIN_390_MAINSIZE_BYTES, // etc...
|
|
MAX_390_MAINSIZE_BYTES }, // etc...
|
|
|
|
#else // 900 == _ARCH_NUM_1
|
|
{ MIN_900_MAINSIZE_BYTES, // etc...
|
|
MAX_900_MAINSIZE_BYTES }, // etc...
|
|
#endif
|
|
#endif
|
|
#if defined( _ARCH_NUM_2 )
|
|
#if 370 == _ARCH_NUM_2
|
|
{ MIN_370_MAINSIZE_BYTES, // etc...
|
|
MAX_370_MAINSIZE_BYTES }, // etc...
|
|
|
|
#elif 390 == _ARCH_NUM_2
|
|
{ MIN_390_MAINSIZE_BYTES, // etc...
|
|
MAX_390_MAINSIZE_BYTES }, // etc...
|
|
|
|
#else // 900 == _ARCH_NUM_2
|
|
{ MIN_900_MAINSIZE_BYTES, // etc...
|
|
MAX_900_MAINSIZE_BYTES }, // etc...
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
if (mainsize < minmax_mainsize[ archnum ][ MIN_ARCH_MAINSIZE_BYTES ])
|
|
mainsize = minmax_mainsize[ archnum ][ MIN_ARCH_MAINSIZE_BYTES ];
|
|
|
|
if (mainsize > minmax_mainsize[ archnum ][ MAX_ARCH_MAINSIZE_BYTES ])
|
|
mainsize = minmax_mainsize[ archnum ][ MAX_ARCH_MAINSIZE_BYTES ];
|
|
|
|
/* Special case: if no CPUs then no storage is needed */
|
|
if (sysblk.maxcpu <= 0)
|
|
mainsize = 0;
|
|
|
|
return mainsize;
|
|
}
|
|
|
|
PUSH_GCC_WARNINGS()
|
|
DISABLE_GCC_WARNING( "-Wpointer-to-int-cast" )
|
|
DISABLE_GCC_WARNING( "-Wint-to-pointer-cast" )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_storage - configure MAIN storage */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static U64 config_allocmsize = 0;
|
|
static BYTE* config_allocmaddr = NULL;
|
|
|
|
int configure_storage( U64 mainsize /* number of 4K pages */ )
|
|
{
|
|
BYTE* mainstor;
|
|
BYTE* storkeys;
|
|
BYTE* dofree = NULL;
|
|
char* mfree = NULL;
|
|
U64 storsize;
|
|
U32 skeysize;
|
|
|
|
/* Ensure all CPUs have been stopped */
|
|
if (are_any_cpus_started())
|
|
return HERRCPUONL;
|
|
|
|
/* Release storage and return if deconfiguring */
|
|
if (mainsize == ~0ULL)
|
|
{
|
|
if (config_allocmaddr)
|
|
free( config_allocmaddr );
|
|
|
|
sysblk.storkeys = 0;
|
|
sysblk.mainstor = 0;
|
|
sysblk.mainsize = 0;
|
|
|
|
config_allocmsize = 0;
|
|
config_allocmaddr = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Round requested storage size to architectural segment boundary
|
|
*
|
|
* ARCH_370_IDX 1 4K page
|
|
* ARCH_390_IDX 256 4K pages (or 1M)
|
|
* ARCH_900_IDX 256 4K pages (or 1M)
|
|
*/
|
|
if (mainsize)
|
|
{
|
|
if (sysblk.arch_mode == ARCH_370_IDX)
|
|
storsize = MAX( 1, mainsize );
|
|
else
|
|
storsize = (mainsize + 255) & ~255ULL;
|
|
mainsize = storsize;
|
|
}
|
|
else
|
|
{
|
|
/* Adjust zero storage case for a subsystem/device server
|
|
* (MAXCPU 0), allocating minimum storage of 4K.
|
|
*/
|
|
storsize = 1;
|
|
}
|
|
|
|
/* Storage key array size rounded to next page boundary */
|
|
switch (_STORKEY_ARRAY_UNITSIZE)
|
|
{
|
|
case _2K:
|
|
/* We need one storekey byte for every 2K page instead
|
|
of only one byte for every 4K page, so we need twice
|
|
as many pages as normal for our storage key array.
|
|
*/
|
|
skeysize = storsize << (SHIFT_4K - SHIFT_2K);
|
|
break;
|
|
case _4K:
|
|
skeysize = storsize;
|
|
break;
|
|
}
|
|
|
|
/* Calculate number of 4K pages we will need to allocate */
|
|
skeysize += (_4K-1);
|
|
skeysize >>= SHIFT_4K;
|
|
|
|
/* Add number of pages needed for our storage key array */
|
|
storsize += skeysize;
|
|
|
|
/* New memory is obtained only if the requested and calculated size
|
|
* is larger than the last allocated size, or if the request is for
|
|
* less than 2M of memory.
|
|
*/
|
|
if (0
|
|
|| (storsize > config_allocmsize)
|
|
|| (storsize < config_allocmsize && mainsize <= DEF_MAINSIZE_PAGES)
|
|
)
|
|
{
|
|
if (config_mfree && mainsize > DEF_MAINSIZE_PAGES)
|
|
mfree = malloc( config_mfree );
|
|
|
|
/* Obtain storage with pagesize hint for cleanest allocation */
|
|
storkeys = calloc( (size_t)(storsize + 1), _4K );
|
|
|
|
if (mfree)
|
|
free( mfree );
|
|
|
|
if (!storkeys)
|
|
{
|
|
char buf[64];
|
|
char memsize[64];
|
|
|
|
sysblk.main_clear = 0;
|
|
|
|
fmt_memsize_KB( mainsize << (SHIFT_4K - SHIFT_1K), memsize, sizeof( memsize ));
|
|
MSGBUF( buf, "configure_storage( %s )", memsize );
|
|
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC01430, "S", buf, strerror( errno ));
|
|
return -1;
|
|
}
|
|
|
|
/* Previously allocated storage to be freed, update actual
|
|
* storage pointers and adjust new storage to page boundary.
|
|
*/
|
|
dofree = config_allocmaddr;
|
|
|
|
config_allocmsize = storsize;
|
|
config_allocmaddr = storkeys;
|
|
|
|
sysblk.main_clear = 1;
|
|
|
|
storkeys = (BYTE*)(((U64)storkeys + (_4K-1)) & ~0x0FFFULL);
|
|
}
|
|
else
|
|
{
|
|
storkeys = sysblk.storkeys;
|
|
sysblk.main_clear = 0;
|
|
dofree = NULL;
|
|
}
|
|
|
|
/* MAINSTOR is located on the page boundary just beyond the
|
|
storage key array (which is already on a page boundary).
|
|
*/
|
|
mainstor = (BYTE*)((U64)(storkeys + (skeysize << SHIFT_4K)));
|
|
|
|
/* Update SYSBLK... */
|
|
sysblk.storkeys = storkeys;
|
|
sysblk.mainstor = mainstor;
|
|
sysblk.mainsize = mainsize << SHIFT_4K;
|
|
|
|
/* Free previously allocated storage if no longer needed
|
|
*
|
|
* FIXME: The storage ordering further limits the amount of storage
|
|
* that may be allocated following the initial storage
|
|
* allocation.
|
|
*/
|
|
if (dofree)
|
|
free( dofree );
|
|
|
|
/* Initial power-on reset for main storage */
|
|
storage_clear(); /* only clears if needed */
|
|
|
|
#if 0 /* DEBUG-JJ - 20/03/2000 */
|
|
|
|
/* Mark selected frames invalid for debugging purposes */
|
|
for (i = 64; i < (sysblk.mainsize / _STORKEY_ARRAY_UNITSIZE); i += 2)
|
|
{
|
|
if (i < (sysblk.mainsize / _STORKEY_ARRAY_UNITSIZE) - 64)
|
|
sysblk.storkeys[i] = STORKEY_BADFRM;
|
|
else
|
|
sysblk.storkeys[i++] = STORKEY_BADFRM;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if 1 // FIXME: The below is a kludge that will need to be cleaned up at some point in time
|
|
|
|
/* Initialize dummy regs.
|
|
* Dummy regs are used by the panel or gui when the target cpu
|
|
* (sysblk.pcpu) is not configured (ie cpu_thread not started).
|
|
*/
|
|
sysblk.dummyregs.mainstor = sysblk.mainstor;
|
|
sysblk.dummyregs.psa = (PSA_3XX*)sysblk.mainstor;
|
|
sysblk.dummyregs.storkeys = sysblk.storkeys;
|
|
sysblk.dummyregs.mainlim = sysblk.mainsize ? (sysblk.mainsize - 1) : 0;
|
|
sysblk.dummyregs.dummy = 1;
|
|
|
|
initial_cpu_reset( &sysblk.dummyregs );
|
|
|
|
sysblk.dummyregs.arch_mode = sysblk.arch_mode;
|
|
sysblk.dummyregs.hostregs = &sysblk.dummyregs;
|
|
|
|
#endif // kludge?
|
|
|
|
configure_region_reloc();
|
|
initial_cpu_reset_all();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_xstorage - configure EXPANDED storage */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static U64 config_allocxsize = 0;
|
|
static BYTE* config_allocxaddr = NULL;
|
|
|
|
int configure_xstorage( U64 xpndsize )
|
|
{
|
|
#ifdef _FEATURE_EXPANDED_STORAGE
|
|
|
|
BYTE* xpndstor;
|
|
BYTE* dofree = NULL;
|
|
char* mfree = NULL;
|
|
|
|
/* Ensure all CPUs have been stopped */
|
|
if (are_any_cpus_started())
|
|
return HERRCPUONL;
|
|
|
|
/* Release storage and return if zero or deconfiguring */
|
|
if (!xpndsize || xpndsize == ~0ULL)
|
|
{
|
|
if (config_allocxaddr)
|
|
free(config_allocxaddr);
|
|
|
|
sysblk.xpndsize = 0;
|
|
sysblk.xpndstor = 0;
|
|
|
|
config_allocxsize = 0;
|
|
config_allocxaddr = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* New memory is obtained only if the requested and calculated size
|
|
* is larger than the last allocated size.
|
|
*/
|
|
if (xpndsize > config_allocxsize)
|
|
{
|
|
if (config_mfree)
|
|
mfree = malloc( config_mfree );
|
|
|
|
/* Obtain expanded storage, hinting to megabyte boundary */
|
|
xpndstor = calloc( (size_t)(xpndsize + 1), ONE_MEGABYTE );
|
|
|
|
if (mfree)
|
|
free( mfree );
|
|
|
|
if (!xpndstor)
|
|
{
|
|
char buf[64];
|
|
char memsize[64];
|
|
|
|
sysblk.xpnd_clear = 0;
|
|
|
|
fmt_memsize_MB( xpndsize, memsize, sizeof( memsize ));
|
|
MSGBUF( buf, "configure_xstorage(%s)", memsize );
|
|
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC01430, "S", buf, strerror( errno ));
|
|
return -1;
|
|
}
|
|
|
|
/* Previously allocated storage to be freed, update actual
|
|
* storage pointers and adjust new storage to megabyte boundary.
|
|
*/
|
|
dofree = config_allocxaddr;
|
|
|
|
config_allocxsize = xpndsize;
|
|
config_allocxaddr = xpndstor;
|
|
|
|
sysblk.xpnd_clear = 1;
|
|
|
|
xpndstor = (BYTE*)(((U64)xpndstor + (ONE_MEGABYTE - 1)) &
|
|
~((U64)ONE_MEGABYTE - 1));
|
|
|
|
sysblk.xpndstor = xpndstor;
|
|
}
|
|
else
|
|
{
|
|
xpndstor = sysblk.xpndstor;
|
|
sysblk.xpnd_clear = 0;
|
|
dofree = NULL;
|
|
}
|
|
|
|
sysblk.xpndstor = xpndstor;
|
|
sysblk.xpndsize = xpndsize << (SHIFT_MEBIBYTE - XSTORE_PAGESHIFT);
|
|
|
|
/* Free previously allocated storage if no longer needed
|
|
*
|
|
* FIXME: The storage ordering further limits the amount of storage
|
|
* that may be allocated following the initial storage
|
|
* allocation.
|
|
*/
|
|
if (dofree)
|
|
free( dofree );
|
|
|
|
/* Initial power-on reset for expanded storage */
|
|
xstorage_clear();
|
|
|
|
configure_region_reloc();
|
|
initial_cpu_reset_all();
|
|
|
|
#else /* !_FEATURE_EXPANDED_STORAGE */
|
|
|
|
UNREFERENCED( xpndsize );
|
|
|
|
// "Expanded storage support not installed"
|
|
WRMSG( HHC01431, "I" );
|
|
|
|
#endif /* !_FEATURE_EXPANDED_STORAGE */
|
|
|
|
return 0;
|
|
}
|
|
|
|
POP_GCC_WARNINGS()
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Next 4 functions used for fast device lookup cache management */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
static void AddDevnumFastLookup( DEVBLK *dev, U16 lcss, U16 devnum )
|
|
{
|
|
unsigned int Channel;
|
|
|
|
int have_config_lock = have_lock( &sysblk.config );
|
|
|
|
if (!have_config_lock)
|
|
obtain_lock( &sysblk.config );
|
|
{
|
|
if (sysblk.devnum_fl == NULL)
|
|
sysblk.devnum_fl = (DEVBLK***)
|
|
calloc( 256 * FEATURE_LCSS_MAX, sizeof( DEVBLK** ));
|
|
|
|
Channel = (devnum >> 8) | ((lcss & (FEATURE_LCSS_MAX-1)) << 8);
|
|
|
|
if (sysblk.devnum_fl[Channel] == NULL)
|
|
sysblk.devnum_fl[Channel] = (DEVBLK**)
|
|
calloc( 256, sizeof( DEVBLK* ));
|
|
|
|
sysblk.devnum_fl[Channel][devnum & 0xff] = dev;
|
|
}
|
|
if (!have_config_lock)
|
|
release_lock( &sysblk.config );
|
|
}
|
|
|
|
static void AddSubchanFastLookup( DEVBLK *dev, U16 ssid, U16 subchan )
|
|
{
|
|
unsigned int schw;
|
|
|
|
int have_config_lock = have_lock( &sysblk.config );
|
|
|
|
if (!have_config_lock)
|
|
obtain_lock( &sysblk.config );
|
|
{
|
|
if (sysblk.subchan_fl == NULL)
|
|
sysblk.subchan_fl = (DEVBLK***)
|
|
calloc( 256 * FEATURE_LCSS_MAX, sizeof( DEVBLK** ));
|
|
|
|
schw = (subchan >> 8) | (SSID_TO_LCSS( ssid ) << 8);
|
|
|
|
if (sysblk.subchan_fl[schw] == NULL)
|
|
sysblk.subchan_fl[schw] = (DEVBLK**)
|
|
calloc( 256, sizeof( DEVBLK* ));
|
|
|
|
sysblk.subchan_fl[schw][subchan & 0xff] = dev;
|
|
}
|
|
if (!have_config_lock)
|
|
release_lock( &sysblk.config );
|
|
}
|
|
|
|
static void DelDevnumFastLookup(U16 lcss,U16 devnum)
|
|
{
|
|
unsigned int Channel;
|
|
if(sysblk.devnum_fl==NULL)
|
|
{
|
|
return;
|
|
}
|
|
Channel=(devnum & 0xff00)>>8 | ((lcss & (FEATURE_LCSS_MAX-1))<<8);
|
|
if(sysblk.devnum_fl[Channel]==NULL)
|
|
{
|
|
return;
|
|
}
|
|
sysblk.devnum_fl[Channel][devnum & 0xff]=NULL;
|
|
}
|
|
|
|
static void DelSubchanFastLookup(U16 ssid, U16 subchan)
|
|
{
|
|
unsigned int schw;
|
|
if(sysblk.subchan_fl==NULL)
|
|
{
|
|
return;
|
|
}
|
|
schw=((subchan & 0xff00)>>8)|(SSID_TO_LCSS(ssid) << 8);
|
|
if(sysblk.subchan_fl[schw]==NULL)
|
|
{
|
|
return;
|
|
}
|
|
sysblk.subchan_fl[schw][subchan & 0xff]=NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* get_devblk */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* NOTE: also does OBTAIN_DEVLOCK( dev ); */
|
|
|
|
static DEVBLK *get_devblk(U16 lcss, U16 devnum)
|
|
{
|
|
DEVBLK* dev;
|
|
DEVBLK** dvpp;
|
|
char buf[32];
|
|
|
|
if (lcss >= FEATURE_LCSS_MAX)
|
|
lcss = 0;
|
|
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (!(dev->allocated) && dev->ssid == LCSS_TO_SSID( lcss ))
|
|
break;
|
|
|
|
if (!dev)
|
|
{
|
|
size_t amt = sizeof( DEVBLK );
|
|
amt = ROUND_UP( amt, _4K );
|
|
|
|
if (!(dev = (DEVBLK*) calloc_aligned( amt, _4K )))
|
|
{
|
|
MSGBUF( buf, "calloc(%d)", (int) amt );
|
|
// "%1d:%04X error in function %s: %s"
|
|
WRMSG( HHC01460, "E", lcss, devnum, buf, strerror( errno ));
|
|
return NULL;
|
|
}
|
|
|
|
INIT_BLOCK_HEADER_TRAILER( dev, DEVBLK );
|
|
|
|
/* Initialize the device lock and conditions */
|
|
|
|
initialize_lock ( &dev->lock );
|
|
initialize_condition ( &dev->kbcond );
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
initialize_condition ( &dev->shiocond );
|
|
#endif // defined( OPTION_SHARED_DEVICES )
|
|
#if defined(OPTION_SCSI_TAPE)
|
|
initialize_condition ( &dev->stape_sstat_cond );
|
|
InitializeListLink ( &dev->stape_statrq.link );
|
|
InitializeListLink ( &dev->stape_mntdrq.link );
|
|
dev->stape_statrq.dev = dev;
|
|
dev->stape_mntdrq.dev = dev;
|
|
#endif
|
|
/* Search for the last device block on the chain */
|
|
for (dvpp = &(sysblk.firstdev); *dvpp != NULL;
|
|
dvpp = &((*dvpp)->nextdev));
|
|
|
|
/* Add the new device block to the end of the chain */
|
|
*dvpp = dev;
|
|
|
|
dev->ssid = LCSS_TO_SSID( lcss );
|
|
dev->subchan = sysblk.highsubchan[ lcss ]++;
|
|
}
|
|
|
|
/* Obtain the device lock. Caller will release it. */
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
dev->group = NULL;
|
|
dev->member = 0;
|
|
|
|
memset(dev->filename, 0, sizeof(dev->filename));
|
|
|
|
dev->cpuprio = sysblk.cpuprio;
|
|
dev->devprio = sysblk.devprio;
|
|
dev->hnd = NULL;
|
|
dev->devnum = devnum;
|
|
|
|
MSGBUF( buf, "&dev->lock %1d:%04X", LCSS_DEVNUM );
|
|
set_lock_name( &dev->lock, buf );
|
|
|
|
dev->chanset = lcss;
|
|
dev->chptype[0] = CHP_TYPE_EIO; /* Interim - default to emulated */
|
|
dev->fd = -1;
|
|
dev->ioint.dev = dev;
|
|
dev->ioint.pending = 1;
|
|
dev->ioint.priority = -1;
|
|
dev->pciioint.dev = dev;
|
|
dev->pciioint.pcipending = 1;
|
|
dev->pciioint.priority = -1;
|
|
dev->attnioint.dev = dev;
|
|
dev->attnioint.attnpending = 1;
|
|
dev->attnioint.priority = -1;
|
|
dev->oslinux = (OS_LINUX == sysblk.pgminttr);
|
|
|
|
/* Initialize storage view */
|
|
dev->mainstor = sysblk.mainstor;
|
|
dev->storkeys = sysblk.storkeys;
|
|
dev->mainlim = sysblk.mainsize - 1;
|
|
|
|
/* Initialize the path management control word */
|
|
memset (&dev->pmcw, 0, sizeof(PMCW));
|
|
dev->pmcw.devnum[0] = dev->devnum >> 8;
|
|
dev->pmcw.devnum[1] = dev->devnum & 0xFF;
|
|
dev->pmcw.lpm = 0x80;
|
|
dev->pmcw.pim = 0x80;
|
|
dev->pmcw.pom = 0xFF;
|
|
dev->pmcw.pam = 0x80;
|
|
dev->pmcw.chpid[0] = dev->devnum >> 8;
|
|
|
|
#if defined(OPTION_SHARED_DEVICES)
|
|
dev->shrdwait = -1;
|
|
#endif /*defined(OPTION_SHARED_DEVICES)*/
|
|
|
|
if (!dev->pGUIStat)
|
|
{
|
|
dev->pGUIStat = malloc( sizeof(GUISTAT) );
|
|
dev->pGUIStat->pszOldStatStr = dev->pGUIStat->szStatStrBuff1;
|
|
dev->pGUIStat->pszNewStatStr = dev->pGUIStat->szStatStrBuff2;
|
|
*dev->pGUIStat->pszOldStatStr = 0;
|
|
*dev->pGUIStat->pszNewStatStr = 0;
|
|
}
|
|
|
|
/* Mark device valid */
|
|
dev->pmcw.flag5 |= PMCW5_V;
|
|
dev->allocated = 1;
|
|
|
|
return dev;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ret_devblk */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* NOTE: also does RELEASE_DEVLOCK( dev );*/
|
|
|
|
static void ret_devblk(DEVBLK *dev)
|
|
{
|
|
/* PROGRAMMING NOTE: the device buffer will be freed by the
|
|
'attach_device' function whenever it gets reused and not
|
|
here where you would normally expect it to be done since
|
|
doing it here might cause Hercules to crash due to poorly
|
|
written device handlers that still access the buffer for
|
|
a brief period after the device has been detached.
|
|
*/
|
|
/* Mark device invalid */
|
|
dev->allocated = 0;
|
|
dev->pmcw.flag5 &= ~PMCW5_V;
|
|
RELEASE_DEVLOCK( dev );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to delete a device configuration block */
|
|
/*-------------------------------------------------------------------*/
|
|
static int detach_devblk (DEVBLK *dev, int locked, const char *msg,
|
|
DEVBLK *errdev, DEVGRP *group)
|
|
{
|
|
int i; /* Loop index */
|
|
|
|
/* Free the entire group if this is a grouped device */
|
|
if (free_group( dev->group, locked, msg, errdev ))
|
|
{
|
|
/* Group successfully freed. All devices in the group
|
|
have been detached. Nothing remains for us to do.
|
|
All work has been completed. Return to caller.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/* Restore group ptr that 'free_group' may have set to NULL */
|
|
dev->group = group;
|
|
|
|
/* Obtain the device lock. ret_devblk will release it */
|
|
if (!locked)
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
DelSubchanFastLookup(dev->ssid, dev->subchan);
|
|
if(dev->pmcw.flag5 & PMCW5_V)
|
|
DelDevnumFastLookup(LCSS_DEVNUM);
|
|
|
|
/* Close file or socket */
|
|
if ((dev->fd >= 0) || dev->console)
|
|
/* Call the device close handler */
|
|
(dev->hnd->close)(dev);
|
|
|
|
/* Issue device detached message and build channel report */
|
|
if (dev != errdev)
|
|
{
|
|
if (MLVL(DEBUG))
|
|
{
|
|
// "%1d:%04X %s detached"
|
|
WRMSG (HHC01465, "I", LCSS_DEVNUM, msg);
|
|
}
|
|
|
|
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
|
|
/* Don't bother with channel report if we're shutting down */
|
|
if (!sysblk.shutdown)
|
|
{
|
|
#if defined(_370)
|
|
if (sysblk.arch_mode != ARCH_370_IDX)
|
|
#endif
|
|
build_detach_chrpt( dev );
|
|
}
|
|
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
|
|
}
|
|
|
|
/* Free the argv array */
|
|
for (i = 0; i < dev->argc; i++)
|
|
if (dev->argv[i])
|
|
free(dev->argv[i]);
|
|
if (dev->argv)
|
|
free(dev->argv);
|
|
|
|
/* Free the device type name */
|
|
free(dev->typname);
|
|
|
|
/* Release lock and return the device to the DEVBLK pool */
|
|
ret_devblk( dev ); /* also does RELEASE_DEVLOCK( dev );*/
|
|
|
|
return 0;
|
|
} /* end function detach_devblk */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to delete a device configuration block by subchannel */
|
|
/*-------------------------------------------------------------------*/
|
|
static int detach_subchan (U16 lcss, U16 subchan, U16 devnum)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
int rc;
|
|
char str[64];
|
|
/* Find the device block */
|
|
dev = find_device_by_subchan ((LCSS_TO_SSID(lcss)<<16)|subchan);
|
|
|
|
MSGBUF( str, "subchannel %1d:%04X", lcss, subchan);
|
|
|
|
if (dev == NULL)
|
|
{
|
|
// "%1d:%04X %s does not exist"
|
|
WRMSG (HHC01464, "E", lcss, devnum, str);
|
|
return 1;
|
|
}
|
|
|
|
obtain_lock(&sysblk.config);
|
|
|
|
if (dev->group)
|
|
MSGBUF( str, "group subchannel %1d:%04X", lcss, subchan);
|
|
|
|
rc = detach_devblk( dev, FALSE, str, NULL, dev->group );
|
|
|
|
release_lock(&sysblk.config);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to terminate all CPUs and devices */
|
|
/*-------------------------------------------------------------------*/
|
|
void release_config( void* arg )
|
|
{
|
|
DEVBLK *dev;
|
|
int cpu;
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
/* Deconfigure all CPU's */
|
|
OBTAIN_INTLOCK(NULL);
|
|
for (cpu = 0; cpu < sysblk.maxcpu; cpu++)
|
|
if(IS_CPU_ONLINE(cpu))
|
|
deconfigure_cpu(cpu);
|
|
RELEASE_INTLOCK(NULL);
|
|
|
|
/* Dump trace tables at exit */
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
if (sysblk.shrddtax)
|
|
shared_print_trace_table();
|
|
#endif
|
|
if (cckd_dtax())
|
|
cckd_print_itrace();
|
|
if (ptt_dtax())
|
|
ptt_pthread_print();
|
|
|
|
/* Detach all devices */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (dev->allocated)
|
|
{
|
|
if (sysblk.arch_mode == ARCH_370_IDX)
|
|
detach_device(LCSS_DEVNUM);
|
|
else
|
|
detach_subchan(SSID_TO_LCSS(dev->ssid), dev->subchan, dev->devnum);
|
|
}
|
|
|
|
/* Terminate device threads */
|
|
OBTAIN_IOQLOCK();
|
|
{
|
|
sysblk.devtwait = 0;
|
|
broadcast_condition( &sysblk.ioqcond );
|
|
}
|
|
RELEASE_IOQLOCK();
|
|
|
|
/* release storage */
|
|
sysblk.lock_mainstor = 0;
|
|
WRMSG( HHC01427, "I", "Main", !configure_storage(~0ULL) ? "" : "not " );
|
|
|
|
/* release expanded storage */
|
|
sysblk.lock_xpndstor = 0;
|
|
WRMSG( HHC01427, "I", "Expanded", !configure_xstorage(~0ULL) ? "" : "not ");
|
|
|
|
WRMSG(HHC01422, "I");
|
|
} /* end function release_config */
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* shrd_devinit */
|
|
/*-------------------------------------------------------------------*/
|
|
static void* shrd_devinit( void* arg )
|
|
{
|
|
DEVBLK* dev = (DEVBLK*) arg;
|
|
dev->hnd->init( dev, 0, NULL );
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* configure_shrdport */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_shrdport( U16 shrdport )
|
|
{
|
|
int rc;
|
|
DEVBLK* dev;
|
|
TID tid;
|
|
|
|
obtain_lock( &sysblk.shrdlock );
|
|
|
|
if (shrdport && sysblk.shrdport)
|
|
{
|
|
release_lock( &sysblk.shrdlock );
|
|
// "Shared: Server already active"
|
|
WRMSG( HHC00744, "E" );
|
|
return -1;
|
|
}
|
|
|
|
/* Set requested shared port number */
|
|
sysblk.shrdport = shrdport;
|
|
|
|
/* Terminate the shared_server thread if requested */
|
|
if (!sysblk.shrdport)
|
|
{
|
|
shutdown_shared_server_locked( NULL );
|
|
release_lock( &sysblk.shrdlock );
|
|
return 0;
|
|
}
|
|
|
|
/* Start the shared server thread */
|
|
rc = create_thread( &sysblk.shrdtid, DETACHED,
|
|
shared_server, NULL, "shared_server" );
|
|
if (rc)
|
|
{
|
|
sysblk.shrdport = 0;
|
|
release_lock( &sysblk.shrdlock );
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
return -1;
|
|
}
|
|
|
|
release_lock( &sysblk.shrdlock );
|
|
|
|
/* Retry any pending connections */
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
{
|
|
if (dev->connecting)
|
|
{
|
|
rc = create_thread( &tid, DETACHED,
|
|
shrd_devinit, dev, "shrd_devinit" );
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined( OPTION_SHARED_DEVICES ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if we're a CPU thread or not. (boolean function) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool are_cpu_thread( int* cpunum )
|
|
{
|
|
TID tid = thread_id();
|
|
int i;
|
|
|
|
for (i=0; i < sysblk.maxcpu; i++)
|
|
{
|
|
if (equal_threads( sysblk.cputid[ i ], tid ))
|
|
{
|
|
if (cpunum)
|
|
*cpunum = i;
|
|
return true; // (we ARE a CPU thread)
|
|
}
|
|
}
|
|
|
|
if (cpunum)
|
|
*cpunum = -1;
|
|
|
|
return false; // (we are NOT a CPU thread)
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if we're a CPU executing diagnose (boolean function) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT bool is_diag_instr()
|
|
{
|
|
REGS* regs;
|
|
bool arecpu;
|
|
int ourcpu;
|
|
|
|
/* Find out if we are a cpu thread */
|
|
if (!(arecpu = are_cpu_thread( &ourcpu )))
|
|
return false;
|
|
|
|
/* Point to our REGS structure */
|
|
regs = sysblk.regs[ ourcpu ];
|
|
|
|
/* Return TRUE/FALSE boolean as appropriate */
|
|
return regs->diagnose ? true : false;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to start a new CPU thread */
|
|
/* This routine MUST be called with OBTAIN_INTLOCK already held. */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_cpu( int target_cpu )
|
|
{
|
|
/* If CPU isn't already configured... */
|
|
if (!IS_CPU_ONLINE( target_cpu ))
|
|
{
|
|
int rc;
|
|
char thread_name[32];
|
|
bool arecpu;
|
|
int ourcpu;
|
|
|
|
/* If no more CPUs are permitted, exit */
|
|
if (sysblk.cpus >= sysblk.maxcpu)
|
|
return HERRCPUOFF; /* CPU offline; maximum reached */
|
|
|
|
MSGBUF( thread_name, "Processor %s%02X",
|
|
PTYPSTR( target_cpu ), target_cpu );
|
|
|
|
rc = create_thread( &sysblk.cputid[ target_cpu ], JOINABLE,
|
|
cpu_thread, &target_cpu, thread_name );
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
return HERRCPUOFF; /* CPU offline; create_thread failed */
|
|
}
|
|
|
|
#if defined( _POSIX_THREAD_CPUTIME ) && (_POSIX_THREAD_CPUTIME >= 0)
|
|
/* Initialise the CPU's thread clockid so that clock_gettime() can use it */
|
|
/* provided the _POSIX_THREAD_CPUTIME is supported. */
|
|
pthread_getcpuclockid( sysblk.cputid[ target_cpu ], &sysblk.cpuclockid[ target_cpu ]);
|
|
if (!sysblk.hhc_111_112)
|
|
WRMSG( HHC00111, "I", (long int)_POSIX_THREAD_CPUTIME );
|
|
#else
|
|
/* When not supported, we zero the cpuclockid, which will trigger a */
|
|
/* different approach to obtain the thread CPU time in clock.c */
|
|
sysblk.cpuclockid[ target_cpu ] = 0;
|
|
if (!sysblk.hhc_111_112)
|
|
WRMSG( HHC00112, "W" );
|
|
#endif
|
|
sysblk.hhc_111_112 = true;
|
|
|
|
/* Find out if we are a cpu thread */
|
|
arecpu = are_cpu_thread( &ourcpu );
|
|
|
|
if (arecpu)
|
|
sysblk.regs[ ourcpu ]->intwait = true;
|
|
|
|
/* Wait for CPU thread to initialize */
|
|
while (!IS_CPU_ONLINE( target_cpu ))
|
|
wait_condition( &sysblk.cpucond, &sysblk.intlock );
|
|
|
|
/* Now wait for it to reach its STOPPED state */
|
|
while (sysblk.regs[ target_cpu ]->cpustate != CPUSTATE_STOPPED)
|
|
wait_condition( &sysblk.cpucond, &sysblk.intlock );
|
|
|
|
if (arecpu)
|
|
sysblk.regs[ ourcpu ]->intwait = false;
|
|
|
|
#if defined( FEATURE_011_CONFIG_TOPOLOGY_FACILITY )
|
|
/* Set topology-change-report-pending condition */
|
|
if (FACILITY_ENABLED( 011_CONFIG_TOPOLOGY, sysblk.regs[ target_cpu ]))
|
|
sysblk.topchnge = 1;
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
} /* end function configure_cpu */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to remove a CPU from the configuration */
|
|
/* This routine MUST be called with OBTAIN_INTLOCK already held. */
|
|
/*-------------------------------------------------------------------*/
|
|
int deconfigure_cpu( int target_cpu )
|
|
{
|
|
/* If CPU isn't already deconfigured... */
|
|
if (IS_CPU_ONLINE( target_cpu ))
|
|
{
|
|
int ourcpu;
|
|
bool arecpu = are_cpu_thread( &ourcpu );
|
|
|
|
if (!arecpu || target_cpu != ourcpu) // NORMAL CASE
|
|
{
|
|
/* We're either not a CPU thread, or if we are,
|
|
we're not attempting to deconfigure ourself.
|
|
*/
|
|
|
|
/* Deconfigure CPU */
|
|
sysblk.regs[ target_cpu ]->configured = 0;
|
|
sysblk.regs[ target_cpu ]->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT( sysblk.regs[ target_cpu ]);
|
|
|
|
/* Wake up CPU as it may be waiting */
|
|
WAKEUP_CPU( sysblk.regs[ target_cpu ]);
|
|
|
|
/* (if we're a cpu thread) */
|
|
if (arecpu)
|
|
sysblk.regs[ ourcpu ]->intwait = true;
|
|
|
|
/* Wait for CPU thread to terminate */
|
|
while (IS_CPU_ONLINE( target_cpu ))
|
|
wait_condition( &sysblk.cpucond, &sysblk.intlock );
|
|
|
|
/* (if we're a cpu thread) */
|
|
if (arecpu)
|
|
sysblk.regs[ ourcpu ]->intwait = false;
|
|
|
|
/* Wait for cpu_thread to completely exit */
|
|
join_thread( sysblk.cputid[ target_cpu ], NULL );
|
|
#if defined( OPTION_FTHREADS )
|
|
detach_thread( sysblk.cputid[ target_cpu ]); // only needed for Fish threads
|
|
#endif
|
|
|
|
/*-----------------------------------------------------------*/
|
|
/* Note: While this is the logical place to cleanup and to */
|
|
/* release the associated regs context, there is also */
|
|
/* post-processing that is done by various callers. */
|
|
/*-----------------------------------------------------------*/
|
|
}
|
|
else // (arecpu && target_cpu == ourcpu) HIGHLY UNUSUAL!
|
|
{
|
|
/* We ARE a cpu thread *AND* we're trying to deconfigure
|
|
ourself! This can only happen if B220 SERVC instruction
|
|
is executed to deconfigure its own CPU, or else the CPU
|
|
issues a Hercules command via the diagnose-8 interface
|
|
to deconfigure its own CPU (i.e. itself).
|
|
*/
|
|
sysblk.regs[ target_cpu ]->configured = 0;
|
|
sysblk.regs[ target_cpu ]->cpustate = CPUSTATE_STOPPING;
|
|
ON_IC_INTERRUPT( sysblk.regs[ target_cpu ]);
|
|
}
|
|
|
|
sysblk.cputid [ target_cpu ] = 0;
|
|
sysblk.cpuclockid[ target_cpu ] = 0;
|
|
|
|
#if defined( FEATURE_011_CONFIG_TOPOLOGY_FACILITY )
|
|
/* Set topology-change-report-pending condition */
|
|
|
|
/* PROGRAMMING NOTE: because the CPU has been deconfigured,
|
|
the REGS pointer in sysblk (i.e. sysblk.regs[ <cpunum> ])
|
|
could now possibly be NULL so to be safe we will use the
|
|
FACILITY_ENABLED_DEV macro instead as "sysblk.arch_mode"
|
|
should ALWAYS be valid.
|
|
*/
|
|
if (FACILITY_ENABLED_DEV( 011_CONFIG_TOPOLOGY ))
|
|
sysblk.topchnge = 1;
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
} /* end function deconfigure_cpu */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Configure number of online CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
static int configure_numcpu_intlock_held( int numcpu )
|
|
{
|
|
int cpu;
|
|
|
|
/* Requested number of online CPUs must be <= maximum */
|
|
if (numcpu > sysblk.maxcpu)
|
|
return HERRCPUOFF; /* CPU offline; number > maximum */
|
|
|
|
/* All CPUs must be stopped beforehand */
|
|
if (sysblk.cpus && !are_all_cpus_stopped_intlock_held())
|
|
return HERRCPUONL; /* CPU online; not all are stopped */
|
|
|
|
/* Keep deconfiguring CPUS until within desired range */
|
|
for (cpu=sysblk.hicpu-1; cpu >= 0 && sysblk.cpus > numcpu; cpu--)
|
|
{
|
|
/* Deconfigure this CPU if it's currently configured */
|
|
if (IS_CPU_ONLINE( cpu ))
|
|
deconfigure_cpu( cpu );
|
|
}
|
|
|
|
/* Keep configuring CPUs until desired amount reached */
|
|
for (cpu=0; cpu < sysblk.maxcpu && sysblk.cpus < numcpu; cpu++)
|
|
{
|
|
/* Configure this CPU if it's not currently configured */
|
|
if (!IS_CPU_ONLINE( cpu ))
|
|
configure_cpu( cpu );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Configure number of online CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_numcpu( int numcpu )
|
|
{
|
|
int rc = 0;
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
rc = configure_numcpu_intlock_held( numcpu );
|
|
|
|
/* Update all CPU IDs to reflect new NUMCPU */
|
|
if (rc == 0)
|
|
resetAllCpuIds();
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Configure maximum number of online CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
int configure_maxcpu( int maxcpu )
|
|
{
|
|
int rc = 0;
|
|
OBTAIN_INTLOCK( NULL );
|
|
{
|
|
/* Requested maximum must be <= absolute maximum possible */
|
|
if (maxcpu > MAX_CPU_ENGS)
|
|
{
|
|
RELEASE_INTLOCK( NULL );
|
|
return HERRCPUOFF; /* CPU offline; number > maximum */
|
|
}
|
|
|
|
/* All CPUs must be stopped beforehand */
|
|
if (sysblk.cpus && !are_all_cpus_stopped_intlock_held())
|
|
{
|
|
RELEASE_INTLOCK( NULL );
|
|
return HERRCPUONL; /* CPU online; not all are stopped */
|
|
}
|
|
|
|
/* Set new maximum number of online CPUs */
|
|
sysblk.maxcpu = maxcpu;
|
|
|
|
/* Deconfigure excess online CPUs if necessary */
|
|
if (sysblk.cpus > maxcpu)
|
|
rc = configure_numcpu_intlock_held( maxcpu );
|
|
|
|
/* Update all CPU IDs to reflect new MAXCPU */
|
|
resetAllCpuIds();
|
|
}
|
|
RELEASE_INTLOCK( NULL );
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to build a device configuration block */
|
|
/*-------------------------------------------------------------------*/
|
|
int attach_device (U16 lcss, U16 devnum, const char *type,
|
|
int addargc, char *addargv[],
|
|
int numconfdev)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
|
|
/* Obtain (re)configuration lock */
|
|
obtain_lock(&sysblk.config);
|
|
|
|
/* Check whether device number has already been defined */
|
|
if (find_device_by_devnum(lcss,devnum) != NULL)
|
|
{
|
|
// "%1d:%04X device already exists"
|
|
WRMSG (HHC01461, "E", lcss, devnum);
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
|
|
/* Obtain device block from our DEVBLK pool and lock the device. */
|
|
dev = get_devblk(lcss, devnum); /* does OBTAIN_DEVLOCK( dev ); */
|
|
|
|
/* Clear (re-initialize) any left-over garbage from previous use */
|
|
if (dev->dasdsfn)
|
|
{
|
|
free( dev->dasdsfn );
|
|
dev->dasdsfn = NULL;
|
|
dev-> dasdsfx = 0;
|
|
}
|
|
|
|
// PROGRAMMING NOTE: the rule is, once a DEVBLK has been obtained
|
|
// from the pool it can be returned back to the pool via a simple
|
|
// call to ret_devblk if the device handler initialization function
|
|
// has NOT yet been called. Once the device handler initialization
|
|
// function has been called however then you MUST use detach_devblk
|
|
// to return it back to the pool so that the entire group is freed.
|
|
|
|
if(!(dev->hnd = hdl_DEVHND(type)))
|
|
{
|
|
// "%1d:%04X devtype %s not recognized"
|
|
WRMSG (HHC01462, "E", lcss, devnum, type);
|
|
ret_devblk(dev); /* also does RELEASE_DEVLOCK( dev );*/
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
|
|
dev->typname = strdup(type);
|
|
|
|
/* Copy the arguments */
|
|
dev->argc = addargc;
|
|
if (addargc)
|
|
{
|
|
dev->argv = malloc ( addargc * sizeof(BYTE *) );
|
|
for (i = 0; i < addargc; i++)
|
|
if (addargv[i])
|
|
dev->argv[i] = strdup(addargv[i]);
|
|
else
|
|
dev->argv[i] = NULL;
|
|
}
|
|
else
|
|
dev->argv = NULL;
|
|
|
|
/* Set the number of config statement device addresses */
|
|
dev->numconfdev = numconfdev;
|
|
|
|
/* Call the device handler initialization function */
|
|
rc = (int)(dev->hnd->init)(dev, addargc, addargv);
|
|
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X device initialization failed"
|
|
WRMSG (HHC01463, "E", lcss, devnum);
|
|
|
|
/* Detach the device and return it back to the DEVBLK pool */
|
|
detach_devblk( dev, TRUE, "device", dev, dev->group );
|
|
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
|
|
/* Obtain device data buffer */
|
|
if (dev->bufsize != 0)
|
|
{
|
|
/* PROGRAMMING NOTE: we free the device buffer here, not in
|
|
the 'ret_devblk' function (where you would normally expect
|
|
it to be done) since doing it in 'ret_devblk' might cause
|
|
Hercules to crash due to poorly written device handlers
|
|
that continue accessing the buffer for a brief period even
|
|
though the device has already been detached.
|
|
*/
|
|
if (dev->buf && dev->buf == dev->prev_buf)
|
|
free_aligned( dev->buf );
|
|
else if (dev->prev_buf)
|
|
free_aligned( dev->prev_buf );
|
|
|
|
dev->buf = dev->prev_buf = malloc_aligned( dev->bufsize, _4K );
|
|
|
|
if (dev->buf == NULL)
|
|
{
|
|
char buf[64];
|
|
// "%1d:%04X error in function %s: %s"
|
|
MSGBUF( buf, "malloc(%lu)", (unsigned long) dev->bufsize);
|
|
WRMSG (HHC01460, "E", lcss, dev->devnum, buf, strerror(errno));
|
|
|
|
/* Detach the device and return it back to the DEVBLK pool */
|
|
detach_devblk( dev, TRUE, "device", dev, dev->group );
|
|
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Release device lock */
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
|
|
/* Build Channel Report */
|
|
#if defined(_370)
|
|
if (sysblk.arch_mode != ARCH_370_IDX)
|
|
#endif
|
|
build_attach_chrpt( dev );
|
|
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
|
|
|
|
/*
|
|
if(lcss!=0 && sysblk.arch_mode==ARCH_370_IDX)
|
|
{
|
|
// "%1d:%04X: only devices on CSS 0 are usable in S/370 mode"
|
|
WRMSG (HHC01458, "W", lcss, devnum);
|
|
}
|
|
*/
|
|
|
|
release_lock(&sysblk.config);
|
|
|
|
if ( rc == 0 && MLVL(DEBUG) )
|
|
{
|
|
// "Device %04X type %04X subchannel %d:%04X attached"
|
|
WRMSG(HHC02198, "I", dev->devnum, dev->devtype, dev->chanset, dev->subchan);
|
|
}
|
|
|
|
return 0;
|
|
} /* end function attach_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to delete a device configuration block by device number */
|
|
/*-------------------------------------------------------------------*/
|
|
int detach_device (U16 lcss,U16 devnum)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
int rc;
|
|
char* str = "device";
|
|
|
|
/* Find the device block */
|
|
dev = find_device_by_devnum (lcss,devnum);
|
|
|
|
if (dev == NULL)
|
|
{
|
|
// "%1d:%04X %s does not exist"
|
|
WRMSG (HHC01464, "E", lcss, devnum, str);
|
|
return 1;
|
|
}
|
|
|
|
obtain_lock(&sysblk.config);
|
|
|
|
if (dev->group)
|
|
str = "group device";
|
|
|
|
rc = detach_devblk( dev, FALSE, str, NULL, dev->group );
|
|
|
|
release_lock(&sysblk.config);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to rename a device configuration block */
|
|
/*-------------------------------------------------------------------*/
|
|
int define_device (U16 lcss, U16 olddevn,U16 newdevn)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
|
|
/* Obtain (re)configuration lock */
|
|
obtain_lock(&sysblk.config);
|
|
|
|
/* Find the device block */
|
|
dev = find_device_by_devnum (lcss, olddevn);
|
|
|
|
if (dev == NULL)
|
|
{
|
|
WRMSG (HHC01464, "E", lcss, olddevn, "device");
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
|
|
/* Check that new device number does not already exist */
|
|
if (find_device_by_devnum(lcss, newdevn) != NULL)
|
|
{
|
|
WRMSG (HHC01461, "E", lcss, newdevn);
|
|
release_lock(&sysblk.config);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
|
|
/* Build Channel Report */
|
|
#if defined(_370)
|
|
if (sysblk.arch_mode != ARCH_370_IDX)
|
|
#endif
|
|
build_detach_chrpt( dev );
|
|
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
|
|
|
|
/* Obtain the device lock */
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
/* Update the device number in the DEVBLK */
|
|
dev->devnum = newdevn;
|
|
|
|
/* Update the device number in the PMCW */
|
|
dev->pmcw.devnum[0] = newdevn >> 8;
|
|
dev->pmcw.devnum[1] = newdevn & 0xFF;
|
|
|
|
DelDevnumFastLookup(lcss,olddevn);
|
|
AddDevnumFastLookup(dev,lcss,newdevn);
|
|
|
|
/* Release device lock */
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
#ifdef _FEATURE_CHANNEL_SUBSYSTEM
|
|
/* Build Channel Report */
|
|
#if defined(_370)
|
|
if (sysblk.arch_mode != ARCH_370_IDX)
|
|
#endif
|
|
build_attach_chrpt( dev );
|
|
#endif /*_FEATURE_CHANNEL_SUBSYSTEM*/
|
|
|
|
// WRMSG (HHC01459, "I", lcss, olddevn, lcss, newdevn);
|
|
|
|
release_lock(&sysblk.config);
|
|
return 0;
|
|
} /* end function define_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to group devblk's belonging to one device (eg OSA, LCS) */
|
|
/* */
|
|
/* group_device is intended to be called from within a device */
|
|
/* initialisation routine to group 1 or more devices to a logical */
|
|
/* device group. */
|
|
/* */
|
|
/* group_device will return true for the device that completes */
|
|
/* the device group. (ie the last device to join the group) */
|
|
/* */
|
|
/* when no group exists, and device group is called with a device */
|
|
/* count of zero, then no group will be created. Otherwise */
|
|
/* a new group will be created and the currently attaching device */
|
|
/* will be the first in the group. */
|
|
/* */
|
|
/* when a device in a group is detached, all devices in the group */
|
|
/* will be detached. The first device to be detached will enter */
|
|
/* its close routine with the group intact. Subsequent devices */
|
|
/* being detached will no longer have access to previously detached */
|
|
/* devices. */
|
|
/* */
|
|
/* Example of a fixed count device group: */
|
|
/* */
|
|
/* device_init(dev) */
|
|
/* { */
|
|
/* if( !device_group(dev, 2) ) */
|
|
/* return 0; */
|
|
/* */
|
|
/* ... all devices in the group have been attached, */
|
|
/* ... group initialisation may proceed. */
|
|
/* */
|
|
/* } */
|
|
/* */
|
|
/* */
|
|
/* Variable device group example: */
|
|
/* */
|
|
/* device_init(dev) */
|
|
/* { */
|
|
/* if( !group_device(dev, 0) && dev->group ) */
|
|
/* return 0; */
|
|
/* */
|
|
/* if( !device->group ) */
|
|
/* { */
|
|
/* ... process parameters to determine number of devices */
|
|
/* */
|
|
/* // Create group */
|
|
/* if( !group_device(dev, variable_count) ) */
|
|
/* return 0; */
|
|
/* } */
|
|
/* */
|
|
/* ... all devices in the group have been attached, */
|
|
/* ... group initialisation may proceed. */
|
|
/* } */
|
|
/* */
|
|
/* */
|
|
/* dev->group : pointer to DEVGRP structure or NULL */
|
|
/* dev->member : index into memdev array in DEVGRP structure for */
|
|
/* : current DEVBLK */
|
|
/* group->members : number of members in group */
|
|
/* group->acount : number active members in group */
|
|
/* group->memdev[] : array of DEVBLK pointers of member devices */
|
|
/* */
|
|
/* */
|
|
/* members will be equal to acount for a complete group */
|
|
/* */
|
|
/* */
|
|
/* Always: (for grouped devices) */
|
|
/* dev->group->memdev[dev->member] == dev */
|
|
/* */
|
|
/* */
|
|
/* Jan Jaeger, 23 Apr 2004 */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int group_device(DEVBLK *dev, int members)
|
|
{
|
|
DEVBLK *tmp;
|
|
|
|
// Find a compatible group that is incomplete
|
|
for (tmp = sysblk.firstdev;
|
|
tmp != NULL
|
|
&& (!tmp->allocated // not allocated
|
|
|| !tmp->group // not a group device
|
|
|| strcmp(tmp->typname,dev->typname) // unequal type
|
|
|| (tmp->group->members == tmp->group->acount) ); // complete
|
|
tmp = tmp->nextdev) ;
|
|
|
|
if(tmp)
|
|
{
|
|
// Join Group
|
|
dev->group = tmp->group;
|
|
dev->member = dev->group->acount++;
|
|
dev->group->memdev[dev->member] = dev;
|
|
}
|
|
else if(members)
|
|
{
|
|
// Allocate a new Group when requested
|
|
size_t size = sizeof(DEVGRP) + (members * sizeof(DEVBLK*));
|
|
dev->group = malloc( size );
|
|
memset( dev->group, 0, size );
|
|
dev->group->members = members;
|
|
dev->group->acount = 1;
|
|
dev->group->memdev[0] = dev;
|
|
dev->member = 0;
|
|
}
|
|
|
|
return (dev->group && (dev->group->members == dev->group->acount));
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to free a device group (i.e. all devices in the group) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT BYTE free_group( DEVGRP *group, int locked,
|
|
const char *msg, DEVBLK *errdev )
|
|
{
|
|
DEVBLK *dev;
|
|
int i;
|
|
|
|
if (!group || !group->members)
|
|
return FALSE; // no group or group has no members
|
|
|
|
for (i=0; i < group->acount; i++)
|
|
{
|
|
if ((dev = group->memdev[i]) && dev->allocated)
|
|
{
|
|
// PROGRAMMING NOTE: detach_devblk automatically calls
|
|
// free_group (i.e. THIS function) to try and free the
|
|
// entire group at once in case it is a grouped device.
|
|
// Therefore we must clear dev->group to NULL *before*
|
|
// calling detach_devblk to prevent infinite recursion.
|
|
|
|
dev->group = NULL;
|
|
detach_devblk( dev, dev == errdev && locked, msg, errdev,
|
|
group );
|
|
}
|
|
}
|
|
|
|
free( group );
|
|
return TRUE; // group successfully freed
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to find a device block given the device number */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT DEVBLK* find_device_by_devnum( U16 lcss, U16 devnum )
|
|
{
|
|
DEVBLK* dev;
|
|
DEVBLK** devtab;
|
|
int chan;
|
|
|
|
chan = (devnum & 0xff00) >> 8 | ((lcss & (FEATURE_LCSS_MAX-1)) << 8);
|
|
|
|
if (sysblk.devnum_fl)
|
|
{
|
|
devtab = sysblk.devnum_fl[ chan ];
|
|
|
|
if (devtab)
|
|
{
|
|
dev = devtab[ devnum & 0xff ];
|
|
|
|
if (dev && IS_DEV( dev ) && dev->devnum == devnum)
|
|
return dev;
|
|
else
|
|
DelDevnumFastLookup( lcss, devnum );
|
|
}
|
|
}
|
|
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (1
|
|
&& IS_DEV( dev )
|
|
&& dev->devnum == devnum
|
|
&& lcss == SSID_TO_LCSS( dev->ssid )
|
|
)
|
|
break;
|
|
|
|
if (dev)
|
|
AddDevnumFastLookup( dev, lcss, devnum );
|
|
|
|
return dev;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to find a device block given the subchannel number */
|
|
/*-------------------------------------------------------------------*/
|
|
DEVBLK* find_device_by_subchan( U32 ioid )
|
|
{
|
|
DEVBLK* dev;
|
|
U16 subchan = ioid & 0xFFFF;
|
|
unsigned int schw = ((subchan & 0xff00)>>8) | (IOID_TO_LCSS( ioid ) << 8);
|
|
|
|
if (1
|
|
&& sysblk.subchan_fl
|
|
&& sysblk.subchan_fl[ schw ]
|
|
&& sysblk.subchan_fl[ schw ][ subchan & 0xff ]
|
|
)
|
|
return sysblk.subchan_fl[ schw ][ subchan & 0xff ];
|
|
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (dev->ssid == IOID_TO_SSID( ioid ) && dev->subchan == subchan)
|
|
break;
|
|
|
|
if (dev)
|
|
AddSubchanFastLookup( dev, IOID_TO_SSID( ioid ), subchan );
|
|
else
|
|
DelSubchanFastLookup( IOID_TO_SSID( ioid ), subchan );
|
|
|
|
return dev;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to Parse a LCSS specification in a device number spec */
|
|
/* Syntax : [lcss:]Anything... */
|
|
/* Function args : */
|
|
/* const char * spec : Parsed string */
|
|
/* char **rest : Rest of string (or original str) */
|
|
/* Returns : */
|
|
/* int : 0 if not specified, 0<=n<FEATURE_LCSS_MAX */
|
|
/* -1 Spec error */
|
|
/* */
|
|
/* If the function returns a positive value, then *rest should */
|
|
/* be freed by the caller. */
|
|
/*-------------------------------------------------------------------*/
|
|
static int parse_lcss( const char* spec, char** rest, int verbose )
|
|
{
|
|
int lcssid;
|
|
char* wrk;
|
|
char* lcss;
|
|
char* r;
|
|
char* strptr;
|
|
char* garbage;
|
|
char* strtok_str = NULL;
|
|
size_t len;
|
|
|
|
len = strlen( spec ) + 1;
|
|
wrk = malloc( len );
|
|
strlcpy( wrk, spec, len );
|
|
|
|
if (!(lcss = strtok_r( wrk, ":", &strtok_str )))
|
|
{
|
|
if (verbose)
|
|
{
|
|
// "Unspecified error occurred while parsing Logical Channel Subsystem Identification"
|
|
WRMSG( HHC01466, "E" );
|
|
}
|
|
free( wrk );
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (!(r=strtok_r( NULL, ":", &strtok_str )))
|
|
{
|
|
*rest = wrk;
|
|
return 0;
|
|
}
|
|
|
|
garbage = strtok_r( NULL, ":", &strtok_str );
|
|
|
|
if (garbage)
|
|
{
|
|
if (verbose)
|
|
{
|
|
// "No more than 1 Logical Channel Subsystem Identification may be specified"
|
|
WRMSG( HHC01467, "E" );
|
|
}
|
|
free( wrk );
|
|
return -1;
|
|
}
|
|
|
|
lcssid = strtoul( lcss, &strptr, 10 );
|
|
|
|
if (*strptr != 0)
|
|
{
|
|
if (verbose)
|
|
{
|
|
// "Non-numeric Logical Channel Subsystem Identification %s"
|
|
WRMSG( HHC01468, "E", lcss );
|
|
}
|
|
free( wrk );
|
|
return -1;
|
|
}
|
|
|
|
if (lcssid >= FEATURE_LCSS_MAX)
|
|
{
|
|
if (verbose)
|
|
{
|
|
// "Logical Channel Subsystem Identification %d exceeds maximum of %d"
|
|
WRMSG( HHC01469, "E", lcssid, FEATURE_LCSS_MAX-1 );
|
|
}
|
|
free( wrk );
|
|
return -1;
|
|
}
|
|
|
|
len = strlen( r ) + 1;
|
|
*rest = malloc( len );
|
|
strlcpy( *rest, r, len );
|
|
free( wrk );
|
|
|
|
return lcssid;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* parse_single_devnum_INTERNAL */
|
|
/*-------------------------------------------------------------------*/
|
|
static int parse_single_devnum_INTERNAL
|
|
(
|
|
const char* spec,
|
|
U16* p_lcss,
|
|
U16* p_devnum,
|
|
int verbose
|
|
)
|
|
{
|
|
int rc;
|
|
U16 lcss;
|
|
char *r;
|
|
char *strptr;
|
|
|
|
if ((rc = parse_lcss( spec, &r, verbose )) < 0)
|
|
return -1;
|
|
|
|
lcss = rc;
|
|
|
|
if (str_caseless_eq( r, "sysg" ))
|
|
{
|
|
*p_devnum = 0;
|
|
*p_lcss = lcss;
|
|
free( r );
|
|
return 0;
|
|
}
|
|
|
|
rc = strtoul( r, &strptr, 16 );
|
|
|
|
if (0
|
|
|| rc < 0
|
|
|| rc > 0xffff
|
|
|| *strptr != 0
|
|
)
|
|
{
|
|
int err = 1;
|
|
|
|
/* Maybe it's just a statement comment? */
|
|
if (1
|
|
&& rc >= 0
|
|
&& rc <= 0xffff
|
|
&& *strptr != 0
|
|
)
|
|
{
|
|
while (*strptr == ' ')
|
|
strptr++;
|
|
|
|
/* End of statement or start of comment? */
|
|
if (0
|
|
|| *strptr == 0
|
|
|| *strptr == '#'
|
|
)
|
|
err = 0; /* Then not an error! */
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
if (verbose)
|
|
{
|
|
// "Incorrect %s near character '%c'"
|
|
WRMSG( HHC01470, "E", "device address specification", *strptr );
|
|
}
|
|
free( r );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
*p_devnum = rc;
|
|
*p_lcss = lcss;
|
|
|
|
free( r );
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* parse_single_devnum */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int parse_single_devnum( const char* spec, U16* lcss, U16* devnum )
|
|
{
|
|
int verbose = TRUE;
|
|
return parse_single_devnum_INTERNAL( spec, lcss, devnum, verbose );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* parse_single_devnum_silent */
|
|
/*-------------------------------------------------------------------*/
|
|
int parse_single_devnum_silent( const char* spec, U16* lcss, U16* devnum )
|
|
{
|
|
int verbose = FALSE;
|
|
return parse_single_devnum_INTERNAL( spec, lcss, devnum, verbose );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to Parse compound device numbers */
|
|
/* Syntax : [lcss:]CCUU[-CUU][,CUU..][.nn][...] */
|
|
/* Examples : 200-23F */
|
|
/* 200,201 */
|
|
/* 200.16 */
|
|
/* 200-23F,280.8 */
|
|
/* etc... */
|
|
/* : is the LCSS id separator (only 0 or 1 allowed and it must be 1st*/
|
|
/* - is the range specification (from CUU to CUU) */
|
|
/* , is the separator */
|
|
/* . is the count indicator (nn is decimal) */
|
|
/* 1st parm is the specification string as specified above */
|
|
/* 2nd parm is the address of an array of DEVARRAY */
|
|
/* Return value : 0 - Parsing error, etc.. */
|
|
/* >0 - Size of da */
|
|
/* */
|
|
/* NOTE : A basic validity check is made for the following : */
|
|
/* All CUUs must belong on the same channel */
|
|
/* (this check is to eventually pave the way to a formal */
|
|
/* channel/cu/device architecture) */
|
|
/* no 2 identical CCUUs */
|
|
/* ex : 200,300 : WRONG */
|
|
/* 200.12,200.32 : WRONG */
|
|
/* 2FF.2 : WRONG */
|
|
/* NOTE : caller should free the array returned in da if the return */
|
|
/* value is not 0 */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT size_t parse_devnums( const char* spec, DEVNUMSDESC* dd )
|
|
{
|
|
size_t gcount; /* Group count */
|
|
size_t i; /* Index runner */
|
|
char* grps; /* Pointer to current devnum group */
|
|
char* sc; /* Specification string copy */
|
|
DEVARRAY* dgrs; /* Device groups */
|
|
U16 cuu1, cuu2; /* CUUs */
|
|
char* strptr; /* strtoul ptr-ptr */
|
|
int basechan = 0; /* Channel for all CUUs */
|
|
int duplicate; /* duplicated CUU indicator */
|
|
int badcuu; /* offending CUU */
|
|
int rc; /* Return code work var */
|
|
char* strtok_str; /* Last token */
|
|
|
|
strtok_str = NULL;
|
|
|
|
if ((rc = parse_lcss( spec, &sc, 1 )) < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
dd->lcss = rc;
|
|
|
|
/* Split by ',' groups */
|
|
|
|
gcount = 0;
|
|
grps = strtok_r( sc, ",", &strtok_str );
|
|
dgrs = NULL;
|
|
|
|
while (grps)
|
|
{
|
|
if (!dgrs)
|
|
dgrs = malloc( sizeof( DEVARRAY ));
|
|
else
|
|
dgrs = realloc( dgrs, sizeof( DEVARRAY ) * (gcount + 1));
|
|
|
|
cuu1 = strtoul( grps, &strptr, 16 );
|
|
|
|
switch (*strptr)
|
|
{
|
|
case 0: /* Single CUU */
|
|
|
|
cuu2 = cuu1;
|
|
break;
|
|
|
|
case '-': /* CUU Range */
|
|
|
|
cuu2 = strtoul( &strptr[1], &strptr, 16 );
|
|
|
|
if (*strptr)
|
|
{
|
|
// "Incorrect %s near character '%c'"
|
|
WRMSG( HHC01470, "E", "second device number in device range", *strptr );
|
|
goto error_ret;
|
|
}
|
|
break;
|
|
|
|
case '.': /* CUU Count */
|
|
|
|
cuu2 = cuu1 + strtoul( &strptr[1], &strptr, 10 );
|
|
cuu2--;
|
|
|
|
if (*strptr)
|
|
{
|
|
// "Incorrect %s near character '%c'"
|
|
WRMSG( HHC01470, "E", "device count", *strptr );
|
|
goto error_ret;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
// "Incorrect %s near character '%c'"
|
|
WRMSG( HHC01470, "E", "device specification", *strptr );
|
|
goto error_ret;
|
|
}
|
|
|
|
/* Check cuu1 <= cuu2 */
|
|
|
|
if (cuu1 > cuu2)
|
|
{
|
|
// "Incorrect device address range %04X < %04X"
|
|
WRMSG( HHC01471, "E", cuu2, cuu1 );
|
|
goto error_ret;
|
|
}
|
|
|
|
if (!gcount)
|
|
{
|
|
basechan = (cuu1 >> 8) & 0xff;
|
|
}
|
|
|
|
badcuu = -1;
|
|
|
|
if (((cuu1 >> 8) & 0xff) != basechan)
|
|
{
|
|
badcuu = cuu1;
|
|
}
|
|
else
|
|
{
|
|
if (((cuu2 >> 8) & 0xff) != basechan)
|
|
{
|
|
badcuu = cuu2;
|
|
}
|
|
}
|
|
|
|
if (badcuu >= 0)
|
|
{
|
|
// "%1d:%04X is on wrong channel, 1st device defined on channel %02X"
|
|
WRMSG( HHC01472, "E", dd->lcss, badcuu, basechan );
|
|
goto error_ret;
|
|
}
|
|
|
|
/* Check for duplicates */
|
|
|
|
duplicate = 0;
|
|
|
|
for (i=0; i < gcount; i++)
|
|
{
|
|
/* check 1st cuu not within existing range */
|
|
if (cuu1 >= dgrs[i].cuu1 && cuu1 <= dgrs[i].cuu2)
|
|
{
|
|
duplicate = 1;
|
|
break;
|
|
}
|
|
|
|
/* check 2nd cuu not within existing range */
|
|
if (cuu2 >= dgrs[i].cuu1 && cuu1 <= dgrs[i].cuu2)
|
|
{
|
|
duplicate = 1;
|
|
break;
|
|
}
|
|
|
|
/* check current range doesn't completely overlap existing range */
|
|
if (cuu1 < dgrs[i].cuu1 && cuu2 > dgrs[i].cuu2)
|
|
{
|
|
duplicate = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (duplicate)
|
|
{
|
|
// "Some or all devices in %04X-%04X duplicate already defined devices"
|
|
WRMSG( HHC01473, "E", cuu1, cuu2 );
|
|
goto error_ret;
|
|
}
|
|
|
|
dgrs[ gcount ].cuu1 = cuu1;
|
|
dgrs[ gcount ].cuu2 = cuu2;
|
|
|
|
gcount++;
|
|
|
|
grps = strtok_r( NULL, ",", &strtok_str );
|
|
}
|
|
|
|
dd->da = dgrs;
|
|
|
|
free( sc );
|
|
|
|
return gcount;
|
|
|
|
error_ret:
|
|
|
|
free( dgrs );
|
|
free( sc );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* parse_and_attach_devices */
|
|
/*-------------------------------------------------------------------*/
|
|
int parse_and_attach_devices(const char *sdevnum,
|
|
const char *sdevtype,
|
|
int addargc,
|
|
char **addargv)
|
|
{
|
|
DEVNUMSDESC dnd;
|
|
int baddev;
|
|
size_t devncount;
|
|
DEVARRAY *da;
|
|
int i;
|
|
U16 devnum;
|
|
int rc;
|
|
int numconfdev = 0;
|
|
int j;
|
|
char **newargv;
|
|
char **orig_newargv;
|
|
|
|
devncount=parse_devnums(sdevnum,&dnd);
|
|
|
|
if(devncount==0)
|
|
return HERRDEVIDA; /* Invalid Device Address */
|
|
|
|
/* Calculate the number of config statement device addresses */
|
|
for (i=0;i<(int)devncount;i++)
|
|
{
|
|
da=dnd.da;
|
|
numconfdev = numconfdev + ((da[i].cuu2 - da[i].cuu1) + 1);
|
|
}
|
|
|
|
newargv=malloc(MAX_ARGS*sizeof(char *));
|
|
orig_newargv=malloc(MAX_ARGS*sizeof(char *));
|
|
|
|
for(baddev=0,i=0;i<(int)devncount;i++)
|
|
{
|
|
da=dnd.da;
|
|
for(devnum=da[i].cuu1;devnum<=da[i].cuu2;devnum++)
|
|
{
|
|
char wrkbfr[32];
|
|
MSGBUF( wrkbfr, "%3.3X",devnum);
|
|
set_symbol("CUU",wrkbfr);
|
|
MSGBUF( wrkbfr, "%4.4X",devnum);
|
|
set_symbol("CCUU",wrkbfr);
|
|
set_symbol("DEVN", wrkbfr);
|
|
MSGBUF( wrkbfr, "%d",dnd.lcss);
|
|
set_symbol("CSS",wrkbfr);
|
|
|
|
for(j=0;j<addargc;j++)
|
|
{
|
|
orig_newargv[j]=newargv[j]=resolve_symbol_string(addargv[j]);
|
|
}
|
|
/* Build the device configuration block */
|
|
rc=attach_device(dnd.lcss, devnum, sdevtype, addargc, newargv, devnum - da[i].cuu1 + 1);
|
|
for(j=0;j<addargc;j++)
|
|
{
|
|
free(orig_newargv[j]);
|
|
}
|
|
|
|
if(rc!=0)
|
|
{
|
|
baddev=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(newargv);
|
|
free(orig_newargv);
|
|
free(dnd.da);
|
|
return baddev?-1:0;
|
|
}
|
|
|
|
#define MAX_LOGO_LINES 256
|
|
/*-------------------------------------------------------------------*/
|
|
/* clearlogo */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void clearlogo()
|
|
{
|
|
size_t i;
|
|
if(sysblk.herclogo!=NULL)
|
|
{
|
|
for(i=0;i<sysblk.logolines;i++)
|
|
{
|
|
free(sysblk.herclogo[i]);
|
|
}
|
|
free(sysblk.herclogo);
|
|
sysblk.herclogo=NULL;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* readlogo */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int readlogo(char *fn)
|
|
{
|
|
char **data;
|
|
char bfr[256];
|
|
char *rec;
|
|
FILE *lf;
|
|
size_t len;
|
|
|
|
clearlogo();
|
|
|
|
lf=fopen(fn,"r");
|
|
if(lf==NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
data=malloc(sizeof(char *)*MAX_LOGO_LINES);
|
|
sysblk.logolines=0;
|
|
while((rec=fgets(bfr,sizeof(bfr),lf))!=NULL)
|
|
{
|
|
rec[strlen(rec)-1]=0;
|
|
len = strlen(rec)+1;
|
|
data[sysblk.logolines]=malloc(len);
|
|
strlcpy(data[sysblk.logolines],rec,len);
|
|
sysblk.logolines++;
|
|
if(sysblk.logolines>MAX_LOGO_LINES)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
fclose(lf);
|
|
sysblk.herclogo=data;
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* parse_conkpalv */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int parse_conkpalv(char* s, int* idle, int* intv, int* cnt )
|
|
{
|
|
size_t n; char *p1, *p2, *p3, c;
|
|
ASSERT(s && *s && idle && intv && cnt);
|
|
if (!s || !*s || !idle || !intv || !cnt) return -1;
|
|
// Format: "(idle,intv,cnt)". All numbers. No spaces.
|
|
if (0
|
|
|| (n = strlen(s)) < 7
|
|
|| s[0] != '('
|
|
|| s[n-1] != ')'
|
|
)
|
|
return -1;
|
|
// 1st sub-operand
|
|
if (!(p1 = strchr(s+1, ','))) return -1;
|
|
c = *p1; *p1 = 0;
|
|
if ( strspn( s+1, "0123456789" ) != strlen(s+1) )
|
|
{
|
|
*p1 = c;
|
|
return -1;
|
|
}
|
|
*p1 = c;
|
|
// 2nd sub-operand
|
|
if (!(p2 = strchr(p1+1, ','))) return -1;
|
|
c = *p2; *p2 = 0;
|
|
if ( strspn( p1+1, "0123456789" ) != strlen(p1+1) )
|
|
{
|
|
*p2 = c;
|
|
return -1;
|
|
}
|
|
*p2 = c;
|
|
// 3rd sub-operand
|
|
if (!(p3 = strchr(p2+1, ')'))) return -1;
|
|
c = *p3; *p3 = 0;
|
|
if ( strspn( p2+1, "0123456789" ) != strlen(p2+1) )
|
|
{
|
|
*p3 = c;
|
|
return -1;
|
|
}
|
|
*p3 = c;
|
|
// convert each to number
|
|
c = *p1; *p1 = 0; *idle = atoi(s+1); *p1 = c;
|
|
c = *p2; *p2 = 0; *intv = atoi(p1+1); *p2 = c;
|
|
c = *p3; *p3 = 0; *cnt = atoi(p2+1); *p3 = c;
|
|
// check results
|
|
if (*idle <= 0 || INT_MAX == *idle) return -1;
|
|
if (*intv <= 0 || INT_MAX == *intv) return -1;
|
|
if (*cnt <= 0 || INT_MAX == *cnt ) return -1;
|
|
return 0;
|
|
}
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|