mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-20 18:57:06 +02:00
2270 lines
81 KiB
C
2270 lines
81 KiB
C
/* VMD250.C (c) Copyright Harold Grovesteen, 2009 */
|
|
/* 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. */
|
|
|
|
// $Id$
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* 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 */
|
|
int d250_init32(DEVBLK *, int *, BIOPL_INIT32 *, REGS *);
|
|
int d250_init64(DEVBLK *, int *, BIOPL_INIT64 *, REGS *);
|
|
struct VMBIOENV* d250_init(DEVBLK *, U32, S64, int *, int *);
|
|
|
|
/* Input/Output Request Functions */
|
|
void d250_preserve(DEVBLK *);
|
|
void d250_restore(DEVBLK *);
|
|
int d250_read(DEVBLK *, S64, S32, void *);
|
|
int d250_write(DEVBLK *, S64, S32, void *);
|
|
/* Note: some I/O request functions are architecture dependent */
|
|
|
|
/* Removal Function */
|
|
int d250_remove(DEVBLK *, int *, BIOPL_REMOVE *, REGS *);
|
|
|
|
/* Asynchronous Interrupt Generation */
|
|
void d250_bio_interrupt(DEVBLK *, U64 intparm, BYTE status, BYTE code);
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Trigger Block I/O External Interrupt */
|
|
/*-------------------------------------------------------------------*/
|
|
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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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,0x00,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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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,0x00,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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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. */
|
|
void d250_preserve(DEVBLK *dev)
|
|
{
|
|
/* Note: this logic comes from the beginning of */
|
|
/* channel.c ARCH_DEP(execute_ccw_chain) */
|
|
obtain_lock(&dev->lock);
|
|
if ( dev->shared )
|
|
{
|
|
while (dev->ioactive != DEV_SYS_NONE
|
|
&& dev->ioactive != DEV_SYS_LOCAL)
|
|
{
|
|
dev->iowaiters++;
|
|
wait_condition(&dev->iocond, &dev->lock);
|
|
dev->iowaiters--;
|
|
}
|
|
dev->ioactive = DEV_SYS_LOCAL;
|
|
}
|
|
else
|
|
{
|
|
dev->ioactive = DEV_SYS_LOCAL;
|
|
}
|
|
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 */
|
|
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);
|
|
}
|
|
}
|
|
dev->ioactive = DEV_SYS_NONE;
|
|
dev->busy = 0;
|
|
release_lock(&dev->lock);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Environment Remove - Any Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
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,0x00,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 */
|
|
/*-------------------------------------------------------------------*/
|
|
int d250_read(DEVBLK *dev, S64 pblknum, S32 blksize, void *buffer)
|
|
{
|
|
BYTE unitstat; /* Device unit status */
|
|
U16 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 */
|
|
/*-------------------------------------------------------------------*/
|
|
int d250_write(DEVBLK *dev, S64 pblknum, S32 blksize, void *buffer)
|
|
{
|
|
BYTE unitstat; /* Device unit status */
|
|
U16 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 */
|
|
int ARCH_DEP(d250_iorq32)(DEVBLK *, int *, BIOPL_IORQ32 *, REGS *);
|
|
int ARCH_DEP(d250_list32)(IOCTL32 *, int);
|
|
U16 ARCH_DEP(d250_addrck)(RADR, RADR, int, BYTE, REGS *);
|
|
|
|
#if defined(FEATURE_ESAME)
|
|
int ARCH_DEP(d250_iorq64)(DEVBLK *, int *, BIOPL_IORQ64 *, REGS *);
|
|
/* void *ARCH_DEP(d250_async64)(void *); */
|
|
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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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,0x00,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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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);
|
|
|
|
} /* end function ARCH_DEP(d250_async64) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Input/Output Request - 64-bit Addressing */
|
|
/*-------------------------------------------------------------------*/
|
|
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,0x00,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 */
|
|
/*-------------------------------------------------------------------*/
|
|
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)*/
|