Files
org-hyperion-cules/dyn76.c
Jan Jaeger 439bdbca92 Remove subversion $Id$ tag
Update copyright statement to 2012
2012-03-24 18:41:51 +11:00

1129 lines
34 KiB
C

/* DYN76.C (c) Copyright Harold Grovesteen, 2010-2012 */
/* Diagnose Instr for file transfers */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/* DYN76 (c) Copyright Jason Paul Winter, 2010 */
/* See license_dyn76.txt for licensing */
#include "hstdinc.h"
#if !defined(_HENGINE_DLL_)
#define _HENGINE_DLL_
#endif /*_HENGINE_DLL_*/
#if !defined(_DYN76_C)
#define _DYN76_C
#endif /* !defined(_DYN76_C) */
#include "hercules.h"
#include "opcode.h"
#include "inline.h"
#include "hdiagf18.h"
#if !defined(_DYN76_H)
#define _DYN76_H
/*-------------------------------------------------------------------*/
/* Internal macro definitions */
/*-------------------------------------------------------------------*/
/* These macros allow retrieving and setting of register contents from/to
the Compatibility Parameter Block, which is in essence a R0-R15 register
save area. These macros ensure storage keys and accesses are checked for
the parameter block.
*/
#define set_reg(_r, _v) \
ARCH_DEP(vstore4)((U32)(_v), (VADR)cmpb+(_r * 4), USE_REAL_ADDR, regs)
#define get_reg(_v, _r) \
_v = ARCH_DEP(vfetch4)((VADR)(cmpb+(_r * 4)), USE_REAL_ADDR, regs)
/* Compile with debugging */
//#define DYN76_DEBUG
//#define DYN76_DEBUG_FKEEPER
/* Keep track of open files, a utility can then be used to close them if needed (fn3) */
struct fkeeper
{
struct fkeeper *next; /* May or may not end up in the global list */
U32 SaveArea; /* Space to save a work register */
U32 id; /* Used to identify this fkeeper to the guest */
int handle; /* Host file system handle */
int mode; /* Text/Binary-Mode */
int data; /* filenames index and for read/write */
char oldname [260];
char filename [260]; /* Also used for 256 byte read/write buffer */
};
/* File Keeper lists. New entries are added at the top of the list
Because both lists are treated a single shared resource they utilize
the same lock, nfile_lock, to control CPU (thread) access */
static struct fkeeper *fkpr_head = NULL; /* Open file status list */
static struct fkeeper *rst_head = NULL; /* Restart list */
static unsigned char DCCascii_to_ebcdic[] =
{
"\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x15\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x3F\x27\x1C\x1D\x1E\x1F"
"\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61"
"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F"
"\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6"
"\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D"
"\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96"
"\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x4F\xD0\xA1\x07"
"\x20\x21\x22\x23\x24\x25\x06\x17\x28\x29\x2A\x2B\x2C\x09\x0A\x1B"
"\x30\x31\x1A\x33\x34\x35\x36\x08\x38\x39\x3A\x3B\x04\x14\x3E\xFF"
"\x41\xAA\x4A\xB1\x9F\xB2\x6A\xB5\xBB\xB4\x9A\x8A\xB0\xCA\xAF\xBC"
"\x90\x8F\xEA\xFA\xBE\xA0\xB6\xB3\x9D\xDA\x9B\x8B\xB7\xB8\xB9\xAB"
"\x64\x65\x62\x66\x63\x67\x9E\x68\x74\x71\x72\x73\x78\x75\x76\x77"
"\xAC\x69\xED\xEE\xEB\xEF\xEC\xBF\x80\xFD\xFE\xFB\xFC\xBA\xAE\x59"
"\x44\x45\x42\x46\x43\x47\x9C\x48\x54\x51\x52\x53\x58\x55\x56\x57"
"\x8C\x49\xCD\xCE\xCB\xCF\xCC\xE1\x70\xDD\xDE\xDB\xDC\x8D\x8E\xDF"
};
static unsigned char DCCebcdic_to_ascii[] =
{
"\x00\x01\x02\x03\x9C\x09\x86\x7F\x97\x8D\x8E\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x9D\x0A\x08\x87\x18\x19\x92\x8F\x1C\x1D\x1E\x1F"
"\x80\x81\x82\x83\x84\x85\x17\x1B\x88\x89\x8A\x8B\x8C\x05\x06\x07"
"\x90\x91\x16\x93\x94\x95\x96\x04\x98\x99\x9A\x9B\x14\x15\x9E\x1A"
"\x20\xA0\xE2\xE4\xE0\xE1\xE3\xE5\xE7\xF1\xA2\x2E\x3C\x28\x2B\x7C"
"\x26\xE9\xEA\xEB\xE8\xED\xEE\xEF\xEC\xDF\x21\x24\x2A\x29\x3B\x5E"
"\x2D\x2F\xC2\xC4\xC0\xC1\xC3\xC5\xC7\xD1\xA6\x2C\x25\x5F\x3E\x3F"
"\xF8\xC9\xCA\xCB\xC8\xCD\xCE\xCF\xCC\x60\x3A\x23\x40\x27\x3D\x22"
"\xD8\x61\x62\x63\x64\x65\x66\x67\x68\x69\xAB\xBB\xF0\xFD\xFE\xB1"
"\xB0\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\xAA\xBA\xE6\xB8\xC6\xA4"
"\xB5\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xA1\xBF\xD0\x5B\xDE\xAE"
"\xAC\xA3\xA5\xB7\xA9\xA7\xB6\xBC\xBD\xBE\xDD\xA8\xAF\x5D\xB4\xD7"
"\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xAD\xF4\xF6\xF2\xF3\xF5"
"\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xB9\xFB\xFC\xF9\xFA\xFF"
"\x5C\xF7\x53\x54\x55\x56\x57\x58\x59\x5A\xB2\xD4\xD6\xD2\xD3\xD5"
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xB3\xDB\xDC\xD9\xDA\x9F"
};
static void StrConverter (char * s, unsigned char * cct)
{
int i = 0;
while (s [i])
{
s [i] = (signed char)cct [(unsigned char)s [i]];
i++;
}
}
static void MemConverter (char * s, unsigned char * cct, int len)
{
int i = 0;
while (i < len)
{
s [i] = (signed char)cct [(unsigned char)s [i]];
i++;
}
}
#ifdef _MSVC_
/* When using MSVC, just use the normal critical section functions: */
#undef initialize_lock
#undef LOCK
#undef obtain_lock
#undef release_lock
#define initialize_lock InitializeCriticalSection
#define LOCK CRITICAL_SECTION
#define obtain_lock EnterCriticalSection
#define release_lock LeaveCriticalSection
#endif
/* File keeper list controls */
static LOCK nfile_lock;
static long nfile_init_req = 1;
static U32 nfile_id = 0;
static U32 restart_id = 0;
#define DO_FREE 1
#define NO_FREE 0
static void nfile_init ()
{
initialize_lock(&nfile_lock);
}
#define dolock(l) obtain_lock (&l); /* This ensures we use the correct fn */
#define unlock(l) release_lock (&l);
#ifdef _MSVC_
#include <io.h>
#define _open w32_hopen
#else
#include <fcntl.h>
#include <unistd.h>
#define _open hopen
#define _read read
#define _write write
#define _lseek lseek
#define _close close
#define _O_TEXT 0x04
#define _O_BINARY 0x08
#define _O_WRONLY O_WRONLY
#define _O_RDWR O_RDWR
#define _O_RDONLY O_RDONLY
#define _O_APPEND O_APPEND
#define _O_TRUNC O_TRUNC
#define _O_CREAT O_CREAT
#define _O_EXCL O_EXCL
#endif
/* WARNING: This function MUST be called with nfile_lock already locked */
static void AddFKByID (U32 id, struct fkeeper *item, struct fkeeper **list)
{
/* struct fkeeper * head = list; */
item->id = id;
item->next = *list;
*list = item;
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: AddFKByID added id=%d at %X with next item at %X to list head=%X at %X\n",
item->id, item, item->next, *list, list);
#endif
}
static struct fkeeper * FindFK (U32 id, struct fkeeper **list)
{
struct fkeeper *fk;
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: FindFK id=%d in list head at=%X\n", id, list);
#endif
dolock (nfile_lock); /* Take ownership of the list */
fk = *list; /* Search the list */
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: FindFK list head points to first entry at %X\n", fk);
#endif
while (fk)
{
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: FindFK id=%d (0x%X) is at: %X\n", fk->id, fk->id, fk);
#endif
if (fk->id == id)
{ /* Found the entry */
unlock (nfile_lock);
return fk;
}
fk = fk->next;
}
unlock (nfile_lock); /* Release ownership of the list */
/* Reached the end of the list without finding the id */
return 0;
}
static void RemoveFKByID (U32 id, struct fkeeper **list, int free_entry)
{
struct fkeeper *pfk;
struct fkeeper *fk;
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: RemoveFKByID id %d from list head at=%X\n", id, list);
#endif
dolock (nfile_lock); /* Take ownership of the list */
pfk = NULL;
fk = *list;
while (fk)
{
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: RemoveFKByID id=%d (0x%X) is at: %X\n", fk->id, fk->id, fk);
#endif
if (fk->id == id)
{ /* Found the entry */
if (pfk) /* Wasn't the head entry */
{
pfk->next = fk->next;
}
else /* Was the head entry */
{
*list = fk->next;
}
if (free_entry)
{
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: RemoveFKByID freeing id=%d at: %X\n", fk->id, fk);
#endif
free (fk);
}
break;
}
pfk = fk;
fk = fk->next;
}
unlock (nfile_lock); /* Release ownership of the list */
}
static int RemoveFKByName (char * filename)
{
int i = -1 * EBADF;
struct fkeeper *pfk;
struct fkeeper *fk;
dolock (nfile_lock); /* Take ownership of the list */
pfk = NULL;
fk = fkpr_head; /* Search the list */
while (fk)
{
if (strcmp (fk->filename, filename) == 0)
{ /* Found the entry! */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - orphan _close(%d)\n", fk->handle);
#endif
i = _close (fk->handle); /* Additional step here is to close the file too */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - orphan _close result: %d\n", i);
#endif
if (i == -1)
{
i = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - orphan _close errno: %d\n", i);
#endif
i = -1 * i;
}
else
{
if (pfk) /* Wasn't the head entry */
{
pfk->next = fk->next;
}
else /* Was the head entry */
{
fkpr_head = fk->next;
}
free (fk);
}
break;
}
pfk = fk;
fk = fk->next;
}
unlock (nfile_lock); /* Release ownership of the list */
return (i);
}
#endif /* !defined(_DYN76_H) */
/*-------------------------------------------------------------------*/
/* 76xx NFL - NFILE Ra,yyy(Rb,Rc) Ra=0,yyy=0,Rb=0,Rc=R2 */
/*-------------------------------------------------------------------*/
/*
R0 = Set to 0 prior to call,
but due to possible instruction restart, >=0 on return
R1 = Function number:
0/ int rename (char * oldname - char * newname);
-special 1 parm points to dual nul-term-strings
1/ int unlink (char * filename);
2/ int open (char * filename, int oflg, int pmode);
3/ int close (char * filename);
-special version to allow abended pgm cleanups "by hand"
4/ int read (char * buf, int h, int count);
5/ int write (char * buf, int h, int count);
6/ int seek (int h, int offset, int origin);
7/ int commit (int h);
8/ int close (int h);
9/ int setmode (int h, int mode);
R2..R4 = Parameters, depending on function number
All may be destroyed on return
R5 = Work structure pointer (not set prior to call and safely restored on return)
R15 = Return Code (0 = ok/handle, +ve = handle, -ve = errno)
*/
void ARCH_DEP(hdiagf18_FC) (U32 options, VADR cmpb, REGS *regs)
{
int space_ctl; /* This is used to control address space selection */
int i;
int res; /* I/O integer results */
int handle = 0; /* Host file file handle for this file */
U32 ghandle = 0; /* Guest file descriptor */
struct fkeeper *fk = NULL; /* Host file structure */
struct fkeeper *rfk = NULL; /* Restart structure */
#ifdef DYN76_DEBUG
int io_error = 0; /* Saves the errno for log messages */
#endif
/* Pseudo register contents are cached here once retrieved */
U32 R0;
U32 R1;
U32 R2;
U32 R3;
U32 R4;
U32 R5;
U32 R15;
/* Initialise the LOCK on our first use */
if (nfile_init_req)
{
nfile_init_req = 0;
nfile_init ();
}
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE Validating FOCPB Address %X\n", cmpb);
#endif
/* CPB must be on a doubleword and must not cross physical page boundary */
if ( ((cmpb & 0x7) != 0 ) ||
(((cmpb + 63) & STORAGE_KEY_PAGEMASK) != (cmpb & STORAGE_KEY_PAGEMASK)) )
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE Validated FOCPB Address\n");
#endif
get_reg(R1, 1); /* Retrieve the function number */
if (R1 > 9)
{ /* Invalid Function - generate an exception */
R0 = 0;
set_reg(0,R0); /* don't restart this */
ARCH_DEP(program_interrupt) (regs, PGM_OPERAND_EXCEPTION);
}
get_reg(R2, 2); /* All functions use parameter 1, so fetch it */
/* read, write, seek, commit, close and setmode use the file descriptor */
if (R1 >= 4 && R1 <= 9)
{
if (R1 == 4 || R1 == 5)
{ /* For read and write guest file descriptor is in pseudo R3 */
get_reg(R3, 3);
ghandle = R3;
}
else
{ /* For seek, commit, close and setmode, file descriptor is in
previously fetched pseudo R2 */
ghandle = R2;
}
/* If read, write, seek, commit, close or setmode */
/* convert the file descriptor into a file handle */
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - looking for guest file descriptor %d\n", ghandle);
#endif
fk = FindFK(ghandle, &fkpr_head);
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - guest file descriptor %d found at %X\n",
ghandle, fk);
#endif
if (!fk)
{ /* Did not find the guest file descriptor.
Treat it as if it were a bad host file handle */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - guest file descriptor not found: %d\n", ghandle);
#endif
R15 = -1 * EBADF;
set_reg(15,R15);
R0 = 0;
set_reg(0,R0); /* No restart on a failure */
return;
}
handle = fk->handle; /* All host file accesses use this variable */
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - host file handle: %d\n", handle);
#endif
}
/* The following 4 functions are always ready to attempt
and they are not interruptible: CLOSE, COMMIT, SEEK, SETMODE
*/
/*------------------------*/
/* SETMODE File Operation */
/*------------------------*/
if (R1 == 9)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - setmode file operation\n");
#endif
get_reg(R3, 3); /* Fetch parameter two - new file mode */
if (R3 & 0x04)
i = _O_TEXT;
else
i = _O_BINARY;
#ifdef _MSVC_
i = _setmode (handle, i); /* Alter the Windows mode */
if (i == -1)
{
R15 = -errno;
set_reg(15,R15);
return;
}
#else
get_reg(R3, 3);
/* *nix doesn't need handle updates */
if (fk->mode)
i = _O_TEXT;
else
i = _O_BINARY;
#endif
if (i == _O_TEXT) /* Previous mode */
R15 = 0x04;
else
R15 = 0x08;
set_reg(15,R15);
if (R3 & 0x04) /* Update translation details: */
fk->mode = 1; /* yes, translate */
else
fk->mode = 0; /* no, dont */
return;
} else
/*------------------------*/
/* CLOSE File Operation */
/*------------------------*/
if (R1 == 8)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _close(%d)\n", handle);
#endif
res = _close (handle);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _close result: %d\n", res);
#endif
R15 = res;
set_reg(15,R15);
if (R15 == 0)
{ RemoveFKByID (fk->id, &fkpr_head, DO_FREE);
}
else
{ R15 = -errno;
set_reg(15,R15);
}
return;
} else
/*------------------------*/
/* COMMIT File Operation */
/*------------------------*/
if (R1 == 7)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - commit file operation\n");
#endif
#ifdef _MSVC_
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _commit(%d)\n", handle);
#endif
res = _commit (handle);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _commit result: %d\n", res);
#endif
#else /* ifdef __MSVC__ */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - fsync(%d)\n", handle);
#endif
res = fsync (handle);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - fsync result: %d \n", res);
#endif
#endif /* ifdef __MSVC__ */
if (res != 0)
{
res = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - fsync/_commit errno: %d \n", res);
#endif
R15 = -1 * res;
}
else
{
R15 = res;
}
set_reg(15,R15);
return;
} else
/*------------------------*/
/* SEEK File Operation */
/*------------------------*/
if (R1 == 6)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - seek\n");
#endif
get_reg(R3,3); /* offset in bytes */
get_reg(R4,4); /* origin of the seek */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _lseek(%d, %d, %d)\n", handle, (long)R3, (int)R4);
#endif
res = _lseek (handle, (long)R3, (int)R4);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _lseek result: %d\n", res);
#endif
if (res == -1)
{
res = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _lseek errno: %d\n", res);
#endif
R15 = -1 * res;
}
else
{
R15 = res;
}
set_reg(15,R15);
return;
}
/*-------------------------------------------------------------*/
/* Interruptible operation initilization */
/*-------------------------------------------------------------*/
if ( (options & SPACE_MASK) == DF18_REAL_SPACE)
space_ctl = USE_REAL_ADDR;
else
space_ctl = USE_PRIMARY_SPACE;
get_reg(R0,0); /* Retrieve Restart Stage */
if (R0 == 0)
{ /* New operation, not a restart. Establish new operational state */
rfk = malloc (sizeof (struct fkeeper));
if (rfk == NULL)
{
R15 = -1 * ENOMEM; /* Error */
set_reg(15,R15);
return;
}
rfk->mode = -1; /* mode is not initially set */
rfk->data = 0; /* Nothing in the buffer yet */
rfk->handle = 0; /* No file handle yet either (could be an open) */
get_reg(R5,5); /* Save pseudo R5 */
rfk->SaveArea = R5;
/* Set this state's id for the guest and increment
and link the structure to the list even if not yet open.
This is required to allow open to be restarted based upon the
id. If the file does not successfully open, then the linked entry
must be removed in stage 3
*/
dolock (nfile_lock); /* Take ownership of the list */
R5 = restart_id++; /* safely increment the id counter */
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - adding restart fkeeper to rst_head list\n");
#endif
AddFKByID(R5, rfk, &rst_head);
unlock (nfile_lock); /* Release ownership of the list */
set_reg(5,R5); /* Set the restart state for this new operation */
/* For read/write we need this cleared here */
R15 = 0;
set_reg(15,R15);
/* Set the restart stage in case the next stage is interrupted */
R0 = 1; /* Set work stage 1 on restart */
set_reg(0,R0);
} else
/*-------------------------------------------------------------*/
/* Interruptible operation restart */
/*-------------------------------------------------------------*/
{ /* Must have restarted, refresh fk */
get_reg(R5,5); /* R5 contains the previous restart id */
rfk = FindFK(R5, &rst_head);
if (!rfk)
{ /* Did not find the restart - generate an exception */
R0 = 0;
set_reg(0,R0); /* restart failed, so don't do it again */
ARCH_DEP(program_interrupt) (regs, PGM_SPECIAL_OPERATION_EXCEPTION);
}
}
/*-------------------------------------------------------------*/
/* Work Stage 1 Initial or Restart */
/*-------------------------------------------------------------*/
if (R0 == 1)
{
if (R1 == 0)
{ /* rename - need to get 2 filenames */
while ((rfk->data == 0) || /* Started? */
(rfk->oldname [rfk->data - 1] != 0))
{ /* Not Finished */
/* WARNING: This is where interruption might occur */
ARCH_DEP(wfetchc)
(&(rfk->oldname [rfk->data]),
0,
R2,
space_ctl,
regs);
/* Exception, can recalculate if/when restart */
R2 += 1;
set_reg(2,R2);
rfk->data += 1; /* Next host byte location */
if (rfk->data >= 259)
{
rfk->oldname [fk->data] = 0;
break;
}
}
StrConverter (rfk->oldname, DCCebcdic_to_ascii);
rfk->data = 0; /* Get ready for newname */
}
R0 = 2; /* Set work stage 2 */
set_reg(0,R0);
}
/*-------------------------------------------------------------*/
/* Work Stage 2 Initial or Restart */
/*-------------------------------------------------------------*/
if (R0 == 2)
{
if (R1 <= 3)
{ /* unlink/open/close - need to get 1 filename */
/* rename - needs to get 2nd filename */
while ((rfk->data == 0) /* Starting */
|| (rfk->filename [rfk->data - 1] != 0)) /* Not Finished */
{
/* WARNING: This is where interruption might occur */
ARCH_DEP(wfetchc)
(&(rfk->filename [rfk->data]),
0,
R2,
space_ctl,
regs);
/* Exception, can recalculate if/when restart */
R2 += 1;
set_reg(2,R2);
rfk->data += 1; /* Next host byte location */
if (rfk->data >= 259)
{
rfk->filename [rfk->data] = 0;
break;
}
}
StrConverter (rfk->filename, DCCebcdic_to_ascii);
}
R0 = 3; /* Set work stage */
set_reg(0,R0);
}
/*---------------------------------------------------------------------*/
/* Work Stage 3 Initial or Restart - Interruptible Operation & Results */
/*---------------------------------------------------------------------*/
/*------------------------*/
/* RENAME File Operation */
/*------------------------*/
if (R1 == 0)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - rename\n");
#endif
/* This is safe to restore early here */
R5 = rfk->SaveArea;
set_reg(5,R5);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - rename(from='%s',to='%s')\n",
rfk->oldname, rfk->filename);
#endif
res = rename (rfk->oldname, rfk->filename);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - rename result: %d\n", res);
#endif
if (res != 0)
{
res = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - rename errno: %d\n", res);
#endif
R15 = -1 * res;
}
else
{
R15 = res;
}
set_reg(15,R15);
} else
/*------------------------*/
/* UNLINK File Operation */
/*------------------------*/
if (R1 == 1)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - unlink\n");
#endif
/* This is safe to restore early here */
R5 = rfk->SaveArea;
set_reg(5,R5);
#ifdef _MSVC_
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _unkink('%s')\n", rfk->filename);
#endif
res = _unlink (rfk->filename);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _unlink result: %d\n", res);
#endif
#else
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - remove('%s')\n", rfk->filename);
#endif
res = remove (rfk->filename);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - remove result: %d\n", res);
#endif
#endif
if (res != 0)
{
res = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - remove/_unlink errno: %d\n", res);
#endif
R15 = -1 * res;
}
else
{
R15 = res;
}
set_reg(15,R15);
} else
/*------------------------*/
/* OPEN File Operation */
/*------------------------*/
if (R1 == 2)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - open\n");
#endif
/* This is safe to restore early here */
R5 = rfk->SaveArea;
set_reg(5,R5);
/* Convert to host platform native open flags */
i = 0;
get_reg(R3,3);
if (R3 & 0x01)
i |= _O_WRONLY;
if (R3 & 0x02)
i |= _O_RDWR;
if ((R3 & 0x03) == 0)
i |= _O_RDONLY;
if (R3 & 0x04)
{
rfk->mode = 1; /* Record the desire to translate codepages */
#ifdef _MSVC_
i |= _O_TEXT; /* Windows can translate newlines to CRLF pairs */
#endif
}
else
{
rfk->mode = 0; /* Binary mode (untranslated) */
}
if (R3 & 0x10)
i |= _O_APPEND;
if (R3 & 0x20)
i |= _O_TRUNC;
if (R3 & 0x40)
{
i |= _O_CREAT;
#ifdef _MSVC_
get_reg(R4,4);
if (R4 == 0) /* Pass 0x100 in Windows for readonly */
{
/* Set a Windows default */
R4 = _S_IWRITE;
set_reg(4,R4);
}
#endif
}
if (R3 & 0x80)
{
i |= _O_EXCL;
}
#ifndef _MSVC_
get_reg(R4,4);
if (R4 == 0)
{ /* Set a *nix default */
R4 = 0666; /* Note octal value */
set_reg(4,R4);
}
#endif
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _open('%s', 0x%X, 0%o)\n", rfk->filename, i, R4);
#endif
res = _open (rfk->filename, i, (mode_t)R4);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _open result: %d\n", res);
#endif
if (res != -1)
{ /* Successful host file open */
/* Save the handle for use in other operations */
rfk->handle = res; /* Save the host handle */
/* Transfer the restart fkeeper to the open file fkeeper list */
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - removing w/o freeing fkeeper from restart list rst_head\n");
#endif
RemoveFKByID (rfk->id, &rst_head, NO_FREE);
dolock(nfile_lock);
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - adding fkeeper to open file list with fkpr_head\n");
#endif
AddFKByID(nfile_id++, rfk, &fkpr_head);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - opened guest file descriptor %d, host handle: %d\n",
rfk->id, rfk->handle);
#endif
unlock(nfile_lock);
R15 = rfk->id;
set_reg(15,R15);
rfk = NULL;
/* Note: during the start of the interruptable open operation the
fkeeper structure was linked to the fkeeper list to allow restart
of the operation from the restart id. Now that we have been
successful in opening the file, we will leave the restart fkeeper
on the list as the link to the host file from the guest.
*/
}
else
{ /* Failed host file open */
res = errno;
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _open errno: %X\n", res);
#endif
R15 = -1 * res;
set_reg(15,R15);
/* Note: during start of the interruptable open operation the
fkeeper structure was linked to the fkeeper list to allow restart
of the operation from the restart id. On a failure to actually open
the file, the structure needs to be removed from the list and that
will happen below just before returning to hdiagf18.c
*/
}
} else
/*-----------------------------*/
/* ORPHAN CLOSE File Operation */
/*-----------------------------*/
if (R1 == 3)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - orphan close: '%s'\n", rfk->filename);
#endif
/* This is safe to restore early here */
R5 = rfk->SaveArea;
set_reg(5,R5);
R15 = RemoveFKByName (rfk->filename);
set_reg(15,R15);
} else
/*-----------------------------*/
/* READ File Operation */
/*-----------------------------*/
if (R1 == 4)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - read\n");
#endif
get_reg(R4,4);
/* Note: R15 has been set to zero above during operation initialization */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - read requested bytes: %d\n", R4);
#endif
while (R4 != 0)
{
if (rfk->data == 0)
{ /* Need to fill our buffer with some data? */
i = R4;
if (i > 256)
i = 256;
rfk->data = _read (handle, rfk->filename, i);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - host read result: %d\n", rfk->data);
#endif
if (fk->mode)
MemConverter (rfk->filename, DCCascii_to_ebcdic, rfk->data);
}
if (rfk->data < 0)
{
R15 = -errno;
set_reg(15,R15);
}
if (rfk->data <= 0)
{ /* All done, we reached EOF (or an error occured) */
break;
}
/* Move the data read to the guest's storage */
i = rfk->data - 1;
/* wstorec was designed to operate with storage-to-storage instructions.
The instruction length field is always one less than the number of
bytes involved in the instruction. Hence the number of bytes to be
moved to storage is decremented by 1 to conform with this behavior. */
/* WARNING: This is where an interruption might occur. An exception
will "nullify" the storing operation.
On restart, the read above will be bypassed and the entire sequence
of bytes, upto 256, will be restored */
ARCH_DEP(wstorec)
(rfk->filename, /* This member is used as a buffer */
(unsigned char)i,
R2,
space_ctl,
regs);
i++;
/* Exception, can recalculate if/when restart */
R2 += i;
set_reg(2,R2);
/* Remember to stop when enough is read */
R4 -= i;
set_reg(4,R4);
/* Return accumulated total */
R15 += i;
set_reg(15,R15);
rfk->data = 0;
}
R5 = rfk->SaveArea;
set_reg(5,R5);
} else
/*-----------------------------*/
/* WRITE File Operation */
/*-----------------------------*/
if (R1 == 5)
{
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - write\n");
#endif
get_reg(R4,4);
/* Note: R15 has been set to zero above during operation initialization */
while (R4 != 0)
{
i = R4 - 1;
/* wfetchc was designed to operate with storage-to-storage instructions.
The instruction length field is always one less than the number of
bytes involved in the instruction. Hence the number of bytes to be
retrieved from storage is decremented by 1 to conform with this
behavior. */
/* Move guest's write data to the internal buffer */
if (i > 255)
{
i = 255;
}
/* WARNING: This is where interruption might occur. The exception
"nullifies" the fetch operation.
On restart, the entire sequence of up to 256 bytes will be refetched
from storage */
ARCH_DEP(wfetchc)
(rfk->filename,
(unsigned char)i,
R2,
space_ctl,
regs);
i++; /* Fix up number of bytes stored to actual count */
if (fk->mode)
MemConverter (rfk->filename, DCCebcdic_to_ascii, i);
/* rfk->filename is being used as a data buffer here */
/* Write to the host file from the internal buffer */
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _write(%d, rfk->filename, %d)\n", handle, i);
#endif
res = _write (handle, rfk->filename, (size_t)i);
#ifdef DYN76_DEBUG
logmsg("DF18: CFILE - _write result: %d\n", res);
#endif
if (res < 0)
{
#ifdef DYN76_DEBUG
io_error = errno;
logmsg("DF18: CFILE - write errno: %d\n", io_error);
#endif
R15 = -errno;
set_reg(15,R15);
break;
}
/* Not an error, so 'res' is the number of bytes actually written */
/* Update the address pointer and remaining bytes to write */
R2 += res;
set_reg(2,R2);
R4 -= res;
set_reg(4,R4);
/* update the accumlated total */
R15 += res;
set_reg(15,R15);
}
R5 = rfk->SaveArea;
set_reg(5,R5);
}
if (rfk) /* clean up, unless open already has */
{
#ifdef DYN76_DEBUG_FKEEPER
logmsg("DF18: CFILE - removing and freeing restart fkeeper in rst_head list\n");
#endif
/* Safely remove from the restart state */
RemoveFKByID (rfk->id, &rst_head, DO_FREE);
}
/* Make sure we do not restart this operation */
R0 = 0;
set_reg(0,R0);
}
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "dyn76.c"
#endif
#if defined(_ARCHMODE3)
#undef _GEN_ARCH
#define _GEN_ARCH _ARCHMODE3
#include "dyn76.c"
#endif
#endif /*!defined(_GEN_ARCH)*/