Avoid redundant declarations in every simulator module that uses them and allow compiler to validate consistency of declarations and definitions.
309 lines
11 KiB
C
309 lines
11 KiB
C
/* vax_mmu.c - VAX memory management
|
|
|
|
Copyright (c) 1998-2013, Robert M Supnik
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
29-Nov-13 RMS Reworked unaligned flows
|
|
24-Oct-12 MB Added support for KA620 virtual addressing
|
|
21-Jul-08 RMS Removed inlining support
|
|
28-May-08 RMS Inlined physical memory routines
|
|
29-Apr-07 RMS Added address masking for system page table reads
|
|
22-Sep-05 RMS Fixed declarations (Sterling Garwood)
|
|
30-Sep-04 RMS Comment and formating changes
|
|
19-Sep-03 RMS Fixed upper/lower case linkage problems on VMS
|
|
01-Jun-03 RMS Fixed compilation problem with USE_ADDR64
|
|
|
|
This module contains the instruction simulators for
|
|
|
|
Read - read virtual
|
|
Write - write virtual
|
|
ReadL(P) - read aligned physical longword (physical context)
|
|
WriteL(P) - write aligned physical longword (physical context)
|
|
ReadB(W) - read aligned physical byte (word)
|
|
WriteB(W) - write aligned physical byte (word)
|
|
Test - test acccess
|
|
|
|
zap_tb - clear TB
|
|
zap_tb_ent - clear TB entry
|
|
chk_tb_ent - check TB entry
|
|
set_map_reg - set up working map registers
|
|
*/
|
|
|
|
#include "vax_defs.h"
|
|
#include "vax_mmu.h"
|
|
#include <setjmp.h>
|
|
|
|
int32 d_p0br, d_p0lr; /* dynamic copies */
|
|
int32 d_p1br, d_p1lr; /* altered per ucode */
|
|
int32 d_sbr, d_slr;
|
|
TLBENT stlb[VA_TBSIZE], ptlb[VA_TBSIZE];
|
|
static const int32 cvtacc[16] = { 0, 0,
|
|
TLB_ACCW (KERN)+TLB_ACCR (KERN),
|
|
TLB_ACCR (KERN),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+TLB_ACCW (USER)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCR (KERN)+TLB_ACCR (EXEC),
|
|
TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC),
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV),
|
|
TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV),
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER),
|
|
TLB_ACCW (KERN)+TLB_ACCW (EXEC)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER),
|
|
TLB_ACCW (KERN)+
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER),
|
|
TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER)
|
|
};
|
|
|
|
t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
|
t_stat tlb_reset (DEVICE *dptr);
|
|
const char *tlb_description (DEVICE *dptr);
|
|
|
|
TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat);
|
|
extern int32 ReadIO (uint32 pa, int32 lnt);
|
|
extern void WriteIO (uint32 pa, int32 val, int32 lnt);
|
|
extern int32 ReadReg (uint32 pa, int32 lnt);
|
|
extern void WriteReg (uint32 pa, int32 val, int32 lnt);
|
|
int32 ReadU (uint32 pa, int32 lnt);
|
|
void WriteU (uint32 pa, int32 val, int32 lnt);
|
|
|
|
/* TLB data structures
|
|
|
|
tlb_dev pager device descriptor
|
|
tlb_unit pager units
|
|
pager_reg pager register list
|
|
*/
|
|
|
|
UNIT tlb_unit[] = {
|
|
{ UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) },
|
|
{ UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) }
|
|
};
|
|
|
|
REG tlb_reg[] = {
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE tlb_dev = {
|
|
"TLB", tlb_unit, tlb_reg, NULL,
|
|
2, 16, VA_N_TBI * 2, 1, 16, 32,
|
|
&tlb_ex, &tlb_dep, &tlb_reset,
|
|
NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&tlb_description
|
|
};
|
|
|
|
|
|
/* TLB fill
|
|
|
|
This routine fills the TLB after a tag or access mismatch, or
|
|
on a write if pte<m> = 0. It fills the TLB and returns the
|
|
pte to the caller. On an error, it aborts directly to the
|
|
fault handler in the CPU.
|
|
|
|
If called from map (VAX PROBEx), the error status is returned
|
|
to the caller, and no fault occurs.
|
|
*/
|
|
|
|
#define MM_ERR(param) { \
|
|
if (stat) { \
|
|
*stat = param; \
|
|
return zero_pte; \
|
|
} \
|
|
p1 = MM_PARAM (acc & TLB_WACC, param); \
|
|
p2 = va; \
|
|
ABORT ((param & PR_TNV)? ABORT_TNV: ABORT_ACV); }
|
|
|
|
TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat)
|
|
{
|
|
int32 ptidx = (((uint32) va) >> 7) & ~03;
|
|
int32 tlbpte, ptead, pte, tbi, vpn;
|
|
static TLBENT zero_pte = { 0, 0 };
|
|
|
|
if (va & VA_S0) { /* system space? */
|
|
if (ptidx >= d_slr) /* system */
|
|
MM_ERR (PR_LNV);
|
|
ptead = (d_sbr + ptidx) & PAMASK;
|
|
}
|
|
else {
|
|
if (va & VA_P1) { /* P1? */
|
|
if (ptidx < d_p1lr)
|
|
MM_ERR (PR_LNV);
|
|
ptead = d_p1br + ptidx;
|
|
}
|
|
else { /* P0 */
|
|
if (ptidx >= d_p0lr)
|
|
MM_ERR (PR_LNV);
|
|
ptead = d_p0br + ptidx;
|
|
}
|
|
#if !defined (VAX_620)
|
|
if ((ptead & VA_S0) == 0)
|
|
ABORT (STOP_PPTE); /* ppte must be sys */
|
|
vpn = VA_GETVPN (ptead); /* get vpn, tbi */
|
|
tbi = VA_GETTBI (vpn);
|
|
if (stlb[tbi].tag != vpn) { /* in sys tlb? */
|
|
ptidx = ((uint32) ptead) >> 7; /* xlate like sys */
|
|
if (ptidx >= d_slr)
|
|
MM_ERR (PR_PLNV);
|
|
pte = ReadLP ((d_sbr + ptidx) & PAMASK); /* get system pte */
|
|
#if defined (VAX_780)
|
|
if ((pte & PTE_ACC) == 0) /* spte ACV? */
|
|
MM_ERR (PR_PACV);
|
|
#endif
|
|
if ((pte & PTE_V) == 0) /* spte TNV? */
|
|
MM_ERR (PR_PTNV);
|
|
stlb[tbi].tag = vpn; /* set stlb tag */
|
|
stlb[tbi].pte = cvtacc[PTE_GETACC (pte)] |
|
|
((pte << VA_N_OFF) & TLB_PFN); /* set stlb data */
|
|
}
|
|
ptead = (stlb[tbi].pte & TLB_PFN) | VA_GETOFF (ptead);
|
|
#endif
|
|
}
|
|
pte = ReadL (ptead); /* read pte */
|
|
tlbpte = cvtacc[PTE_GETACC (pte)] | /* cvt access */
|
|
((pte << VA_N_OFF) & TLB_PFN); /* set addr */
|
|
if ((tlbpte & acc) == 0) /* chk access */
|
|
MM_ERR (PR_ACV);
|
|
if ((pte & PTE_V) == 0) /* check valid */
|
|
MM_ERR (PR_TNV);
|
|
if (acc & TLB_WACC) { /* write? */
|
|
if ((pte & PTE_M) == 0)
|
|
WriteL (ptead, pte | PTE_M);
|
|
tlbpte = tlbpte | TLB_M; /* set M */
|
|
}
|
|
vpn = VA_GETVPN (va);
|
|
tbi = VA_GETTBI (vpn);
|
|
if ((va & VA_S0) == 0) { /* process space? */
|
|
ptlb[tbi].tag = vpn; /* store tlb ent */
|
|
ptlb[tbi].pte = tlbpte;
|
|
return ptlb[tbi];
|
|
}
|
|
stlb[tbi].tag = vpn; /* system space */
|
|
stlb[tbi].pte = tlbpte; /* store tlb ent */
|
|
return stlb[tbi];
|
|
}
|
|
|
|
/* Utility routines */
|
|
|
|
void set_map_reg (void)
|
|
{
|
|
d_p0br = P0BR & ~03;
|
|
d_p1br = (P1BR - 0x800000) & ~03; /* VA<30> >> 7 */
|
|
d_sbr = (SBR - 0x1000000) & ~03; /* VA<31> >> 7 */
|
|
d_p0lr = (P0LR << 2);
|
|
d_p1lr = (P1LR << 2) + 0x800000; /* VA<30> >> 7 */
|
|
d_slr = (SLR << 2) + 0x1000000; /* VA<31> >> 7 */
|
|
}
|
|
|
|
/* Zap process (0) or whole (1) tb */
|
|
|
|
void zap_tb (int stb)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < VA_TBSIZE; i++) {
|
|
ptlb[i].tag = ptlb[i].pte = -1;
|
|
if (stb)
|
|
stlb[i].tag = stlb[i].pte = -1;
|
|
}
|
|
}
|
|
|
|
/* Zap single tb entry corresponding to va */
|
|
|
|
void zap_tb_ent (uint32 va)
|
|
{
|
|
int32 tbi = VA_GETTBI (VA_GETVPN (va));
|
|
|
|
if (va & VA_S0)
|
|
stlb[tbi].tag = stlb[tbi].pte = -1;
|
|
else ptlb[tbi].tag = ptlb[tbi].pte = -1;
|
|
}
|
|
|
|
/* Check for tlb entry corresponding to va */
|
|
|
|
t_bool chk_tb_ent (uint32 va)
|
|
{
|
|
int32 vpn = VA_GETVPN (va);
|
|
int32 tbi = VA_GETTBI (vpn);
|
|
TLBENT xpte;
|
|
|
|
xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi];
|
|
if (xpte.tag == vpn)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* TLB examine */
|
|
|
|
t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
int32 tlbn = uptr - tlb_unit;
|
|
uint32 idx = (uint32) addr >> 1;
|
|
|
|
if (idx >= VA_TBSIZE)
|
|
return SCPE_NXM;
|
|
if (addr & 1)
|
|
*vptr = ((uint32) (tlbn? stlb[idx].pte: ptlb[idx].pte));
|
|
else *vptr = ((uint32) (tlbn? stlb[idx].tag: ptlb[idx].tag));
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* TLB deposit */
|
|
|
|
t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
int32 tlbn = uptr - tlb_unit;
|
|
uint32 idx = (uint32) addr >> 1;
|
|
|
|
if (idx >= VA_TBSIZE)
|
|
return SCPE_NXM;
|
|
if (addr & 1) {
|
|
if (tlbn) stlb[idx].pte = (int32) val;
|
|
else ptlb[idx].pte = (int32) val;
|
|
}
|
|
else {
|
|
if (tlbn) stlb[idx].tag = (int32) val;
|
|
else ptlb[idx].tag = (int32) val;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* TLB reset */
|
|
|
|
t_stat tlb_reset (DEVICE *dptr)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < VA_TBSIZE; i++)
|
|
stlb[i].tag = ptlb[i].tag = stlb[i].pte = ptlb[i].pte = -1;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *tlb_description (DEVICE *dptr)
|
|
{
|
|
return "translation buffer";
|
|
}
|