📄 mem_control.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. * *//**************************************************************** * mem_control.c * * Author: $Author: bosch $ * Date: $Date: 1998/02/10 00:30:41 $ *****************************************************************//* include the sim stuff first so we don't expand our macros in their code */#include <stdio.h>#include <unistd.h>#include "simmisc.h"#include "embra.h"#include "mem_control.h"#include "qc.h"#include "cache.h"#include "debug.h"#include "driver.h"#include "cp0.h"#include "clock.h"#include "main_run.h"#include "tc_coherence.h"#include "stats.h"#include "callout.h"#include "main_run.h"#include "tc.h"#include "annotations.h"PA mem_translate( int cpuNum, VA vAddr ){ PA pAddr; int status;#ifdef wrongl_place /* * This is definitively not the right place to do this. * As a matter of fact, we should get rid of sim_misc.first.... * altogether. * (bugnion) */ /* Should I enter the debugger? -- Since this is done in Periodic_Callout,*/ /* This is really just to handle cpus in the prom slave loop */ if( sim_misc.first_cpu_into_debugger != -1 ) { Embra_Collect_Processes_For_Debug(cpuNum); }#endif /* Since this is the start of a basic block, we are not in the delay slot */ status = Em_TranslateVirtual( cpuNum, vAddr, &pAddr, ACT_IREAD); if( status == BACKDOOR_CODE ) { /* Assume this is a call. Execute it on the real CPU. This * assumes all backdoor calls take less than 4 arguments. */ /* If we are counting time for the translator, stop */ STAT_TIMER_STOP( trans_timer ); /* Set this global ONLY for backdoor calls where we don't want to */ /* force a cpuNum parameter */ /* curr_cpu = cpuNum; */ curEmp = &EMP[cpuNum]; STAT_INC( backdoor_calls ); if( !EMP[cpuNum].outOfSlaveLoop ) { /* This CPU has not been kicked yet -- it's in the PROM */ uint launchAddr = (uint)sim_misc.launchAddr[cpuNum]; if( !embra.MPinUP ) if(!launchAddr) sginap(20); /* XXX ??? */ /* Get master's cycle count */ EMP[cpuNum].cycleCount = EMP[0].cycleCount; /* Make time go foward */ EMP[cpuNum].cycleCountdown = 0; /* XXX - non-backdoor address is launch, backdoor address is call */ if( IS_BACKDOOR( launchAddr ) ) { int64 result; /* Do call */ result = ((int64 (*)(int,int,int,int))launchAddr) ( sim_misc.launchArg[cpuNum][0], sim_misc.launchArg[cpuNum][1], sim_misc.launchArg[cpuNum][2], sim_misc.launchArg[cpuNum][3] ); if( result == SLAVELOOP_CONTINUE ) { /* signal init function done */ sim_misc.launchAddr[cpuNum] = 0; /* clear so will be 0 if not set up on next call */ sim_misc.launchArg[cpuNum][0] = 0; sim_misc.launchArg[cpuNum][1] = 0; sim_misc.launchArg[cpuNum][2] = 0; sim_misc.launchArg[cpuNum][3] = 0; CPUPut("Slave %d returning to launch wait\n",cpuNum ); } } else { if( launchAddr != 0 ) { /* Start emulation */ CPUWarning("Launch slave PC 0x%x\n", launchAddr); /* Note we set PC to RA before returning from here */ EMP[cpuNum].R[31] = launchAddr; EMP[cpuNum].cpuStatus = cpu_running; EMP[cpuNum].R[REG_A0] = sim_misc.launchArg[cpuNum][0]; EMP[cpuNum].R[REG_A1] = sim_misc.launchArg[cpuNum][1]; EMP[cpuNum].R[REG_A2] = sim_misc.launchArg[cpuNum][2]; EMP[cpuNum].R[REG_A3] = sim_misc.launchArg[cpuNum][3]; EMP[cpuNum].outOfSlaveLoop = 1; } } } else { /* Not a Prom call, just do it */ int64 bdoorRetval; ASSERT(pAddr < 0x80000000); bdoorRetval = ((int64 (*)(int,int,int,int))pAddr) (EMP[cpuNum].R[REG_A0], EMP[cpuNum].R[REG_A1], EMP[cpuNum].R[REG_A2], EMP[cpuNum].R[REG_A3]); EMP[cpuNum].R[2] = bdoorRetval >> 32; EMP[cpuNum].R[3] = bdoorRetval & 0xffffffff; } ASSERT( embra.emode == EMBRA_PAGE ||embra.sequential || EMP[cpuNum].outTC ); EMP[cpuNum].PC = EMP[cpuNum].R[31]; ASSERT( (EMP[cpuNum].PC & 0x3) == 0 ); if (embra.MPinUP) { /* NOTE: to get things to boot we need to do a CX after the */ /* slave loop. */ if( !EMP[cpuNum].outOfSlaveLoop ) { EMP[cpuNum].jumpPC = (uint)continue_run_without_chaining; ReenterTC_CX( &EMP[cpuNum] ); ASSERT(0); /* NOT REACHED */ } } ReenterTC( &EMP[cpuNum] ); /* NOT REACHED */ } if( status == EXCEPTION_CODE ) { ReenterTC(&EMP[cpuNum]); ASSERT(0); /* NOT REACHED */ return 0; } return pAddr;#ifdef gone return PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), pAddr);#endif}static int debugOnWatchpoint[SIM_MAXCPUS];/* called on miss in Quickcheck and Physarray * In virtual quickcheck mode we first have to check * the physical quickcheck in phys_mem_ref */MA mem_ref( VA vAddr, EmVQCMemState new_state, int cpuNum ){ int prevTCGenNumber = tcGenNumber; PA pAddr; int status; Em_accesstype act; MA retval; exceptionDuringBackdoor = FALSE; if( VQC_INST(new_state) ) { vAddr = IN_BD(EMP[cpuNum].PC)?CLEAR_BD(EMP[cpuNum].PC)+INST_SIZE: EMP[cpuNum].PC; act = ACT_IREAD; } else { act = VQC_EXCL( new_state ) ? ACT_DWRITE : ACT_DREAD; } /* In cache mode, PQC missed, in page mode, mmu reloc missed */ STAT_PQC(new_state); status = Em_TranslateVirtual( cpuNum, vAddr, &pAddr, act ); /* * I guess that we could check even if the translation fails, but * it's overall faster to do it this way */ if (status==NORMAL_CODE && annWatchpoints == TRUE) { if (VQC_SHARED(new_state)) { EmbraAnnExec(cpuNum,AnnFMLookup(vAddr, ANNFM_LD_TYPE),ANNFM_LD_TYPE); } if (VQC_EXCL(new_state)) { EmbraAnnExec(cpuNum,AnnFMLookup(vAddr, ANNFM_ST_TYPE),ANNFM_ST_TYPE); } } if( status == NORMAL_CODE ) { /* NOTE: this code detects the case where we write code and then */ /* jump to it. In that case we need to downgrade the code so we */ /* can detect future writes. This detection occurs here and in */ /* pc_tc_lookup depending on whether we detect the condition when */ /* we jump to the code, or if we are executing inside a */ /* traslation */ if( embra.emode == EMBRA_PAGE ) { if( VQC_EXCL( new_state ) ) { if( EmbraTCCoherenceCheck( cpuNum, vAddr,pAddr, pAddr+8 ) ) { CPUWarning("Flushing the TC in mem_ref:1 (TC coherence) PC=0x%llx vAddr=0x%llx \n", (Reg64)EMP[cpuNum].PC, (Reg64)vAddr); ReenterTC( &EMP[cpuNum] ); /* NOT REACHED */ } }#ifdef EMBRA_USE_QC64 if( VQC_INST( new_state )) { /* Downgrade page to read/execute so we can detect */ /* writes to it */ qc_downgrade( cpuNum, vAddr, new_state ); }#else if( VQC_INST( new_state ) && (IS_MMU_PROT_WRITE(EMP[cpuNum].mmu[PAGE_NUMBER(vAddr)]) ) ) { /* Downgrade page to read/execute so we can detect */ /* writes to it */ qc_downgrade( cpuNum, vAddr, new_state ); }#endif /* This returns to callout.s */ return PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), pAddr); } else { /* ASSERT(embra.emode == EMBRA_CACHE); */ /* If we are doing cache simulation, make the reference */ if( NUM_CPUS(M_FROM_CPU(cpuNum)) == 1 ) { UPCache_Ref( cpuNum, pAddr, vAddr, new_state ); } else { if( embra.MPinUP ) { MPinUPCache_Ref( cpuNum, pAddr, vAddr, new_state ); } else { MPCache_Ref( cpuNum, pAddr, vAddr, new_state ); } } if( VQC_INST( new_state )){ if (embra.useVQC){ if (VQC_EXCL(EMP[cpuNum].qc_v[ADDR2SLINE(vAddr)])) { /* Downgrade page to read/execute so we can detect */ /* writes to it */ qc_downgrade( cpuNum, vAddr, new_state ); } } else { /* !useVQC */#ifdef EMBRA_USE_QC64 if (1) { /* alway safe to do in QC64 */#else if (IS_MMU_PROT_WRITE(EMP[cpuNum].mmu[PAGE_NUMBER(vAddr)]) ) {#endif /* Downgrade page to read/execute so we can detect */ /* writes to it */ qc_downgrade( cpuNum, vAddr, new_state ); } } } /* This maintains a data structure which allows us to determine */ /* if we are writing to a page which has code in it which we are */ /* exectuing. If such a conflict occurs, we flush the TC and */ /* ReenterTC with the pc value */ /* This is conservative because most stores are not doubles */ if( VQC_EXCL( new_state ) ) { if( EmbraTCCoherenceCheck( cpuNum, vAddr,pAddr, pAddr+8 ) ) { CPUWarning("Flushing the TC in mem_ref:2 (TC coherence) PC=0x%llx vAddr=0x%llx \n", (Reg64)EMP[cpuNum].PC, (Reg64)vAddr); ReenterTC( &EMP[cpuNum] ); /* NOT REACHED */ } } /* And return Zero indicating rewind the QC */ if( embra.sequential ) { /* No need to rewind in MPinUP, if line is stolen during stall, access still suceeds, but sc fails */ return PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), pAddr); } else { return 0; } } } else if (status == BACKDOOR_CODE) { if (tcGenNumber != prevTCGenNumber) { /* * the TC was flushed during the badoor data addressed function * at this point, the RT register has already been set, * so we simply reenter the TC. * Caveat: we better NOT be in a BD. */ if (!exceptionDuringBackdoor) { CPUPrint("EMBRA: %10lld cpu=%d TC flushed on backdoor ref (ok) \n", (uint64) EmbraCpuCycleCount(cpuNum),cpuNum); ASSERT( !IN_BD(EMP[cpuNum].PC)); EMP[cpuNum].PC += INST_SIZE; EMP[cpuNum].cycleCountdown--; } else { ASSERT( !(EMP[cpuNum].CP0[C0_CAUSE] & CAUSE_BD)); EMP[cpuNum].CP0[C0_EPC] += INST_SIZE; /* Don't reexecute on return */ } ReenterTC(&EMP[cpuNum]); } exceptionDuringBackdoor = FALSE; /* * XXX this cast is very important and makes sense. */ return (MA)pAddr; } else { if (status != EXCEPTION_CODE) { CPUWarning("PROBABLY a user-levle SIGSEGV! stats=0x%x when 0x%x (EXCEPTION_CODE) expected.cpu=%d vAddr=0x%x \n", status,EXCEPTION_CODE,cpuNum,vAddr); Em_EXCEPTION(cpuNum, (act==ACT_IREAD) ? EXC_IBE : EXC_DBE, 0); ReenterTC( &EMP[cpuNum] ); /* NOT REACHED */ return 0; } ASSERT( status == EXCEPTION_CODE ); ReenterTC( &EMP[cpuNum] ); /* NOT REACHED */ return 0; }}/* ***************************************************** * EmbraTCCoherenceCheck * *****************************************************/ static int just_flushed;static VA last_pc;static PA last_pAddr;int EmbraTCCoherenceCheck(int cpuNum, VA vAddr, PA pAddr, PA end){ if (TCcoherence_check(PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), pAddr), PHYS_TO_MEMADDR(M_FROM_CPU(cpuNum), end))) { if( just_flushed ) { /* This catches and disallows an infinite loop case */ /* The problem is that the kernel writes say the UTLB miss */ /* handler, then flushed the cache. The cache flush looks */ /* like a self-writing line because we just flushed from */ /* the write. We special case this by checking for vAddr */ /* == 0 */ if( (EMP[cpuNum].PC == last_pc) && (pAddr == last_pAddr) && vAddr ) { CPUWarning("SELF-WRITING LINE code PA 0x%x VA 0x%x vPC 0x%x pPC 0x%x\n", pAddr, vAddr, EMP[cpuNum].PC, K0_TO_PHYS_REMAP(non_excepting_tv(cpuNum, EMP[cpuNum].PC), cpuNum) ); } return 0; } /* This doesn't add appreciable cost, so always maintain it */ em_stats.icache_coherence++; /* CPUWarning("Write to code PA 0x%x VA 0x%x PC 0x%x\n", pAddr, vAddr, EMP[cpuNum].PC ); */ /* Kernel text is overwritten (ex. UTLB miss handler) */ Clear_Translation_State(TCFLUSH_ALL ); last_pc = EMP[cpuNum].PC; last_pAddr = pAddr; just_flushed = 1; if( IN_BD( EMP[cpuNum].PC ) ) { EMP[cpuNum].PC = CLEAR_BD( EMP[cpuNum].PC ); EMP[cpuNum].PC -= INST_SIZE; } ASSERT( (EMP[cpuNum].PC & 0x3) == 0 );#if 0 CPUWarning("TCcoherence_check_code detected conflict at pc=0x%llx. \n", (Reg64)EMP[cpuNum].PC);#endif return 1; } else { just_flushed = 0; return 0; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -