⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 r4k_cp0.c

📁 一个用在mips体系结构中的操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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 + -