Files
org-hyperion-cules/external.c
Jan Jaeger 5449a14d31 NLS support
git-svn-id: file:///home/jj/hercules.svn/trunk@887 956126f8-22a0-4046-8f4a-272fa8102e63
2002-05-11 15:45:58 +00:00

506 lines
17 KiB
C

/* EXTERNAL.C (c) Copyright Roger Bowler, 1999-2002 */
/* ESA/390 External Interrupt and Timer */
/* Interpretive Execution - (c) Copyright Jan Jaeger, 1999-2002 */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2002 */
/*-------------------------------------------------------------------*/
/* This module implements external interrupt, timer, and signalling */
/* functions for the Hercules ESA/390 emulator. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* TOD clock offset contributed by Jay Maynard */
/* Correction to timer interrupt by Valery Pogonchenko */
/* TOD clock drag factor contributed by Jan Jaeger */
/* Synchronized broadcasting contributed by Jan Jaeger */
/* CPU timer and clock comparator interrupt improvements by */
/* Jan Jaeger, after a suggestion by Willem Koynenberg */
/* Prevent TOD clock and CPU timer from going back - Jan Jaeger */
/* Use longjmp on all interrupt types - Jan Jaeger */
/* Fix todclock - Jay Maynard */
/* Modifications for Interpretive Execution (SIE) by Jan Jaeger */
/*-------------------------------------------------------------------*/
#include "hercules.h"
#include "opcode.h"
#include "inline.h"
/*-------------------------------------------------------------------*/
/* Synchronize broadcast request */
/* Input: */
/* regs A pointer to the CPU register context */
/* */
/* The intlock MUST be held when calling synchronize_broadcast() */
/* */
/* Signals all other CPU's to perform a requested function */
/* synchronously, such as purging the ALB and TLB buffers. */
/* The CPU issuing the broadcast request will wait until */
/* all other CPU's have performed the requested action. *JJ */
/* */
/*-------------------------------------------------------------------*/
void ARCH_DEP(synchronize_broadcast) (REGS *regs, int code, U64 pfra)
{
int i; /* Array subscript */
REGS *realregs; /* Real REGS if guest */
realregs =
#if defined(_FEATURE_SIE)
regs->sie_state ? regs->hostregs :
#endif /*defined(_FEATURE_SIE)*/
regs;
#if MAX_CPU_ENGINES > 1
if (code > 0)
{
obtain_lock (&sysblk.intlock);
while (IS_IC_BROADCAST_ON)
if (IS_IC_BROADCAST(realregs))
ARCH_DEP(synchronize_broadcast)(realregs, 0, 0);
else
{
release_lock (&sysblk.intlock);
#ifdef OPTION_CS_USLEEP
usleep (1L);
#endif
obtain_lock (&sysblk.intlock);
}
ON_IC_BROADCAST;
sysblk.broadcast_mask = sysblk.started_mask;
sysblk.broadcast_code = code;
sysblk.broadcast_pfra = pfra;
}
#else /* MAX_CPU_ENGINES > 1 */
sysblk.broadcast_code = code;
sysblk.broadcast_pfra = pfra;
#endif /* MAX_CPU_ENGINES > 1 */
/* Purge TLB */
if (sysblk.broadcast_code & BROADCAST_PTLB)
ARCH_DEP(purge_tlb) (realregs);
#if defined(FEATURE_ACCESS_REGISTERS)
/* Purge ALB */
if (sysblk.broadcast_code & BROADCAST_PALB)
ARCH_DEP(purge_alb) (realregs);
#endif /*defined(FEATURE_ACCESS_REGISTERS)*/
/* Invalidate TLB entries */
if (sysblk.broadcast_code & BROADCAST_ITLB)
{
for (i = 0; i < (sizeof(regs->tlb)/sizeof(TLBE)); i++)
if ((regs->tlb[i].TLB_PTE & PAGETAB_PFRA) == sysblk.broadcast_pfra
&& regs->tlb[i].valid)
regs->tlb[i].valid = 0;
for (i = 0; i < (sizeof(regs->tlb)/sizeof(TLBE)) && realregs != regs; i++)
if ((realregs->tlb[i].TLB_PTE & PAGETAB_PFRA) == sysblk.broadcast_pfra
&& realregs->tlb[i].valid)
realregs->tlb[i].valid = 0;
}
#if MAX_CPU_ENGINES > 1
/* Wait for the other cpus */
sysblk.broadcast_mask &= ~realregs->cpumask;
if (code > 0)
{
if (sysblk.broadcast_mask != 0)
{
WAKEUP_WAITING_CPUS(ALL_CPUS, CPUSTATE_STARTED);
wait_condition (&sysblk.broadcast_cond, &sysblk.intlock);
}
OFF_IC_BROADCAST;
release_lock (&sysblk.intlock);
}
else
{
if (sysblk.broadcast_mask == 0)
broadcast_condition (&sysblk.broadcast_cond);
else
wait_condition (&sysblk.broadcast_cond, &sysblk.intlock);
}
#endif /*MAX_CPU_ENGINES > 1*/
} /* end function synchronize_broadcast */
/*-------------------------------------------------------------------*/
/* Load external interrupt new PSW */
/*-------------------------------------------------------------------*/
static void ARCH_DEP(external_interrupt) (int code, REGS *regs)
{
PSA *psa;
int rc;
/* reset the cpuint indicator */
RESET_IC_CPUINT(regs);
release_lock(&sysblk.intlock);
#if defined(_FEATURE_SIE)
/* Set the main storage reference and change bits */
if(regs->sie_state)
{
/* Point to SIE copy of PSA in state descriptor */
psa = (void*)(sysblk.mainstor + regs->sie_state + SIE_IP_PSA_OFFSET);
STORAGE_KEY(regs->sie_state) |= (STORKEY_REF | STORKEY_CHANGE);
}
else
#endif /*defined(_FEATURE_SIE)*/
{
/* Point to PSA in main storage */
psa = (void*)(sysblk.mainstor + regs->PX);
STORAGE_KEY(regs->PX) |= (STORKEY_REF | STORKEY_CHANGE);
}
/* Store the interrupt code in the PSW */
regs->psw.intcode = code;
/* Zero extcpuad field unless extcall or ems signal */
if(code != EXT_EXTERNAL_CALL_INTERRUPT
&& code != EXT_EMERGENCY_SIGNAL_INTERRUPT)
STORE_HW(psa->extcpad,0);
#if defined(FEATURE_BCMODE)
/* For ECMODE, store external interrupt code at PSA+X'86' */
if ( regs->psw.ecmode )
#endif /*defined(FEATURE_BCMODE)*/
STORE_HW(psa->extint,code);
#if defined(_FEATURE_SIE)
if(!regs->sie_state)
#endif /*defined(_FEATURE_SIE)*/
{
/* Store current PSW at PSA+X'18' */
ARCH_DEP(store_psw) (regs, psa->extold);
/* Load new PSW from PSA+X'58' */
rc = ARCH_DEP(load_psw) (regs, psa->extnew);
if ( rc )
ARCH_DEP(program_interrupt)(regs, rc);
}
longjmp (regs->progjmp, SIE_INTERCEPT_EXT);
} /* end function external_interrupt */
/*-------------------------------------------------------------------*/
/* Perform external interrupt if pending */
/* */
/* This function is called by the CPU to check whether any */
/* external interrupt conditions are pending, and to perform */
/* an external interrupt if so. If multiple external interrupts */
/* are pending, then only the highest priority interrupt is taken, */
/* and any other interrupts remain pending. Remaining interrupts */
/* will be processed one-by-one during subsequent calls. */
/* */
/* Important notes: */
/* (i) This function must NOT be called if the CPU is disabled */
/* for external interrupts (PSW bit 7 is zero). */
/* (ii) The caller MUST hold the interrupt lock (sysblk.intlock) */
/* to ensure correct serialization of interrupt pending bits. */
/*-------------------------------------------------------------------*/
void ARCH_DEP(perform_external_interrupt) (REGS *regs)
{
PSA *psa; /* -> Prefixed storage area */
U16 cpuad; /* Originating CPU address */
/* External interrupt if console interrupt key was depressed */
if (OPEN_IC_INTKEY(regs)
#if defined(_FEATURE_SIE)
&& !regs->sie_state
#endif /*!defined(_FEATURE_SIE)*/
)
{
logmsg (_("External interrupt: Interrupt key\n"));
/* Reset interrupt key pending */
OFF_IC_INTKEY;
/* Generate interrupt key interrupt */
ARCH_DEP(external_interrupt) (EXT_INTERRUPT_KEY_INTERRUPT, regs);
}
/* External interrupt if malfunction alert is pending */
if (OPEN_IC_MALFALT(regs))
{
/* Find first CPU which generated a malfunction alert */
for (cpuad = 0; regs->malfcpu[cpuad] == 0; cpuad++)
{
if (cpuad >= MAX_CPU_ENGINES)
{
OFF_IC_MALFALT(regs);
return;
}
} /* end for(cpuad) */
// /*debug*/ logmsg (_("External interrupt: Malfuction Alert from CPU %d\n"),
// /*debug*/ cpuad);
/* Reset the indicator for the CPU which was found */
regs->malfcpu[cpuad] = 0;
/* Store originating CPU address at PSA+X'84' */
psa = (void*)(sysblk.mainstor + regs->PX);
STORE_HW(psa->extcpad,cpuad);
/* Reset emergency signal pending flag if there are
no other CPUs which generated emergency signal */
OFF_IC_MALFALT(regs);
while (++cpuad < MAX_CPU_ENGINES)
{
if (regs->malfcpu[cpuad])
{
ON_IC_MALFALT(regs);
break;
}
} /* end while */
/* Generate emergency signal interrupt */
ARCH_DEP(external_interrupt) (EXT_MALFUNCTION_ALERT_INTERRUPT, regs);
}
/* External interrupt if emergency signal is pending */
if (OPEN_IC_EMERSIG(regs))
{
/* Find first CPU which generated an emergency signal */
for (cpuad = 0; regs->emercpu[cpuad] == 0; cpuad++)
{
if (cpuad >= MAX_CPU_ENGINES)
{
OFF_IC_EMERSIG(regs);
return;
}
} /* end for(cpuad) */
// /*debug*/ logmsg (_("External interrupt: Emergency Signal from CPU %d\n"),
// /*debug*/ cpuad);
/* Reset the indicator for the CPU which was found */
regs->emercpu[cpuad] = 0;
/* Store originating CPU address at PSA+X'84' */
psa = (void*)(sysblk.mainstor + regs->PX);
STORE_HW(psa->extcpad,cpuad);
/* Reset emergency signal pending flag if there are
no other CPUs which generated emergency signal */
OFF_IC_EMERSIG(regs);
while (++cpuad < MAX_CPU_ENGINES)
{
if (regs->emercpu[cpuad])
{
ON_IC_EMERSIG(regs);
break;
}
} /* end while */
/* Generate emergency signal interrupt */
ARCH_DEP(external_interrupt) (EXT_EMERGENCY_SIGNAL_INTERRUPT, regs);
}
/* External interrupt if external call is pending */
if (OPEN_IC_EXTCALL(regs))
{
// /*debug*/logmsg (_("External interrupt: External Call from CPU %d\n"),
// /*debug*/ regs->extccpu);
/* Reset external call pending */
OFF_IC_EXTCALL(regs);
/* Store originating CPU address at PSA+X'84' */
psa = (void*)(sysblk.mainstor + regs->PX);
STORE_HW(psa->extcpad,regs->extccpu);
/* Generate external call interrupt */
ARCH_DEP(external_interrupt) (EXT_EXTERNAL_CALL_INTERRUPT, regs);
}
/* External interrupt if TOD clock exceeds clock comparator */
if ((sysblk.todclk + regs->todoffset) > regs->clkc
&& sysblk.insttrace == 0
&& sysblk.inststep == 0
&& OPEN_IC_CLKC(regs) )
{
if (sysblk.insttrace || sysblk.inststep)
{
logmsg (_("External interrupt: Clock comparator\n"));
}
ARCH_DEP(external_interrupt) (EXT_CLOCK_COMPARATOR_INTERRUPT, regs);
}
/* External interrupt if CPU timer is negative */
if ((S64)regs->ptimer < 0
&& OPEN_IC_PTIMER(regs) )
{
if (sysblk.insttrace || sysblk.inststep)
{
logmsg (_("External interrupt: CPU timer=%16.16llX\n"),
(long long)regs->ptimer);
}
ARCH_DEP(external_interrupt) (EXT_CPU_TIMER_INTERRUPT, regs);
}
/* External interrupt if interval timer interrupt is pending */
#if defined(FEATURE_INTERVAL_TIMER)
if (OPEN_IC_ITIMER(regs)
#if defined(_FEATURE_SIE)
&& !(regs->sie_state
&& (regs->siebk->m & SIE_M_ITMOF))
#endif /*defined(_FEATURE_SIE)*/
)
{
if (sysblk.insttrace || sysblk.inststep)
{
logmsg (_("External interrupt: Interval timer\n"));
}
OFF_IC_ITIMER(regs);
ARCH_DEP(external_interrupt) (EXT_INTERVAL_TIMER_INTERRUPT, regs);
}
#endif /*FEATURE_INTERVAL_TIMER*/
/* External interrupt if service signal is pending */
if (OPEN_IC_SERVSIG(regs)
#if defined(_FEATURE_SIE)
&& !regs->sie_state
#endif /*!defined(_FEATURE_SIE)*/
)
{
/* Apply prefixing if the parameter is a storage address */
if ( (sysblk.servparm & SERVSIG_ADDR) )
sysblk.servparm =
APPLY_PREFIXING (sysblk.servparm, regs->PX);
// logmsg (_("External interrupt: Service signal %8.8X\n"),
// sysblk.servparm);
/* Store service signal parameter at PSA+X'80' */
psa = (void*)(sysblk.mainstor + regs->PX);
STORE_FW(psa->extparm,sysblk.servparm);
/* Reset service parameter */
sysblk.servparm = 0;
/* Reset service signal pending */
OFF_IC_SERVSIG;
/* Generate service signal interrupt */
ARCH_DEP(external_interrupt) (EXT_SERVICE_SIGNAL_INTERRUPT, regs);
}
/* reset the cpuint indicator */
RESET_IC_CPUINT(regs);
} /* end function perform_external_interrupt */
/*-------------------------------------------------------------------*/
/* Store Status */
/* Input: */
/* sregs Register context of CPU whose status is to be stored */
/* aaddr A valid absolute address of a 512-byte block into */
/* which status is to be stored */
/*-------------------------------------------------------------------*/
void ARCH_DEP(store_status) (REGS *ssreg, RADR aaddr)
{
U64 dreg; /* Double register work area */
int i; /* Array subscript */
PSA *sspsa; /* -> Store status area */
/* Point to the PSA into which status is to be stored */
sspsa = (void*)(sysblk.mainstor + aaddr);
/* Store CPU timer in bytes 216-223 */
dreg = ssreg->ptimer;
STORE_DW(sspsa->storeptmr, ssreg->ptimer);
/* Store clock comparator in bytes 224-231 */
STORE_DW(sspsa->storeclkc, ssreg->clkc << 8);
/* Store PSW in bytes 256-263 */
ARCH_DEP(store_psw) (ssreg, sspsa->storepsw);
/* Store prefix register in bytes 264-267 */
STORE_FW(sspsa->storepfx,ssreg->PX);
#if defined(FEATURE_ESAME)
/* Store Floating Point Control Register */
STORE_FW(sspsa->storefpc,ssreg->fpc);
/* Store TOD Programable register */
STORE_FW(sspsa->storetpr,ssreg->todpr);
#endif /*defined(FEATURE_ESAME)*/
#if defined(FEATURE_ESAME)
sspsa->arch = 1;
#endif /*defined(FEATURE_ESAME)*/
#if defined(_900)
sspsa->arch = 0;
#endif /*defined(_900)*/
/* Store access registers in bytes 288-351 */
for (i = 0; i < 16; i++)
STORE_FW(sspsa->storear[i],ssreg->AR(i));
/* Store floating-point registers in bytes 352-383 */
#if defined(FEATURE_ESAME)
for (i = 0; i < 32; i++)
#else /*!defined(FEATURE_ESAME)*/
for (i = 0; i < 8; i++)
#endif /*!defined(FEATURE_ESAME)*/
STORE_FW(sspsa->storefpr[i],ssreg->fpr[i]);
/* Store general-purpose registers in bytes 384-447 */
for (i = 0; i < 16; i++)
STORE_W(sspsa->storegpr[i],ssreg->GR(i));
/* Store control registers in bytes 448-511 */
for (i = 0; i < 16; i++)
STORE_W(sspsa->storecr[i],ssreg->CR(i));
} /* end function store_status */
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "external.c"
#endif
#if defined(_ARCHMODE3)
#undef _GEN_ARCH
#define _GEN_ARCH _ARCHMODE3
#include "external.c"
#endif
void store_status (REGS *ssreg, U64 aaddr)
{
switch(ssreg->arch_mode) {
#if defined(_370)
case ARCH_370:
aaddr &= 0x7FFFFFFF;
s370_store_status (ssreg, aaddr);
break;
#endif
#if defined(_390)
case ARCH_390:
aaddr &= 0x7FFFFFFF;
s390_store_status (ssreg, aaddr);
break;
#endif
#if defined(_900)
case ARCH_900:
z900_store_status (ssreg, aaddr);
break;
#endif
}
}
#endif /*!defined(_GEN_ARCH)*/