Files
org-hyperion-cules/qdio.c
2013-03-23 21:05:37 -07:00

431 lines
12 KiB
C

/* QDIO.C (c) Copyright Jan Jaeger, 2003-2012 */
/* (C) Copyright Harold Grovesteen, 2011 */
/* Queued Direct Input Output */
/* */
/* This module contains the Signal Adapter instruction */
/* and QEBSM EQBS and SQBS instructions */
#include "hstdinc.h"
#if !defined(_HENGINE_DLL_)
#define _HENGINE_DLL_
#endif
#if !defined(_QDIO_C_)
#define _QDIO_C_
#endif
#include "hercules.h"
#include "opcode.h"
#include "qdio.h"
#include "inline.h"
#undef PTIO
#define PTIO(_class, _name) \
PTT(PTT_CL_ ## _class,_name,regs->GR_L(1),(U32)(effective_addr2 & 0xffffffff),regs->psw.IA_L)
#if defined(FEATURE_QUEUED_DIRECT_IO)
/*-------------------------------------------------------------------*/
/* B274 SIGA - Signal Adapter [S] */
/*-------------------------------------------------------------------*/
DEF_INST(signal_adapter)
{
int b2;
RADR effective_addr2;
DEVBLK *dev; /* -> device block */
S(inst, regs, b2, effective_addr2);
// ARCH_DEP(display_inst) (regs, inst);
PRIV_CHECK(regs);
#if defined(_FEATURE_SIE)
if(SIE_STATNB(regs, EC3, SIGAA))
longjmp(regs->progjmp, SIE_INTERCEPT_INST);
#endif /*defined(_FEATURE_SIE)*/
PTIO(IO,"SIGA");
/* Specification exception if invalid function code */
if((regs->GR_L(0)
#if defined(FEATURE_QEBSM)
& ~(FACILITY_ENABLED(QEBSM,regs) ? SIGA_TOKEN : 0)
#endif /*defined(FEATURE_QEBSM)*/
) > SIGA_FC_MAX)
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
/* Locate the device block for this subchannel */
#if defined(FEATURE_QEBSM)
if(FACILITY_ENABLED(QEBSM, regs)
&& (regs->GR_L(0) & SIGA_TOKEN))
dev = find_device_by_subchan (TKN2IOID(regs->GR_G(1)));
else
#endif /*defined(FEATURE_QEBSM)*/
{
/* Program check if the ssid including lcss is invalid */
SSID_CHECK(regs);
dev = find_device_by_subchan (regs->GR_L(1));
}
/* Condition code 3 if subchannel does not exist,
is not valid, or is not enabled or is not a QDIO subchannel */
if (dev == NULL
|| (dev->pmcw.flag5 & PMCW5_V) == 0
|| (dev->pmcw.flag5 & PMCW5_E) == 0
|| (dev->pmcw.flag4 & PMCW4_Q) == 0)
{
PTIO(ERR,"*SIGA");
#if defined(_FEATURE_QUEUED_DIRECT_IO_ASSIST)
SIE_INTERCEPT(regs);
#endif
regs->psw.cc = 3;
return;
}
/* Obtain the device lock */
obtain_lock (&dev->lock);
/* Check that device is QDIO active */
if ((dev->scsw.flag2 & SCSW2_Q) == 0)
{
PTIO(ERR,"*SIGA");
release_lock (&dev->lock);
regs->psw.cc = 1;
return;
}
switch(
regs->GR_L(0)
#if defined(FEATURE_QEBSM)
& ~(FACILITY_ENABLED(QEBSM,regs) ? SIGA_TOKEN : 0)
#endif /*defined(FEATURE_QEBSM)*/
) {
case SIGA_FC_R:
if(dev->hnd->siga_r)
regs->psw.cc = (dev->hnd->siga_r) (dev, regs->GR_L(2) );
else
{
PTIO(ERR,"*SIGA");
regs->psw.cc = 3;
}
break;
case SIGA_FC_W:
if(dev->hnd->siga_w)
regs->psw.cc = (dev->hnd->siga_w) (dev, regs->GR_L(2) );
else
{
PTIO(ERR,"*SIGA");
regs->psw.cc = 3;
}
break;
case SIGA_FC_S:
#if SIGA_FC_MAX >= SIGA_FC_S
if(dev->hnd->siga_s)
regs->psw.cc = (dev->hnd->siga_s) (dev, regs->GR_L(2), regs->GR_L(3) );
else
{
PTIO(ERR,"*SIGA");
regs->psw.cc = 3;
}
#else
/* No signalling required for sync requests as we emulate
a real machine */
regs->psw.cc = 0;
#endif
break;
#if SIGA_FC_MAX >= SIGA_FC_M
case SIGA_FC_M:
if(dev->hnd->siga_m)
regs->psw.cc = (dev->hnd->siga_m) (dev, regs->GR_L(2) );
else
{
PTIO(ERR,"*SIGA");
regs->psw.cc = 3;
}
break;
#endif
default:
PTIO(ERR,"*SIGA");
}
release_lock (&dev->lock);
}
#if defined(FEATURE_QEBSM)
/*-------------------------------------------------------------------*/
/* EB8A SQBS - Set Queue Buffer State [RSY] */
/*-------------------------------------------------------------------*/
DEF_INST(set_queue_buffer_state)
{
int r1, r3; /* Register numbers */
int b2; /* effective address base */
VADR effective_addr2; /* effective address */
DEVBLK *dev; /* Data device DEVBLK */
BYTE state; /* Target buffer state */
BYTE oldstate; /* Current buffer state */
BYTE nextstate; /* Next buffer's state */
U32 queues; /* Total number of queues */
U32 count; /* Number of buffers to set */
U32 qidx; /* Queue index */
U32 bidx; /* SLSB buffer state index */
U64 slsba; /* Storage list state block address */
RSY(inst, regs, r1, r3, b2, effective_addr2);
FACILITY_CHECK(QEBSM, regs);
// ARCH_DEP(display_inst) (regs, inst);
PRIV_CHECK(regs);
#if defined(_FEATURE_SIE)
if(SIE_STATNB(regs, EC3, SIGAA))
longjmp(regs->progjmp, SIE_INTERCEPT_INST);
#endif /*defined(_FEATURE_SIE)*/
PTIO(INF,"SQBS");
dev = find_device_by_subchan(TKN2IOID(regs->GR_G(1)));
/* Condition code 3 if subchannel does not exist,
is not valid, or is not enabled or is not a QDIO subchannel */
if (dev == NULL
|| (dev->pmcw.flag5 & PMCW5_V) == 0
|| (dev->pmcw.flag5 & PMCW5_E) == 0
|| (dev->pmcw.flag4 & PMCW4_Q) == 0)
{
PTIO(ERR,"*SQBS");
#if defined(_FEATURE_QUEUED_DIRECT_IO_ASSIST)
SIE_INTERCEPT(regs);
#endif
regs->psw.cc = 2;
return;
}
#if 0
/* Check that device is QDIO active */
if ((dev->scsw.flag2 & SCSW2_Q) == 0)
{
PTIO(ERR,"*SQBS");
regs->psw.cc = 2;
return;
}
#endif
qidx = regs->GR_H(r1); /* Queue index */
bidx = regs->GR_L(r1); /* Buffer index */
count = regs->GR_L(r3) < 128 ? regs->GR_L(r3) : 128; /* Number of buffers */
state = effective_addr2 & 0xFF;
queues = (U32)(dev->qdio.i_qcnt + dev->qdio.o_qcnt);
if ( (qidx >= queues) || (bidx > 127) )
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
if (qidx < (U32)dev->qdio.i_qcnt)
slsba = dev->qdio.i_slsbla[qidx];
else
slsba = dev->qdio.o_slsbla[qidx - dev->qdio.i_qcnt];
oldstate = nextstate = ARCH_DEP(wfetchb)((VADR)(slsba+bidx), USE_REAL_ADDR, regs);
while (count && oldstate == nextstate)
{
ARCH_DEP(wstoreb)(state, (VADR)(slsba+bidx), USE_REAL_ADDR, regs);
bidx++; bidx &= 0x7F; /* Advance and wrap index */
count--;
if(count)
nextstate = ARCH_DEP(wfetchb)((VADR)(slsba+bidx), USE_REAL_ADDR, regs);
}
regs->GR_L(r1) = bidx; /* Return buffer state index */
regs->GR_L(r3) = count; /* Return number of unchanged buffers */
regs->psw.cc = count ? 1 : 0;
return;
}
/*-------------------------------------------------------------------*/
/* B99C EQBS - Extract Queue Buffer State [RRF] */
/*-------------------------------------------------------------------*/
DEF_INST(extract_queue_buffer_state)
{
int r1, r2, r3, m4; /* Register numbers */
VADR effective_addr2 = 0; /* effective address */
DEVBLK *dev; /* Data device DEVBLK */
int autoack; /* Flag for auto-acknowkledgement */
BYTE state; /* State extracted from first buffer */
BYTE nextstate; /* next buffer */
U32 queues; /* Total number of queues */
U32 count; /* Number of buffers to set */
U32 qidx; /* Queue index */
U32 bidx; /* SLSB buffer state index */
U64 slsba; /* Storage list state block address */
RRF_RM(inst, regs, r1, r2, r3, m4);
FACILITY_CHECK(QEBSM, regs);
// ARCH_DEP(display_inst) (regs, inst);
PRIV_CHECK(regs);
#if defined(_FEATURE_SIE)
if(SIE_STATNB(regs, EC3, SIGAA))
longjmp(regs->progjmp, SIE_INTERCEPT_INST);
#endif /*defined(_FEATURE_SIE)*/
PTIO(INF,"EQBS");
dev = find_device_by_subchan(TKN2IOID(regs->GR_G(1)));
/* Condition code 3 if subchannel does not exist,
is not valid, or is not enabled or is not a QDIO subchannel */
if (dev == NULL
|| (dev->pmcw.flag5 & PMCW5_V) == 0
|| (dev->pmcw.flag5 & PMCW5_E) == 0
|| (dev->pmcw.flag4 & PMCW4_Q) == 0)
{
PTIO(ERR,"*EQBS");
#if defined(_FEATURE_QUEUED_DIRECT_IO_ASSIST)
SIE_INTERCEPT(regs);
#endif
regs->psw.cc = 2;
return;
}
#if 0
/* Check that device is QDIO active */
if ((dev->scsw.flag2 & SCSW2_Q) == 0)
{
PTIO(ERR,"*EQBS");
regs->psw.cc = 2;
return;
}
#endif
qidx = regs->GR_H(r1); /* Queue index */
bidx = regs->GR_L(r1); /* Buffer index */
autoack = (regs->GR_H(r2) & 0x80000000);
count = regs->GR_L(r3) < 128 ? regs->GR_L(r3) : 128; /* Number of buffers */
queues = (U32)(dev->qdio.i_qcnt + dev->qdio.o_qcnt);
if ( (qidx >= queues) || (bidx > 127) )
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
if (qidx < (U32)dev->qdio.i_qcnt)
slsba = dev->qdio.i_slsbla[qidx];
else
slsba = dev->qdio.o_slsbla[qidx - dev->qdio.i_qcnt];
state = nextstate = ARCH_DEP(wfetchb)((VADR)(slsba+bidx), USE_REAL_ADDR, regs);
while(count && state == nextstate)
{
if (autoack)
switch(nextstate) {
case SLSBE_INPUT_COMPLETED:
ARCH_DEP(wstoreb)
(SLSBE_INPUT_ACKED, (VADR)(slsba+bidx), USE_REAL_ADDR, regs);
break;
#if 0
case SLSBE_OUTPUT_COMPLETED:
ARCH_DEP(wstoreb)
(SLSBE_OUTPUT_PRIMED, (VADR)(slsba+bidx), USE_REAL_ADDR, regs);
break;
#endif
}
bidx++; bidx &= 0x7F; /* Advance and wrap index */
count--;
if(count)
nextstate = ARCH_DEP(wfetchb)((VADR)(slsba+bidx), USE_REAL_ADDR, regs);
}
regs->GR_L(r1) = bidx;
regs->GR_LHLCL(r2) = state;
regs->GR_L(r3) = count;
regs->psw.cc = count ? 1 : 0;
return;
}
#endif /*defined(FEATURE_QEBSM)*/
#if defined(FEATURE_SVS)
/*-------------------------------------------------------------------*/
/* B265 SVS - Set Vector Summary [RRE] */
/*-------------------------------------------------------------------*/
DEF_INST(set_vector_summary)
{
int r1, unused; /* Register numbers */
RRE0(inst, regs, r1, unused);
// ARCH_DEP(display_inst) (regs, inst);
FACILITY_CHECK(SVS, regs);
PRIV_CHECK(regs);
SIE_INTERCEPT(regs);
ODD_CHECK(r1, regs);
switch(regs->GR_L(1)) {
case 3: // Clear Global Summary
regs->GR(r1+1) = 0;
break;
default:
ARCH_DEP(program_interrupt) (regs, PGM_SPECIFICATION_EXCEPTION);
}
}
#endif /*defined(FEATURE_SVS)*/
#endif /*defined(FEATURE_QUEUED_DIRECT_IO)*/
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "qdio.c"
#endif
#if defined(_ARCHMODE3)
#undef _GEN_ARCH
#define _GEN_ARCH _ARCHMODE3
#include "qdio.c"
#endif
#endif /*!defined(_GEN_ARCH)*/