"failstar" sounds like a name for a cruise liner from the 80s. As "*" isn't a desirable part of directory names, just name the whole thing "fail/", the core parts being stored in "fail/core/". Additionally fixing two build system dependency issues: - missing jobserver -> protomessages dependency - broken bochs -> fail dependency (add_custom_target DEPENDS only allows plain file dependencies ... cmake for the win) git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@956 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
1684 lines
48 KiB
C
1684 lines
48 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
// 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(byteOffset<arm->exceptNum) {
|
|
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(byteOffset<arm->exceptNum) {
|
|
|
|
// 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(exceptionNum<arm->exceptNum) {
|
|
|
|
// 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(wordOffset<arm->exceptMaskNum) {
|
|
|
|
// 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(wordOffset<arm->exceptMaskNum) {
|
|
|
|
// 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(wordOffset<arm->exceptMaskNum) {
|
|
|
|
// 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; i<arm->exceptMaskNum; i++) {
|
|
arm->xPend [i] = 0;
|
|
arm->xEnable[i] = 0;
|
|
}
|
|
|
|
// reset exception priority numbers
|
|
for(i=0; i<arm->exceptNum; 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(id<SCS_ID(FirstPseudoReg)) {
|
|
info->rs = 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(id<SCS_ID(FirstPseudoReg)) {
|
|
info->rd = 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<offset+bytes; i++) {
|
|
|
|
// update one byte, preserving read-only bits
|
|
uValue.u8[i] = (
|
|
(uValue.u8[i] & ~uMask.u8[i]) |
|
|
(newValue & uMask.u8[i])
|
|
);
|
|
|
|
// shift newValue for next iteration
|
|
newValue >>= 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<offset+bytes; i++) {
|
|
|
|
// update one byte, preserving read-only bits
|
|
regValue8[i] = (
|
|
(regValue8[i] & ~writeMask) |
|
|
(newValue & writeMask)
|
|
);
|
|
|
|
// shift newValue for next iteration
|
|
newValue >>= 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->lowAddr<b->lowAddr ? -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; id<SCS_ID(Size); id++) {
|
|
|
|
// is the system register supported on this variant?
|
|
if(armGetSysRegSupported(id, arm)) {
|
|
|
|
const scsRegAttrs *attrs = &scsRegInfo[id];
|
|
SCSRegAddrP regAddr = &scsRegAddrs[numRegs++];
|
|
|
|
// save register bounds
|
|
regAddr->lowAddr = 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; i<numRegs; i++) {
|
|
|
|
SCSRegAddrP regAddr = &scsRegAddrs[i];
|
|
|
|
if(regAddr->lowAddr > prevLow) {
|
|
vmirtMapCallbacks(
|
|
domain, prevLow, regAddr->lowAddr-1, readPPB, writePPB, 0
|
|
);
|
|
}
|
|
|
|
prevLow = regAddr->highAddr+1;
|
|
}
|
|
|
|
// map final PPB subregion using default callbacks
|
|
if(prevLow<PPB_HIGH) {
|
|
vmirtMapCallbacks(domain, prevLow, PPB_HIGH, readPPB, writePPB, 0);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// DEBUGGER ACCESS TO SYSTEM REGISTERS
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Perform a privileged-mode read of the system register
|
|
//
|
|
Bool armReadSysRegPriv(armSCSRegId id, armP arm, Uns32 *result) {
|
|
|
|
// get the read callback to apply
|
|
armSCSReadFn readCB = getSysPrivRegReadCallBack(id, arm);
|
|
|
|
// either apply the callback or set the result to 0
|
|
if(readCB) {
|
|
*(Uns32*)result = readCB(arm, id, 0);
|
|
return True;
|
|
} else {
|
|
*(Uns32*)result = 0;
|
|
return False;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform a privileged-mode write of the system register
|
|
//
|
|
Bool armWriteSysRegPriv(armSCSRegId id, armP arm, Uns32 value) {
|
|
|
|
// get the write callback to apply
|
|
armSCSWriteFn writeCB = getSysPrivRegWriteCallBack(id, arm);
|
|
|
|
// apply the callback if it is found
|
|
if(writeCB) {
|
|
writeCB(arm, id, value, 0);
|
|
return True;
|
|
} else {
|
|
return False;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// SYSTEM REGISTER PROGRAMMER'S VIEW
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Callback to obtain the value of a system register
|
|
//
|
|
static VMI_VIEW_VALUE_FN(scsRegViewCB) {
|
|
|
|
// get the processor for this view object
|
|
vmiViewObjectP baseObject = vmiviewGetViewObjectParent(object);
|
|
vmiViewObjectP processorObject = vmiviewGetViewObjectParent(baseObject);
|
|
armP arm = vmirtGetViewObjectUserData(processorObject);
|
|
UnsPS id = (UnsPS)clientData;
|
|
|
|
armReadSysRegPriv(id, arm, (Uns32*)buffer);
|
|
|
|
return VMI_VVT_UNS32;
|
|
}
|
|
|
|
//
|
|
// Add programmer's view of system register
|
|
//
|
|
void armAddSysRegisterView(
|
|
armSCSRegId id,
|
|
armP arm,
|
|
vmiViewObjectP baseObject,
|
|
const char *name
|
|
) {
|
|
accessAction action = AA_PRIV_READ;
|
|
armSCSReadInfo info = {regDesc : {id:id}};
|
|
|
|
if(!getSysReadInfo(arm, &info, id, action)) {
|
|
|
|
// unknown register
|
|
|
|
} else if(!info.cb && VMI_ISNOREG(info.rs)) {
|
|
|
|
// register not readable on this variant
|
|
|
|
} else {
|
|
|
|
// extract fields from id (where known)
|
|
const scsRegAttrs *attrs = &scsRegInfo[id];
|
|
|
|
// create register description string
|
|
char description[32];
|
|
snprintf(description, sizeof(description), "0x%08x", attrs->address);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|