📄 r4k_cp0.c
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees * of Leland Stanford Junior University. * * This file is part of the SimOS distribution. * See LICENSE file for terms of the license. * *//**************************************************************** * r4k_cp0.c * * TranslateVirtual manages the simulator's TLB and EXCEPTION allows * normal exception handling. The R4000 tlb consists of 48 entries, * VPN2 subblocking factor = 2, thus there are * Two pages mapped to one VPN. Lo0 is even, Lo1 is odd. * All comments have been slightly revised with the most important * changes noted by the R4000 CHANGED >> syntax. * * $Author: bosch $ * $Date: 1998/02/10 00:31:55 $ * *****************************************************************/#include <stdlib.h>#include "mipsy.h"#include "cpu.h"#include "cpu_state.h"#include "sim_error.h"#include "cp0.h"#include "simmisc.h"#include "cpu_interface.h"#include "hw_events.h"#include "registry.h"#include "machine_params.h"#include "tcl_init.h"#include "mips_arch.h"#include "cpu_stats.h"#include "trace.h"#ifdef MIPSY_MXS#include "ms.h"#endif/* NOTE: the following is included only because of OS_IEC_MAGICERR. * This should be fixed! */#include "machine_defs.h"#include "simmagic.h" /* to raise the interrupt line on r4k timing mech. */#include "simmisc.h" /* simlocks for raising i bits XXXXXX */ /* Local CP0 functions */static void UTLB_EXCEPTION(CPUState *, int);static void UpdateCPUMode(CPUState *P);static Result DoTLBWrite(CPUState *P, int);static void CompareWritten(CPUState *P);static void CountWritten(CPUState *P);static void ExceptionReturn(CPUState *P);static void ProbeTLB(CPUState *P);static void ReadTLBEntry(CPUState *P);static Result WriteRandomTLBEntry(CPUState *P);static Result WriteTLBEntry(CPUState *P);static void WriteC0Register(CPUState *P, int c0RegNum, Reg value, int is64bit);static Cp0RegControl cp0RegCtl[NUM_CP0_REGS] = CP0_REG_CONTROL_ARRAY;/* Useful macros */#if defined(SIM_MIPS32)#define ANY_HIGH32_BITS(_vAddr) (0)#define HAS_BAD_VADDR_BITS(_vAddr) (0)#define REGION_COMPARE(_a,_b) (1)#define IN_32BIT_MODE(_p) (1)#define ONLY_4K_PAGE_SIZE /* 32bit only needs 4K pages */#define IS_R10000(P) 0#else#define ANY_HIGH32_BITS(_vAddr) ((_vAddr) >> 32)#define _BAD_VADDR_MASK (~(((0x1LL << VA_VALID_BITS)-1)|(0x3LL<<62)))#define HAS_BAD_VADDR_BITS(_vAddr) ((_vAddr) & _BAD_VADDR_MASK)#define REGION_COMPARE(_a,_b) ((_a) == (_b))#define IN_32BIT_MODE(_p) ((_p)->is32bitMode)/* * Is the R10000 TLB? */#define IS_R10000(P) ((P)->numTlbEntries == 64)#endif#ifdef ONLY_4K_PAGE_SIZE#define ComputeTlbEntrySize(_pgmask) (0)#elsestatic unsigned char ComputeTlbEntrySize(uint pgMsk);#endif#if defined(SIM_MIPS32)#define TLBHash(_vpn2,_region, _asid) TLBHash32BitOnly(_vpn2,_asid)/***************************************************************** * Hash function for converting a virtual page number and ASID * into an entry in the hash table. Hash function of R3000 == * hash function of R4000. Hash table stores chained TLB entry * numbers at each bucket, thus decreasing translation time. * R4000 COMMENTS >> In all calls to this function, the vpn is * actually vpn2. (you want both vpn's to map to the same index!) *****************************************************************/static int TLBHash32BitOnly(register VPN vpn2, register ASID asid){ return ((vpn2 ^ (vpn2 >> (NUM_VPN_BITS - LOG_TLB_HASH_SIZE)) ^ (asid << (LOG_TLB_HASH_SIZE - NUM_ASID_BITS))) % TLB_HASH_SIZE);}#else#define TLBHash(_vpn2,_region, _asid) TLBHash64bit(_vpn2,_region, _asid)/***************************************************************** * Hash function for converting a virtual page number and ASID * into an entry in the hash table. * Hash table stores chained TLB entry * numbers at each bucket, thus decreasing translation time. *****************************************************************/static int TLBHash64bit(register VPN vpn2, register uint region, register ASID asid){ return ((vpn2 ^ (vpn2 >> (NUM_VPN2_BITS - LOG_TLB_HASH_SIZE)) ^ (vpn2 >> (32 - (NUM_OFFSET_BITS+1) - LOG_TLB_HASH_SIZE)) ^ region ^ (asid << (LOG_TLB_HASH_SIZE - NUM_ASID_BITS))) % TLB_HASH_SIZE);}#endif/***************************************************************** * The TLB indices are stored in a hash table which maps an odd/even * pair of pages to an entry. * R4000 COMMENTS >> use IS_GLOBAL_HI, GET_VPN2. In the future will * worry about the PageMask register. Right now pages 4k. *****************************************************************/void MipsyInitTLBHashTable(CPUState *P){ int i; int hashNum; UpdateCPUMode(P); /* Set up the hash bucket list headers */ for (i=0; i < TLB_HASH_SIZE; i++) { List_Init(&(P->tlbIndexHeaders[i])); } for (i=0; i < P->numTlbEntries; i++) { List_InitElement(&(P->indexList[i].links)); P->indexList[i].index = i; P->indexList[i].onList = 0; } /* Reconstruct hash table information for when you want to * directly jump into mipsy after accumulating some state. * I am assuming that a completely blank TLB entry is what * it looks like at startup. */ for (i=0; i < P->numTlbEntries; i++) { if (IS_UNMAPPED_TLBHI(P->tlbEntry[i].Hi)) { continue; } if(IS_GLOBAL_HI(P->tlbEntry[i].Hi)) { hashNum = TLBHash(GET_VPN2(P->tlbEntry[i].Hi), GET_REGION(P->tlbEntry[i].Hi), 0); } else { hashNum = TLBHash(GET_VPN2( P->tlbEntry[i].Hi), GET_REGION(P->tlbEntry[i].Hi), GET_ASID( P->tlbEntry[i].Hi)); } List_Insert(&(P->indexList[i].links), LIST_ATFRONT(&(P->tlbIndexHeaders[hashNum]))); P->indexList[i].onList = 1; P->tlbEntrySize[i] = ComputeTlbEntrySize(P->tlbEntry[i].PgMsk); } P->pcVPNcache = 0;}static struct { Reg mask; uint loBit; uint offset_mask;} PgSz[TLBPGMASK_NUMSIZES] = { { ~(TLBPGMASK_4K>>13), (1 << 12), 4*1024-1}, { ~(TLBPGMASK_16K>>13), (1 << 14), 16*1024-1}, { ~(TLBPGMASK_64K>>13), (1 << 16), 64*1024-1}, { ~(TLBPGMASK_256K>>13), (1 << 18), 256*1024-1}, { ~(TLBPGMASK_1M>>13), (1 << 20), 1024*1024-1}, { ~(TLBPGMASK_4M>>13), (1 << 22), 4*1024*1024-1}, { ~(TLBPGMASK_16M>>13), (1 << 24), 16*1024*1024-1},};#ifndef ONLY_4K_PAGE_SIZE#define SZ2MASK(_s) PgSz[(_s)].mask#define SIZES_TO_CHECK TLBPGMASK_NUMSIZES#else#define SZ2MASK(_s) -1#define SIZES_TO_CHECK 1#endif#ifndef ONLY_4K_PAGE_SIZEstatic unsigned char ComputeTlbEntrySize(uint pgMsk){ unsigned char s; uint match = ~(pgMsk >> 13); ASSERT(SIZES_TO_CHECK < (1 << (sizeof(unsigned char)*8))); for (s = 0; s < SIZES_TO_CHECK; s++) { if ((uint)SZ2MASK(s) == match) { return s; } } CPUError("Bad PgMsk 0x%x in ComputeTlbEntrySize\n", pgMsk); return (unsigned char) 0;}#endif /***************************************************************** * Given a vpn and asid, this function returns the index+1 of * the corresponding TLB. It returns 0 if the index is not found. * First probe the hash table under the given asid then probe under * asid 0 (to catch global entries ) ******************************************************************/unsigned TLBLookup(CPUState *P, uint region, VPN vAddrPFN2, ASID realASID){ int hashNum; List_Links* indexPtr; register unsigned idx; register int s; for (s = 0; s < SIZES_TO_CHECK; s++) { VPN vpn; VPN vpnLookup = vAddrPFN2 & SZ2MASK(s); /* Check for pages under this process's true asid */ hashNum = TLBHash(vpnLookup, region, realASID); LIST_FORALL( &(P->tlbIndexHeaders[hashNum]), indexPtr) { idx = ((IndexListLink*)indexPtr)->index; vpn = vAddrPFN2 & SZ2MASK(P->tlbEntrySize[idx]); /* Don't check global bit because if the global bit is set, the * entry is stored under ASID 0 */ if ((vpn == GET_VPN2(P->tlbEntry[idx].Hi)) && (realASID == GET_ASID(P->tlbEntry[idx].Hi)) && REGION_COMPARE(region, GET_REGION(P->tlbEntry[idx].Hi))) { return idx+1; } } /* Now check for global pages, all of which are stored under asid 0 */ hashNum = TLBHash(vpnLookup, region, 0); LIST_FORALL( &(P->tlbIndexHeaders[hashNum]), indexPtr) { idx = ((IndexListLink*)indexPtr)->index; vpn = vAddrPFN2 & SZ2MASK(P->tlbEntrySize[idx]); if ((vpn == GET_VPN2(P->tlbEntry[idx].Hi)) && (IS_GLOBAL_HI(P->tlbEntry[idx].Hi)) && REGION_COMPARE(region,GET_REGION(P->tlbEntry[idx].Hi))) { return idx+1; } } } return 0;}/***************************************************************** * Address translation on R4000. * * input: virtual address * pointer to phys addr * Fully associative search (using hash table) * 1.) User Mode or Supervisor Mode * if MSB == 1 in user or MSB = 0 | 110 * 2.) VPN2 match? * a.) if no VPN2 match and 32 bit-mode, -> TLB Refill. * b.) VPN2 match, Not Global, No ASID match, -> TLB Refill. * c.) VPN2 match, not Global and ASID match, OR Global, then if not valid * -> TLB Invalid. * d.) valid, not dirty, but writing -> TLB Mod. * e.) valid, dirty, or not dirty and not writing, Non_Cacheable == T * then -> Access Main Mem, else Access Cache. Phys addr output. * * Returns FAILURE if an exception occurred telling the CPU to try * this instruction again. tlbFlavor will be set to TLB_BDOOR if this * is a backdoor reference and to TLB_UNCACHED if it is to be uncached. * * tlbFlavor is also used as input to this procedure in an attempt to * keep the argument list down. Coming in it will signal whether or not * this address is an ifetch and if this is a write or a read. * * RPB: The TLB_PREFETCH flavor is used to indicate that the translation * is being performed for a prefetch. When a prefetch translation fails,ii * no exception is taken -- we simply return failure and the prefetch is * dropped. * *****************************************************************/Result TranslateVirtual(CPUState *P, VA vAddr, PA *pAddr, uint *tlbFlavor, void **bdoorAddr){ uint tlbIndex; ContextReg contextReg; XContextReg xcontextReg; int myASID; bool isIfetch, isPrefetch, writing; uint region; Reg lo_reg; Reg tlbhi; Reg32 sr_reg; Reg VPN2; /* Gather tlbFlavor information */ isIfetch = (bool) TLB_IS_IFETCH(*tlbFlavor); isPrefetch = (bool) TLB_IS_PREFETCH(*tlbFlavor); writing = (bool) TLB_IS_WRITE(*tlbFlavor); /* Setting this to 0 means that by this is not a backdoor reference */ *tlbFlavor = 0; myASID = GET_ASID(P->CP0[C0_TLBHI]); region = GET_REGION(vAddr); sr_reg = (Reg32)(P->CP0[C0_SR]); /* * Handle the case of unmapped addresses and address errors. Although * we assume most address will hit in the TLB we still need to do * illegal address detection because the TLB lookup doesn't catch them * call (i.e. bit 50 being set in user mode). * We do the check by region starting with the user region. */ if (region == 0) { /* Make sure the address is with acceptable range. For the * 32 bit mode this means 32 bits, for 64bit mode this means * impelementation defined bits. */ if (IN_32BIT_MODE(P)) { if (ANY_HIGH32_BITS(vAddr)) goto addrErr; } else { if (HAS_BAD_VADDR_BITS(vAddr)) goto addrErr; if (IS_R10000(P) && !(sr_reg & SR_UX) && ANY_HIGH32_BITS(vAddr)) goto addrErr; } /* KUSEG becomes an uncached space when the ERL bit is set. This is * needed for cache error handling. */ if (sr_reg & SR_ERL) { /* need to add k1base because registry works on kseg1 addresses */ vAddr += K1BASE; goto bdoor; } /* Fall thru to TLB lookup */ } else if (region == 3) { /* Kernel region, no user and limited supervisor */ if (vAddr >= CKSEG0_START_ADDR) { if (!IS_KERNEL_MODE(P)) { /* No user and supervisor limited to single range */ if (sr_reg & SR_KSU_USR) goto addrErr; if ((sr_reg & SR_KSU_SUP) && !IS_SUPERV_SEG(vAddr)) goto addrErr; } if (IS_KSEG0(vAddr)) { if (!IS_KERNEL_MODE(P)) goto addrErr; /* Kernel only */ *pAddr = K0_TO_PHYS(vAddr); /* XXX fix this */ return SUCCESS; } if (IS_KSEG1(vAddr)) goto bdoor; /* Fall thru to TLB lookup */ } else { /* If we got here we better not be in 32bit mode and * there is nothing that the user or supvisor can access. */ if (IN_32BIT_MODE(P) || !IS_KERNEL_MODE(P)) goto addrErr; if (vAddr > XKSEG_END_ADDR) goto addrErr; } /* Fall thru to TLB lookup */#if defined(SIM_MIPS64) } else if (region == 2) { uint cache_algorithm = XKPHYS_CACHE_ALGORITHM(vAddr); /* xkphys - only available in kernel 64bit mode */ if (IN_32BIT_MODE(P) || !IS_KERNEL_MODE(P)) goto addrErr; switch (cache_algorithm) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -