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