mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-12 06:58:12 +02:00
423 lines
18 KiB
C
423 lines
18 KiB
C
/* DAT.H (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) and others 2013-2021 */
|
|
/* Dynamic Address Translation */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* Interpretive Execution - (C) Copyright Jan Jaeger, 1999-2012 */
|
|
/* z/Architecture support - (C) Copyright Jan Jaeger, 1999-2012 */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ARCH_DEP section: compiled multiple times, ONCE for each arch. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if (ARCH_370_IDX == ARCH_IDX && !defined( DID_370_DAT_H )) \
|
|
|| (ARCH_390_IDX == ARCH_IDX && !defined( DID_390_DAT_H )) \
|
|
|| (ARCH_900_IDX == ARCH_IDX && !defined( DID_900_DAT_H ))
|
|
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
/** **/
|
|
/** LARGE NON-INLINED FUNCTIONS **/
|
|
/** **/
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
|
|
void ARCH_DEP( purge_tlbe )( REGS* regs, RADR pfra );
|
|
void ARCH_DEP( invalidate_tlb )( REGS* regs, BYTE mask );
|
|
void ARCH_DEP( invalidate_pte )( BYTE ibyte, RADR pto, VADR vaddr, REGS* regs, bool local );
|
|
|
|
#if defined( FEATURE_ACCESS_REGISTERS )
|
|
U16 ARCH_DEP( translate_alet )( U32 alet, U16 eax, int acctype, REGS* regs, U32* asteo, U32 aste[] );
|
|
#endif
|
|
|
|
#if defined( FEATURE_DUAL_ADDRESS_SPACE )
|
|
U16 ARCH_DEP( translate_asn )( U16 asn, REGS* regs, U32* asteo, U32 aste[] );
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* All 3 build architecture variants of the below function must */
|
|
/* all be declared at once (we cannot wait for them to be declared */
|
|
/* later on a subsequent pass when the next build architecture is */
|
|
/* eventually built) since some functions might need invoke the */
|
|
/* "SIE_TRANSLATE_ADDR" macro, which might itself need to call */
|
|
/* the "translate_addr" function for a build architecture different */
|
|
/* from the one currently being built (or currently executing). */
|
|
/*-------------------------------------------------------------------*/
|
|
#ifndef TRANSLATE_ADDR_DEFINED
|
|
#define TRANSLATE_ADDR_DEFINED
|
|
int s370_translate_addr ( U32 vaddr, int arn, REGS* regs, int acctype );
|
|
int s390_translate_addr ( U32 vaddr, int arn, REGS* regs, int acctype );
|
|
int z900_translate_addr ( U64 vaddr, int arn, REGS* regs, int acctype );
|
|
#endif
|
|
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
/** **/
|
|
/** SMALL INLINED FUNCTIONS FOR SPEED **/
|
|
/** **/
|
|
/*********************************************************************/
|
|
/*********************************************************************/
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge all translation lookaside buffers for all CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
inline void ARCH_DEP( purge_tlb_all )( REGS* regs, U16 cpuad )
|
|
{
|
|
int cpu;
|
|
|
|
if (0xFFFF == cpuad && !IS_INTLOCK_HELD( regs )) // (sanity check)
|
|
CRASH(); // (logic error!)
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (1
|
|
&& IS_CPU_ONLINE(cpu)
|
|
&& (sysblk.regs[ cpu ]->cpubit & sysblk.started_mask)
|
|
&& (0
|
|
|| 0xFFFF == cpuad
|
|
|| sysblk.regs[ cpu ]->cpuad == cpuad
|
|
)
|
|
)
|
|
{
|
|
switch (sysblk.regs[ cpu ]->arch_mode)
|
|
{
|
|
case ARCH_370_IDX: s370_purge_tlb( sysblk.regs[ cpu ]); break;
|
|
case ARCH_390_IDX: s390_purge_tlb( sysblk.regs[ cpu ]); break;
|
|
case ARCH_900_IDX: z900_purge_tlb( sysblk.regs[ cpu ]); break;
|
|
default: CRASH();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge specific translation lookaside buffer entry from all CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
inline void ARCH_DEP( purge_tlbe_all )( REGS* regs, RADR pfra, U16 cpuad )
|
|
{
|
|
int cpu;
|
|
|
|
if (0xFFFF == cpuad && !IS_INTLOCK_HELD( regs )) // (sanity check)
|
|
CRASH(); // (logic error!)
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (1
|
|
&& IS_CPU_ONLINE(cpu)
|
|
&& (sysblk.regs[ cpu ]->cpubit & sysblk.started_mask)
|
|
&& (0
|
|
|| 0xFFFF == cpuad
|
|
|| sysblk.regs[ cpu ]->cpuad == cpuad
|
|
)
|
|
)
|
|
{
|
|
switch (sysblk.regs[ cpu ]->arch_mode)
|
|
{
|
|
case ARCH_370_IDX: s370_purge_tlbe( sysblk.regs[ cpu ], pfra ); break;
|
|
case ARCH_390_IDX: s390_purge_tlbe( sysblk.regs[ cpu ], pfra ); break;
|
|
case ARCH_900_IDX: z900_purge_tlbe( sysblk.regs[ cpu ], pfra ); break;
|
|
default: CRASH();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if defined( FEATURE_ACCESS_REGISTERS )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Purge the ART lookaside buffer for all CPUs */
|
|
/*-------------------------------------------------------------------*/
|
|
inline void ARCH_DEP( purge_alb_all )( REGS* regs )
|
|
{
|
|
int cpu;
|
|
|
|
if (!IS_INTLOCK_HELD( regs )) // (sanity check)
|
|
CRASH(); // (logic error!)
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (1
|
|
&& IS_CPU_ONLINE(cpu)
|
|
&& (sysblk.regs[ cpu ]->cpubit & sysblk.started_mask)
|
|
)
|
|
{
|
|
switch (sysblk.regs[ cpu ]->arch_mode)
|
|
{
|
|
case ARCH_390_IDX: s390_purge_alb( sysblk.regs[ cpu ]); break;
|
|
case ARCH_900_IDX: z900_purge_alb( sysblk.regs[ cpu ]); break;
|
|
default: CRASH(); // (370 doesn't have access registers)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* defined( FEATURE_ACCESS_REGISTERS ) */
|
|
|
|
|
|
#if defined( FEATURE_DUAL_ADDRESS_SPACE )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform ASN authorization process */
|
|
/* */
|
|
/* Input: */
|
|
/* ax Authorization index */
|
|
/* aste Pointer to 16-word area containing a copy of the */
|
|
/* ASN second table entry associated with the ASN */
|
|
/* atemask Specifies which authority bit to test in the ATE: */
|
|
/* ATE_PRIMARY (for PT instruction) */
|
|
/* ATE_SECONDARY (for PR, SSAR, and LASP instructions, */
|
|
/* and all access register translations) */
|
|
/* regs Pointer to the CPU register context */
|
|
/* */
|
|
/* Operation: */
|
|
/* The AX is used to select an entry in the authority table */
|
|
/* pointed to by the ASTE, and an authorization bit in the ATE */
|
|
/* is tested. For ATE_PRIMARY (X'80'), the P bit is tested. */
|
|
/* For ATE_SECONDARY (X'40'), the S bit is tested. */
|
|
/* Authorization is successful if the ATE falls within the */
|
|
/* authority table limit and the tested bit value is 1. */
|
|
/* */
|
|
/* Returns: */
|
|
/* true == Authorization *WAS* successful. */
|
|
/* false == Authorization was *NOT* successful. */
|
|
/* */
|
|
/* A program check may be generated for addressing exception */
|
|
/* if the authority table entry address is invalid, and in */
|
|
/* this case the function does not return. */
|
|
/*-------------------------------------------------------------------*/
|
|
inline bool ARCH_DEP( authorize_asn )( U16 ax, U32 aste[], int atemask, REGS* regs )
|
|
{
|
|
RADR ato; /* Authority table origin */
|
|
int atl; /* Authority table length */
|
|
BYTE ate; /* Authority table entry */
|
|
|
|
/* [3.10.3.1] Authority table lookup */
|
|
|
|
/* Isolate the authority table origin and length */
|
|
ato = aste[0] & ASTE0_ATO;
|
|
atl = aste[1] & ASTE1_ATL;
|
|
|
|
/* Authorization fails if AX is outside table */
|
|
if ((ax & 0xFFF0) > atl)
|
|
return true;
|
|
|
|
/* Calculate the address of the byte in the authority
|
|
table which contains the 2 bit entry for this AX */
|
|
ato += (ax >> 2);
|
|
|
|
/* Ignore carry into bit position 0 */
|
|
ato &= 0x7FFFFFFF;
|
|
|
|
/* Addressing exception if ATE is outside main storage */
|
|
if (ato > regs->mainlim)
|
|
goto auth_addr_excp;
|
|
|
|
/* Load the byte containing the authority table entry
|
|
and shift the entry into the leftmost 2 bits */
|
|
ato = APPLY_PREFIXING( ato, regs->PX );
|
|
|
|
/* Translate SIE host virt to SIE host abs. Note: macro
|
|
is treated as a no-operation if SIE_MODE not active */
|
|
SIE_TRANSLATE( &ato, ACCTYPE_SIE, regs );
|
|
|
|
ate = regs->mainstor[ato];
|
|
ate <<= ((ax & 0x03)*2);
|
|
|
|
/* Set the main storage reference bit */
|
|
ARCH_DEP( or_storage_key )( ato, STORKEY_REF );
|
|
|
|
/* Authorization fails if the specified bit (either X'80' or
|
|
X'40' of the 2 bit authority table entry) is zero */
|
|
if ((ate & atemask) == 0)
|
|
return true;
|
|
|
|
/* Exit with successful return code */
|
|
return false;
|
|
|
|
/* Conditions which always cause program check */
|
|
auth_addr_excp:
|
|
regs->program_interrupt( regs, PGM_ADDRESSING_EXCEPTION );
|
|
UNREACHABLE_CODE( return false );
|
|
}
|
|
#endif /* defined( FEATURE_DUAL_ADDRESS_SPACE ) */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* maddr_l */
|
|
/* PRIMARY DAT TLB LOOKUP FUNCTION */
|
|
/*-------------------------------------------------------------------*/
|
|
/* For compatibility this function is usually */
|
|
/* invoked using the MADDRL macro in feature.h */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* Convert logical address to absolute address. This is the DAT */
|
|
/* logic that does an accelerated TLB lookup to return the prev- */
|
|
/* iously determined value from an earlier translation for this */
|
|
/* logical address. It performs a series of checks to ensure the */
|
|
/* values that were used in the previous translation (the results */
|
|
/* of which are in the corresponding TLB entry) haven't changed */
|
|
/* for the current address being translated. If any of the cond- */
|
|
/* itions have changed (i.e. if any of the comparisons fail) then */
|
|
/* the TLB cannot be used (TLB miss) and "logical_to_main_l" is */
|
|
/* called to perform a full address translation. Otherwise if all */
|
|
/* of the conditions are still true (nothing has changed from the */
|
|
/* the last time we translated this address), then the previously */
|
|
/* translated address from the TLB is returned instead (TLB hit). */
|
|
/* */
|
|
/* PLEASE NOTE that the address that is retrieved from the TLB is */
|
|
/* an absolute address from the Hercules guest's point of view but */
|
|
/* the address RETURNED TO THE CALLER is a Hercules host address */
|
|
/* pointing to MAINSTOR that Hercules can then directly use. */
|
|
/* */
|
|
/* Input: */
|
|
/* */
|
|
/* addr Logical address to be translated */
|
|
/* len Length of data access for PER SA purpose */
|
|
/* arn Access register number or the special value: */
|
|
/* USE_INST_SPACE */
|
|
/* USE_REAL_ADDR */
|
|
/* USE_PRIMARY_SPACE */
|
|
/* USE_SECONDARY_SPACE */
|
|
/* USE_HOME_SPACE */
|
|
/* USE_ARMODE + access register number */
|
|
/* An access register number ORed with the special */
|
|
/* value USE_ARMODE forces this routine to use AR-mode */
|
|
/* address translation regardless of the PSW address- */
|
|
/* space control setting. */
|
|
/* regs Pointer to the CPU register context */
|
|
/* acctype Type of access requested: READ, WRITE, INSTFETCH, */
|
|
/* LRA, IVSK, TPROT, STACK, PTE, LPTEA */
|
|
/* akey Bits 0-3=access key, 4-7=zeroes */
|
|
/* */
|
|
/* Returns: */
|
|
/* */
|
|
/* If successful, a directly usable guest absolute storage */
|
|
/* MAINADDR address. */
|
|
/* */
|
|
/* Otherwise if the logical address (as a result of having */
|
|
/* to call logical_to_main_l due to a TLB miss) causes an */
|
|
/* addressing, protection, or translation exception then a */
|
|
/* program check is generated and the function does not return. */
|
|
/*-------------------------------------------------------------------*/
|
|
inline BYTE* ARCH_DEP( maddr_l )
|
|
( VADR addr, size_t len, const int arn, REGS* regs, const int acctype, const BYTE akey )
|
|
{
|
|
/* Note: ALL of the below conditions must be true for a TLB hit
|
|
to occur. If ANY of them are false, then it's a TLB miss,
|
|
requiring us to then perform a full DAT address translation.
|
|
|
|
Note too that on the grand scheme of things the order/sequence
|
|
of the below tests (if statements) is completely unimportant
|
|
since ALL conditions must be checked anyway in order for a hit
|
|
to occur, and it doesn't matter that a miss tests a few extra
|
|
conditions since it's going to do a full translation anyway!
|
|
(which is many, many instructions)
|
|
*/
|
|
|
|
int aea_crn = (arn >= USE_ARMODE) ? 0 : regs->AEA_AR( arn );
|
|
U16 tlbix = TLBIX( addr );
|
|
BYTE *maddr = NULL;
|
|
|
|
/* Non-zero AEA Control Register number? */
|
|
if (aea_crn)
|
|
{
|
|
/* Same Addess Space Designator as before? */
|
|
/* Or if not, is address in a common segment? */
|
|
if (0
|
|
|| (regs->CR( aea_crn ) == regs->tlb.TLB_ASD( tlbix ))
|
|
|| (regs->AEA_COMMON( aea_crn ) & regs->tlb.common[ tlbix ])
|
|
)
|
|
{
|
|
/* Storage Key zero? */
|
|
/* Or if not, same Storage Key as before? */
|
|
if (0
|
|
|| akey == 0
|
|
|| akey == regs->tlb.skey[ tlbix ]
|
|
)
|
|
{
|
|
/* Does the page address match the one in the TLB? */
|
|
/* (does a TLB entry exist for this page address?) */
|
|
if (
|
|
((addr & TLBID_PAGEMASK) | regs->tlbID)
|
|
==
|
|
regs->tlb.TLB_VADDR( tlbix )
|
|
)
|
|
{
|
|
/* Is storage being accessed same way as before? */
|
|
if (acctype & regs->tlb.acc[ tlbix ])
|
|
{
|
|
/*------------------------------------------*/
|
|
/* TLB hit: use previously translated value */
|
|
/*------------------------------------------*/
|
|
|
|
if (acctype & ACC_CHECK)
|
|
regs->dat.storkey = regs->tlb.storkey[ tlbix ];
|
|
|
|
maddr = MAINADDR( regs->tlb.main[tlbix], addr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------*/
|
|
/* TLB miss: do full address translation */
|
|
/*---------------------------------------*/
|
|
if (!maddr)
|
|
maddr = ARCH_DEP( logical_to_main_l )( addr, arn, regs, acctype, akey, len );
|
|
|
|
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
if (FACILITY_ENABLED( 073_TRANSACT_EXEC, regs ))
|
|
{
|
|
/* SA22-7832-12 Principles of Operation, page 5-99:
|
|
|
|
"Storage accesses for instruction and DAT- and ART-
|
|
table fetches follow the non-transactional rules."
|
|
*/
|
|
if (0
|
|
|| !regs
|
|
|| !regs->txf_tnd
|
|
|| arn == USE_INST_SPACE /* Instruction fetching */
|
|
|| arn == USE_REAL_ADDR /* Address translation */
|
|
)
|
|
return maddr;
|
|
|
|
/* Quick exit if NTSTG call */
|
|
if (regs->txf_NTSTG)
|
|
{
|
|
regs->txf_NTSTG = false;
|
|
return maddr;
|
|
}
|
|
|
|
/* Translate to alternate TXF address */
|
|
maddr = TXF_MADDRL( addr, len, arn, regs, acctype, maddr );
|
|
}
|
|
#endif
|
|
|
|
return maddr;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* We only need to compile this header ONCE for each architecture! */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#if ARCH_370_IDX == ARCH_IDX
|
|
#define DID_370_DAT_H
|
|
#endif
|
|
|
|
#if ARCH_390_IDX == ARCH_IDX
|
|
#define DID_390_DAT_H
|
|
#endif
|
|
|
|
#if ARCH_900_IDX == ARCH_IDX
|
|
#define DID_900_DAT_H
|
|
#endif
|
|
|
|
#endif // #if (ARCH_xxx_IDX == ARCH_IDX && !defined( DID_xxx_DAT_H )) ...
|
|
|
|
/* end of DAT.H */
|