mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-13 07:25:22 +02:00
1129 lines
34 KiB
C
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)*/
|