Files
fail/ovp/armmModel/armmVM.c
hsc b70b6fb43a another directory rename: failstar -> fail
"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
2012-03-08 19:43:02 +00:00

1272 lines
34 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.
*
*/
#include <stdio.h> // for sprintf
// Imperas header files
#include "hostapi/impAlloc.h"
// VMI header files
#include "vmi/vmiAttrs.h"
#include "vmi/vmiMessage.h"
#include "vmi/vmiRt.h"
#include "vmi/vmiTypes.h"
// model header files
#include "armExceptions.h"
#include "armFunctions.h"
#include "armStructure.h"
#include "armMessage.h"
#include "armSys.h"
#include "armVM.h"
#include "armUtils.h"
//
// Prefix for messages from this module
//
#define CPU_PREFIX "ARM_VM"
//
// This is the minimum region size usable with SRD bits and the number of
// subregions per region
//
#define SRD_MIN_SIZE 256
#define SRD_REGIONS 8
////////////////////////////////////////////////////////////////////////////////
// UTILITIES
////////////////////////////////////////////////////////////////////////////////
//
// Return current program counter
//
inline static Uns32 getPC(armP arm) {
return vmirtGetPC((vmiProcessorP)arm);
}
//
// Return instruction memory domain set for the passed processor
//
inline static armDomainSetP getDomainSetI(armP arm) {
return &arm->ids;
}
//
// Return data memory domain set for the passed processor
//
inline static armDomainSetP getDomainSetD(armP arm) {
return &arm->dds;
}
//
// Get the current code memDomain
//
inline static memDomainP getVirtualCodeDomain(armP arm) {
return vmirtGetProcessorCodeDomain((vmiProcessorP)arm);
}
//
// Get the current data memDomain
//
inline static memDomainP getVirtualDataDomain(armP arm) {
return vmirtGetProcessorDataDomain((vmiProcessorP)arm);
}
//
// Is code demain required for the passed privilege?
//
inline static Bool isFetch(memPriv priv) {
return priv==MEM_PRIV_X;
}
//
// Return the memory domain set to use for the passed memory access type
//
static armDomainSetP getDomainSetPriv(armP arm, memPriv priv) {
return isFetch(priv) ? getDomainSetI(arm) : getDomainSetD(arm);
}
//
// Return the virtual memory domain to use for the passed memory access type
//
static memDomainP getVirtualDomainPriv(armP arm, memPriv priv) {
return isFetch(priv) ? getVirtualCodeDomain(arm) : getVirtualDataDomain(arm);
}
//
// Set the current data memDomain
//
inline static void setVirtualDataDomain(armP arm, memDomainP domain) {
vmirtSetProcessorDataDomain((vmiProcessorP)arm, domain);
}
//
// Push to a new data memDomain, marking the processor to indicate restoration
// is required
//
inline static void pushVirtualDataDomain(armP arm, memDomainP domain) {
setVirtualDataDomain(arm, domain);
arm->restoreDomain = True;
}
//
// Return the virtual data domain for the passed mode
//
static memDomainP getVirtualDataDomainMode(armP arm) {
armDomainSetP set = getDomainSetD(arm);
return IN_USER_MODE(arm) ? set->vmUser : set->vmPriv;
}
//
// Restore correct virtual code domain for the processor, if required
//
static void restoreVirtualDataDomain(armP arm) {
setVirtualDataDomain(arm, getVirtualDataDomainMode(arm));
arm->restoreDomain = False;
}
//
// Given a raw privilege (AP format), return the effective privilege based on
// mode only for user mode MPU accesses
//
static memPriv getPrivMPUU(armP arm, Uns8 AP, Bool XN) {
static const memPriv privMap[] = {
[0] = MEM_PRIV_NONE,
[1] = MEM_PRIV_NONE,
[2] = MEM_PRIV_RX,
[3] = MEM_PRIV_RWX,
[4] = MEM_PRIV_NONE,
[5] = MEM_PRIV_NONE,
[6] = MEM_PRIV_RX,
[7] = MEM_PRIV_NONE
};
memPriv result = (AP>7) ? MEM_PRIV_NONE : privMap[AP];
// apply XN bit if required
if(XN) {result &= ~MEM_PRIV_X;}
return result;
}
//
// Given a raw privilege (AP format), return the effective privilege based on
// mode only for privileged mode MPU accesses
//
static memPriv getPrivMPUP(armP arm, Uns8 AP, Bool XN) {
static const memPriv privMap[] = {
[0] = MEM_PRIV_NONE,
[1] = MEM_PRIV_RWX,
[2] = MEM_PRIV_RWX,
[3] = MEM_PRIV_RWX,
[4] = MEM_PRIV_NONE,
[5] = MEM_PRIV_RX,
[6] = MEM_PRIV_RX,
[7] = MEM_PRIV_NONE
};
memPriv result = (AP>7) ? MEM_PRIV_NONE : privMap[AP];
// apply XN bit if required
if(XN) {result &= ~MEM_PRIV_X;}
return result;
}
//
// Given a raw privilege (AP format), return the effective privilege considering
// mode only (MPU accesses). A special case is that accesses in kernel mode
// where the current data memDomain is actually the user mode memDomain should
// be treated as user mode accesses (LDRT, STRT).
//
static memPriv getPrivMPU(armP arm, Uns8 AP, Bool XN) {
if(IN_USER_MODE(arm)) {
// normal user-mode case
return getPrivMPUU(arm, AP, XN);
} else if(getVirtualDataDomain(arm)==getDomainSetD(arm)->vmUser) {
// special case for LDRT and STRT accesses
return getPrivMPUU(arm, AP, XN);
} else {
// normal privileged-mode case
return getPrivMPUP(arm, AP, XN);
}
}
//
// Return a domian mask (the largest valid address in a domain)
//
static Uns64 getDomainMask(memDomainP domain) {
Uns8 bits = vmirtGetDomainAddressBits(domain);
return (bits==64) ? -1ULL : ((1ULL<<bits)-1);
}
//
// Create an alias of the passed memDomain
//
static memDomainP makeDomainAlias(memDomainP master, const char *name) {
Uns8 bits = vmirtGetDomainAddressBits(master);
memDomainP slave = vmirtNewDomain(name, bits);
vmirtAliasMemory(master, slave, 0, getDomainMask(master), 0, 0);
return slave;
}
//
// Map memory virtual addresses starting at 'lowVA' to the physical address
// range 'lowPA':'highPA' with privilege 'priv'. Return a code indicating
// whether the mapping was successful (it may fail if there is no physical
// memory at the mapped address).
//
static void mapDomainPairRange(
armP arm,
memPriv requiredPriv,
memPriv priv,
Uns64 lowPA,
Uns64 highPA,
Uns32 lowVA,
Bool G,
Uns8 ASID
) {
armDomainSetP domainSet = getDomainSetPriv(arm, requiredPriv);
memDomainP domainV = getVirtualDomainPriv(arm, requiredPriv);
// create the required mapping (with ASID, if required)
vmirtAliasMemoryVM(
domainSet->external, domainV, lowPA, highPA, lowVA, 0, priv, G, ASID
);
}
//
// Do required actions when an invalid memory access occurs, either a processor
// access or a DMA unit access
//
static void handleInvalidAccess(
armP arm,
Uns32 MVA,
memPriv requiredPriv,
memAccessAttrs attrs
) {
if(!attrs) {
// no action for artifact access
} else {
// processor-generated exception
armMemoryAbort(arm, MVA, requiredPriv);
}
}
////////////////////////////////////////////////////////////////////////////////
// MPU FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
typedef enum protRegionCodeE {
PEC_ENABLED, // enabled area description
PEC_DISABLED, // disabled area description
PEC_INVALID_SIZE, // invalid size (less than minumum)
PEC_INVALID_BASE, // invalid base (not a multiple of size)
} protRegionCode;
// this type represents a single protection area entry
typedef struct protRegionS {
Uns32 Base; // entry base address
Uns32 SRD : 8; // subrange disable
Uns32 Size : 5; // area size
Uns32 TEX : 3; // TEX field (ARMv6 and later)
Uns32 AP : 3; // area permissions
Bool E : 1; // enabled?
Bool XN : 1; // XN bit (ARMv6 and later)
Bool S : 1; // S bit (ARMv6 and later)
Bool C : 1; // C bit (ARMv6 and later)
Bool B : 1; // B bit (ARMv6 and later)
} protRegion;
// pointer to a constant protRegion
typedef const struct protRegionS *protRegionCP;
//
// Return description string identifying MPU
//
static const char *getMPUName(armP arm, Bool isData) {
return MPU_UNIFIED(arm) ? "" : isData ? " (D)" : " (I)";
}
//
// Return the count of the number of protection regions
//
static Uns32 mpuRegionNum(armP arm, Bool isData) {
if(isData || MPU_UNIFIED(arm)) {
return SCS_FIELD(arm, MPU_TYPE, DREGION);
} else {
return SCS_FIELD(arm, MPU_TYPE, DREGION);
}
}
//
// Validate that the passed index is a valid MPU region index
//
static Bool validateRegionNum(armP arm, Bool isData, Uns32 index) {
Uns32 regionNum = mpuRegionNum(arm, isData);
if(index >= regionNum) {
vmiMessage("a", CPU_PREFIX"_ROOR",
SRCREF_FMT "protection area index %u exceeds maximum (%u)",
SRCREF_ARGS(arm, getPC(arm)),
index, regionNum-1
);
return False;
} else {
return True;
}
}
//
// Return a pointer to the Protection Area Control registers
//
inline static protRegionP getPACRegs(armP arm, Bool isData) {
return (isData && arm->dmpu) ? arm->dmpu : arm->impu;
}
//
// If the passed protection entry describes a valid region, set the bounds of
// that region and return PEC_ENABLED; otherwise return an enum member giving
// the reason why it is not valid
//
static protRegionCode getProtectionEntryRange(
armP arm,
protRegionCP pa,
Uns32 *lowP,
Uns32 *highP
) {
if(!pa->E) {
// disabled region
return PEC_DISABLED;
} else {
// minimum region size allowed depends on MPU revision
Uns32 minSize = 4;
Uns32 minDelta = (2 << minSize) - 1;
// get the region size (minus 1)
Uns32 size = pa->Size;
Uns32 delta = (2 << size) - 1;
// are some subranges enabled?
Uns32 SRE = ~pa->SRD;
if(!SRE && (delta>=(SRD_MIN_SIZE-1))) {
// all subranges disabled
return PEC_DISABLED;
} else {
// get the region base address
Uns32 low = pa->Base;
// return implied bounds
*lowP = low;
*highP = low + delta;
if(delta<minDelta) {
// invalid size
return PEC_INVALID_SIZE;
} else if(low & delta) {
// base not a multiple of size
return PEC_INVALID_BASE;
} else {
// bounds valid
return PEC_ENABLED;
}
}
}
}
//
// Remove all access privileges from the instruction and/or data domain pair,
// depending on whether this is an instruction or data access and whether the
// MPU is unified
//
static void removePrivMPU(
armP arm,
memDomainP iDomain,
memDomainP dDomain,
Uns32 lowAddr,
Uns32 highAddr,
Bool isData
) {
Bool unified = MPU_UNIFIED(arm);
Bool removeI = unified || !isData;
Bool removeD = unified || isData;
memPriv priv = MEM_PRIV_NONE;
if(removeI) {
vmirtProtectMemory(iDomain, lowAddr, highAddr, priv, MEM_PRIV_SET);
}
if(removeD) {
vmirtProtectMemory(dDomain, lowAddr, highAddr, priv, MEM_PRIV_SET);
}
}
//
// If the indexed protection entry is valid, unmap it, internal routine
//
static void unmapProtectionEntryInt(
armP arm,
Uns32 low,
Uns32 high,
Bool isData
) {
// if debug mode is enabled, report the range being unmapped
if(ARM_DEBUG_MMU(arm)) {
vmiPrintf(
"MPU%s UNMAP VA 0x%08x:0x%08x\n",
getMPUName(arm, isData), low, high
);
}
// remove privileged domain access permissions
removePrivMPU(arm, arm->ids.vmPriv, arm->dds.vmPriv, low, high, isData);
// remove user domain access permissions
removePrivMPU(arm, arm->ids.vmUser, arm->dds.vmUser, low, high, isData);
}
//
// If the indexed protection entry is valid, unmap it
//
static void unmapProtectionEntry(armP arm, Uns32 low, Uns32 high, Bool isData) {
// remove main mappings
unmapProtectionEntryInt(arm, low, high, isData);
}
//
// Update MPU status when an MPU region entry has been updated
//
static void resetProtectionEntryRange(
armP arm,
Uns32 index,
Bool isData,
Bool verbose
) {
protRegionP pacRegs = getPACRegs(arm, isData);
protRegionP pacReg = pacRegs+index;
Uns32 low;
Uns32 high;
// validate new protection area permissions
switch(getProtectionEntryRange(arm, pacReg, &low, &high)) {
case PEC_ENABLED:
// remove any existing mapping for the new protection entry range
unmapProtectionEntry(arm, low, high, isData);
break;
case PEC_INVALID_SIZE:
// size error assertion
if(verbose) {
vmiMessage("a", CPU_PREFIX"_PEIS",
SRCREF_FMT "protection area %u has invalid size (%u bytes)",
SRCREF_ARGS(arm, getPC(arm)),
index, high - low + 1
);
}
break;
case PEC_INVALID_BASE:
// base error assertion
if(verbose) {
vmiMessage("a", CPU_PREFIX"_PEIB",
SRCREF_FMT "protection area %u base 0x%08x is not a "
"multiple of size",
SRCREF_ARGS(arm, getPC(arm)),
index, low
);
}
break;
default:
// disabled entry
break;
}
}
//
// Update the indexed region Base, SRD, Size or E fields
//
static void updateRegionBaseSRDSizeE(
armP arm,
Uns32 index,
Bool isData,
Uns32 Base,
Uns32 SRD,
Uns32 Size,
Uns32 E
) {
protRegionP pacRegs = getPACRegs(arm, isData);
protRegionP pacReg = pacRegs+index;
// action is required only if the register changes value
if(
(pacReg->Base!=Base) ||
(pacReg->SRD!=SRD) ||
(pacReg->Size!=Size) ||
(pacReg->E!=E)
) {
// remove any current mapping for the protection entry
resetProtectionEntryRange(arm, index, isData, False);
// update the entry
pacReg->Base = Base;
pacReg->SRD = SRD;
pacReg->Size = Size;
pacReg->E = E;
// remove any mapping for the new region location
resetProtectionEntryRange(arm, index, isData, True);
}
}
//
// Modify AP or XN fields for the indexed MPU region
//
static void updateRegionAPXN(
armP arm,
Uns32 index,
Bool isData,
Uns8 newAP,
Bool newXN
) {
protRegionP pacRegs = getPACRegs(arm, isData);
protRegionP pacReg = pacRegs+index;
Uns8 oldAP = pacReg->AP;
Uns8 oldXN = pacReg->XN;
// update AP and XN fields
pacReg->AP = newAP;
pacReg->XN = newXN;
// have region permissions on an enabled region changed?
if(pacReg->E && ((oldAP!=newAP) || (oldXN!=newXN))) {
// get privileges implied by old and new permissions in each mode
memPriv oldPrivU = getPrivMPUU(arm, oldAP, oldXN);
memPriv oldPrivP = getPrivMPUP(arm, oldAP, oldXN);
memPriv newPrivU = getPrivMPUU(arm, newAP, newXN);
memPriv newPrivP = getPrivMPUP(arm, newAP, newXN);
// if privileges have been reduced, disable all access to the area of
// memory assocaited with the region
if((oldPrivU & ~newPrivU) || (oldPrivP & ~newPrivP)) {
resetProtectionEntryRange(arm, index, isData, False);
}
}
}
//
// This specifies the regions in the default system address map
//
static const protRegion sysRegions[] = {
{.Base=0x00000000, .Size=0x1c, .AP=3, .TEX=0, .C=1, .B=0, .E=1, .S=1, .XN=0},
{.Base=0x20000000, .Size=0x1c, .AP=3, .TEX=1, .C=1, .B=1, .E=1, .S=1, .XN=0},
{.Base=0x40000000, .Size=0x1c, .AP=3, .TEX=0, .C=0, .B=1, .E=1, .S=1, .XN=1},
{.Base=0x60000000, .Size=0x1c, .AP=3, .TEX=1, .C=1, .B=1, .E=1, .S=1, .XN=0},
{.Base=0x80000000, .Size=0x1c, .AP=3, .TEX=0, .C=1, .B=0, .E=1, .S=1, .XN=0},
{.Base=0xa0000000, .Size=0x1c, .AP=3, .TEX=0, .C=0, .B=1, .E=1, .S=1, .XN=1},
{.Base=0xc0000000, .Size=0x1c, .AP=3, .TEX=2, .C=0, .B=0, .E=1, .S=1, .XN=1},
{.Base=0xe0000000, .Size=0x1c, .AP=3, .TEX=2, .C=0, .B=0, .E=1, .S=1, .XN=1}
};
//
// Return a protRegion structure representing the implicit Private Peripheral
// Bus region
//
static protRegionCP getPPBRegion(void) {
static const protRegion ppbRegion = {
.Base=PPB_LOW, .Size=0x13, .AP=3, .E=1, .XN=1
};
return &ppbRegion;
}
//
// If the MVA lies in the region range rlow:rhigh, set by-ref arguments 'low'
// and 'high' with that range; otherwise, exclude the range rlow:rhigh from the
// currennt range in low:high
//
static Bool matchRegion(
Uns32 MVA,
Uns32 rlow,
Uns32 rhigh,
Uns32 *low,
Uns32 *high
) {
if((rlow<=MVA) && (rhigh>=MVA)) {
// match in this region
*low = rlow;
*high = rhigh;
return True;
} else if((rlow>MVA) && (rlow<*high)) {
// remove part of region ABOVE matching address
*high = rlow-1;
} else if((rhigh<MVA) && (rhigh>*low)) {
// remove part of region BELOW matching address
*low = rhigh+1;
}
return False;
}
//
// Return a boolean indicating if the passed MVA lies in the protection entry
// and update by-ref arguments 'low' and 'high' with the largest bounds
// enclosing the address
//
static Bool selectProtectionEntry(
armP arm,
protRegionCP try,
Uns32 MVA,
Uns32 *low,
Uns32 *high
) {
Uns32 rlow, rhigh;
if(getProtectionEntryRange(arm, try, &rlow, &rhigh)!=PEC_ENABLED) {
// entry is not enabled
return False;
} else if(try->SRD && ((rhigh-rlow)>=(SRD_MIN_SIZE-1))) {
// possible match in a subregion
Uns8 SRE = ~try->SRD;
Uns32 delta = ((Uns64)rhigh-rlow+1)/SRD_REGIONS;
Uns32 i;
// iterate over all enabled subregions
for(i=0; SRE; i++, SRE>>=1) {
// is this subregion enabled?
if(SRE & 1) {
// derive subregion bounds
Uns32 srlow = rlow+(delta*i);
Uns32 srhigh = srlow+delta-1;
// return if subregion matches
if(matchRegion(MVA, srlow, srhigh, low, high)) {
return True;
}
}
}
// no subregion matches
return False;
} else {
// possible match in an entire region
return matchRegion(MVA, rlow, rhigh, low, high);
}
}
//
// Try mapping memory at the passed address for the specified access type and
// return a Boolean indicating whether the mapping succeeded
//
static Bool mpuMiss(
armP arm,
memPriv requiredPriv,
Uns32 address,
Uns32 *lowVAP,
Uns32 *highVAP
) {
Uns32 MVA = address;
Bool isData = !(requiredPriv & MEM_PRIV_X);
protRegionP pacRegs = getPACRegs(arm, isData);
protRegionCP ppb = getPPBRegion();
protRegionCP match = 0;
Uns32 regionNum = mpuRegionNum(arm, isData);
Uns32 low = 0;
Uns32 high = 0;
Uns32 i;
// use the system address map as a background region in privileged mode if
// MPU_CONTROL.PRIVDEFENA is specified
if(!IN_USER_MODE(arm) && SCS_FIELD(arm, MPU_CONTROL, PRIVDEFENA)) {
match = sysRegions+(MVA/0x20000000);
selectProtectionEntry(arm, match, MVA, &low, &high);
}
// scan regions in lowest-to-highest priority order
for(i=0; i<regionNum; i++) {
if(selectProtectionEntry(arm, pacRegs+i, MVA, &low, &high)) {
match = pacRegs+i;
}
}
// include the implicit Private Peripheral Bus region which is always
// present at highest priority
if(selectProtectionEntry(arm, ppb, MVA, &low, &high)) {
match = ppb;
}
// map in region if a match was found
if(match) {
// get access permissions applicable to the range
Uns8 AP = match->AP;
Bool XN = match->XN;
memPriv priv = getPrivMPU(arm, AP, XN);
Uns32 lowVA = low;
Uns32 highVA = high;
// if debug mode is enabled, report the range being mapped
if(ARM_DEBUG_MMU(arm)) {
vmiPrintf(
"MPU%s MAP VA 0x%08x:0x%08x PA 0x%08x:0x%08x AP %u%s\n",
getMPUName(arm, isData),
lowVA, highVA,
low, high,
AP,
XN ? "-x" : ""
);
}
// does the entry have sufficient permissions?
if((priv & requiredPriv) == requiredPriv) {
// map MPU entry memory
mapDomainPairRange(
arm, requiredPriv, priv, low, high, lowVA, True, 0
);
// MPU entry permissions are ok, but access may still not be
// possible if no physical memory exists at the physical address
memDomainP domainV = getVirtualDomainPriv(arm, requiredPriv);
memPriv actualPriv = vmirtGetDomainPrivileges(domainV, address);
// indicate the address range that has been mapped
*lowVAP = lowVA;
*highVAP = highVA;
return (actualPriv & requiredPriv) && True;
}
}
// invalid access, either because no matching entry found or existing entry
// has insufficient permissions
return False;
}
//
// Free an MPU data structure
//
static void freeMPU(protRegionP *mpuHandle) {
protRegionP mpu = *mpuHandle;
STYPE_FREE(mpu);
*mpuHandle = 0;
}
//
// Free an MPU data structure
//
static protRegionP newMPU(armP arm, Bool isData) {
Uns32 regionNum = mpuRegionNum(arm, isData);
return regionNum ? STYPE_CALLOC_N(protRegion, regionNum) : 0;
}
//
// Reset an MPU data structure
//
static void resetMPU(armP arm, Bool isData) {
Uns32 regionNum = mpuRegionNum(arm, isData);
Uns32 i;
for(i=0; i<regionNum; i++) {
updateRegionBaseSRDSizeE(arm, i, isData, 0, 0, 0, 0);
}
}
//
// Free MPU structures for the passed processor
//
static void freeMPUs(armP arm) {
// free instruction/unified MPU
if(arm->impu) {
freeMPU(&arm->impu);
}
// free data MPU if required
if(arm->dmpu) {
freeMPU(&arm->dmpu);
}
}
//
// Allocate MPU structures for the passed processor
//
static void newMPUs(armP arm) {
// create instruction/unified MPU
arm->impu = newMPU(arm, False);
// create data MPU if required
if(!MPU_UNIFIED(arm)) {
arm->dmpu = newMPU(arm, True);
}
}
//
// Reset MPU contents
//
static void resetMPUs(armP arm) {
// reset instruction/unified MPU
if(arm->impu) {
resetMPU(arm, False);
}
// reset data MPU if required
if(arm->dmpu) {
resetMPU(arm, True);
}
}
////////////////////////////////////////////////////////////////////////////////
// BIT-BAND REGIONS
////////////////////////////////////////////////////////////////////////////////
//
// This is the size of a bit-band region
//
#define BIT_BAND_SIZE 0x100000
//
// This is the offset to the alias region from the bit-band base
//
#define ALIAS_OFFSET 0x2000000
//
// Callback function to read a system register
//
static VMI_MEM_READ_FN(readBitBand) {
if(processor) {
armP arm = (armP)processor;
memDomainP domain = arm->dds.external;
UnsPS regionLow = (UnsPS)userData;
Uns32 aliasLow = regionLow + ALIAS_OFFSET;
Uns32 regionAddr = regionLow + (address - aliasLow) / 32;
Uns32 bitOffset = ((address - aliasLow) % 32) / 4;
// get byte alias value
Uns8 result = vmirtRead1ByteDomain(domain, regionAddr, MEM_AA_TRUE);
// extract required bit
result = (result>>bitOffset) & 1;
// update correct-size result value
if(bytes==1) {
*(Uns8*)value = result;
} else if(bytes==2) {
*(Uns16*)value = result;
} else if(bytes==4) {
*(Uns32*)value = result;
} else {
VMI_ABORT("unimplemented bit-band access size %u bytes", bytes);
}
}
}
//
// Callback function to read a system register
//
static VMI_MEM_WRITE_FN(writeBitBand) {
if(processor) {
armP arm = (armP)processor;
memDomainP domain = arm->dds.external;
UnsPS regionLow = (UnsPS)userData;
Uns32 aliasLow = regionLow + ALIAS_OFFSET;
Uns32 regionAddr = regionLow + (address - aliasLow) / 32;
Uns32 bitOffset = ((address - aliasLow) % 32) / 4;
Uns8 mask = 1<<bitOffset;
// get initial value of the byte to modify
Uns8 partial = vmirtRead1ByteDomain(domain, regionAddr, MEM_AA_TRUE);
// set or clear the required bit
if((*(Uns8*)value) & 1) {
partial |= mask;
} else {
partial &= ~mask;
}
// write back modified value
vmirtWrite1ByteDomain(domain, regionAddr, partial, MEM_AA_TRUE);
}
}
//
// Create bit-band region at 'regionLow' with alias region at 'aliasLow'
//
static void createBitBandRegion(memDomainP domain, Uns32 regionLow) {
Uns32 aliasLow = regionLow + ALIAS_OFFSET;
Uns32 aliasSize = BIT_BAND_SIZE*32;
Uns32 aliasHigh = aliasLow+aliasSize-1;
// remove any existing mapping for the alias address
vmirtUnaliasMemory(domain, aliasLow, aliasHigh);
// install callbacks to implement the alias
vmirtMapCallbacks(
domain, aliasLow, aliasHigh, readBitBand, writeBitBand, (void *)(UnsPS)regionLow
);
}
////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////
//
// Try mapping memory at the passed address for the specified access type and
// return a status code indicating whether the mapping succeeded
//
armVMAction armVMMiss(
armP arm,
memPriv requiredPriv,
Uns32 address,
Uns32 bytes,
memAccessAttrs attrs
) {
Bool ok = False;
// no action unless the MPU is enabled
if(arm->mode&ARM_MODE_MPU) {
Uns32 lastVA = address+bytes-1;
Uns32 highVA = address-1;
Uns32 lowVA = address;
// iterate over all MPU ranges straddled by this access
do {
address = highVA+1;
ok = mpuMiss(arm, requiredPriv, address, &lowVA, &highVA);
} while(ok && ((lastVA<lowVA) || (lastVA>highVA)));
}
// generate exception for the first faulting address
if(ok) {
return MA_OK;
} else {
handleInvalidAccess(arm, address, requiredPriv, attrs);
return MA_EXCEPTION;
}
}
//
// Set the privileged mode data domain to the user domain (for LDRT, STRT)
//
void armVMSetUserPrivilegedModeDataDomain(armP arm) {
pushVirtualDataDomain(arm, getDomainSetD(arm)->vmUser);
}
//
// Restore the normal data domain for the current mode
//
void armVMRestoreNormalDataDomain(armP arm) {
restoreVirtualDataDomain(arm);
}
//
// Write the indexed MPU RBAR register value
//
void armVMWriteRBAR(armP arm, Uns32 index, Bool isData, Uns32 newValue) {
if(validateRegionNum(arm, isData, index)) {
protRegionP pacReg = getPACRegs(arm, isData) + index;
// prepare Base, SRD, Size and E arguments
Uns32 Base = newValue & SCS_MASK_MPU_RBAR_ADDR;
Uns32 SRD = pacReg->SRD;
Uns32 Size = pacReg->Size;
Uns32 E = pacReg->E;
// update the required register
updateRegionBaseSRDSizeE(arm, index, isData, Base, SRD, Size, E);
}
}
//
// Read the indexed MPU RBAR register value
//
Uns32 armVMReadRBAR(armP arm, Uns32 index, Bool isData) {
if(validateRegionNum(arm, isData, index)) {
protRegionP pacReg = getPACRegs(arm, isData) + index;
// create mask to select Base field
union {SCS_REG_DECL(MPU_RBAR); Uns32 u32;} uMask = {{ADDR:-1}};
// return masked result
return pacReg->Base & uMask.u32;
} else {
return 0;
}
}
//
// Write the indexed MPU RASR register value
//
void armVMWriteRASR(armP arm, Uns32 index, Bool isData, Uns32 newValue) {
if(validateRegionNum(arm, isData, index)) {
protRegionP pacReg = getPACRegs(arm, isData) + index;
// create union to enable field extraction
union {Uns32 u32; SCS_REG_DECL(MPU_RASR);} u = {newValue};
// update fields with no simulation effect
pacReg->B = u.MPU_RASR.B;
pacReg->C = u.MPU_RASR.C;
pacReg->S = u.MPU_RASR.S;
pacReg->TEX = u.MPU_RASR.TEX;
// prepare Base, SRD, Size, E, AP and XN arguments
Uns32 Base = pacReg->Base;
Uns32 SRD = u.MPU_RASR.SRD;
Uns32 Size = u.MPU_RASR.SIZE;
Uns32 E = u.MPU_RASR.ENABLE;
Uns8 AP = u.MPU_RASR.AP;
Bool XN = u.MPU_RASR.XN;
// update the required register
updateRegionBaseSRDSizeE(arm, index, isData, Base, SRD, Size, E);
updateRegionAPXN(arm, index, isData, AP, XN);
}
}
//
// Read the indexed MPU RASR register value
//
Uns32 armVMReadRASR(armP arm, Uns32 index, Bool isData) {
if(validateRegionNum(arm, isData, index)) {
protRegionP pacReg = getPACRegs(arm, isData) + index;
// initialize result
union {Uns32 u32; SCS_REG_DECL(MPU_RASR);} u = {0};
// extract required fields
u.MPU_RASR.SRD = pacReg->SRD;
u.MPU_RASR.SIZE = pacReg->Size;
u.MPU_RASR.ENABLE = pacReg->E;
u.MPU_RASR.B = pacReg->B;
u.MPU_RASR.C = pacReg->C;
u.MPU_RASR.S = pacReg->S;
u.MPU_RASR.TEX = pacReg->TEX;
u.MPU_RASR.AP = pacReg->AP;
u.MPU_RASR.XN = pacReg->XN;
// return composed result
return u.u32;
} else {
return 0;
}
}
//
// Flush the privileged mode MPU
//
void armVMFlushMPUPriv(armP arm) {
Bool isData = True;
// if debug mode is enabled, report the range being unmapped
if(ARM_DEBUG_MMU(arm)) {
vmiPrintf("MPU%s FLUSH\n", getMPUName(arm, isData));
}
// remove privileged domain access permissions
removePrivMPU(arm, arm->ids.vmPriv, arm->dds.vmPriv, 0, -1, isData);
}
//
// Virtual memory constructor
//
VMI_VMINIT_FN(armVMInit) {
armP arm = (armP)processor;
Uns32 bits = ARM_GPR_BITS;
memDomainP extCodeDomain = codeDomains[0];
memDomainP extDataDomain = dataDomains[0];
memDomainP sysCodeDomain;
memDomainP sysDataDomain;
// create system physical domains
if(extCodeDomain==extDataDomain) {
extCodeDomain = makeDomainAlias(extCodeDomain, "external");
extDataDomain = extCodeDomain;
sysCodeDomain = makeDomainAlias(extCodeDomain, "system");
sysDataDomain = sysCodeDomain;
} else {
extCodeDomain = makeDomainAlias(extCodeDomain, "external Code");
extDataDomain = makeDomainAlias(extDataDomain, "external Data");
sysCodeDomain = makeDomainAlias(extCodeDomain, "system Code");
sysDataDomain = makeDomainAlias(extDataDomain, "system Data");
}
// add System Control Space register callbacks
armSysCreateSCSRegion(arm, extDataDomain);
if (!arm->disableBitBand) {
// create bit-band regions
createBitBandRegion(extDataDomain, 0x20000000);
createBitBandRegion(extDataDomain, 0x40000000);
}
// addresses in System Control Space are never executable
vmirtProtectMemory(
extCodeDomain, SYSTEM_LOW, SYSTEM_HIGH, MEM_PRIV_X, MEM_PRIV_SUB
);
// some addresses in the default system address map are also not executable
vmirtProtectMemory(
sysCodeDomain, PERIPH_LOW, PERIPH_HIGH, MEM_PRIV_X, MEM_PRIV_SUB
);
vmirtProtectMemory(
sysCodeDomain, DEVICE_LOW, DEVICE_HIGH, MEM_PRIV_X, MEM_PRIV_SUB
);
// save physical memDomains on processor structure
arm->ids.external = extCodeDomain;
arm->ids.system = sysCodeDomain;
arm->dds.external = extDataDomain;
arm->dds.system = sysDataDomain;
// set physical code memDomains for each mode
codeDomains[ARM_MODE_PRIV] = sysCodeDomain;
codeDomains[ARM_MODE_USER] = sysCodeDomain;
// set physical data memDomains for each mode
dataDomains[ARM_MODE_PRIV] = sysDataDomain;
dataDomains[ARM_MODE_USER] = sysDataDomain;
// initialize MPU data structures if required
if(MPU_PRESENT(arm)) {
// create MPU-managed memDomains
arm->ids.vmPriv = vmirtNewDomain("priv MPU Code", bits);
arm->ids.vmUser = vmirtNewDomain("user MPU Code", bits);
arm->dds.vmPriv = vmirtNewDomain("priv MPU Data", bits);
arm->dds.vmUser = vmirtNewDomain("user MPU Data", bits);
// set MPU code memDomains for each mode
codeDomains[ARM_MODE_PRIV_MPU] = arm->ids.vmPriv;
codeDomains[ARM_MODE_USER_MPU] = arm->ids.vmUser;
// set MPU data memDomains for each mode
dataDomains[ARM_MODE_PRIV_MPU] = arm->dds.vmPriv;
dataDomains[ARM_MODE_USER_MPU] = arm->dds.vmUser;
// initialize MPU if required
if(MPU_PRESENT(arm)) {
newMPUs(arm);
}
}
}
//
// Reset VM structures
//
void armVMReset(armP arm) {
if(MPU_PRESENT(arm)) {
resetMPUs(arm);
}
}
//
// Free structures used for virtual memory management
//
void armVMFree(armP arm) {
if(MPU_PRESENT(arm)) {
freeMPUs(arm);
}
}