mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-11 14:44:35 +02:00
Completely disable (for the time being) all Shared Device Server code (since it's causing problems) until we can determine how to properly integrate it into our existing more accurate (architecturally correct) channel subsystem code.
2263 lines
80 KiB
C
2263 lines
80 KiB
C
/* VMD250.C (c) Copyright Harold Grovesteen, 2009-2012 */
|
|
/* z/VM 5.4 DIAGNOSE code X'250' */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module implements DIAGNOSE code X'250' */
|
|
/* described in SC24-6084 z/VM 5.4 CP Programming Services. */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules extends the use to System/370 with 4K pages and 2K pages */
|
|
/* with recommended constraints */
|
|
|
|
/* Unconditional log messages generated */
|
|
/* - Could not allocate storage for Block I/O environment */
|
|
/* - Invalid list processor status code returned */
|
|
/* - Error creating asynchronous thread */
|
|
/* - Could not allocate storage for asynchronous I/O request*/
|
|
|
|
/* Conditional log messages when device tracing is enabled */
|
|
/* - BLKTAB information used when device tracing is enabled */
|
|
/* - Established environment when device tracing is enabled */
|
|
/* - Block I/O owns device */
|
|
/* - Block I/O returned device */
|
|
/* - BIOE status returned */
|
|
/* - List processor parameters */
|
|
/* - BIOE operation being performed */
|
|
/* - List processor status code */
|
|
/* - Read/Write I/O operation */
|
|
/* - Syncronous or asynchronous request information */
|
|
/* - Address checking results */
|
|
/* - Device driver results */
|
|
/* - Block I/O environment removed */
|
|
/* - Triggered Block I/O interrupt */
|
|
|
|
|
|
/* The following structure exists between the different functions */
|
|
/* */
|
|
/* From diagnose.c */
|
|
/* */
|
|
/* AD:vm_blockio */
|
|
/* | */
|
|
/* INIT: */
|
|
/* +---> d250_init32---+ */
|
|
/* | +---> d250_init */
|
|
/* +---> d250_init64---+ */
|
|
/* | */
|
|
/* IOREQ: */
|
|
/* +-> AD:d250_iorq32--+---SYNC----> d250_list32--+ */
|
|
/* | V ^ | */
|
|
/* | ASYNC Thread | | d250_read */
|
|
/* | +-> AD:d250_async32-+ +--> d250_write */
|
|
/* | d250_bio_interrupt | (calls */
|
|
/* | | drivers) */
|
|
/* +-> AD:d250_iorq64--+----SYNC---> d250_list64--+ */
|
|
/* | V ^ */
|
|
/* | ASYNC Thread | */
|
|
/* | +-> AD:d250_async64-+ */
|
|
/* | d250_bio_interrupt */
|
|
/* REMOVE: */
|
|
/* +---> d250_remove */
|
|
/* */
|
|
/* Function ARCH_DEP On CPU On Async Owns */
|
|
/* thread thread device */
|
|
/* */
|
|
/* vm_blockio Yes Yes No No */
|
|
/* d250_init32/64 No Yes No No */
|
|
/* d250_init No Yes No No */
|
|
/* d250_iorq32/64 Yes Yes No No */
|
|
/* d250_async32/64 Yes No Yes No */
|
|
/* d250_list32/64 Yes Yes Yes Yes */
|
|
/* d250_bio_interrup No No Yes N/A */
|
|
/* d250_read No Yes Yes Yes */
|
|
/* d250_write No Yes Yes Yes */
|
|
/* d250_remove No Yes No No */
|
|
/* d250_addrck Yes Yes Yes No */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#if !defined(_HENGINE_DLL_)
|
|
#define _HENGINE_DLL_
|
|
#endif /*_HENGINE_DLL_*/
|
|
|
|
#if !defined(_VMD250_C_)
|
|
#define _VMD250_C_
|
|
#endif /* !defined(_VMD250_C_) */
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "inline.h"
|
|
#include "vmd250.h"
|
|
#define FEATURE_VM_BLOCKIO
|
|
#if defined(FEATURE_VM_BLOCKIO)
|
|
|
|
#if !defined(_VMD250_C)
|
|
#define _VMD250_C
|
|
|
|
#if !defined(_VMD250_H)
|
|
#define _VMD250_H
|
|
/*-------------------------------------------------------------------*/
|
|
/* Internal macro definitions */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Block I/O Function, Condition and Return Codes */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* Function Codes (Ry) */
|
|
#define INIT 0x00 /* Initialize Block I/O environment */
|
|
#define IOREQ 0x01 /* Perform I/O request */
|
|
#define REMOVE 0x02 /* Remove the Block I/O environment */
|
|
|
|
/* Condition Codes (cc) */
|
|
#define CC_SUCCESS 0 /* Function completed successfully */
|
|
#define CC_PARTIAL 1 /* Function partially completed */
|
|
#define CC_FAILED 2 /* Function failed */
|
|
/* Note: cc 3 is not returned by Block I/O */
|
|
|
|
/* General Return Codes (Rx+1)- All functions */
|
|
#define RC_SUCCESS 0 /* Function competed successfully */
|
|
#define RC_NODEV 16 /* Device not defined */
|
|
#define RC_STATERR 28 /* Env. state error */
|
|
#define RC_ERROR 255 /* Irrecoverable error */
|
|
/* Note: RC_ERROR is used to indicate an internal Hercules error */
|
|
/* and it should be accompanied by a log message */
|
|
|
|
/* INIT Return Codes (Rx+1) */
|
|
#define RC_READONLY 4 /* Successful for R/O device */
|
|
#define RC_NOSUPP 20 /* Not a supported DASD device */
|
|
#define RC_BADBLKSZ 24 /* Unsupported block size */
|
|
|
|
/* IOREQ Return Codes (Rx+1) */
|
|
#define RC_ASYNC 8 /* Asynchronous request initiated */
|
|
#define RC_SYN_PART 12 /* Synchronous request partially successful */
|
|
#define RC_CNT_ERR 36 /* Block count not between 1 and 256 */
|
|
#define RC_ALL_BAD 40 /* All blocks unsuccessful */
|
|
#define RC_REM_PART 44 /* Remove aborted request - should not occur */
|
|
#define RC_EXI_PART 48 /* Exigent aborted req. - should not occur */
|
|
/* Note: Because the DEVBLK lock is held RC_REM_PART and RC_EXI_PART */
|
|
/* should not occur in Hercules */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Block I/O Parameter List Formats (BIOPL) */
|
|
/*-------------------------------------------------------------------*/
|
|
#define INIT32R1_LEN 21
|
|
#define INIT32R2_LEN 24
|
|
typedef struct _BIOPL_INIT32 {
|
|
HWORD devnum; /* Device number */
|
|
BYTE flaga; /* Addressing mode flag */
|
|
BYTE resv1[INIT32R1_LEN]; /* reserved - must be zeros */
|
|
FWORD blksize; /* Block size */
|
|
FWORD offset; /* Physical block 1 offset */
|
|
FWORD startblk; /* First physical block number */
|
|
FWORD endblk; /* Last physical block number */
|
|
BYTE resv2[INIT32R2_LEN]; /* reserved - must be zeros */
|
|
} BIOPL_INIT32;
|
|
|
|
#define INIT64R1_LEN 21
|
|
#define INIT64R2_LEN 4
|
|
#define INIT64R3_LEN 8
|
|
typedef struct _BIOPL_INIT64 {
|
|
HWORD devnum; /* Device number */
|
|
BYTE flaga; /* Addressing mode flag */
|
|
BYTE resv1[INIT64R1_LEN]; /* reserved - must be zeros */
|
|
FWORD blksize; /* Block size */
|
|
BYTE resv2[INIT64R2_LEN]; /* Physical block 1 offset */
|
|
DBLWRD offset; /* Physical block 1 offset */
|
|
DBLWRD startblk; /* First physical block number */
|
|
DBLWRD endblk; /* Last physical block number */
|
|
BYTE resv3[INIT64R3_LEN]; /* reserved - must be zeros */
|
|
} BIOPL_INIT64;
|
|
|
|
#define IORQ32R1_LEN 21
|
|
#define IORQ32R2_LEN 2
|
|
#define IORQ32R3_LEN 20
|
|
typedef struct _BIOPL_IORQ32 {
|
|
HWORD devnum; /* Device number */
|
|
BYTE flaga; /* Addressing mode flag */
|
|
BYTE resv1[IORQ32R1_LEN]; /* reserved - must be zeros */
|
|
BYTE key; /* Storage key */
|
|
BYTE flags; /* I/O request flags */
|
|
BYTE resv2[IORQ32R2_LEN]; /* reserved - must be zeros */
|
|
FWORD blkcount; /* Block count of request */
|
|
FWORD bioealet; /* BIOE list ALET */
|
|
FWORD bioeladr; /* BIOE list address */
|
|
FWORD intparm; /* Interrupt parameter */
|
|
BYTE resv3[IORQ32R3_LEN]; /* reserved - must be zeros */
|
|
} BIOPL_IORQ32;
|
|
|
|
#define IORQ64R1_LEN 21
|
|
#define IORQ64R2_LEN 2
|
|
#define IORQ64R3_LEN 4
|
|
#define IORQ64R4_LEN 8
|
|
typedef struct _BIOPL_IORQ64 {
|
|
HWORD devnum; /* Device number */
|
|
BYTE flaga; /* Addressing mode flag */
|
|
BYTE resv1[IORQ64R1_LEN]; /* reserved - must be zeros */
|
|
BYTE key; /* Storage key */
|
|
BYTE flags; /* I/O request flags */
|
|
BYTE resv2[IORQ64R2_LEN]; /* reserved - must be zeros */
|
|
FWORD blkcount; /* Block count of request */
|
|
FWORD bioealet; /* BIOE list ALET */
|
|
BYTE resv3[IORQ64R3_LEN]; /* reserved - must be zeros */
|
|
DBLWRD intparm; /* Interrupt parameter */
|
|
DBLWRD bioeladr; /* BIOE list address */
|
|
BYTE resv4[IORQ64R4_LEN]; /* reserved - must be zeros */
|
|
} BIOPL_IORQ64;
|
|
|
|
#define REMOVER1_LEN 62
|
|
typedef struct _BIOPL_REMOVE {
|
|
HWORD devnum; /* Device number */
|
|
BYTE resv1[REMOVER1_LEN]; /* reserved - must be zeros */
|
|
} BIOPL_REMOVE;
|
|
|
|
typedef struct _BIOPL {
|
|
HWORD devnum; /* Device number */
|
|
BYTE flaga; /* Addressing mode flag */
|
|
BYTE resv1[61]; /* Additional Parameters */
|
|
} BIOPL;
|
|
|
|
/* BIOPL flaga field related values */
|
|
#define BIOPL_FLAGARSV 0x7F /* Reserved bits must be zero */
|
|
#define BIOPL_FLAGAMSK 0x80 /* Addressing flag */
|
|
#define BIOPL_32BIT 0x00 /* 32-bit BIOPL/BIOE Formats */
|
|
#define BIOPL_64BIT 0x80 /* 64-bit BIOPL/BIOE Formats */
|
|
|
|
/* BIOPL flags field related values */
|
|
#define BIOPL_FLAGSRSV 0xFC /* Reserved bits must be zero */
|
|
#define BIOPL_ASYNC 0x02 /* Asynchronous request */
|
|
#define BIOPL_CACHE 0x01 /* Not used - z/VM specific */
|
|
|
|
/* BIOPL key field values */
|
|
#define BIOPL_KEYRSV 0x0F /* Reserved bits must be zero */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Block I/O Entry Formats (BIOE) */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
typedef struct _BIOE32 {
|
|
BYTE type; /* Type of I/O request */
|
|
BYTE status; /* Status of I/O request */
|
|
BYTE resv1[2]; /* reserved - must be zeros */
|
|
FWORD blknum; /* Requested block number */
|
|
FWORD bufalet; /* Data buffer ALET */
|
|
FWORD bufaddr; /* Data buffer absolute address */
|
|
} BIOE32;
|
|
|
|
typedef struct _BIOE64 {
|
|
BYTE type; /* Type of I/O request */
|
|
BYTE status; /* Status of I/O request */
|
|
BYTE resv1[2]; /* reserved - must be zeros */
|
|
FWORD bufalet; /* Data buffer ALET */
|
|
DBLWRD blknum; /* Requested block number */
|
|
DBLWRD bufaddr; /* Data buffer absolute address */
|
|
} BIOE64;
|
|
|
|
/* BIOE type field values */
|
|
#define BIOE_WRITE 0x01 /* Write block request type */
|
|
#define BIOE_READ 0x02 /* Read block request type */
|
|
|
|
/* BIOE status field values */
|
|
#define BIOE_SUCCESS 0x00 /* Request successful */
|
|
#define BIOE_BADBLOCK 0x01 /* Invalid block specified */
|
|
#define BIOE_ADDREXC 0x02 /* Data buffer address exception */
|
|
#define BIOE_DASDRO 0x03 /* Write request to read-only device */
|
|
#define BIOE_CKDRECL 0x04 /* CKD record size not block size */
|
|
#define BIOE_IOERROR 0x05 /* I/O error on device */
|
|
#define BIOE_BADREQ 0x06 /* Request type invalid */
|
|
#define BIOE_PROTEXC 0x07 /* A protection exception occurred */
|
|
#define BIOE_ADRCAP 0x08 /* Not used - z/VM specific */
|
|
#define BIOE_ALENEXC 0x09 /* Not used - z/VM specific */
|
|
#define BIOE_ALETEXC 0x0A /* Not used - z/VM specific */
|
|
#define BIOE_NOTZERO 0x0B /* Reserved fields not zero */
|
|
#define BIOE_ABORTED 0x0C /* Request aborted */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* I/O Request Control Structures and Flags */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if 0
|
|
#define ADDR32 0 /* 32-bit addressing fields being used */
|
|
#define ADDR64 1 /* 64-bit addressing fields being used */
|
|
#endif
|
|
|
|
#define SYNC 0 /* Synchronous request being processed */
|
|
#define ASYNC 1 /* Asynchronous request being processed */
|
|
|
|
/* Synchronous or asynchronous processing status codes */
|
|
#define PSC_SUCCESS 0x00 /* Successful processing */
|
|
#define PSC_PARTIAL 0x01 /* Partial success */
|
|
#define PSC_STGERR 0x02 /* Error on storage of BIOE */
|
|
#define PSC_REMOVED 0x03 /* Block I/O environment removed */
|
|
|
|
/* Structure passed to the asynchronous thread */
|
|
typedef struct _IOCTL32 {
|
|
/* Hercules structures involved in the request */
|
|
REGS *regs; /* CPU register structure */
|
|
DEVBLK *dev; /* Device block */
|
|
/* External interrupt related values */
|
|
BYTE subintcod; /* Sub-interruption code */
|
|
BYTE statuscod; /* Interruption status code */
|
|
U32 intrparm; /* 32-bit interrupt parm */
|
|
/* List control structure */
|
|
S32 blkcount; /* Block count */
|
|
U32 listaddr; /* BIOE list address */
|
|
BYTE key; /* Storage Key */
|
|
int goodblks; /* Number of successful I/O's */
|
|
int badblks; /* Number of unsuccessful I/O's */
|
|
} IOCTL32;
|
|
|
|
/* Structure passed to the asynchronous thread */
|
|
typedef struct _IOCTL64 {
|
|
/* Hercules structures involved in the request */
|
|
REGS *regs; /* CPU register structure */
|
|
DEVBLK *dev; /* Device block */
|
|
/* External interrupt related values related values */
|
|
BYTE subintcod; /* Sub-interruption code */
|
|
BYTE statuscod; /* Interruption status code */
|
|
U64 intrparm; /* 64-bit interrupt parm */
|
|
/* List control structure */
|
|
S64 blkcount; /* Block count */
|
|
U64 listaddr; /* BIOE list address */
|
|
BYTE key; /* Storage Key */
|
|
int goodblks; /* Number of successful I/O's */
|
|
int badblks; /* Number of unsuccessful I/O's */
|
|
} IOCTL64;
|
|
|
|
#endif /* !defined(_VMD250_H) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Internal Architecture Independent Function Prototypes */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialization Functions */
|
|
static int d250_init32(DEVBLK *, int *, BIOPL_INIT32 *, REGS *);
|
|
static int d250_init64(DEVBLK *, int *, BIOPL_INIT64 *, REGS *);
|
|
static struct VMBIOENV* d250_init(DEVBLK *, U32, S64, int *, int *);
|
|
|
|
/* Input/Output Request Functions */
|
|
static void d250_preserve(DEVBLK *);
|
|
static void d250_restore(DEVBLK *);
|
|
static int d250_read(DEVBLK *, S64, S32, void *);
|
|
static int d250_write(DEVBLK *, S64, S32, void *);
|
|
/* Note: some I/O request functions are architecture dependent */
|
|
|
|
/* Removal Function */
|
|
static int d250_remove(DEVBLK *, int *, BIOPL_REMOVE *, REGS *);
|
|
|
|
/* Asynchronous Interrupt Generation */
|
|
static void d250_bio_interrupt(DEVBLK *, U64 intparm, BYTE status, BYTE code);
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Trigger Block I/O External Interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
static void d250_bio_interrupt(DEVBLK *dev, U64 intparm, BYTE status, BYTE subcode)
|
|
{
|
|
|
|
OBTAIN_INTLOCK(NULL);
|
|
|
|
/* This is inspired by sclp_attn_thread function in service.c */
|
|
/* Make sure a service signal interrupt is not pending */
|
|
while(IS_IC_SERVSIG)
|
|
{
|
|
RELEASE_INTLOCK(NULL);
|
|
sched_yield();
|
|
OBTAIN_INTLOCK(NULL);
|
|
}
|
|
|
|
/* Can now safely store my interrupt information */
|
|
sysblk.bioparm = intparm; /* Trigger with the interrupt parameter */
|
|
sysblk.biostat = status; /* Trigger with the status */
|
|
sysblk.biosubcd = subcode; /* Trigger with the subcode */
|
|
sysblk.biodev = dev; /* Trigger for this device */
|
|
sysblk.servcode = EXT_BLOCKIO_INTERRUPT;
|
|
|
|
/* The Block I/O external interrupt is enabled by the same CR0 bit */
|
|
/* as are service signal interrupts. This means the guest will */
|
|
/* be interrupted when it has enabled service signals. For this */
|
|
/* reason the Block I/O is being treated like a service signal and */
|
|
/* handled by the service signal handling logic in external.c */
|
|
|
|
/* Make the "service signal" interrupt pending */
|
|
ON_IC_SERVSIG;
|
|
/* Wake up any waiters */
|
|
WAKEUP_CPUS_MASK (sysblk.waiting_mask);
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01905, "I",
|
|
sysblk.biodev->devnum,
|
|
sysblk.servcode,
|
|
sysblk.bioparm,
|
|
sysblk.biostat,
|
|
sysblk.biosubcd
|
|
);
|
|
}
|
|
|
|
/* Free the interrupt lock */
|
|
RELEASE_INTLOCK(NULL);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize Environment - 32-bit Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
static int d250_init32(DEVBLK *dev, int *diag_rc, BIOPL_INIT32 *biopl,
|
|
REGS *regs)
|
|
{
|
|
BIOPL_INIT32 bioplx00; /* Use to check reserved fields */
|
|
|
|
/* Passed to the generic INIT function */
|
|
U32 blksize; /* Blocksize */
|
|
S32 offset; /* Offset */
|
|
|
|
/* Returned by generic INIT function */
|
|
struct VMBIOENV *bioenv; /* -->allocated environement */
|
|
int rc; /* return code */
|
|
int cc; /* Condition code to return */
|
|
|
|
/* Clear the reserved BIOPL */
|
|
memset(&bioplx00,0,sizeof(BIOPL_INIT32));
|
|
|
|
/* Make sure reserved fields are binary zeros */
|
|
if ((memcmp(&biopl->resv1,&bioplx00,INIT32R1_LEN)!=0) ||
|
|
(memcmp(&biopl->resv2,&bioplx00,INIT32R2_LEN)!=0))
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Fetch the block size from the BIOPL */
|
|
FETCH_FW(blksize,&biopl->blksize);
|
|
|
|
/* Fetch the offset from the BIOPL provided by the guest */
|
|
FETCH_FW(offset,&biopl->offset);
|
|
|
|
/* Call the addressing independent initialization function */
|
|
bioenv=d250_init(dev, blksize, (S64)offset, &cc, &rc);
|
|
|
|
if (bioenv)
|
|
{
|
|
/* Save the values in the BIOPL for return to guest */
|
|
STORE_FW(&biopl->startblk,(U32)bioenv->begblk);
|
|
STORE_FW(&biopl->endblk,(U32)bioenv->endblk);
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01906, "I",
|
|
dev->devnum,
|
|
blksize,
|
|
(S64)offset,
|
|
bioenv->begblk,
|
|
bioenv->endblk
|
|
);
|
|
}
|
|
}
|
|
*diag_rc = rc;
|
|
return cc;
|
|
} /* end function d250_init32 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize Environment - 64-bit Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
static int d250_init64(DEVBLK *dev, int *diag_rc, BIOPL_INIT64 *biopl,
|
|
REGS *regs)
|
|
{
|
|
BIOPL_INIT64 bioplx00; /* Use to check reserved fields */
|
|
|
|
/* Passed to the generic INIT function */
|
|
U32 blksize; /* Blocksize */
|
|
S64 offset; /* Offset */
|
|
|
|
/* Returned by generic INIT function */
|
|
struct VMBIOENV *bioenv; /* -->allocated environement */
|
|
int rc; /* return code */
|
|
int cc; /* condition code */
|
|
|
|
/* Clear the reserved BIOPL */
|
|
memset(&bioplx00,0,sizeof(BIOPL_INIT64));
|
|
|
|
/* Make sure reserved fields are binary zeros */
|
|
if ((memcmp(&biopl->resv1,&bioplx00,INIT64R1_LEN)!=0) ||
|
|
(memcmp(&biopl->resv2,&bioplx00,INIT64R2_LEN)!=0) ||
|
|
(memcmp(&biopl->resv3,&bioplx00,INIT64R3_LEN)!=0))
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Fetch the block size from the BIOPL */
|
|
FETCH_FW(blksize,&biopl->blksize);
|
|
|
|
/* Fetch the offset from the BIOPL provided by the guest */
|
|
FETCH_DW(offset,&biopl->offset);
|
|
|
|
bioenv=d250_init(dev, blksize, offset, &cc, &rc);
|
|
|
|
if (bioenv)
|
|
{
|
|
/* Save the values in the BIOPL for return to guest */
|
|
STORE_DW(&biopl->startblk,bioenv->begblk);
|
|
STORE_DW(&biopl->endblk,bioenv->endblk);
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01906, "I",
|
|
dev->devnum,
|
|
blksize,
|
|
offset,
|
|
bioenv->begblk,
|
|
bioenv->endblk
|
|
);
|
|
}
|
|
}
|
|
*diag_rc = rc;
|
|
return cc;
|
|
} /* end function d250_init64 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize Environment - Addressing Independent */
|
|
/*-------------------------------------------------------------------*/
|
|
static struct VMBIOENV* d250_init(DEVBLK *dev, U32 blksize, S64 offset,
|
|
int *cc, int *rc )
|
|
{
|
|
int isCKD; /* Flag for CKD device */
|
|
int isRO; /* Flag for readonly device */
|
|
int seccyl;/* Number of sectors/block or records/track for dev. */
|
|
int numblks; /* Number of blocks on the device */
|
|
S32 begblk; /* Starting block number */
|
|
S32 endblk; /* Ending block number */
|
|
BLKTAB *blktab; /* Pointer to device std block-to-physical info */
|
|
/* Established environement */
|
|
struct VMBIOENV *bioenv; /* -->allocated environement */
|
|
|
|
/* Return with an error if the device does not exist */
|
|
if (!dev)
|
|
{
|
|
*rc = RC_NODEV; /* Set the return code for no device */
|
|
*cc = CC_FAILED; /* Indicate the function failed */
|
|
return 0;
|
|
}
|
|
|
|
/* Look up the block-to-phyical mapping information */
|
|
blktab=dasd_lookup(DASD_STDBLK,NULL,(U32)dev->devtype,0);
|
|
if (!blktab)
|
|
{
|
|
*rc = RC_NOSUPP; /* Set the return code for unsuppored device */
|
|
*cc = CC_FAILED; /* Indicate the function failed */
|
|
return NULL;
|
|
}
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01907, "I",
|
|
dev->devnum,
|
|
blktab->devt,
|
|
blktab->darch,
|
|
blktab->phys512,
|
|
blktab->phys1024,
|
|
blktab->phys2048,
|
|
blktab->phys4096
|
|
);
|
|
}
|
|
|
|
/* Save the device architecture */
|
|
isCKD = blktab->darch;
|
|
|
|
/* Determine if the blocksize is valid and its physical/block value */
|
|
switch(blksize)
|
|
{
|
|
case 4096:
|
|
seccyl=blktab->phys4096;
|
|
break;
|
|
case 512:
|
|
seccyl=blktab->phys512;
|
|
break;
|
|
case 1024:
|
|
seccyl=blktab->phys1024;
|
|
break;
|
|
case 2048:
|
|
seccyl=blktab->phys2048;
|
|
break;
|
|
default:
|
|
*rc = RC_BADBLKSZ;
|
|
*cc = CC_FAILED;
|
|
return NULL;
|
|
}
|
|
|
|
isRO = 0; /* Assume device is read-write */
|
|
if (isCKD)
|
|
{
|
|
/* Number of standard blocks is based upon number of primary */
|
|
/* cylinders */
|
|
numblks=(dev->ckdtab->cyls * dev->ckdtab->heads * seccyl);
|
|
if (dev->ckdrdonly)
|
|
{
|
|
isRO = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
numblks=(dev->fbanumblk*dev->fbablksiz)/blksize;
|
|
/* FBA devices are never read only */
|
|
}
|
|
|
|
/* Establish the environment's beginning and ending block numbers */
|
|
/* Block numbers in the environment are relative to 1, not zero */
|
|
begblk=1-offset;
|
|
endblk=numblks-offset;
|
|
|
|
if (!(bioenv=(struct VMBIOENV *)malloc(sizeof(struct VMBIOENV))))
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)",(int)sizeof(struct VMBIOENV));
|
|
WRMSG (HHC01908, "E", buf, strerror(errno));
|
|
*rc = RC_ERROR; /* Indicate an irrecoverable error occurred */
|
|
*cc = CC_FAILED;
|
|
return NULL;
|
|
}
|
|
|
|
/* Set the fields in the environment structure */
|
|
bioenv->dev = dev ; /* Set device block pointer */
|
|
bioenv->blksiz = blksize ; /* Pass the block size */
|
|
bioenv->offset = offset ; /* Pass the offset */
|
|
bioenv->begblk = begblk ; /* Save the starting block */
|
|
bioenv->endblk = endblk ; /* Save the ending block */
|
|
bioenv->isCKD = isCKD ; /* Save the device type */
|
|
bioenv->isRO = isRO ; /* Save the read/write status */
|
|
bioenv->blkphys = seccyl ; /* Save the block-to-phys mapping */
|
|
|
|
/* Attach the environment to the DEVBLK */
|
|
/* Lock the DEVBLK in case another thread wants it */
|
|
obtain_lock (&dev->lock);
|
|
if (dev->vmd250env == NULL)
|
|
{
|
|
/* If an environment does not exist, establish it */
|
|
dev->vmd250env = bioenv ;
|
|
/* No need to hold the device lock now, environment is set */
|
|
release_lock (&dev->lock);
|
|
|
|
/* Set the appropriate successful return and condition codes */
|
|
if (isRO)
|
|
{
|
|
*rc = RC_READONLY ; /* Set READ ONLY success return code */
|
|
}
|
|
else
|
|
{
|
|
*rc = RC_SUCCESS ; /* Set the READ/WRITE success return code */
|
|
}
|
|
*cc = CC_SUCCESS ; /* Set that the function has succeeded */
|
|
}
|
|
else
|
|
{
|
|
/* If an environment already exists, this is an error: */
|
|
/* 1. Release the device block */
|
|
/* 2. free the environment just built */
|
|
/* 3. Reset the retuned environment to NULL and */
|
|
/* 4. Reset return and condition codes to reflect */
|
|
/* the error condition */
|
|
release_lock (&dev->lock);
|
|
free(bioenv);
|
|
bioenv = NULL ;
|
|
*rc = RC_STATERR;
|
|
*cc = CC_FAILED;
|
|
}
|
|
|
|
/* Return the bioenv so that the start and end blocks can */
|
|
/* be returned to the guest in the addressing mode specific */
|
|
/* BIOPL format */
|
|
return bioenv ;
|
|
|
|
} /* end function d250_init */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Preserve Device Status */
|
|
/*-------------------------------------------------------------------*/
|
|
/* WARNING: This function must NOT be called with the device lock held */
|
|
|
|
/* From the perspective of Hercules, Block I/O is occurring at the */
|
|
/* level of the device, NOT the channel or channel subsystem. For */
|
|
/* the channel subsytem or other CPU's, the device is made to */
|
|
/* appear busy and reserved if shared. */
|
|
/* */
|
|
/* It is possible for block I/O to take control between the */
|
|
/* completion of a CCW chain and the presentation by the device of */
|
|
/* related sense information. The device related sense information*/
|
|
/* is therefore stored in the Block I/O environment and restored */
|
|
/* upon completion. This also allows the device drivers to set */
|
|
/* sense information during Block I/O operations, which they do. */
|
|
/* A pointer is used by the device drivers for unit status, so */
|
|
/* this can be redirected to a Block I/O location. */
|
|
/* */
|
|
/* A cleaner but more intrusive change would be to have the */
|
|
/* the drivers use a pointer for where sense is stored rather than */
|
|
/* assume the device block. This is deferred for a possible future*/
|
|
/* enhancement. */
|
|
static void d250_preserve(DEVBLK *dev)
|
|
{
|
|
obtain_lock(&dev->lock);
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
/* Wait for the device to become available */
|
|
/* Note: this logic comes from the beginning of */
|
|
/* channel.c ARCH_DEP(execute_ccw_chain) */
|
|
shared_iowait( dev );
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
#endif // defined( OPTION_SHARED_DEVICES )
|
|
|
|
dev->busy = 1;
|
|
dev->startpending = 0;
|
|
if (dev->sns_pending)
|
|
{
|
|
/* Save the pending sense */
|
|
memcpy(&dev->vmd250env->sense,&dev->sense,sizeof(dev->sense));
|
|
if (dev->ccwtrace)
|
|
{ WRMSG(HHC01909, "I", dev->devnum);
|
|
}
|
|
}
|
|
|
|
/* Both fbadasd.c and ckddasd.c set the local reserved flag before */
|
|
/* calling the shared device client */
|
|
dev->reserved = 1;
|
|
if (dev->hnd->reserve)
|
|
{
|
|
release_lock(&dev->lock);
|
|
(dev->hnd->reserve)(dev);
|
|
}
|
|
else
|
|
{
|
|
release_lock(&dev->lock);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Restore Device Status */
|
|
/*-------------------------------------------------------------------*/
|
|
/* WARNING: This function MUST not be called with the device lock held */
|
|
static void d250_restore(DEVBLK *dev)
|
|
{
|
|
obtain_lock(&dev->lock);
|
|
if (dev->hnd->release)
|
|
{
|
|
release_lock(&dev->lock);
|
|
(dev->hnd->release)(dev);
|
|
obtain_lock(&dev->lock);
|
|
}
|
|
/* Both fbadasd.c and ckddasd.c reset the local reserved flag */
|
|
/* after calling the shared device client */
|
|
dev->reserved = 0;
|
|
if (dev->sns_pending)
|
|
{
|
|
/* Restore the pending sense */
|
|
memcpy(&dev->sense,&dev->vmd250env->sense,sizeof(dev->sense));
|
|
if (dev->ccwtrace)
|
|
{ WRMSG (HHC01920, "I", dev->devnum);
|
|
}
|
|
}
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
#endif // defined( OPTION_SHARED_DEVICES )
|
|
dev->busy = 0;
|
|
release_lock(&dev->lock);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Environment Remove - Any Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
static int d250_remove(DEVBLK *dev, int *rc, BIOPL_REMOVE * biopl, REGS *regs)
|
|
{
|
|
BIOPL_REMOVE bioplx00; /* Use to check reserved fields */
|
|
struct VMBIOENV *bioenv; /* -->allocated environement */
|
|
int cc; /* Condition code to return */
|
|
|
|
/* Clear the reserved BIOPL */
|
|
memset(&bioplx00,0,sizeof(BIOPL_REMOVE));
|
|
|
|
|
|
/* Make sure reserved fields are binary zeros */
|
|
if (memcmp(&biopl->resv1,&bioplx00,REMOVER1_LEN)!=0)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Return with an error if the device does not exist */
|
|
if (!dev)
|
|
{
|
|
*rc = RC_NODEV; /* Set the return code for no device */
|
|
return CC_FAILED; /* Indicate the function failed */
|
|
}
|
|
|
|
/* Attach the environment to the DEVBLK */
|
|
/* Lock the DEVBLK in case another CPU is trying something */
|
|
obtain_lock (&dev->lock);
|
|
bioenv=dev->vmd250env;
|
|
if (dev->vmd250env == NULL)
|
|
{
|
|
/* If an environment does not exist, it should be */
|
|
/* Return the environment state error return code */
|
|
/* and failed condition code */
|
|
release_lock (&dev->lock);
|
|
*rc = RC_STATERR;
|
|
cc = CC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
/* 1. Restore pending sense, if any */
|
|
/* 2. Free the environment previously established */
|
|
/* 3. Set the device environment pointer to NULL, */
|
|
/* telling others that the environment doesn't exist */
|
|
/* 4. Release the device block */
|
|
/* 5. Free the environment previously established */
|
|
/* 6. Return successful return and condition codes */
|
|
if (dev->sns_pending)
|
|
{
|
|
memcpy(&dev->sense,dev->vmd250env->sense,sizeof(dev->sense));
|
|
}
|
|
dev->vmd250env = NULL ;
|
|
/* No need to hold the device lock while freeing the environment */
|
|
release_lock (&dev->lock);
|
|
free(bioenv);
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01921, "I", dev->devnum);
|
|
}
|
|
*rc = RC_SUCCESS;
|
|
cc = CC_SUCCESS ; /* Set that the function has succeeded */
|
|
}
|
|
return cc ;
|
|
} /* end function d250_remove */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Device Independent Read Block */
|
|
/*-------------------------------------------------------------------*/
|
|
static int d250_read(DEVBLK *dev, S64 pblknum, S32 blksize, void *buffer)
|
|
{
|
|
BYTE unitstat; /* Device unit status */
|
|
U32 residual; /* Residual byte count */
|
|
|
|
/* Note: Not called with device lock held */
|
|
|
|
obtain_lock(&dev->lock);
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01922, "I", dev->devnum, blksize, pblknum);
|
|
}
|
|
|
|
if (dev->vmd250env->isCKD)
|
|
{
|
|
release_lock(&dev->lock);
|
|
/* Do CKD I/O */
|
|
|
|
/* CKD to be supplied */
|
|
|
|
return BIOE_IOERROR;
|
|
}
|
|
else
|
|
{
|
|
/* Do FBA I/O */
|
|
|
|
/* Call the I/O start exit */
|
|
if (dev->hnd->start) (dev->hnd->start) (dev);
|
|
|
|
unitstat = 0;
|
|
|
|
/* Call the FBA driver's read standard block routine */
|
|
fbadasd_read_block(dev, (int)pblknum, (int)blksize,
|
|
dev->vmd250env->blkphys,
|
|
buffer, &unitstat, &residual );
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01923, "I", dev->devnum, unitstat, residual );
|
|
}
|
|
|
|
/* Call the I/O end exit */
|
|
if (dev->hnd->end) (dev->hnd->end) (dev);
|
|
|
|
release_lock(&dev->lock);
|
|
}
|
|
/* If an I/O error occurred, return status of I/O Error */
|
|
if ( unitstat != ( CSW_CE | CSW_DE ) )
|
|
{
|
|
return BIOE_IOERROR;
|
|
}
|
|
|
|
/* If there was a residual count, block size error, return status of 2 */
|
|
/* Note: This can only happen for CKD devices */
|
|
if ( residual != 0 )
|
|
{
|
|
return BIOE_CKDRECL;
|
|
}
|
|
|
|
/* Success! return 0 status */
|
|
return BIOE_SUCCESS;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Device Independent Write Block */
|
|
/*-------------------------------------------------------------------*/
|
|
static int d250_write(DEVBLK *dev, S64 pblknum, S32 blksize, void *buffer)
|
|
{
|
|
BYTE unitstat; /* Device unit status */
|
|
U32 residual; /* Residual byte count */
|
|
|
|
obtain_lock(&dev->lock);
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01922, "I", dev->devnum, blksize, pblknum);
|
|
}
|
|
|
|
if (!dev->vmd250env)
|
|
{
|
|
release_lock(&dev->lock);
|
|
return BIOE_ABORTED;
|
|
}
|
|
if (dev->vmd250env->isCKD)
|
|
{
|
|
release_lock(&dev->lock);
|
|
/* Do CKD I/O */
|
|
|
|
/* CKD to be supplied */
|
|
|
|
return BIOE_IOERROR;
|
|
}
|
|
else
|
|
{
|
|
/* Do FBA I/O */
|
|
|
|
/* Call the I/O start exit */
|
|
if (dev->hnd->start) (dev->hnd->start) (dev);
|
|
|
|
unitstat = 0;
|
|
|
|
/* Call the FBA driver's write standard block routine */
|
|
fbadasd_write_block(dev, (int)pblknum, (int)blksize,
|
|
dev->vmd250env->blkphys,
|
|
buffer, &unitstat, &residual );
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01923, "I", dev->devnum, unitstat, residual );
|
|
}
|
|
|
|
/* Call the I/O end exit */
|
|
if (dev->hnd->end) (dev->hnd->end) (dev);
|
|
|
|
release_lock(&dev->lock);
|
|
}
|
|
|
|
/* If an I/O error occurred, return status of 1 */
|
|
if ( unitstat != ( CSW_CE | CSW_DE ) )
|
|
{
|
|
return BIOE_IOERROR;
|
|
}
|
|
|
|
/* If there was a residual count, block size error, return status of 2 */
|
|
/* Note: This can only happen for CKD devices */
|
|
if ( residual != 0 )
|
|
{
|
|
return BIOE_CKDRECL;
|
|
}
|
|
|
|
/* Success! */
|
|
return BIOE_SUCCESS;
|
|
}
|
|
|
|
#endif /*!defined(_VMD250_C)*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Internal Architecture Dependent Function Prototypes */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Output Request Functions */
|
|
static int ARCH_DEP(d250_iorq32)(DEVBLK *, int *, BIOPL_IORQ32 *, REGS *);
|
|
static int ARCH_DEP(d250_list32)(IOCTL32 *, int);
|
|
static U16 ARCH_DEP(d250_addrck)(RADR, RADR, int, BYTE, REGS *);
|
|
|
|
#if defined(FEATURE_ESAME)
|
|
static int ARCH_DEP(d250_iorq64)(DEVBLK *, int *, BIOPL_IORQ64 *, REGS *);
|
|
/* void *ARCH_DEP(d250_async64)(void *); */
|
|
static int ARCH_DEP(d250_list64)(IOCTL64 *, int);
|
|
#endif /* defined(FEATURE_ESAME) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process Standard Block I/O (Function code 0x250) */
|
|
/*-------------------------------------------------------------------*/
|
|
int ARCH_DEP(vm_blockio) (int r1, int r2, REGS *regs)
|
|
{
|
|
/* Guest related paramters and values */
|
|
RADR biopaddr; /* BIOPL address */
|
|
|
|
union parmlist{ /* BIOPL formats that */
|
|
BIOPL biopl; /* May be supplied by the */
|
|
BIOPL_INIT32 init32; /* guest */
|
|
BIOPL_IORQ32 iorq32;
|
|
BIOPL_REMOVE remove;
|
|
#if defined(FEATURE_ESAME)
|
|
BIOPL_INIT64 init64;
|
|
BIOPL_IORQ64 iorq64;
|
|
#endif /* defined(FEATURE_ESAME) */
|
|
};
|
|
union parmlist bioplin; /* BIOPL from/to guest */
|
|
|
|
U16 devnum; /* Device number */
|
|
DEVBLK *dev; /* --> Device block */
|
|
int rc; /* return code in Rx+1 */
|
|
int cc; /* condition code */
|
|
|
|
rc = RC_ERROR; /* Initialize the return code to error */
|
|
cc = CC_FAILED; /* Failure assumed unless otherwise successful */
|
|
|
|
#if 0
|
|
if (sizeof(BIOPL) != 64)
|
|
{
|
|
logmsg("BIOPL size not 64: %d\n",sizeof(BIOPL));
|
|
}
|
|
if (sizeof(BIOPL_INIT32) != 64)
|
|
{
|
|
logmsg("BIOPL_INIT32 size not 64: %d\n",sizeof(BIOPL_INIT32));
|
|
}
|
|
if (sizeof(BIOPL_INIT64) != 64)
|
|
{
|
|
logmsg("BIOPL_INIT64 size not 64: %d\n",sizeof(BIOPL_INIT64));
|
|
}
|
|
if (sizeof(BIOPL_IORQ32) != 64)
|
|
{
|
|
logmsg("BIOPL_IORQ32 size not 64: %d\n",sizeof(BIOPL_IORQ32));
|
|
}
|
|
if (sizeof(BIOPL_REMOVE) != 64)
|
|
{
|
|
logmsg("BIOPL_REMOVE size not 64: %d\n",sizeof(BIOPL_REMOVE));
|
|
}
|
|
if (sizeof(BIOPL_IORQ64) != 64)
|
|
{
|
|
logmsg("BIOPL_IORQ64 size not 64: %d\n",sizeof(BIOPL_IORQ64));
|
|
}
|
|
if (sizeof(BIOE32) != 16)
|
|
{
|
|
logmsg("BIOE32 size not 16: %d\n",sizeof(BIOE32));
|
|
}
|
|
if (sizeof(BIOE64) != 24)
|
|
{
|
|
logmsg("BIOE64 size not 24: %d\n",sizeof(BIOE64));
|
|
}
|
|
#endif
|
|
|
|
/* Retrieve the BIOPL address from R1 */
|
|
biopaddr = regs->GR(r1);
|
|
|
|
/* Specification exception if the BIOPL is not on a doubleword boundary */
|
|
if (biopaddr & 0x00000007)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Fetch the BIOPL from guest storage */
|
|
ARCH_DEP(wfetchc) (&bioplin, sizeof(bioplin)-1,
|
|
biopaddr, USE_REAL_ADDR, regs);
|
|
|
|
/* Access the targeted device number from the BIOPL*/
|
|
FETCH_HW(devnum,&bioplin.biopl.devnum);
|
|
|
|
/* Locate the device by the number */
|
|
dev = find_device_by_devnum (0,devnum);
|
|
/* Device not found will be checked by the called function */
|
|
|
|
switch(regs->GR_L(r2))
|
|
{
|
|
|
|
/*--------------------------------------------------------*/
|
|
/* Initialize the Block I/O Device Environment */
|
|
/*--------------------------------------------------------*/
|
|
case INIT:
|
|
|
|
#if !defined(FEATURE_ESAME)
|
|
/* 64-bit formats not supported for S/370 or ESA/390 */
|
|
/* and bits 1-7 must be zero */
|
|
if (bioplin.biopl.flaga != 0x00)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Call the 32-bit addressing function */
|
|
cc = d250_init32(dev,&rc,&bioplin.init32,regs);
|
|
#else
|
|
/* Bits 1-7 must be zero for z/Architecture */
|
|
if (bioplin.biopl.flaga & BIOPL_FLAGARSV)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
/* Call the addressing sensitive function */
|
|
if (bioplin.biopl.flaga & BIOPL_FLAGAMSK)
|
|
{
|
|
cc = d250_init64(dev,&rc,&bioplin.init64,regs);
|
|
}
|
|
else
|
|
{
|
|
cc = d250_init32(dev,&rc,&bioplin.init32,regs);
|
|
}
|
|
#endif /* !FEATURE_ESAME */
|
|
break;
|
|
|
|
/*--------------------------------------------------------*/
|
|
/* Perform block I/O read/write requests to the device */
|
|
/*--------------------------------------------------------*/
|
|
case IOREQ:
|
|
|
|
#if !defined(FEATURE_ESAME)
|
|
/* 64-bit formats not supported for S/370 or ESA/390 */
|
|
/* and bits 1-7 must be zero */
|
|
if (bioplin.biopl.flaga != 0x00)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
cc = ARCH_DEP(d250_iorq32)(dev,&rc,&bioplin.iorq32,regs);
|
|
#else
|
|
/* Bits 1-7 must be zero for z/Architecture */
|
|
if (bioplin.biopl.flaga & BIOPL_FLAGARSV)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
if (bioplin.biopl.flaga & BIOPL_FLAGAMSK)
|
|
{
|
|
cc = ARCH_DEP(d250_iorq64)(dev,&rc,&bioplin.iorq64,regs);
|
|
}
|
|
else
|
|
{
|
|
cc = ARCH_DEP(d250_iorq32)(dev,&rc,&bioplin.iorq32,regs);
|
|
}
|
|
#endif /* !FEATURE_ESAME */
|
|
break;
|
|
|
|
/*--------------------------------------------------------*/
|
|
/* Remove the Block I/O Device Environment */
|
|
/*--------------------------------------------------------*/
|
|
case REMOVE:
|
|
cc = d250_remove(dev,&rc,&bioplin.remove,regs);
|
|
break;
|
|
default:
|
|
ARCH_DEP(program_interrupt)(regs, PGM_SPECIFICATION_EXCEPTION);
|
|
} /* end switch(regs->GR_L(r2)) */
|
|
|
|
/* Update the BIOPL in main storage */
|
|
ARCH_DEP(wstorec) (&bioplin, sizeof(bioplin)-1,
|
|
biopaddr, USE_REAL_ADDR, regs);
|
|
|
|
/* Set the return code in Rx+1 */
|
|
regs->GR_L((r1+1)&0xF) = rc;
|
|
|
|
/* Return the condition code */
|
|
return cc;
|
|
|
|
} /* end function vm_blockio */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Asynchronous Input/Outut 32-bit Driver Thread */
|
|
/*-------------------------------------------------------------------*/
|
|
static void *ARCH_DEP(d250_async32)(void *ctl)
|
|
{
|
|
IOCTL32 *ioctl; /* 32-bit IO request controls */
|
|
BYTE psc; /* List processing status code */
|
|
|
|
/* Fetch the IO request control structure */
|
|
ioctl=(IOCTL32 *)ctl;
|
|
|
|
/* Call the 32-bit BIOE request processor on this async thread*/
|
|
psc=ARCH_DEP(d250_list32)(ioctl, ASYNC);
|
|
|
|
/* Trigger the external interrupt here */
|
|
|
|
d250_bio_interrupt(ioctl->dev, ioctl->intrparm, psc, 0x03);
|
|
|
|
free(ioctl);
|
|
return NULL;
|
|
} /* end function ARCH_DEP(d250_async32) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Output Request - 32-bit Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
static int ARCH_DEP(d250_iorq32)(DEVBLK *dev, int *rc, BIOPL_IORQ32 *biopl,
|
|
REGS *regs)
|
|
{
|
|
BIOPL_IORQ32 bioplx00; /* Used to check reserved fields */
|
|
IOCTL32 ioctl; /* Request information */
|
|
BYTE psc; /* List processing status code */
|
|
|
|
/* Asynchronous request related fields */
|
|
TID tid; /* Asynchronous thread ID */
|
|
char tname[32]; /* Thread name */
|
|
IOCTL32 *asyncp; /* Pointer to async thread's storage */
|
|
int rc2;
|
|
|
|
/* Clear the reserved BIOPL */
|
|
memset(&bioplx00,0,sizeof(BIOPL_IORQ32));
|
|
|
|
/* Make sure reserved fields and bits are binary zeros */
|
|
if ((memcmp(&biopl->resv1,&bioplx00,IORQ32R1_LEN)!=0) ||
|
|
(memcmp(&biopl->resv2,&bioplx00,IORQ32R2_LEN)!=0) ||
|
|
(memcmp(&biopl->resv3,&bioplx00,IORQ32R3_LEN)!=0) ||
|
|
(biopl->flags & BIOPL_FLAGSRSV) ||
|
|
(biopl->key & BIOPL_KEYRSV)
|
|
)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Return with an error return code if the device does not exist */
|
|
if (!dev)
|
|
{
|
|
*rc = RC_NODEV; /* Set the return code for no device */
|
|
return CC_FAILED; /* Indicate the function failed */
|
|
}
|
|
|
|
/* If no environment, return with an error */
|
|
if (!(dev->vmd250env))
|
|
{
|
|
*rc = RC_STATERR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Fetch the block count from the BIOPL */
|
|
FETCH_FW(ioctl.blkcount,&biopl->blkcount);
|
|
|
|
/* Block count must be between 1 and 256, inclusive */
|
|
if ((ioctl.blkcount<1) || (ioctl.blkcount>256))
|
|
{
|
|
*rc = RC_CNT_ERR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Fetch the address of the BIO entry list from the BIOPL */
|
|
FETCH_FW(ioctl.listaddr,&biopl->bioeladr);
|
|
|
|
/* Extract the storage key from the BIOPL */
|
|
ioctl.key=biopl->key;
|
|
|
|
/* Set the structures that are involved in this request */
|
|
ioctl.dev = dev;
|
|
ioctl.regs = regs;
|
|
|
|
/* Set I/O success/failure counts to zero */
|
|
ioctl.goodblks = 0;
|
|
ioctl.badblks = 0;
|
|
|
|
if (biopl->flags & BIOPL_ASYNC)
|
|
{
|
|
/* Build the request structure */
|
|
|
|
/* Extract the 32-bit interrupt parameter from the BIOPL */
|
|
FETCH_FW(ioctl.intrparm,biopl->intparm);
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01924, "I",
|
|
dev->devnum,
|
|
ioctl.listaddr,
|
|
ioctl.blkcount,
|
|
ioctl.key,
|
|
ioctl.intrparm);
|
|
}
|
|
|
|
/* Set the default status code to an aborted list */
|
|
/* Note: This should be set correctly from the returned PSC */
|
|
ioctl.statuscod = PSC_STGERR;
|
|
|
|
/* Get the storage for the thread's parameters */
|
|
if (!(asyncp=(IOCTL32 *)malloc(sizeof(IOCTL32))))
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)sizeof(IOCTL32));
|
|
WRMSG (HHC01908, "E", buf, strerror(errno));
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Copy the thread's parameters to its own storage */
|
|
memcpy(asyncp,&ioctl,sizeof(IOCTL32));
|
|
|
|
/* Launch the asynchronous request on a separate thread */
|
|
snprintf(tname,sizeof(tname),"d250_async %4.4X",dev->devnum);
|
|
tname[sizeof(tname)-1]=0;
|
|
rc2 = create_thread (&tid, DETACHED, ARCH_DEP(d250_async32),
|
|
asyncp, tname);
|
|
if(rc2)
|
|
{
|
|
WRMSG (HHC00102, "E", strerror(rc2));
|
|
release_lock (&dev->lock);
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
}
|
|
/* Launched the async request successfully */
|
|
*rc = RC_ASYNC;
|
|
return CC_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Perform the I/O request synchronously on this thread */
|
|
/* Call the 32-bit BIOE request processor */
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01925, "I",
|
|
dev->devnum,
|
|
ioctl.listaddr,
|
|
ioctl.blkcount,
|
|
ioctl.key);
|
|
}
|
|
|
|
psc=ARCH_DEP(d250_list32)(&ioctl, SYNC);
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01926, "I", dev->devnum,psc,ioctl.goodblks,ioctl.badblks);
|
|
}
|
|
|
|
}
|
|
|
|
/* Processor status used to determine return and condition codes */
|
|
switch(psc)
|
|
{
|
|
case PSC_SUCCESS:
|
|
*rc = RC_SUCCESS;
|
|
return CC_SUCCESS;
|
|
case PSC_PARTIAL:
|
|
if (ioctl.goodblks == 0)
|
|
{
|
|
*rc = RC_ALL_BAD;
|
|
return CC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
*rc = RC_SYN_PART;
|
|
return CC_PARTIAL;
|
|
}
|
|
case PSC_REMOVED:
|
|
*rc = RC_REM_PART;
|
|
return CC_PARTIAL;
|
|
default:
|
|
WRMSG (HHC01927, "I", psc);
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
} /* end function ARCH_DEP(d250_iorq32) */
|
|
|
|
|
|
/* BIOE Address and Status Handling (32-bit and 64-bit formats) */
|
|
/* */
|
|
/* The entire BIOE is fetched from storage, but only the status */
|
|
/* field will be updated. BIOE addresses and block addresses are */
|
|
/* handled the same. Only key protection and addressing */
|
|
/* exceptions are recognized for these absolute addresses. */
|
|
/* Architecture will determine the maximum address allowed not the */
|
|
/* addressing mode specified in the PSW. An addressing exception */
|
|
/* will be recognized if a BIOE extends beyond the architecture */
|
|
/* maximum address. */
|
|
/* */
|
|
/* Setting of reference bit in a storage key is based upon */
|
|
/* inline.h ARCH_DEP(fetch_xxx_absolute) functions */
|
|
/* Setting of changed bit in a storage key is based upon */
|
|
/* inline.h ARCH_DEP(store_xxx_absolute) functions */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Outut 32-bit List Processor - Sychronicity Independent */
|
|
/*-------------------------------------------------------------------*/
|
|
static int ARCH_DEP(d250_list32)(IOCTL32* ioctl, int async)
|
|
/* WARNING: Device Block lock must be released before returning */
|
|
{
|
|
BIOE32 bioe; /* 32-bit BIOE fetched from absolute storage */
|
|
RADR bioebeg; /* Starting address of the BIOE */
|
|
RADR bioeend; /* Address of last byte of BIOE */
|
|
U16 xcode; /* Detected exception condition */
|
|
int blocks; /* Number of blocks being processed */
|
|
int block; /* counter used in block I/O loop */
|
|
S32 blknum; /* Block number of the request */
|
|
BYTE status; /* Returned BIOE status */
|
|
/* Passed to generic block I/O function */
|
|
int physblk; /* Physical block number */
|
|
RADR bufbeg; /* Address where the read/write will occur */
|
|
RADR bufend; /* Last byte read or written */
|
|
|
|
xcode = 0; /* Initialize the address check exception code */
|
|
status = 0;
|
|
|
|
/* Preserve pending sense if any and establish my ownership */
|
|
/* of the device by reserving it if shared and locking it */
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01928, "I",
|
|
ioctl->dev->devnum,
|
|
ioctl->blkcount,
|
|
(RADR)ioctl->listaddr,
|
|
ioctl->key
|
|
);
|
|
}
|
|
|
|
/* Take ownership of the device */
|
|
d250_preserve(ioctl->dev);
|
|
/* Note: the DEVBLK is now locked */
|
|
|
|
if (!ioctl->dev->vmd250env)
|
|
{
|
|
d250_restore(ioctl->dev);
|
|
/* Note: the device lock is now released */
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
blocks=(int)ioctl->blkcount;
|
|
bioebeg=ioctl->listaddr & AMASK31 ;
|
|
|
|
/* Process each of the BIOE's supplied by the BIOPL count field */
|
|
for ( block = 0 ; block < blocks ; block++ )
|
|
{
|
|
status = 0xFF; /* Set undefined status */
|
|
|
|
bioeend=( bioebeg + sizeof(BIOE32) - 1 ) & AMASK31;
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bioebeg,bioeend,ACCTYPE_READ,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01929,"I",ioctl->dev->devnum,xcode,bioebeg,bioeend,ioctl->key);
|
|
}
|
|
if ( xcode )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Fetch the BIOE from storage */
|
|
memcpy(&bioe,ioctl->regs->mainstor+bioebeg,sizeof(BIOE32));
|
|
STORAGE_KEY(bioebeg, ioctl->regs) |= (STORKEY_REF);
|
|
STORAGE_KEY(bioeend, ioctl->regs) |= (STORKEY_REF);
|
|
|
|
/* Process a single BIOE */
|
|
do
|
|
{
|
|
|
|
/* Make sure reserved field is zeros */
|
|
if ( bioe.resv1[0]!=0x00 || bioe.resv1[1]!=0x00 )
|
|
{
|
|
status=BIOE_NOTZERO;
|
|
continue;
|
|
}
|
|
|
|
/* Fetch and validate block number */
|
|
FETCH_FW(blknum,&bioe.blknum);
|
|
if ( (blknum < ioctl->dev->vmd250env->begblk) ||
|
|
(blknum > ioctl->dev->vmd250env->endblk)
|
|
)
|
|
{
|
|
status=BIOE_BADBLOCK;
|
|
continue;
|
|
}
|
|
|
|
/* Fetch the storage address used for I/O */
|
|
FETCH_FW(bufbeg,&bioe.bufaddr);
|
|
bufbeg &= AMASK31;
|
|
|
|
/* Ensure the environment still exists */
|
|
if (!ioctl->dev->vmd250env)
|
|
{
|
|
d250_restore(ioctl->dev);
|
|
/* Note: the device lock is now released */
|
|
status=BIOE_ABORTED;
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
/* The I/O handler routines are normally called without the */
|
|
/* device lock being held. The device is reserved by the */
|
|
/* busy status. */
|
|
|
|
/* Determine the last byte of the I/O buffer */
|
|
bufend=( bufbeg + ioctl->dev->vmd250env->blksiz -1 ) & AMASK31 ;
|
|
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01930, "I",
|
|
ioctl->dev->devnum,
|
|
bioebeg,
|
|
bioe.type,
|
|
blknum,
|
|
bufbeg
|
|
);
|
|
}
|
|
|
|
/* Determine the physical block on the device relative to zero */
|
|
physblk=(S64)blknum+ioctl->dev->vmd250env->offset-1;
|
|
/* The read/write routines will convert this to a physical disk */
|
|
/* location for reading or writing */
|
|
|
|
if (bioe.type == BIOE_READ)
|
|
{
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bufbeg,bufend,ACCTYPE_READ,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01931, "I",
|
|
ioctl->dev->devnum,xcode,bufbeg,bufend,ioctl->key);
|
|
}
|
|
switch ( xcode )
|
|
{
|
|
case PGM_ADDRESSING_EXCEPTION:
|
|
status=BIOE_ADDREXC;
|
|
continue;
|
|
case PGM_PROTECTION_EXCEPTION:
|
|
status=BIOE_PROTEXC;
|
|
continue;
|
|
}
|
|
/* At this point, the block number has been validated */
|
|
/* and the buffer is addressable and accessible */
|
|
status=d250_read(ioctl->dev,
|
|
physblk,
|
|
ioctl->dev->vmd250env->blksiz,
|
|
ioctl->regs->mainstor+bufbeg);
|
|
|
|
/* Set I/O storage key references if successful */
|
|
if (!status)
|
|
{
|
|
STORAGE_KEY(bufbeg, ioctl->regs) |= (STORKEY_REF);
|
|
STORAGE_KEY(bufend, ioctl->regs) |= (STORKEY_REF);
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
if ( ioctl->dev->vmd250env->blksiz == 4096 )
|
|
{
|
|
STORAGE_KEY(bufbeg+2048, ioctl->regs) |= (STORKEY_REF);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
continue;
|
|
} /* end of BIOE_READ */
|
|
else
|
|
{ if (bioe.type == BIOE_WRITE)
|
|
{
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bufbeg,bufend,ACCTYPE_WRITE,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01932, "I",
|
|
ioctl->dev->devnum,
|
|
xcode,bufbeg,
|
|
bufend,
|
|
ioctl->key);
|
|
}
|
|
|
|
switch ( xcode )
|
|
{
|
|
case PGM_ADDRESSING_EXCEPTION:
|
|
status=BIOE_ADDREXC;
|
|
continue;
|
|
case PGM_PROTECTION_EXCEPTION:
|
|
status=BIOE_PROTEXC;
|
|
continue;
|
|
}
|
|
|
|
if (ioctl->dev->vmd250env->isRO)
|
|
{
|
|
status=BIOE_DASDRO;
|
|
continue;
|
|
}
|
|
status=d250_write(ioctl->dev,
|
|
physblk,
|
|
ioctl->dev->vmd250env->blksiz,
|
|
ioctl->regs->mainstor+bufbeg);
|
|
|
|
/* Set I/O storage key references if good I/O */
|
|
if (!status)
|
|
{
|
|
STORAGE_KEY(bufbeg, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
STORAGE_KEY(bufend, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
if ( ioctl->dev->vmd250env->blksiz == 4096 )
|
|
{
|
|
STORAGE_KEY(bufbeg+2048, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
continue;
|
|
} /* end of if BIOE_WRITE */
|
|
else
|
|
{
|
|
status=BIOE_BADREQ;
|
|
continue;
|
|
} /* end of else BIOE_WRITE */
|
|
} /* end of else BIOE_READ */
|
|
}while(0); /* end of do */
|
|
|
|
/* Determine if we can store the status in the BIOE */
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bioebeg+1,bioebeg+1,ACCTYPE_WRITE,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01933, "I",
|
|
ioctl->dev->devnum,xcode,bioebeg+1,bioebeg+1,ioctl->key);
|
|
}
|
|
|
|
/* If the status byte is store protected, give up on processing any */
|
|
/* more BIOE's. Leave the BIOE list process for loop */
|
|
if ( xcode )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Store the status in the BIOE */
|
|
memcpy(ioctl->regs->mainstor+bioebeg+1,&status,1);
|
|
|
|
/* Set the storage key change bit */
|
|
STORAGE_KEY(bioebeg+1, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01934, "I", ioctl->dev->devnum,bioebeg,status);
|
|
}
|
|
|
|
/* Count if this BIOE was a success or failure */
|
|
if ( status )
|
|
{
|
|
ioctl->badblks+=1;
|
|
if ( status == BIOE_ABORTED )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ioctl->goodblks+=1;
|
|
}
|
|
|
|
/* Determine the address of the next BIOE */
|
|
bioebeg += sizeof(BIOE32);
|
|
bioebeg &= AMASK31;
|
|
} /* end of for loop */
|
|
|
|
|
|
#if 0
|
|
logmsg(_("(d250_list32) BIOE's processed: %d\n"),block);
|
|
#endif
|
|
|
|
/* Restore device to guest ownership */
|
|
d250_restore(ioctl->dev);
|
|
/* Note: device lock not held */
|
|
|
|
/* If an access exception occurred: */
|
|
/* If this is a synchronous request, generate a program exception */
|
|
/* or if this is asynchrnous, just return with a storage error */
|
|
if ( xcode )
|
|
{
|
|
if (async)
|
|
return PSC_STGERR;
|
|
else
|
|
ARCH_DEP(program_interrupt)(ioctl->regs, xcode);
|
|
}
|
|
|
|
if ( status == BIOE_ABORTED )
|
|
{
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
/* Determine if we were completely successful or only partially */
|
|
/* successful. 'Partial' includes none successful. */
|
|
/* Synchronous and asynchronous requests handle all failed */
|
|
/* differently. The good and bad blocks field are used by the */
|
|
/* caller */
|
|
if (ioctl->goodblks < blocks)
|
|
{
|
|
return PSC_PARTIAL;
|
|
}
|
|
return PSC_SUCCESS;
|
|
|
|
} /* end function d250_list32 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Absolue Address Checking without Reference and Change Recording */
|
|
/*-------------------------------------------------------------------*/
|
|
static U16 ARCH_DEP(d250_addrck)
|
|
(RADR beg, RADR end, int acctype, BYTE key, REGS *regs)
|
|
/* Note: inline.h and vstore.c functions are not used because they */
|
|
/* will generate program exceptions automatically. DIAGNOSE X'250' in */
|
|
/* the asynchronous case, must not do that, but rather reflect the */
|
|
/* error in the interrupt status code or BIOE status field. So this */
|
|
/* function only detects and reports the error. */
|
|
/* The caller must decide whether to generate a program interrupt or */
|
|
/* just report the encountered error without a program interruption */
|
|
/* and must set the reference and change bit as appropriate */
|
|
{
|
|
BYTE sk1; /* Storage key of first byte of area */
|
|
BYTE sk2; /* Storage key of last byte of area */
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
BYTE skmid; /* Storage key of middle byte of area */
|
|
#endif /* defined(FEATURE_2K_STORAGE_KEYS) */
|
|
|
|
|
|
if ( (end > regs->mainlim) || (end > MAXADDRESS) || end < beg )
|
|
{
|
|
return PGM_ADDRESSING_EXCEPTION;
|
|
}
|
|
|
|
/* Note this logic is inspired by */
|
|
/* inline.h ARCH_DEP(is_fetch_protected) */
|
|
/* inline.h ARCH_DEP(is_store_protected) */
|
|
|
|
if (key == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
sk1=STORAGE_KEY(beg,regs);
|
|
sk2=STORAGE_KEY(end,regs);
|
|
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
if ( ( end - beg ) > 2048 )
|
|
{
|
|
skmid=STORAGE_KEY(beg + 2048,regs);
|
|
}
|
|
else
|
|
{
|
|
skmid = sk2;
|
|
}
|
|
#endif /* defined(FEATURE_2K_STORAGE_KEYS) */
|
|
|
|
if (acctype == ACCTYPE_READ)
|
|
{ /* Check for fetch protection */
|
|
if ( ((sk1 & STORKEY_FETCH) && (key != (sk1 & STORKEY_KEY)))
|
|
||((sk2 & STORKEY_FETCH) && (key != (sk2 & STORKEY_KEY)))
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
||((skmid & STORKEY_FETCH) && (key != (skmid & STORKEY_KEY)))
|
|
#endif /* defined(FEATURE_2K_STORAGE_KEYS) */
|
|
)
|
|
{
|
|
return PGM_PROTECTION_EXCEPTION;
|
|
}
|
|
}
|
|
else /* assume ACCTYPE_WRITE */
|
|
{ /* Check for store protection */
|
|
if ( (key != (sk1 & STORKEY_KEY))
|
|
||(key != (sk2 & STORKEY_KEY))
|
|
#if defined(FEATURE_2K_STORAGE_KEYS)
|
|
||(key != (skmid & STORKEY_KEY))
|
|
#endif /* defined(FEATURE_2K_STORAGE_KEYS) */
|
|
)
|
|
{
|
|
return PGM_PROTECTION_EXCEPTION;
|
|
}
|
|
}
|
|
return 0;
|
|
} /* end of function ARCH_DEP(d250_addrck) */
|
|
|
|
|
|
#if defined(FEATURE_ESAME)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Asynchronous Input/Output 64-bit Driver Thread */
|
|
/*-------------------------------------------------------------------*/
|
|
static void* ARCH_DEP(d250_async64)(void *ctl)
|
|
{
|
|
IOCTL64 *ioctl; /* 64-bit IO request controls */
|
|
BYTE psc; /* List processing status code */
|
|
|
|
/* Fetch the IO request control structure */
|
|
ioctl=(IOCTL64 *)ctl;
|
|
|
|
/* Call the 32-bit BIOE request processor on this async thread*/
|
|
psc=ARCH_DEP(d250_list64)(ioctl, ASYNC);
|
|
|
|
d250_bio_interrupt(ioctl->dev, ioctl->intrparm, psc, 0x07);
|
|
|
|
free(ioctl);
|
|
return NULL;
|
|
|
|
} /* end function ARCH_DEP(d250_async64) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Output Request - 64-bit Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
static int ARCH_DEP(d250_iorq64)(DEVBLK *dev, int *rc, BIOPL_IORQ64 *biopl,
|
|
REGS *regs)
|
|
{
|
|
BIOPL_IORQ64 bioplx00; /* Used to check reserved fields */
|
|
IOCTL64 ioctl; /* Request information */
|
|
BYTE psc; /* List processing status code */
|
|
|
|
/* Asynchronous request related fields */
|
|
TID tid; /* Asynchronous thread ID */
|
|
char tname[32]; /* Thread name */
|
|
IOCTL64 *asyncp; /* Pointer to async thread's free standing storage */
|
|
int rc2;
|
|
|
|
#if 0
|
|
logmsg("(d250_iorq64) Entered\n");
|
|
#endif
|
|
|
|
/* Clear the reserved BIOPL */
|
|
memset(&bioplx00,0,sizeof(BIOPL_IORQ64));
|
|
|
|
/* Make sure reserved fields are binary zeros */
|
|
if ((memcmp(&biopl->resv1,&bioplx00,IORQ64R1_LEN)!=0) ||
|
|
(memcmp(&biopl->resv2,&bioplx00,IORQ64R2_LEN)!=0) ||
|
|
(memcmp(&biopl->resv3,&bioplx00,IORQ64R3_LEN)!=0) ||
|
|
(memcmp(&biopl->resv4,&bioplx00,IORQ64R4_LEN)!=0) ||
|
|
(biopl->flags & BIOPL_FLAGSRSV) ||
|
|
(biopl->key & BIOPL_KEYRSV)
|
|
)
|
|
{
|
|
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
|
|
}
|
|
|
|
/* Return with an error return code if the device does not exist */
|
|
if (!dev)
|
|
{
|
|
*rc = RC_NODEV; /* Set the return code for no device */
|
|
return CC_FAILED; /* Indicate the function failed */
|
|
}
|
|
|
|
/* If no environment, return with an error */
|
|
if (!dev->vmd250env)
|
|
{
|
|
*rc = RC_STATERR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Fetch the block count from the BIOPL */
|
|
FETCH_FW(ioctl.blkcount,&biopl->blkcount);
|
|
#if 0
|
|
logmsg("(d250_iorq64) ioctl.blkcount=%d,\n",
|
|
ioctl.blkcount);
|
|
#endif
|
|
|
|
/* Block count must be between 1 and 256, inclusive */
|
|
if ((ioctl.blkcount<1) || (ioctl.blkcount>256))
|
|
{
|
|
*rc = RC_CNT_ERR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Fetch the address of the BIO entry list from the BIOPL */
|
|
FETCH_DW(ioctl.listaddr,&biopl->bioeladr);
|
|
#if 0
|
|
logmsg (_("(d250_iorq64) ioctl.listaddr=%16.16X,\n"),
|
|
ioctl.listaddr);
|
|
#endif
|
|
|
|
/* Extract the storage key from the BIOPL */
|
|
ioctl.key=biopl->key;
|
|
|
|
/* Set the structures that are involved in this request */
|
|
ioctl.dev = dev;
|
|
ioctl.regs = regs;
|
|
|
|
/* Set I/O success/failure counts to zero */
|
|
ioctl.goodblks = 0;
|
|
ioctl.badblks = 0;
|
|
|
|
/* Determine if request is an asynchronous or synchronous */
|
|
if (biopl->flags & BIOPL_ASYNC)
|
|
{
|
|
/* Build the request structure */
|
|
|
|
/* Extract the 64-bit interrupt parameter from the BIOPL */
|
|
FETCH_DW(ioctl.intrparm,&biopl->intparm);
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01935, "I",
|
|
dev->devnum,
|
|
ioctl.listaddr,
|
|
ioctl.blkcount,
|
|
ioctl.key,
|
|
ioctl.intrparm);
|
|
}
|
|
|
|
/* Set the default status code to an aborted list */
|
|
/* Note: This should be set correctly from the returned PSC */
|
|
ioctl.statuscod = PSC_STGERR;
|
|
|
|
/* Get the storage for the thread's parameters */
|
|
if (!(asyncp=(IOCTL64 *)malloc(sizeof(IOCTL64))))
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)sizeof(IOCTL64));
|
|
WRMSG (HHC01908, "E", buf, strerror(errno));
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
}
|
|
|
|
/* Copy the thread's parameters to its own storage */
|
|
memcpy(asyncp,&ioctl,sizeof(IOCTL64));
|
|
|
|
/* Launch the asynchronous request on a separate thread */
|
|
snprintf(tname,sizeof(tname),"d250_async %4.4X",dev->devnum);
|
|
tname[sizeof(tname)-1]=0;
|
|
rc2 = create_thread (&tid, DETACHED, ARCH_DEP(d250_async64),
|
|
asyncp, tname);
|
|
if(rc2)
|
|
{
|
|
WRMSG (HHC00102, "E", strerror(rc2));
|
|
release_lock (&dev->lock);
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
}
|
|
/* Launched the async request successfully */
|
|
*rc = RC_ASYNC;
|
|
return CC_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01936, "I",
|
|
dev->devnum,
|
|
ioctl.listaddr,
|
|
ioctl.blkcount,
|
|
ioctl.key);
|
|
}
|
|
|
|
psc=ARCH_DEP(d250_list64)(&ioctl, SYNC);
|
|
|
|
if (dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01937, "I", dev->devnum,psc,ioctl.goodblks,ioctl.badblks);
|
|
}
|
|
}
|
|
|
|
/* Processor status used to determine return and condition codes */
|
|
switch(psc)
|
|
{
|
|
case PSC_SUCCESS:
|
|
*rc = RC_SUCCESS;
|
|
return CC_SUCCESS;
|
|
case PSC_PARTIAL:
|
|
if (ioctl.goodblks == 0)
|
|
{
|
|
*rc = RC_ALL_BAD;
|
|
return CC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
*rc = RC_SYN_PART;
|
|
return CC_PARTIAL;
|
|
}
|
|
case PSC_REMOVED:
|
|
*rc = RC_REM_PART;
|
|
return CC_PARTIAL;
|
|
default:
|
|
WRMSG (HHC01938, "E", psc);
|
|
*rc = RC_ERROR;
|
|
return CC_FAILED;
|
|
} /* end switch(psc) */
|
|
|
|
} /* end function d250_iorq64 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Outut 64-bit List Processor - Sychronicity Independent */
|
|
/*-------------------------------------------------------------------*/
|
|
static int ARCH_DEP(d250_list64)(IOCTL64* ioctl, int async)
|
|
/* WARNING: Device Block lock must be released before returning */
|
|
{
|
|
BIOE64 bioe; /* 32-bit BIOE fetched from absolute storage */
|
|
RADR bioebeg; /* Starting address of the BIOE */
|
|
RADR bioeend; /* Address of last byte of BIOE */
|
|
U16 xcode; /* Detected exception condition */
|
|
int blocks; /* Number of blocks being processed */
|
|
int block; /* counter used in block I/O loop */
|
|
S64 blknum; /* Block number of the request */
|
|
BYTE status; /* Returned BIOE status */
|
|
/* Passed to generic block I/O function */
|
|
int physblk; /* Physical block number */
|
|
RADR bufbeg; /* Address where the read/write will occur */
|
|
RADR bufend; /* Last byte read or written */
|
|
|
|
|
|
xcode = 0; /* Initialize the address check exception code */
|
|
status = 0;
|
|
|
|
/* Preserve pending sense if any and establish my ownership */
|
|
/* of the device by reserving it if shared and locking it */
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01939, "I",
|
|
ioctl->dev->devnum,
|
|
ioctl->blkcount,
|
|
(RADR)ioctl->listaddr,
|
|
ioctl->key
|
|
);
|
|
}
|
|
|
|
/* Take ownership of the device */
|
|
d250_preserve(ioctl->dev);
|
|
/* Note: the DEVBLK is now locked */
|
|
|
|
if (!ioctl->dev->vmd250env)
|
|
{
|
|
d250_restore(ioctl->dev);
|
|
/* Note: the device lock is now released */
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
blocks=(int)ioctl->blkcount;
|
|
bioebeg=ioctl->listaddr & AMASK64 ;
|
|
|
|
/* Process each of the BIOE's supplied by the BIOPL count field */
|
|
for ( block = 0 ; block < blocks ; block++ )
|
|
{
|
|
status = 0xFF; /* Set undefined status */
|
|
|
|
bioeend=( bioebeg + sizeof(BIOE32) - 1 ) & AMASK31;
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bioebeg,bioeend,ACCTYPE_READ,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01940, "I",ioctl->dev->devnum,xcode,bioebeg,bioeend,ioctl->key);
|
|
}
|
|
if ( xcode )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Fetch the BIOE from storage */
|
|
memcpy(&bioe,ioctl->regs->mainstor+bioebeg,sizeof(BIOE64));
|
|
STORAGE_KEY(bioebeg, ioctl->regs) |= (STORKEY_REF);
|
|
STORAGE_KEY(bioeend, ioctl->regs) |= (STORKEY_REF);
|
|
|
|
/* Process a single BIOE */
|
|
do
|
|
{
|
|
|
|
/* Make sure reserved field is zeros */
|
|
if ( bioe.resv1[0]!=0x00 || bioe.resv1[1]!=0x00 )
|
|
{
|
|
status=BIOE_NOTZERO;
|
|
continue;
|
|
}
|
|
|
|
/* Fetch and validate block number */
|
|
FETCH_DW(blknum,&bioe.blknum);
|
|
if ( (blknum < ioctl->dev->vmd250env->begblk) ||
|
|
(blknum > ioctl->dev->vmd250env->endblk)
|
|
)
|
|
{
|
|
status=BIOE_BADBLOCK;
|
|
continue;
|
|
}
|
|
|
|
/* Fetch the storage address used for I/O */
|
|
FETCH_DW(bufbeg,&bioe.bufaddr);
|
|
bufbeg &= AMASK64;
|
|
|
|
/* Ensure the environment still exists */
|
|
if (!ioctl->dev->vmd250env)
|
|
{
|
|
d250_restore(ioctl->dev);
|
|
/* Note: the device lock is now released */
|
|
status=BIOE_ABORTED;
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
/* The I/O handler routines are normally called without the */
|
|
/* device lock being held. The device is reserved by the */
|
|
/* busy status. */
|
|
|
|
/* Determine the last byte of the I/O buffer */
|
|
bufend=( bufbeg + ioctl->dev->vmd250env->blksiz -1 ) & AMASK64 ;
|
|
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01941, "I",
|
|
ioctl->dev->devnum,
|
|
bioebeg,
|
|
bioe.type,
|
|
blknum,
|
|
bufbeg
|
|
);
|
|
}
|
|
|
|
/* Determine the physical block on the device relative to zero */
|
|
physblk=(S64)blknum+ioctl->dev->vmd250env->offset-1;
|
|
/* The read/write routines will convert this to a physical disk */
|
|
/* location for reading or writing */
|
|
|
|
if (bioe.type == BIOE_READ)
|
|
{
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bufbeg,bufend,ACCTYPE_READ,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01942,"I",
|
|
ioctl->dev->devnum,xcode,bufbeg,bufend,ioctl->key);
|
|
}
|
|
switch ( xcode )
|
|
{
|
|
case PGM_ADDRESSING_EXCEPTION:
|
|
status=BIOE_ADDREXC;
|
|
continue;
|
|
case PGM_PROTECTION_EXCEPTION:
|
|
status=BIOE_PROTEXC;
|
|
continue;
|
|
}
|
|
/* At this point, the block number has been validated */
|
|
/* and the buffer is addressable and accessible */
|
|
status=d250_read(ioctl->dev,
|
|
physblk,
|
|
ioctl->dev->vmd250env->blksiz,
|
|
ioctl->regs->mainstor+bufbeg);
|
|
|
|
/* Set I/O storage key references if successful */
|
|
if (!status)
|
|
{
|
|
STORAGE_KEY(bufbeg, ioctl->regs) |= (STORKEY_REF);
|
|
STORAGE_KEY(bufend, ioctl->regs) |= (STORKEY_REF);
|
|
}
|
|
|
|
continue;
|
|
} /* end of BIOE_READ */
|
|
else
|
|
{ if (bioe.type == BIOE_WRITE)
|
|
{
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bufbeg,bufend,ACCTYPE_WRITE,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01943, "I",
|
|
ioctl->dev->devnum,
|
|
xcode,bufbeg,
|
|
bufend,
|
|
ioctl->key);
|
|
}
|
|
|
|
switch ( xcode )
|
|
{
|
|
case PGM_ADDRESSING_EXCEPTION:
|
|
status=BIOE_ADDREXC;
|
|
continue;
|
|
case PGM_PROTECTION_EXCEPTION:
|
|
status=BIOE_PROTEXC;
|
|
continue;
|
|
}
|
|
|
|
if (ioctl->dev->vmd250env->isRO)
|
|
{
|
|
status=BIOE_DASDRO;
|
|
continue;
|
|
}
|
|
status=d250_write(ioctl->dev,
|
|
physblk,
|
|
ioctl->dev->vmd250env->blksiz,
|
|
ioctl->regs->mainstor+bufbeg);
|
|
/* Set I/O storage key references if good I/O */
|
|
if (!status)
|
|
{
|
|
STORAGE_KEY(bufbeg, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
STORAGE_KEY(bufend, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
}
|
|
continue;
|
|
} /* end of if BIOE_WRITE */
|
|
else
|
|
{
|
|
status=BIOE_BADREQ;
|
|
continue;
|
|
} /* end of else BIOE_WRITE */
|
|
} /* end of else BIOE_READ */
|
|
}while(0); /* end of do */
|
|
|
|
/* Determine if we can store the status in the BIOE */
|
|
xcode=ARCH_DEP(d250_addrck)
|
|
(bioebeg+1,bioebeg+1,ACCTYPE_WRITE,ioctl->key,ioctl->regs);
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG(HHC01944,"I",ioctl->dev->devnum,xcode,bioebeg+1,bioebeg+1,ioctl->key);
|
|
}
|
|
|
|
/* If the status byte is store protected, give up on processing any */
|
|
/* more BIOE's. Leave the BIOE list process for loop */
|
|
if ( xcode )
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Store the status in the BIOE */
|
|
memcpy(ioctl->regs->mainstor+bioebeg+1,&status,1);
|
|
|
|
/* Set the storage key change bit */
|
|
STORAGE_KEY(bioebeg+1, ioctl->regs)
|
|
|= (STORKEY_REF | STORKEY_CHANGE);
|
|
|
|
if (ioctl->dev->ccwtrace)
|
|
{
|
|
WRMSG (HHC01945,"I",ioctl->dev->devnum,bioebeg,status);
|
|
}
|
|
|
|
/* Count if this BIOE was a success or failure */
|
|
if ( status )
|
|
{
|
|
ioctl->badblks+=1;
|
|
if ( status == BIOE_ABORTED )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ioctl->goodblks+=1;
|
|
}
|
|
|
|
/* Determine the address of the next BIOE */
|
|
bioebeg += sizeof(BIOE64);
|
|
bioebeg &= AMASK64;
|
|
} /* end of for loop */
|
|
|
|
#if 0
|
|
/* remove after testing */
|
|
logmsg(_("(d250_list64) BIOE's processed: %d\n"),block);
|
|
#endif
|
|
|
|
/* Restore device to guest ownership */
|
|
d250_restore(ioctl->dev);
|
|
/* Note: device lock not held */
|
|
|
|
/* If an access exception occurred: */
|
|
/* If this is a synchronous request, generate a program exception */
|
|
/* or if this is asynchrnous, just return with a storage error */
|
|
if ( xcode )
|
|
{
|
|
if (async)
|
|
return PSC_STGERR;
|
|
else
|
|
ARCH_DEP(program_interrupt)(ioctl->regs, xcode);
|
|
}
|
|
|
|
if ( status == BIOE_ABORTED )
|
|
{
|
|
return PSC_REMOVED;
|
|
}
|
|
|
|
/* Determine if we were completely successful or only partially */
|
|
/* successful. 'Partial' includes none successful. */
|
|
/* Synchronous and asynchronous requests handle all failed */
|
|
/* differently. The good and bad blocks field are used by the */
|
|
/* caller */
|
|
if (ioctl->goodblks < blocks)
|
|
{
|
|
return PSC_PARTIAL;
|
|
}
|
|
return PSC_SUCCESS;
|
|
|
|
} /* end function ARCH_DEP(d250_list64) */
|
|
|
|
#endif /* defined(FEATURE_ESAME) */
|
|
|
|
#endif /*FEATURE_VM_BLOCKIO*/
|
|
|
|
#if !defined(_GEN_ARCH)
|
|
|
|
#if defined(_ARCHMODE2)
|
|
#define _GEN_ARCH _ARCHMODE2
|
|
#include "vmd250.c"
|
|
#endif
|
|
|
|
#if defined(_ARCHMODE3)
|
|
#undef _GEN_ARCH
|
|
#define _GEN_ARCH _ARCHMODE3
|
|
#include "vmd250.c"
|
|
#endif
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|