/* * Copyright (c) 2005-2011 Imperas Software Ltd., www.imperas.com * * YOUR ACCESS TO THE INFORMATION IN THIS MODEL IS CONDITIONAL * UPON YOUR ACCEPTANCE THAT YOU WILL NOT USE OR PERMIT OTHERS * TO USE THE INFORMATION FOR THE PURPOSES OF DETERMINING WHETHER * IMPLEMENTATIONS OF THE ARM ARCHITECTURE INFRINGE ANY THIRD * PARTY PATENTS. * * THE LICENSE BELOW EXTENDS ONLY TO USE OF THE SOFTWARE FOR * MODELING PURPOSES AND SHALL NOT BE CONSTRUED AS GRANTING * A LICENSE TO CREATE A HARDWARE IMPLEMENTATION OF THE * FUNCTIONALITY OF THE SOFTWARE LICENSED HEREUNDER. * YOU MAY USE THE SOFTWARE SUBJECT TO THE LICENSE TERMS BELOW * PROVIDED THAT YOU ENSURE THAT THIS NOTICE IS REPLICATED UNMODIFIED * AND IN ITS ENTIRETY IN ALL DISTRIBUTIONS OF THE SOFTWARE, * MODIFIED OR UNMODIFIED, IN SOURCE CODE OR IN BINARY FORM. * * Licensed under an Imperas Modfied Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ovpworld.org/licenses/OVP_MODIFIED_1.0_APACHE_OPEN_SOURCE_LICENSE_2.0.pdf * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * */ // standard header files #include #include #include // VMI header files #include "vmi/vmiMessage.h" #include "vmi/vmiRt.h" #include "vmi/vmiView.h" // model header files #include "armExceptions.h" #include "armExceptionTypes.h" #include "armMessage.h" #include "armStructure.h" #include "armSys.h" #include "armSysRegisters.h" #include "armUtils.h" #include "armVM.h" // // Prefix for messages from this module // #define CPU_PREFIX "ARM_SCS" //////////////////////////////////////////////////////////////////////////////// // UTILITIES //////////////////////////////////////////////////////////////////////////////// // // Return current program counter // inline static Uns32 getPC(armP arm) { return vmirtGetPC((vmiProcessorP)arm); } // // Is the numbered exception enabled? // inline static Bool isEnabled(armP arm, armExceptNum num) { return EX_MASK_GET(arm->xEnable, num); } // // Is the numbered exception pending? // inline static Bool isPending(armP arm, armExceptNum num) { return EX_MASK_GET(arm->xPend, num); } // // Is the numbered exception active? // inline static Bool isActive(armP arm, armExceptNum num) { return EX_MASK_GET(arm->xActive, num); } // // Enable or disabled the numbered exception // inline static void setEnabled(armP arm, armExceptNum num, Bool set) { EX_MASK_SET_V(arm->xEnable, num, set); } // // Set or clear the pending bit for the passed exception // inline static void setPending(armP arm, armExceptNum num, Bool set) { EX_MASK_SET_V(arm->xPend, num, set); } // // Set or clear the active bit for the passed exception // inline static void setActive(armP arm, armExceptNum num, Bool set) { EX_MASK_SET_V(arm->xActive, num, set); } // // Write a value to the passed net // inline static void writeNet(armP arm, Uns32 netHandle, Uns32 value) { if(netHandle) { vmirtWriteNetPort((vmiProcessorP)arm, netHandle, value); } } // // Is the region number valid? // inline static Bool validRegion(armP arm, Uns32 region) { return (region < SCS_FIELD(arm, MPU_TYPE, DREGION)); } //////////////////////////////////////////////////////////////////////////////// // READ AND WRITE CALLBACKS //////////////////////////////////////////////////////////////////////////////// // // System register read callback type // #define ARM_SCS_READFN(_NAME) Uns32 _NAME( \ armP arm, \ armSCSRegId id, \ Uns32 byteOffset \ ) typedef ARM_SCS_READFN((*armSCSReadFn)); // // System register write callback type // #define ARM_SCS_WRITEFN(_NAME) void _NAME( \ armP arm, \ armSCSRegId id, \ Uns32 newValue, \ Uns32 byteOffset \ ) typedef ARM_SCS_WRITEFN((*armSCSWriteFn)); // // Update a system register, preserving read-only bits // #define UPDATE_SCS_MASKED(_P, _R, _NEW) \ SCS_REG_UNS32(_P, _R) = ( \ (SCS_REG_UNS32(_P, _R) & ~SCS_WRITE_MASK_##_R) | \ (_NEW & SCS_WRITE_MASK_##_R) \ ) // // Dummy function to ignore a system register read // static ARM_SCS_READFN(ignoreSysRead) { Uns32 thisPC = getPC(arm); vmiMessage("W", CPU_PREFIX"_ISR", SRCREF_FMT "SCS register read ignored (return 0)", SRCREF_ARGS(arm, thisPC) ); return 0; } // // Dummy function to ignore a system register write // static ARM_SCS_WRITEFN(ignoreSysWrite) { Uns32 thisPC = getPC(arm); vmiMessage("W", CPU_PREFIX"_ISW", SRCREF_FMT "SCS register write ignored", SRCREF_ARGS(arm, thisPC) ); } // // Write MPU_CONTROL // static ARM_SCS_WRITEFN(writeMPU_CONTROL) { // update field, preserving read-only bits Bool oldENABLE = SCS_FIELD(arm, MPU_CONTROL, ENABLE); Bool oldHFNMIENA = SCS_FIELD(arm, MPU_CONTROL, HFNMIENA); Bool oldPRIVDEFENA = SCS_FIELD(arm, MPU_CONTROL, PRIVDEFENA); UPDATE_SCS_MASKED(arm, MPU_CONTROL, newValue); Bool newENABLE = SCS_FIELD(arm, MPU_CONTROL, ENABLE); Bool newHFNMIENA = SCS_FIELD(arm, MPU_CONTROL, HFNMIENA); Bool newPRIVDEFENA = SCS_FIELD(arm, MPU_CONTROL, PRIVDEFENA); // mode switch may be required if((oldENABLE!=newENABLE) || (oldHFNMIENA!=newHFNMIENA)) { armSwitchMode(arm); } // if PRIVDEFENA has been *cleared*, the privileged mode MPU must be // refilled if(oldPRIVDEFENA && !newPRIVDEFENA) { armVMFlushMPUPriv(arm); } } // // Write MPU_RNR // static ARM_SCS_WRITEFN(writeMPU_RNR) { // out-of-range writes to this register are ignored if(validRegion(arm, newValue)) { SCS_REG_UNS32(arm, MPU_RNR) = newValue; } } // // Read MPU_RBAR // static ARM_SCS_READFN(readMPU_RBAR) { Uns32 region = SCS_FIELD(arm, MPU_RNR, REGION); // use union to assemble composed value union {Uns32 u32; SCS_REG_DECL(MPU_RBAR);} u = { armVMReadRBAR(arm, region, True) }; // MPU_RBAR.VALID is RAZ u.MPU_RBAR.VALID = 0; // MPU_RBAR.REGION is an alias of MPU_RNR.REGION u.MPU_RBAR.REGION = SCS_FIELD(arm, MPU_RNR, REGION); // return composed value return u.u32; } // // Write MPU_RBAR // static ARM_SCS_WRITEFN(writeMPU_RBAR) { Uns32 region; // use union to extract value fields union {Uns32 u32; SCS_REG_DECL(MPU_RBAR);} u = {newValue}; // update region if valid and in range, and get the region to update if(u.MPU_RBAR.VALID && validRegion(arm, u.MPU_RBAR.REGION)) { region = SCS_FIELD(arm, MPU_RNR, REGION) = u.MPU_RBAR.REGION; } else { region = SCS_FIELD(arm, MPU_RNR, REGION); } // update the region armVMWriteRBAR(arm, region, True, newValue); } // // Read MPU_RASR // static ARM_SCS_READFN(readMPU_RASR) { // derive region number, allowing for alias offset Uns32 region = SCS_FIELD(arm, MPU_RNR, REGION); return armVMReadRASR(arm, region, True); } // // Write MPU_RASR // static ARM_SCS_WRITEFN(writeMPU_RASR) { // derive region number, allowing for alias offset Uns32 region = SCS_FIELD(arm, MPU_RNR, REGION); armVMWriteRASR(arm, region, True, newValue); } // // Read ICSR // static ARM_SCS_READFN(readICSR) { // use union to assemble composed value union {Uns32 u32; SCS_REG_DECL(ICSR);} u = {0}; // seed exception number fields u.ICSR.VECTACTIVE = PSR_FIELD(arm, exceptNum); u.ICSR.ISRPENDING = arm->pendingInterrupt; u.ICSR.VECTPENDING = arm->pendingException; // seed pending fields u.ICSR.PENDSTSET = isPending(arm, AEN_SysTick); u.ICSR.PENDSVSET = isPending(arm, AEN_PendSV); u.ICSR.NMIPENDSET = isPending(arm, AEN_NMI); // seed RETTOBASE u.ICSR.RETTOBASE = armGetRetToBase(arm); // return composed value return u.u32; } // // Write ICSR // static ARM_SCS_WRITEFN(writeICSR) { // use union to extract value fields union {Uns32 u32; SCS_REG_DECL(ICSR);} u = {newValue}; // raise NMI if(u.ICSR.NMIPENDSET) { setPending(arm, AEN_NMI, 1); } // raise/clear PendSV if(u.ICSR.PENDSVSET) { setPending(arm, AEN_PendSV, 1); } else if(u.ICSR.PENDSVCLR) { setPending(arm, AEN_PendSV, 0); } // raise/clear SysTick if(u.ICSR.PENDSTSET) { setPending(arm, AEN_SysTick, 1); } else if(u.ICSR.PENDSTCLR) { setPending(arm, AEN_SysTick, 0); } // refresh any pending exception state armRefreshPendingException(arm); } // // Read AIRCR // static ARM_SCS_READFN(readAIRCR) { // VECTKEY field has a fixed magic value SCS_FIELD(arm, AIRCR, VECTKEY) = 0xfa05; // return composed value return SCS_REG_UNS32(arm, AIRCR); } // // Write AIRCR // static ARM_SCS_WRITEFN(writeAIRCR) { // use union to extract value fields union {Uns32 u32; SCS_REG_DECL(AIRCR);} u = {newValue}; // update only on magic code match if(u.AIRCR.VECTKEY == 0x05fa) { // update PRIGROUP if required if(SCS_FIELD(arm, AIRCR, PRIGROUP) != u.AIRCR.PRIGROUP) { SCS_FIELD(arm, AIRCR, PRIGROUP) = u.AIRCR.PRIGROUP; armRefreshExecutionPriorityPendingException(arm); } // assert interrupt request if required SCS_FIELD(arm, AIRCR, SYSRESETREQ) = u.AIRCR.SYSRESETREQ; if(SCS_FIELD(arm, AIRCR, SYSRESETREQ)) { writeNet(arm, arm->sysResetReq, 1); } } } // // Write CCR // static ARM_SCS_WRITEFN(writeCCR) { // update field, preserving read-only bits Bool oldUnalignedTrap = SCS_FIELD(arm, CCR, UNALIGN_TRP); UPDATE_SCS_MASKED(arm, CCR, newValue); Bool newUnalignedTrap = SCS_FIELD(arm, CCR, UNALIGN_TRP); // modify blockMask if anligned access trap has changed if(oldUnalignedTrap!=newUnalignedTrap) { // if this is the first time that unaligned accesses have been enabled // or disabled, discard the current code dictionaries (they need to be // regenerated with unaligned access checking enabled in the block mask) if(!arm->checkUnaligned) { vmirtFlushAllDicts((vmiProcessorP)arm); arm->checkUnaligned = True; } // refresh block mask armSetBlockMask(arm); } } // // Write CFSR // NOTE: register has W1C (write-1-to-clear) semantics // static ARM_SCS_WRITEFN(writeCFSR) { SCS_REG_UNS32(arm, CFSR) &= ~newValue; } // // Write HFSR // NOTE: register has W1C (write-1-to-clear) semantics // static ARM_SCS_WRITEFN(writeHFSR) { SCS_REG_UNS32(arm, HFSR) &= ~newValue; } // // Write CPACR register value // static ARM_SCS_WRITEFN(writeCPACR) { // get original register value and writable mask Uns32 oldValue = SCS_REG_UNS32(arm, CPACR); Uns32 mask = SCS_MASK_UNS32(arm, CPACR); // set the new register value, allowing for writable bits SCS_REG_UNS32(arm, CPACR) = ((oldValue&~mask) | (newValue&mask)); // update blockMask to reflect enabled or disabled coprocessors armSetBlockMask(arm); } // // Write FPCCR register value // static ARM_SCS_WRITEFN(writeFPCCR) { // update register, preserving read-only bits Uns32 oldBMValue = SCS_REG_UNS32(arm, FPCCR) & SCS_BLOCKMASK_FPCCR; UPDATE_SCS_MASKED(arm, FPCCR, newValue); Uns32 newBMValue = SCS_REG_UNS32(arm, FPCCR) & SCS_BLOCKMASK_FPCCR; // refresh blockMask if any bits included in block mask have changed if(oldBMValue!=newBMValue) { armSetBlockMask(arm); } } // // Read SYST_CSR // static ARM_SCS_READFN(readSYST_CSR) { return armReadSYST_CSR(arm); } // // Write SYST_CSR // static ARM_SCS_WRITEFN(writeSYST_CSR) { armWriteSYST_CSR(arm, newValue); } // // Write SYST_RVR // static ARM_SCS_WRITEFN(writeSYST_RVR) { armWriteSYST_RVR(arm, newValue); } // // Read SYST_CVR // static ARM_SCS_READFN(readSYST_CVR) { return armReadSYST_CVR(arm); } // // Write SYST_CVR // NOTE: value written is always zero (supplied value is ignored) // static ARM_SCS_WRITEFN(writeSYST_CVR) { armWriteSYST_CVR(arm, 0); } // // Read SHCSR // static ARM_SCS_READFN(readSHCSR) { // use union to assemble composed value union {Uns32 u32; SCS_REG_DECL(SHCSR);} u = {0}; // seed enable fields u.SHCSR.USGFAULTENA = isEnabled(arm, AEN_UsageFault); u.SHCSR.BUSFAULTENA = isEnabled(arm, AEN_BusFault); u.SHCSR.MEMFAULTENA = isEnabled(arm, AEN_MemManage); // seed pending fields u.SHCSR.SVCALLPENDED = isPending(arm, AEN_SVCall); u.SHCSR.USGFAULTPENDED = isPending(arm, AEN_UsageFault); u.SHCSR.BUSFAULTPENDED = isPending(arm, AEN_BusFault); u.SHCSR.MEMFAULTPENDED = isPending(arm, AEN_MemManage); // seed active fields u.SHCSR.SYSTICKACT = isActive(arm, AEN_SysTick); u.SHCSR.PENDSVACT = isActive(arm, AEN_PendSV); u.SHCSR.MONITORACT = isActive(arm, AEN_DebugMonitor); u.SHCSR.SVCALLACT = isActive(arm, AEN_SVCall); u.SHCSR.USGFAULTACT = isActive(arm, AEN_UsageFault); u.SHCSR.BUSFAULTACT = isActive(arm, AEN_BusFault); u.SHCSR.MEMFAULTACT = isActive(arm, AEN_MemManage); // return composed value return u.u32; } // // Write SHCSR // static ARM_SCS_WRITEFN(writeSHCSR) { // use union to extract value fields union {Uns32 u32; SCS_REG_DECL(SHCSR);} u = {newValue}; // extract enable fields setEnabled(arm, AEN_UsageFault, u.SHCSR.USGFAULTENA); setEnabled(arm, AEN_BusFault, u.SHCSR.BUSFAULTENA); setEnabled(arm, AEN_MemManage, u.SHCSR.MEMFAULTENA); // extract pending fields setPending(arm, AEN_SVCall, u.SHCSR.SVCALLPENDED); setPending(arm, AEN_UsageFault, u.SHCSR.USGFAULTPENDED); setPending(arm, AEN_BusFault, u.SHCSR.BUSFAULTPENDED); setPending(arm, AEN_MemManage, u.SHCSR.MEMFAULTPENDED); // extract active fields setActive(arm, AEN_SysTick, u.SHCSR.SYSTICKACT); setActive(arm, AEN_PendSV, u.SHCSR.PENDSVACT); setActive(arm, AEN_DebugMonitor, u.SHCSR.MONITORACT); setActive(arm, AEN_SVCall, u.SHCSR.SVCALLACT); setActive(arm, AEN_UsageFault, u.SHCSR.USGFAULTACT); setActive(arm, AEN_BusFault, u.SHCSR.BUSFAULTACT); setActive(arm, AEN_MemManage, u.SHCSR.MEMFAULTACT); // refresh any pending exception state armRefreshPendingException(arm); } // // Common routine for SHPR/NVIC_IPR read // static Uns32 readPriority(armP arm, Uns32 byteOffset) { if(byteOffsetexceptNum) { return *(Uns32 *)(&arm->xPriority[byteOffset]); } else { return 0; } } // // Common routine for SHPR/NVIC_IPR write // static void writePriority(armP arm, Uns32 byteOffset, Uns32 newValue) { if(byteOffsetexceptNum) { // priority value must be masked to the supported bits newValue &= arm->priorityMask; // get indexed value in priority array Uns32 *valueP = (Uns32 *)(&arm->xPriority[byteOffset]); // update priority entry if required if(newValue != *valueP) { *valueP = newValue; armRefreshExecutionPriorityPendingException(arm); } } } // // Read SHPR1 // static ARM_SCS_READFN(readSHPR1) { return readPriority(arm, 4); } // // Write SHPR1 // static ARM_SCS_WRITEFN(writeSHPR1) { writePriority(arm, 4, newValue & SCS_WRITE_MASK_SHPR1); } // // Read SHPR2 // static ARM_SCS_READFN(readSHPR2) { return readPriority(arm, 8); } // // Write SHPR2 // static ARM_SCS_WRITEFN(writeSHPR2) { writePriority(arm, 8, newValue & SCS_WRITE_MASK_SHPR2); } // // Read SHPR3 // static ARM_SCS_READFN(readSHPR3) { return readPriority(arm, 12); } // // Write SHPR3 // static ARM_SCS_WRITEFN(writeSHPR3) { writePriority(arm, 12, newValue & SCS_WRITE_MASK_SHPR3); } // // Write STIR // NOTE: this can be made accessible to user mode // static ARM_SCS_WRITEFN(writeSTIR) { // use union to extract value fields union {Uns32 u32; SCS_REG_DECL(STIR);} u = {newValue}; Uns32 exceptionNum = u.STIR.INTID + AEN_ExternalInt0; // ensure interrupt is within bounds if(exceptionNumexceptNum) { // raise the exception setPending(arm, exceptionNum, 1); // refresh any pending exception state armRefreshPendingException(arm); } } // // Return a composed value from an interrupt mask register // static Uns32 readIntReg(armP arm, Uns32 *reg, Uns32 byteOffset) { Uns32 wordOffset = byteOffset*4; Uns32 result = 0; // validate interrupt register is in bounds if(wordOffsetexceptMaskNum) { // get lower half of the result result = reg[wordOffset] >> AEN_ExternalInt0; // upper half is extracted only if this is not the last entry if(wordOffset!=(arm->exceptMaskNum-1)) { result |= reg[wordOffset+1] << (32-AEN_ExternalInt0); } } return result; } // // Add one-bits from the passed value into the register // static void setIntReg(armP arm, Uns32 *reg, Uns32 byteOffset, Uns32 newValue) { Uns32 wordOffset = byteOffset*4; // validate interrupt register is in bounds if(wordOffsetexceptMaskNum) { // set lower half of the result reg[wordOffset] |= (newValue << AEN_ExternalInt0); // upper half is set only if this is not the last entry if(wordOffset!=(arm->exceptMaskNum-1)) { reg[wordOffset+1] |= (newValue >> (32-AEN_ExternalInt0)); } // refresh any pending exception state armRefreshPendingException(arm); } } // // Remove one-bits from the passed value into the register // static void clearIntReg(armP arm, Uns32 *reg, Uns32 byteOffset, Uns32 newValue) { Uns32 wordOffset = byteOffset*4; // validate interrupt register is in bounds if(wordOffsetexceptMaskNum) { // set lower half of the result reg[wordOffset] &= ~(newValue << AEN_ExternalInt0); // upper half is set only if this is not the last entry if(wordOffset!=(arm->exceptMaskNum-1)) { reg[wordOffset+1] &= ~(newValue >> (32-AEN_ExternalInt0)); } // refresh any pending exception state armRefreshPendingException(arm); } } // // Read NVIC_ISER // static ARM_SCS_READFN(readNVIC_ISER) { return readIntReg(arm, arm->xEnable, byteOffset); } // // Write NVIC_ISER // static ARM_SCS_WRITEFN(writeNVIC_ISER) { setIntReg(arm, arm->xEnable, byteOffset, newValue); } // // Read NVIC_ICER // static ARM_SCS_READFN(readNVIC_ICER) { return readIntReg(arm, arm->xEnable, byteOffset); } // // Write NVIC_ICER // static ARM_SCS_WRITEFN(writeNVIC_ICER) { clearIntReg(arm, arm->xEnable, byteOffset, newValue); } // // Read NVIC_ISPR // static ARM_SCS_READFN(readNVIC_ISPR) { return readIntReg(arm, arm->xPend, byteOffset); } // // Write NVIC_ISPR // static ARM_SCS_WRITEFN(writeNVIC_ISPR) { setIntReg(arm, arm->xPend, byteOffset, newValue); } // // Read NVIC_ICPR // static ARM_SCS_READFN(readNVIC_ICPR) { return readIntReg(arm, arm->xPend, byteOffset); } // // Write NVIC_ICPR // static ARM_SCS_WRITEFN(writeNVIC_ICPR) { clearIntReg(arm, arm->xPend, byteOffset, newValue); } // // Read NVIC_IABR // static ARM_SCS_READFN(readNVIC_IABR) { return readIntReg(arm, arm->xActive, byteOffset); } // // Read NVIC_IPR // static ARM_SCS_READFN(readNVIC_IPR) { return readPriority(arm, byteOffset+AEN_ExternalInt0); } // // Write NVIC_IPR // static ARM_SCS_WRITEFN(writeNVIC_IPR) { writePriority(arm, byteOffset+AEN_ExternalInt0, newValue); } //////////////////////////////////////////////////////////////////////////////// // RESET AND INITIALIZATION //////////////////////////////////////////////////////////////////////////////// // // Macro used to set initial value of read-only system register from configuration // #define INITIALIZE_SCS_REG(_P, _N) \ SCS_REG_STRUCT(_P, _N) = SCS_REG_STRUCT_DEFAULT(_P, _N) // // Macro used to set initial value of read-only system field from configuration // #define INITIALIZE_SCS_FIELD(_P, _N, _F) \ SCS_FIELD(_P, _N, _F) = SCS_FIELD_DEFAULT(_P, _N, _F) // // Macro use to force reset of a system register to a fixed initial value // #define RESET_SCS_REG_VALUE(_P, _R, _V) \ armWriteSysRegPriv(SCS_ID(_R), _P, _V) // // Macro use to force reset of a system register to its initial configured state // #define RESET_SCS_REG_CONFIG(_P, _R) { \ union {SCS_REG_DECL(_R); Uns32 u32;} u = {SCS_REG_STRUCT_DEFAULT(_P, _R)}; \ RESET_SCS_REG_VALUE(_P, _R, u.u32); \ } // // Call on initialization // void armSysInitialize(armP arm) { // initialize SCS registers that are directly represented INITIALIZE_SCS_REG(arm, ICTR); INITIALIZE_SCS_REG(arm, CPUID); INITIALIZE_SCS_REG(arm, SYST_CALIB); INITIALIZE_SCS_REG(arm, ID_PFR0); INITIALIZE_SCS_REG(arm, ID_PFR1); INITIALIZE_SCS_REG(arm, ID_DFR0); INITIALIZE_SCS_REG(arm, ID_AFR0); INITIALIZE_SCS_REG(arm, ID_MMFR0); INITIALIZE_SCS_REG(arm, ID_MMFR1); INITIALIZE_SCS_REG(arm, ID_MMFR2); INITIALIZE_SCS_REG(arm, ID_MMFR3); INITIALIZE_SCS_REG(arm, ID_ISAR0); INITIALIZE_SCS_REG(arm, ID_ISAR1); INITIALIZE_SCS_REG(arm, ID_ISAR2); INITIALIZE_SCS_REG(arm, ID_ISAR3); INITIALIZE_SCS_REG(arm, ID_ISAR4); INITIALIZE_SCS_REG(arm, ID_ISAR5); INITIALIZE_SCS_REG(arm, MVFR0); INITIALIZE_SCS_REG(arm, MVFR1); INITIALIZE_SCS_REG(arm, MPU_TYPE); // configure permanently-enabled exceptions arm->xEnable[0] = ARM_EXCEPT_PERMANENT_ENABLE; // ICTR.INTLINESNUM depends on the number of configured interrupts SCS_FIELD(arm, ICTR, INTLINESNUM) = NUM_INTERRUPTS(arm)/32; } // // Call on reset // void armSysReset(armP arm) { Uns32 i; // reset SCS registers that are directly represented RESET_SCS_REG_CONFIG(arm, ACTLR); RESET_SCS_REG_CONFIG(arm, CPACR); RESET_SCS_REG_VALUE (arm, ICSR, 0x00000000); RESET_SCS_REG_VALUE (arm, VTOR, 0x00000000); RESET_SCS_REG_VALUE (arm, AIRCR, 0x05fa0000); RESET_SCS_REG_VALUE (arm, SCR, 0x00000000); RESET_SCS_REG_VALUE (arm, CCR, 0x00000200); RESET_SCS_REG_VALUE (arm, SHCSR, 0x00000000); RESET_SCS_REG_VALUE (arm, CFSR, 0xffffffff); RESET_SCS_REG_VALUE (arm, HFSR, 0x00000000); RESET_SCS_REG_VALUE (arm, SYST_CSR, 0x00000000); RESET_SCS_REG_VALUE (arm, FPCCR, 0xc0000000); RESET_SCS_REG_VALUE (arm, FPCAR, 0x00000000); RESET_SCS_REG_VALUE (arm, FPDSCR, 0x00000000); arm->FPCARdomain = NULL; // reset exception pending and enable masks for(i=0; iexceptMaskNum; i++) { arm->xPend [i] = 0; arm->xEnable[i] = 0; } // reset exception priority numbers for(i=0; iexceptNum; i++) { arm->xPriority[i] = 0; } // configure permanently-enabled exceptions arm->xEnable[0] = ARM_EXCEPT_PERMANENT_ENABLE; } //////////////////////////////////////////////////////////////////////////////// // SYSTEM REGISTER ACCESS //////////////////////////////////////////////////////////////////////////////// // // PPB address range // #define PPB_LOW 0xe0000000 #define PPB_HIGH 0xe00fffff // // System space base address // #define SCS_BASE 0xe000e000 // // Type used to return information enabling a system register to be read // typedef struct armSCSReadInfoS { vmiReg rs; // if the register can be read by a simple access armSCSReadFn cb; // if the read requires a callback armSysRegDesc regDesc; // regDesc for read callback } armSCSReadInfo, *armSCSReadInfoP; // // Type used to return information enabling a system register to be written // typedef struct armSCSWriteInfoS { vmiReg rd; // if the register can be written by a simple access armSCSWriteFn cb; // if the write requires a callback armSysRegDesc regDesc; // regDesc for write callback Uns32 writeMask;// mask of writable bits in this register } armSCSWriteInfo, *armSCSWriteInfoP; // // Type enumerating the units for which system registers are present // typedef enum armUnitE { AU_ALL, // always present AU_MPU, // present for cores with MPU only AU_USM, // access possible in user mode if CCR.USERSETMPEND AU_FPU, // access possible if FPU implemented } armUnit; // // Type enumerating classes of access to register // typedef enum accessActionE { AA_READ = 0x0, AA_WRITE = 0x1, AA_PRIV = 0x0, AA_USER = 0x2, AA_PRIV_READ = AA_READ | AA_PRIV, // privileged mode read AA_PRIV_WRITE = AA_WRITE | AA_PRIV, // privileged mode write AA_USER_READ = AA_READ | AA_USER, // user mode read AA_USER_WRITE = AA_WRITE | AA_USER, // user mode write AA_LAST } accessAction; // // Descr strings for accessAction values // static const char *accessActionRW[2][2] = { {"--", "-w"}, {"r-", "rw"}, }; // // This structure records information about each system register // typedef struct scsRegAttrsS { const char *name; Bool access[AA_LAST]; armUnit unit; armSCSReadFn readCB; armSCSWriteFn writeCB; Uns32 writeMask; Uns32 address; Uns32 numWords; } scsRegAttrs; // // Macro to initialize one-word entry in scsRegInfo // #define SCS_ATTR(_ID, _PR,_PW,_UR,_UW, _UNIT, _READCB, _WRITECB) \ [SCS_ID(_ID)] = { \ name : #_ID, \ access : {_PR,_PW,_UR,_UW}, \ unit : _UNIT, \ readCB : _READCB, \ writeCB : _WRITECB, \ writeMask : SCS_WRITE_MASK_##_ID, \ address : SCS_BASE | SCS_ADDRESS_##_ID, \ numWords : 1 \ } // // Macro to initialize multi-word entry in scsRegInfo // #define SCS_ATTR_N(_ID, _N, _PR,_PW,_UR,_UW, _UNIT, _READCB, _WRITECB) \ [SCS_ID(_ID##x##_N)] = { \ name : #_ID"x"#_N, \ access : {_PR,_PW,_UR,_UW}, \ unit : _UNIT, \ readCB : _READCB, \ writeCB : _WRITECB, \ writeMask : SCS_WRITE_MASK_##_ID, \ address : SCS_BASE | SCS_ADDRESS_##_ID, \ numWords : _N \ } // // Table of system register attributes // const static scsRegAttrs scsRegInfo[SCS_ID(Size)] = { //////////////////////////////////////////////////////////////////////////// // true registers (represented in processor structure) //////////////////////////////////////////////////////////////////////////// // id access unit readCB writeCB SCS_ATTR( ICTR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ACTLR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( CPUID, 1,0,0,0, AU_ALL, 0, 0 ), SCS_ATTR( VTOR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( AIRCR, 1,1,0,0, AU_ALL, readAIRCR, writeAIRCR ), SCS_ATTR( SCR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( CCR, 1,1,0,0, AU_ALL, 0, writeCCR ), SCS_ATTR( CFSR, 1,1,0,0, AU_ALL, 0, writeCFSR ), SCS_ATTR( HFSR, 1,1,0,0, AU_ALL, 0, writeHFSR ), SCS_ATTR( AFSR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( MMAR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( BFAR, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( CPACR, 1,1,0,0, AU_ALL, 0, writeCPACR ), // id access unit readCB writeCB SCS_ATTR( SYST_CSR, 1,1,0,0, AU_ALL, readSYST_CSR, writeSYST_CSR ), SCS_ATTR( SYST_RVR, 1,1,0,0, AU_ALL, 0, writeSYST_RVR ), SCS_ATTR( SYST_CVR, 1,1,0,0, AU_ALL, readSYST_CVR, writeSYST_CVR ), SCS_ATTR( SYST_CALIB, 1,1,0,0, AU_ALL, 0, 0 ), // id access unit readCB writeCB SCS_ATTR( ID_PFR0, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_PFR1, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_DFR0, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_AFR0, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_MMFR0, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_MMFR1, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_MMFR2, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_MMFR3, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR0, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR1, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR2, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR3, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR4, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( ID_ISAR5, 1,1,0,0, AU_ALL, 0, 0 ), // id access unit readCB writeCB SCS_ATTR( FPCCR, 1,1,0,0, AU_FPU, 0, writeFPCCR ), SCS_ATTR( FPCAR, 1,1,0,0, AU_FPU, 0, 0 ), SCS_ATTR( FPDSCR, 1,1,0,0, AU_FPU, 0, 0 ), SCS_ATTR( MVFR0, 1,0,0,0, AU_FPU, 0, 0 ), SCS_ATTR( MVFR1, 1,0,0,0, AU_FPU, 0, 0 ), // id access unit readCB writeCB SCS_ATTR( MPU_TYPE, 1,1,0,0, AU_ALL, 0, 0 ), SCS_ATTR( MPU_CONTROL, 1,1,0,0, AU_ALL, 0, writeMPU_CONTROL), SCS_ATTR( MPU_RNR, 1,1,0,0, AU_ALL, 0, writeMPU_RNR ), //////////////////////////////////////////////////////////////////////////// // pseudo-registers (not represented in processor structure) //////////////////////////////////////////////////////////////////////////// // id access unit readCB writeCB SCS_ATTR( ICSR, 1,1,0,0, AU_ALL, readICSR, writeICSR ), SCS_ATTR( SHCSR, 1,1,0,0, AU_ALL, readSHCSR, writeSHCSR ), // id access unit readCB writeCB SCS_ATTR( SHPR1, 1,1,0,0, AU_ALL, readSHPR1, writeSHPR1 ), SCS_ATTR( SHPR2, 1,1,0,0, AU_ALL, readSHPR2, writeSHPR2 ), SCS_ATTR( SHPR3, 1,1,0,0, AU_ALL, readSHPR3, writeSHPR3 ), SCS_ATTR( STIR, 1,1,1,1, AU_USM, ignoreSysRead, writeSTIR ), // id num access unit readCB writeCB SCS_ATTR_N(NVIC_ISER, 16, 1,1,0,0, AU_ALL, readNVIC_ISER, writeNVIC_ISER ), SCS_ATTR_N(NVIC_ICER, 16, 1,1,0,0, AU_ALL, readNVIC_ICER, writeNVIC_ICER ), SCS_ATTR_N(NVIC_ISPR, 16, 1,1,0,0, AU_ALL, readNVIC_ISPR, writeNVIC_ISPR ), SCS_ATTR_N(NVIC_ICPR, 16, 1,1,0,0, AU_ALL, readNVIC_ICPR, writeNVIC_ICPR ), SCS_ATTR_N(NVIC_IABR, 16, 1,1,0,0, AU_ALL, readNVIC_IABR, ignoreSysWrite ), SCS_ATTR_N(NVIC_IPR, 255, 1,1,0,0, AU_ALL, readNVIC_IPR, writeNVIC_IPR ), // id access unit readCB writeCB SCS_ATTR( MPU_RBAR, 1,1,0,0, AU_MPU, readMPU_RBAR, writeMPU_RBAR ), SCS_ATTR( MPU_RASR, 1,1,0,0, AU_MPU, readMPU_RASR, writeMPU_RASR ), SCS_ATTR( MPU_RBAR_A1, 1,1,0,0, AU_MPU, readMPU_RBAR, writeMPU_RBAR ), SCS_ATTR( MPU_RASR_A1, 1,1,0,0, AU_MPU, readMPU_RASR, writeMPU_RASR ), SCS_ATTR( MPU_RBAR_A2, 1,1,0,0, AU_MPU, readMPU_RBAR, writeMPU_RBAR ), SCS_ATTR( MPU_RASR_A2, 1,1,0,0, AU_MPU, readMPU_RASR, writeMPU_RASR ), SCS_ATTR( MPU_RBAR_A3, 1,1,0,0, AU_MPU, readMPU_RBAR, writeMPU_RBAR ), SCS_ATTR( MPU_RASR_A3, 1,1,0,0, AU_MPU, readMPU_RASR, writeMPU_RASR ) }; // // Iterator filling 'desc' with the next system register description - // 'desc.name' should be initialized to NULL prior to the first call // Bool armGetSysRegisterDesc(armSysRegDescP desc) { armSCSRegId id = desc->name ? desc->id+1 : 0; if(id>=SCS_ID(FirstPseudoReg)) { return False; } else { const scsRegAttrs *attrs = &scsRegInfo[id]; desc->name = attrs->name; desc->id = id; desc->address = attrs->address; desc->privRW = accessActionRW[attrs->access[AA_PRIV_READ]][attrs->access[AA_PRIV_WRITE]]; desc->userRW = accessActionRW[attrs->access[AA_USER_READ]][attrs->access[AA_USER_WRITE]]; return True; } } // // Can the indicated system register be accessed using the passed action on // this ARM processor variant? // static Bool canAccessSysReg( armP arm, const scsRegAttrs *attrs, accessAction action ) { if(!attrs->access[action]) { return False; } else switch(attrs->unit) { case AU_MPU: return MPU_PRESENT(arm); case AU_USM: return !(action&AA_USER) || SCS_FIELD(arm, CCR, USERSETMPEND); case AU_FPU: return FPU_PRESENT(arm); default: return True; } } // // Fill armSCSReadInfo structure with information about how to perform a system // register read // static Bool getSysReadInfo( armP arm, armSCSReadInfoP info, armSCSRegId id, accessAction action ) { info->rs = VMI_NOREG; if(id==SCS_ID(INVALID)) { // invalid register access info->cb = ignoreSysRead; } else { // read of plain register or by callback if(scsRegInfo[id].readCB) { info->cb = scsRegInfo[id].readCB; } else if(idrs = ARM_SCS_REG(id); } } return True; } // // Fill armSCSWriteInfo structure with information about how to perform a // system register write // static Bool getSysWriteInfo( armP arm, armSCSWriteInfoP info, Uns32 id, accessAction action ) { info->rd = VMI_NOREG; if(id==SCS_ID(INVALID)) { // invalid register access info->cb = ignoreSysWrite; } else { // write of plain register or by callback if(scsRegInfo[id].writeCB) { info->cb = scsRegInfo[id].writeCB; } else if(idrd = ARM_SCS_REG(id); } // get mask of writable bits info->writeMask = scsRegInfo[id].writeMask; } return True; } // // Return a value read from a fixed offset in the processor structure // static ARM_SCS_READFN(readSCS) { return arm->scs.regs[id]; } // // Write a value to a fixed offset in the processor structure // static ARM_SCS_WRITEFN(writeSCS) { Uns32 writeMask = scsRegInfo[id].writeMask; Uns32 oldValue = arm->scs.regs[id]; arm->scs.regs[id] = ((oldValue&~writeMask) | (newValue&writeMask)); } // // Is the indicated system register supported on this processor? // Bool armGetSysRegSupported(armSCSRegId id, armP arm) { const scsRegAttrs *attrs = &scsRegInfo[id]; return ( canAccessSysReg(arm, attrs, AA_PRIV_READ) || canAccessSysReg(arm, attrs, AA_PRIV_WRITE) ); } // // Get system register read callback (privileged mode) // static armSCSReadFn getSysPrivRegReadCallBack(armSCSRegId id, armP arm) { accessAction action = AA_PRIV_READ; const scsRegAttrs *attrs = &scsRegInfo[id]; armSCSReadInfo info = {regDesc : {id:id}}; if(!canAccessSysReg(arm, attrs, action)) { return 0; } else if(!getSysReadInfo(arm, &info, id, action)) { return 0; } else if(info.cb) { return info.cb; } else if(!VMI_ISNOREG(info.rs)) { return readSCS; } else { return 0; } } // // Get system register write callback (privileged mode) // static armSCSWriteFn getSysPrivRegWriteCallBack(armSCSRegId id, armP arm) { accessAction action = AA_PRIV_WRITE; const scsRegAttrs *attrs = &scsRegInfo[id]; armSCSWriteInfo info = {regDesc : {id:id}}; if(!canAccessSysReg(arm, attrs, action)) { return 0; } else if(!getSysWriteInfo(arm, &info, id, action)) { return 0; } else if(info.cb) { return info.cb; } else if(!VMI_ISNOREG(info.rd)) { return writeSCS; } else { return 0; } } //////////////////////////////////////////////////////////////////////////////// // INSERT SYSTEM REGISTERS INTO DOMAIN //////////////////////////////////////////////////////////////////////////////// // // Callback function to write a system register // static VMI_MEM_WRITE_FN(writeSys) { if(processor) { armP arm = (armP)processor; const scsRegAttrs *attrs = userData; armSCSRegId id = attrs-scsRegInfo; Uns32 offset = address - attrs->address; Uns32 writeMask = attrs->writeMask; Bool bigEndian = SCS_FIELD(arm, AIRCR, ENDIANNESS); Uns32 newValue; // get the value to write (may be partial register) if(bytes==1) { newValue = *(Uns8*)value; } else if(bytes==2) { newValue = SWAP_2_BYTE_COND(*(Uns16*)value, bigEndian); } else if(bytes==4) { newValue = SWAP_4_BYTE_COND(*(Uns32*)value, bigEndian); } else { VMI_ABORT("unimplemented system register size %u bytes", bytes); } if(IN_USER_MODE(arm) && !canAccessSysReg(arm, attrs, AA_USER_WRITE)) { // no access to this register in user mode armBusFault(arm, address, MEM_PRIV_W); } else if(attrs->writeCB) { // detect partial register write if((offset&3) || (bytes!=4)) { // access within register - do read-modify-write of entire 4-byte // register Uns32 offset4 = offset&~3; Uns32 oldValue; Uns32 i; // get 4-byte value, either directly or by callback if(attrs->readCB) { oldValue = attrs->readCB(arm, id, offset4); } else { oldValue = arm->scs.regs[id]; } // get 4-byte value with its mask union {Uns32 u32; Uns8 u8[4];} uValue = {oldValue}; union {Uns32 u32; Uns8 u8[4];} uMask = {writeMask}; // update writable bits in the subrange for(i=offset; i>= 8; } // put back the modified value attrs->writeCB(arm, id, uValue.u32, offset4); } else { // write full register attrs->writeCB(arm, id, newValue, offset); } } else { Uns32 *regValue = &arm->scs.regs[id]; // detect partial register write if((offset&3) || (bytes!=4)) { // access within register Uns8 *regValue8 = (Uns8 *)regValue; Uns32 i; // align writeMask writeMask >>= (offset*8); // update writable bits in the subrange for(i=offset; i>= 8; writeMask >>= 8; } } else { // write full register *regValue = ( (*regValue & ~writeMask) | (newValue & writeMask) ); } } } } // // Callback function to read a system register // static VMI_MEM_READ_FN(readSys) { if(processor) { armP arm = (armP)processor; const scsRegAttrs *attrs = userData; armSCSRegId id = attrs-scsRegInfo; Uns32 offset = address - attrs->address; Uns32 result = 0; Bool bigEndian = SCS_FIELD(arm, AIRCR, ENDIANNESS); if(IN_USER_MODE(arm) && !canAccessSysReg(arm, attrs, AA_USER_READ)) { // no access to this register in user mode armBusFault(arm, address, MEM_PRIV_R); } else if(attrs->readCB) { // read using callback Uns32 offset4 = offset&~3; Uns32 shift = (offset&3) * 8; result = attrs->readCB(arm, id, offset4) >> shift; } else { // read from processor structure Uns32 *regValue = &arm->scs.regs[id]; Uns32 offset4 = offset/4; Uns32 shift = (offset&3) * 8; result = regValue[offset4] >> shift; } // get the value read (may be partial register) if(bytes==1) { *(Uns8*)value = result; } else if(bytes==2) { *(Uns16*)value = SWAP_2_BYTE_COND(result, bigEndian); } else if(bytes==4) { *(Uns32*)value = SWAP_4_BYTE_COND(result, bigEndian); } else { VMI_ABORT("unimplemented system register size %u bytes", bytes); } } } // // Callback function to write PPB region not mapped to a system register // static VMI_MEM_WRITE_FN(writePPB) { armP arm = (armP)processor; if(arm && IN_USER_MODE(arm)) { armBusFault(arm, address, MEM_PRIV_W); } } // // Callback function to read PPB region not mapped to a system register // static VMI_MEM_READ_FN(readPPB) { armP arm = (armP)processor; if(arm && IN_USER_MODE(arm)) { armBusFault(arm, address, MEM_PRIV_W); } else if(bytes==1) { *(Uns8*)value = 0; } else if(bytes==2) { *(Uns16*)value = 0; } else if(bytes==4) { *(Uns32*)value = 0; } else { VMI_ABORT("unimplemented system register size %u bytes", bytes); } } // // Type used to record SCS register bounds // typedef struct SCSRegAddrS { Uns32 lowAddr; // low address Uns32 highAddr; // high address } SCSRegAddr, *SCSRegAddrP; // // Callback used to sort SCS register entries in ascending address // static Int32 compareRegAddr(const void *va, const void *vb) { const SCSRegAddr *a = va; const SCSRegAddr *b = vb; return a->lowAddrlowAddr ? -1 : 1; } // // Insert SCS region into the passed domain at the standard location // void armSysCreateSCSRegion(armP arm, memDomainP domain) { armSCSRegId id; SCSRegAddr scsRegAddrs[SCS_ID(Size)]; Uns32 prevLow = PPB_LOW; Uns32 numRegs = 0; Uns32 i; // remove any existing PPB region mapping vmirtUnaliasMemory(domain, PPB_LOW, PPB_HIGH); // install system registers for(id=0; idlowAddr = attrs->address; regAddr->highAddr = regAddr->lowAddr + attrs->numWords*4 - 1; // install callbacks to implement the register vmirtMapCallbacks( domain, regAddr->lowAddr, regAddr->highAddr, readSys, writeSys, (void *)attrs ); } } // sort register descriptions in ascending address order qsort(scsRegAddrs, numRegs, sizeof(scsRegAddrs[0]), compareRegAddr); // fill unmapped sections in PPB region with default callback for(i=0; ilowAddr > prevLow) { vmirtMapCallbacks( domain, prevLow, regAddr->lowAddr-1, readPPB, writePPB, 0 ); } prevLow = regAddr->highAddr+1; } // map final PPB subregion using default callbacks if(prevLowaddress); // create new register object vmiViewObjectP regObject = vmirtAddViewObject( baseObject, name, description ); // either reference the value directly or use a callback if(info.cb) { vmirtSetViewObjectValueCallback(regObject, scsRegViewCB, (void *)id); } else { Uns32 *refValue = &arm->scs.regs[id]; vmirtSetViewObjectRefValue(regObject, VMI_VVT_UNS32, refValue); } } }