D4.2.10 Pseudocode description of VMSAv8-64 address translation
The following subsections gives a pseudocode description of the translation table walk:
- Definitions required for address translation.
- Performing the full address translation.
- Stage 1 translation on page D4-1681.
- Stage 2 translation on page D4-1683.
- Translation table walk on page D4-1684.
- Support functions on page D4-1689.
Definitions required for address translation
In pseudocode, the result of a translation table lookup, in either Execution state, is returned in a TLBRecord structure.
type TLBRecord is (
Permissions perms,
bit nG, // '0' = Global, '1' = not Global
bits(4) domain, // AArch32 only
boolean contiguous, // Contiguous bit from page table
integer level, // In AArch32 Short-descriptort format, indicates Section/Page
integer blocksize, // Describes size of memory translated in KBytes
AddressDescriptor addrdesc
)
Memory data type definitions on page D3-1626 includes definitions of the Permissions and AddressDescriptor parameters.
Performing the full address translation
The function AArch64.FullTranslate() performs a full translation table walk. For any translation regime it performs a stage 1 translation for the supplied virtual address, and for the Non-secure EL1&0 translation regime it then performs a stage 2 translation of the returned address.
// AArch64.FullTranslate()
// =======================
// Perform both stage 1 and stage 2 translation walks for the current translation regime. The
// function used by Address Translation operations is similar except it uses the translation
// regime specified for the instruction.
AddressDescriptor AArch64.FullTranslate(bits(64) vaddress, AccType acctype, boolean iswrite,
// First Stage Translation
S1 = AArch64.FirstStageTranslate(vaddress, acctype, iswrite, wasaligned, size);
if !IsFault(S1) && HaveEL(EL2) && !IsSecure() && PSTATE.EL IN {EL0,EL1} then
s2fs1walk = FALSE;
result = AArch64.SecondStageTranslate(S1, vaddress, acctype, iswrite, wasaligned, s2fs1walk,
size);
else
result = S1;
return result;
Stage 1 translation
The function AArch64.FirstStageTranslate() performs a stage 1 translation, calling the function AArch64.TranslationTableWalk(), described in Translation table walk on page D4-1684, to perform the required translation table walk. However, if stage 1 translation is disabled, it calls the function AArch64.TranslateAddressS1Off(), described in this section, to set the memory attributes.
// AArch64.FirstStageTranslate()
// =============================
// Perform a stage 1 translation walk. The function used by Address Translation operations is
// similar except it uses the translation regime specified for the instruction.
AddressDescriptor AArch64.FirstStageTranslate(bits(64) vaddress, AccType acctype, boolean iswrite, boolean wasaligned, integer size)
if HaveEL(EL2) && !IsSecure() && PSTATE.EL IN {EL0,EL1} then
s1_enabled = HCR_EL2.TGE == '0' && HCR_EL2.DC == '0' && SCTLR_EL1.M == '1';
else
s1_enabled = SCTLR[].M == '1';
ipaddress = bits(48) UNKNOWN;
secondstage = FALSE;
s2fs1walk = FALSE;
if s1_enabled then // First stage enabled
S1 = AArch64.TranslationTableWalk(ipaddress, vaddress, acctype, iswrite, secondstage,
s2fs1walk, size);
permissioncheck = TRUE;
else
S1 = AArch64.TranslateAddressS1Off(vaddress, acctype, iswrite);
permissioncheck = FALSE;
// Check for unaligned data accesses to Device memory
if (!wasaligned && !IsFault(S1.addrdesc) && S1.addrdesc.memattrs.type == MemType_Device &&
acctype != AccType_IFETCH) then
S1.addrdesc.fault = AArch64.AlignmentFault(acctype, iswrite, secondstage);
if !IsFault(S1.addrdesc) && permissioncheck then
S1.addrdesc.fault = AArch64.CheckPermission(S1.perms, vaddress, S1.level,
S1.addrdesc.paddress.NS,
acctype, iswrite);
// Check for instruction fetches from Device memory not marked as execute-never. If there has
// not been a Permission Fault then the memory is not marked execute-never.
if (!IsFault(S1.addrdesc) && S1.addrdesc.memattrs.type == MemType_Device &&
acctype == AccType_IFETCH) then
S1.addrdesc = AArch64.InstructionDevice(S1.addrdesc, vaddress, ipaddress, S1.level,
acctype, iswrite,
secondstage, s2fs1walk);
return S1.addrdesc;
When stage 1 translation is disabled, the function AArch64.TranslateAddressS1Off() sets the memory attributes.
// AArch64.TranslateAddressS1Off()
// ===============================
// Called for stage 1 translations when translation is disabled to supply a default translation.
// Note that there are additional constraints on instruction prefetching that are not described in
// this pseudocode.
TLBRecord AArch64.TranslateAddressS1Off(bits(64) vaddress, AccType acctype, boolean iswrite)
assert !ELUsingAArch32(S1TranslationRegime());
TLBRecord result;
Top = AddrTop(vaddress);
if !IsZero(vaddress<Top:PAMax()>) then
level = 0;
ipaddress = bits(48) UNKNOWN;
secondstage = FALSE;
s2fs1walk = FALSE;
result.addrdesc.fault = AArch64.AddressSizeFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
default_cacheable = (HaveEL(EL2) && !IsSecure() && PSTATE.EL IN {EL0,EL1} && HCR_EL2.DC == '1');
if default_cacheable then
// Use default cacheable settings
result.addrdesc.memattrs.type = MemType_Normal;
result.addrdesc.memattrs.inner.attrs = MemAttr_WB; // Write-back
result.addrdesc.memattrs.inner.hints = MemHint_RWA;
result.addrdesc.memattrs.shareable = FALSE;
result.addrdesc.memattrs.outershareable = FALSE;
elsif acctype != AccType_IFETCH then
// Treat data as Device
result.addrdesc.memattrs.type = MemType_Device;
result.addrdesc.memattrs.device = DeviceType_nGnRnE;
result.addrdesc.memattrs.inner = MemAttrHints UNKNOWN;
else
// Instruction cacheability controlled by SCTLR_ELx.I
cacheable = SCTLR[].I == '1';
result.addrdesc.memattrs.type = MemType_Normal;
if cacheable then
result.addrdesc.memattrs.inner.attrs = MemAttr_WT;
result.addrdesc.memattrs.inner.hints = MemHint_RA;
else
result.addrdesc.memattrs.inner.attrs = MemAttr_NC;
result.addrdesc.memattrs.inner.hints = MemHint_No;
result.addrdesc.memattrs.shareable = TRUE;
result.addrdesc.memattrs.outershareable = TRUE;
result.addrdesc.memattrs.outer = result.addrdesc.memattrs.inner;
result.addrdesc.memattrs = MemAttrDefaults(result.addrdesc.memattrs);
result.perms.ap = bits(3) UNKNOWN;
result.perms.xn = '0';
result.perms.pxn = '0';
result.nG = bit UNKNOWN;
result.contiguous = boolean UNKNOWN;
result.domain = bits(4) UNKNOWN;
result.level = integer UNKNOWN;
result.blocksize = integer UNKNOWN;
result.addrdesc.paddress.physicaladdress = vaddress<47:0>;
result.addrdesc.paddress.NS = if IsSecure() then '0' else '1';
result.addrdesc.fault = AArch64.NoFault();
return result;
Stage 2 translation
In the Non-secure EL1&0 translation regime, a descriptor address returned by stage 1 lookup is in the IPA address space, and must be mapped to a PA by a stage 2 translation. Function AArch64.SecondStageWalk() performs this translation, by calling the AArch64.SecondStageTranslate() function.When called from AArch64.SecondStageWalk(), the AArch64.SecondStageTranslate() function performs a second stage translation, from IPA to PA, of the supplied address, including checking that the access has read permission at the second stage. If the access does not have second stage read permission it generates a second stage Permission fault on the first stage translation table walk. The second stage translation might hit in a TLB, or might involve a translation table walk, which will use the algorithm described in this section.
// AArch64.SecondStageWalk()
// =========================
// Perform a stage 2 translation on a stage 1 translation page table walk access.
AddressDescriptor AArch64.SecondStageWalk(AddressDescriptor S1, bits(64) vaddress, AccType acctype,
boolean iswrite, integer size)
assert HaveEL(EL2) && !IsSecure() && PSTATE.EL IN {EL0,EL1};
s2fs1walk = TRUE;
wasaligned = TRUE;
return AArch64.SecondStageTranslate(S1, vaddress, acctype, iswrite, wasaligned, s2fs1walk,
size);
The AArch64.SecondStageTranslate() function performs the stage 2 address translation.
// AArch64.SecondStageTranslate()
// ==============================
// Perform a stage 2 translation walk. The function used by Address Translation operations is
// similar except it uses the translation regime specified for the instruction.
AddressDescriptor AArch64.SecondStageTranslate(AddressDescriptor S1, bits(64) vaddress,
AccType acctype, boolean iswrite, boolean wasaligned,
boolean s2fs1walk, integer size)
assert HaveEL(EL2) && !IsSecure() && PSTATE.EL IN {EL0,EL1};
s2_enabled = HCR_EL2.VM == '1' || HCR_EL2.DC == '1';
secondstage = TRUE;
if s2_enabled then // Second stage enabled
ipaddress = S1.paddress.physicaladdress<47:0>;
S2 = AArch64.TranslationTableWalk(ipaddress, vaddress, acctype, iswrite, secondstage,
s2fs1walk, size);
// Check for unaligned data accesses to Device memory
if (!wasaligned && !IsFault(S2.addrdesc) && S2.addrdesc.memattrs.type == MemType_Device &&
acctype != AccType_IFETCH) then
S2.addrdesc.fault = AArch64.AlignmentFault(acctype, iswrite, secondstage);
if !IsFault(S2.addrdesc) then
S2.addrdesc.fault = AArch64.CheckS2Permission(S2.perms, vaddress, ipaddress, S2.level,
acctype, iswrite, s2fs1walk);
// Check for instruction fetches from Device memory not marked as execute-never. As there
// has not been a Permission Fault then the memory is not marked execute-never.
if (!IsFault(S2.addrdesc) && S2.addrdesc.memattrs.type == MemType_Device &&
acctype == AccType_IFETCH) then
S2.addrdesc = AArch64.InstructionDevice(S2.addrdesc, vaddress, ipaddress, S2.level,
acctype, iswrite,
secondstage, s2fs1walk);
// Check for protected table walk
if (s2fs1walk && !IsFault(S2.addrdesc) && HCR_EL2.PTW == '1' &&
S2.addrdesc.memattrs.type == MemType_Device) then
S2.addrdesc.fault = AArch64.PermissionFault(ipaddress, S2.level, acctype,
iswrite, secondstage, s2fs1walk);
result = CombineS1S2Desc(S1, S2.addrdesc);
else
result = S1;
return result;
Translation table walk
The function AArch64.TranslationTableWalk() returns the result, in the form of a TLBRecord, of a translation table walk made for a memory access from an Exception level that is using AArch64.
// AArch64.TranslationTableWalk()
// ==============================
// Returns a result of a translation table walk
//
// Implementations might cache information from memory in any number of non-coherent TLB
// caching structures, and so avoid memory accesses that have been expressed in this
// pseudocode. The use of such TLBs is not expressed in this pseudocode.
TLBRecord AArch64.TranslationTableWalk(bits(48) ipaddress, bits(64) vaddress,
AccType acctype, boolean iswrite, boolean secondstage,
boolean s2fs1walk, integer size)
if !secondstage then
assert !ELUsingAArch32(S1TranslationRegime());
else
assert HaveEL(EL2) && !IsSecure() && !ELUsingAArch32(EL2) && PSTATE.EL != EL2;
TLBRecord result;
AddressDescriptor descaddr;
bits(64) baseregister;
bits(64) inputaddr; // Input Address is 'vaddress' for stage 1, 'ipaddress' for stage 2
descaddr.memattrs.type = MemType_Normal;
// Derived parameters for the page table walk:
// grainsize = Log2(Size of Table) - Size of Table is 4KB, 16KB or 64KB in AArch64
// stride = Log2(Address per Level) - Bits of address consumed at each level
// firstblocklevel = First level where a block entry is allowed
// ps = Physical Address size as encoded in TCR_EL1.IPS or TCR_ELx/VTCR_EL2.PS
// inputsize = Log2(Size of Input Address) - Input Address size in bits
// level = Level to start walk from
// This means that the number of levels after start level = 3-level
if !secondstage then
// First stage translation
inputaddr = ZeroExtend(vaddress);
top = AddrTop(inputaddr);
if PSTATE.EL == EL3 then
inputsize = 64 - UInt(TCR_EL3.T0SZ);
if inputsize > 48 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 48;
if inputsize < 25 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 25;
largegrain = TCR_EL3.TG0 == '01';
midgrain = TCR_EL3.TG0 == '10';
ps = TCR_EL3.PS;
basefound = inputsize >= 25 && inputsize <= 48 && IsZero(inputaddr<top:inputsize>); disabled = FALSE;
baseregister = TTBR0_EL3;
descaddr.memattrs = WalkAttrDecode(TCR_EL3.SH0, TCR_EL3.ORGN0, TCR_EL3.IRGN0); reversedescriptors = SCTLR_EL3.EE == '1';
lookupsecure = TRUE;
singlepriv = TRUE;
elsif PSTATE.EL == EL2 then
inputsize = 64 - UInt(TCR_EL2.T0SZ);
if inputsize > 48 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 48;
if inputsize < 25 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 25;
largegrain = TCR_EL2.TG0 == '01';
midgrain = TCR_EL2.TG0 == '10';
ps = TCR_EL2.PS;
basefound = inputsize >= 25 && inputsize <= 48 && IsZero(inputaddr<top:inputsize>); disabled = FALSE;
baseregister = TTBR0_EL2;
descaddr.memattrs = WalkAttrDecode(TCR_EL2.SH0, TCR_EL2.ORGN0, TCR_EL2.IRGN0); reversedescriptors = SCTLR_EL2.EE == '1';
lookupsecure = FALSE;
singlepriv = TRUE;
else
if inputaddr<top> == '0' then
inputsize = 64 - UInt(TCR_EL1.T0SZ);
if inputsize > 48 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 48;
if inputsize < 25 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 25;
largegrain = TCR_EL1.TG0 == '01';
midgrain = TCR_EL1.TG0 == '10';
basefound = inputsize >= 25 && inputsize <= 48 && IsZero(inputaddr<top:inputsize>); disabled = TCR_EL1.EPD0 == '1';
baseregister = TTBR0_EL1;
descaddr.memattrs = WalkAttrDecode(TCR_EL1.SH0, TCR_EL1.ORGN0, TCR_EL1.IRGN0);
else
inputsize = 64 - UInt(TCR_EL1.T1SZ);
if inputsize > 48 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 48;
if inputsize < 25 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 25;
largegrain = TCR_EL1.TG1 == '11'; // TG1 and TG0 encodings differ
midgrain = TCR_EL1.TG1 == '01';
basefound = inputsize >= 25 && inputsize <= 48 && IsOnes(inputaddr<top:inputsize>); disabled = TCR_EL1.EPD1 == '1';
baseregister = TTBR1_EL1;
descaddr.memattrs = WalkAttrDecode(TCR_EL1.SH1, TCR_EL1.ORGN1, TCR_EL1.IRGN1);
ps = TCR_EL1.IPS;
reversedescriptors = SCTLR_EL1.EE == '1';
lookupsecure = IsSecure();
singlepriv = FALSE;
if largegrain then
grainsize = 16; // Log2(64KB page size)
firstblocklevel =2; // Largest block is 512MB (2^29 bytes)
elsif midgrain then
grainsize = 14; // Log2(16KB page size)
firstblocklevel =2; // Largest block is 32MB (2^25 bytes)
else // Small grain
grainsize = 12; // Log2(4KB page size)
firstblocklevel = 1; // Largest block is 1GB (2^30 bytes)
stride = grainsize - 3; // Log2(page size / 8 bytes)
// The starting level is the number of strides needed to consume the input address
level = 4 - RoundUp(Real(inputsize - grainsize) / Real(stride));
else
// Second stage translation
inputaddr = ZeroExtend(ipaddress);
inputsize = 64 - UInt(VTCR_EL2.T0SZ);
if inputsize > 48 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 48;
if inputsize < 25 then
c = ConstrainUnpredictable();
assert c IN {Constraint_FORCE, Constraint_FAULT};
if c == Constraint_FORCE then inputsize = 25;
largegrain = VTCR_EL2.TG0 == '01';
midgrain = VTCR_EL2.TG0 == '10';
ps = VTCR_EL2.PS;
basefound = inputsize >= 25 && inputsize <= 48 && IsZero(inputaddr<63:inputsize>);
disabled = FALSE;
baseregister = VTTBR_EL2;
descaddr.memattrs = WalkAttrDecode(VTCR_EL2.IRGN0, VTCR_EL2.ORGN0, VTCR_EL2.SH0); reversedescriptors = SCTLR_EL2.EE == '1';
lookupsecure = FALSE;
singlepriv = TRUE;
startlevel = UInt(VTCR_EL2.SL0);
if largegrain then
grainsize = 16; // Log2(64KB page size)
level = 3 - startlevel;
firstblocklevel = 2; // Largest block is 512MB (2^29 bytes)
elsif midgrain then
grainsize = 14; // Log2(16KB page size)
level = 3 - startlevel;
firstblocklevel = 2; // Largest block is 32MB (2^25 bytes)
else // Small grain
grainsize = 12; // Log2(4KB page size)
level = 2 - startlevel;
firstblocklevel = 1; // Largest block is 1GB (2^30 bytes)
stride = grainsize - 3; // Log2(page size / 8 bytes)
// Limits on IPA controls based on implemented PA size. Level 0 is only
// supported by small grain translations
if largegrain then // 64KB pages
// Level 1 only supported if implemented PA size is greater than 2^42 bytes
if level == 0 || (level == 1 && PAMax() <= 42) then basefound = FALSE;
elsif midgrain then // 16KB pages
// Level 1 only supported if implemented PA size is greater than 2^40 bytes
if level == 0 || (level == 1 && PAMax() <= 40) then basefound = FALSE;
else // Small grain, 4KB pages
// Level 0 only supported if implemented PA size is greater than 2^42 bytes
if level < 0 || (level == 0 && PAMax() <= 42) then basefound = FALSE;
// If the inputsize exceeds the PAMax value, the behavior is CONSTRAINED UNPREDICTABLE
inputsizecheck = inputsize;
if inputsize > PAMax() && (!ELUsingAArch32(EL1) || inputsize > 40) then
case ConstrainUnpredictable() of
when Constraint_FORCE
// Restrict the inputsize to the PAMax value
inputsize = PAMax();
inputsizecheck = PAMax();
when Constraint_FORCENOSLCHECK
// As FORCE, except use the configured inputsize in the size checks below
inputsize = PAMax();
when Constraint_FAULT
// Generate a translation fault
basefound = FALSE;
otherwise
Unreachable();
// Number of entries in the starting level table =
// (Size of Input Address)/((Address per level)^(Num levels remaining)*(Size of Table)) startsizecheck = inputsizecheck - ((3 - level)*stride + grainsize); // Log2(Num of entries)
// Check for starting level table with fewer than 2 entries or longer than 16 pages.
// Lower bound check is: startsizecheck < Log2(2 entries)
// Upper bound check is: startsizecheck > Log2(pagesize/8*16)
if startsizecheck < 1 || startsizecheck > stride + 4 then basefound = FALSE;
if !basefound || disabled then
level = 0; // AArch32 reports this as a level 1 fault
result.addrdesc.fault = AArch64.TranslationFault(ipaddress, level, acctype, iswrite,
secondstage, s2fs1walk);
return result;
case ps of
when '000' outputsize = 32;
when '001' outputsize = 36;
when '010' outputsize = 40;
when '011' outputsize = 42;
when '100' outputsize = 44;
when '101' outputsize = 48;
otherwise outputsize = 48;
if outputsize > PAMax() then outputsize = PAMax();
if outputsize != 48 && !IsZero(baseregister<47:outputsize>) then
level = 0;
result.addrdesc.fault = AArch64.AddressSizeFault(ipaddress, level, acctype, iswrite,
secondstage, s2fs1walk);
return result;
// Bottom bound of the Base address is:
// Log2(8 bytes per entry)+Log2(Number of entries in starting level table)
// Number of entries in starting level table =
// (Size of Input Address)/(Address pre level)^(Num level remaining)*(Size of Table))
baselowerbound = 3 + inputsize - ((3-level)*stride + grainsize); // Log2(Num of entries*8)
baseaddress = baseregister<47:baselowerbound>:Zeros(baselowerbound);
ns_table = if lookupsecure then '0' else '1';
ap_table = '00';
xn_table = '0';
pxn_table = '0';
addrselecttop = inputsize - 1;
repeat
addrselectbottom = (3-level)*stride + grainsize;
bits(48) index = ZeroExtend(inputaddr<addrselecttop:addrselectbottom>:'000'); descaddr.paddress.physicaladdress = baseaddress OR index;
descaddr.paddress.NS = ns_table;
// If there are two stages of translation, then the first stage table walk addresses
// are themselves subject to translation
if !HaveEL(EL2) || secondstage || IsSecure() || PSTATE.EL == EL2 then
descaddr2 = descaddr;
else
descaddr2 = AArch64.SecondStageWalk(descaddr, vaddress, acctype, iswrite, 8);
// Check for a fault on the stage 2 walk
if IsFault(descaddr2) then
result.addrdesc.fault = descaddr2.fault;
return result;
// Update virtual address for abort functions
descaddr2.vaddress = ZeroExtend(vaddress);
desc = _Mem[descaddr2, 8, AccType_PTW];
if reversedescriptors then desc = BigEndianReverse(desc);
if desc<0> == '0' || (desc<1:0> == '01' && level == 3) then
// Fault (00), Reserved (10), or Block (01) at level 3
result.addrdesc.fault = AArch64.TranslationFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
// Valid Block, Page, or Table entry
if desc<1:0> == '01' || level == 3 then // Block (01) or Page (11)
blocktranslate = TRUE;
else // Table (11)
if outputsize != 48 && !IsZero(desc<47:outputsize>) then
result.addrdesc.fault = AArch64.AddressSizeFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
baseaddress = desc<47:grainsize>:Zeros(grainsize);
if !secondstage then
// Unpack the upper and lower table attributes
ns_table = ns_table OR desc<63>;
ap_table<1> = ap_table<1> OR desc<62>; // read-only
xn_table = xn_table OR desc<60>;
// pxn_table and ap_table[0] apply only in EL1&0 translation regimes
if !singlepriv then
ap_table<0> = ap_table<0> OR desc<61>; // privileged
pxn_table = pxn_table OR desc<59>;
level = level + 1;
addrselecttop = addrselectbottom - 1; blocktranslate = FALSE;
until blocktranslate;
// Check block size is supported at this level
if level < firstblocklevel then
result.addrdesc.fault = AArch64.TranslationFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
// Check for misprogramming of the contiguous bit
if largegrain then
contiguousbitcheck = level == 2 && inputsize < 34;
elsif midgrain then
contiguousbitcheck = level == 2 && inputsize < 30;
else
contiguousbitcheck = level == 1 && inputsize < 34;
if contiguousbitcheck && desc<52> == '1' then
if boolean IMPLEMENTATION_DEFINED "Translation fault on misprogrammed contiguous bit" then
result.addrdesc.fault = AArch64.TranslationFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
// Check the output address is inside the supported range
if outputsize != 48 && !IsZero(desc<47:outputsize>) then
result.addrdesc.fault = AArch64.AddressSizeFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
// Check the access flag
if desc<10> == '0' then
result.addrdesc.fault = AArch64.AccessFlagFault(ipaddress, level, acctype,
iswrite, secondstage, s2fs1walk);
return result;
// Unpack the descriptor into address and upper and lower block attributes
outputaddress = desc<47:addrselectbottom>:inputaddr<addrselectbottom-1:0>;
xn = desc<54>;
pxn = desc<53>;
contiguousbit = desc<52>;
nG = desc<11>;
sh = desc<9:8>;
ap = desc<7:6>:'1';
memattr = desc<5:2>; // AttrIndx and NS bit in stage 1
result.domain = bits(4) UNKNOWN; // Domains not used
result.level = level;
result.blocksize = 2^((3-level)*stride + grainsize);
// Stage 1 translation regimes also inherit attributes from the tables
if !secondstage then
result.perms.xn = xn OR xn_table;
result.perms.ap<2> = ap<2> OR ap_table<1>; // Force read-only
// PXN, nG and AP[1] apply only in EL1&0 stage 1 translation regimes
if !singlepriv then
result.perms.ap<1> = ap<1> AND NOT(ap_table<0>); // Force privileged only
result.perms.pxn = pxn OR pxn_table;
// Pages from Non-secure tables are marked non-global in Secure EL1&0
if IsSecure() then
result.nG = nG OR ns_table;
else
result.nG = nG;
else
result.perms.ap<1> = '1';
result.perms.pxn = '0';
result.nG = '0';
result.perms.ap<0> = '1';
result.addrdesc.memattrs = AArch64.S1AttrDecode(sh, memattr<2:0>, acctype); result.addrdesc.paddress.NS = memattr<3> OR ns_table;
else
result.perms.ap<2:1> = ap<2:1>;
result.perms.ap<0> = '1';
result.perms.xn = xn;
result.perms.pxn = '0';
result.nG = '0';
result.addrdesc.memattrs = S2AttrDecode(sh, memattr, acctype);
result.addrdesc.paddress.NS = '1';
result.addrdesc.paddress.physicaladdress = outputaddress;
result.addrdesc.fault = AArch64.NoFault();
result.contiguous = contiguousbit == '1';
return result;
Support functions
In the translation table walk functions, the WalkAttrDecode() function determines the attributes for a translation table lookup.
// WalkAttrDecode()
// ================
MemoryAttributes WalkAttrDecode(bits(2) SH, bits(2) ORGN, bits(2) IRGN)
MemoryAttributes memattrs;
AccType acctype = AccType_NORMAL;
memattrs.type = MemType_Normal;
memattrs.inner = ShortConvertAttrsHints(IRGN, acctype);
memattrs.outer = ShortConvertAttrsHints(ORGN, acctype);
memattrs.shareable = SH<1> == '1';
memattrs.outershareable = SH == '10';
return MemAttrDefaults(memattrs);
The function AArch64.S1AttrDecode() decodes the attributes from a stage 1 translation table lookup.
// AArch64.S1AttrDecode()
// ======================
// Converts the Stage 1 attribute fields, using the MAIR, to orthogonal
// attributes and hints.
MemoryAttributes AArch64.S1AttrDecode(bits(2) SH, bits(3) attr, AccType acctype)
MemoryAttributes memattrs;
mair = MAIR[];
index = 8 * UInt(attr);
attrfield = mair<index+7:index>;
if ((attrfield<7:4> != '0000' && attrfield<3:0> == '0000') ||
(attrfield<7:4> == '0000' && attrfield<3:0> != 'xx00')) then
// Reserved, maps to an allocated value
(-, attrfield) = ConstrainUnpredictableBits();
if attrfield<7:4> == '0000' then // Device
memattrs.type = MemType_Device;
case attrfield<3:0> of
when '0000' memattrs.device = DeviceType_nGnRnE;
when '0100' memattrs.device = DeviceType_nGnRE;
when '1000' memattrs.device = DeviceType_nGRE;
when '1100' memattrs.device = DeviceType_GRE;
otherwise Unreachable(); // Reserved, handled above
elsif attrfield<3:0> != '0000' then // Normal
memattrs.type = MemType_Normal;
memattrs.outer = LongConvertAttrsHints(attrfield<7:4>, acctype);
memattrs.inner = LongConvertAttrsHints(attrfield<3:0>, acctype);
memattrs.shareable = SH<1> == '1';
memattrs.outershareable = SH == '10';
else
Unreachable(); // Reserved, handled above
return MemAttrDefaults(memattrs);
The function AArch64.CheckPermission() checks the access permissions returned by a stage 1 translation table lookup, see Access permission checking on page D3-1631.
The function AArch64.CheckS2Permission() checks the access permissions returned by a stage 2 translation table lookup.
// AArch64.CheckS2Permission()
// ===========================
// Function used for permission checking from AArch64 stage 2 translations
FaultRecord AArch64.CheckS2Permission(Permissions perms, bits(64) vaddress, bits(48) ipaddress,
integer level, AccType acctype, boolean iswrite,
boolean s2fs1walk)
assert HaveEL(EL2) && !IsSecure() && !ELUsingAArch32(EL2) && PSTATE.EL != EL2;
r = perms.ap<1> == '1';
w = perms.ap<2> == '1';
xn = perms.xn == '1';
// Stage 1 walk is checked as a read, regardless of the original type
if acctype == AccType_IFETCH && !s2fs1walk then
fail = xn;
elsif iswrite && !s2fs1walk then
fail = !w;
failedread = FALSE;
else
fail = !r;
failedread = TRUE;
if fail then
domain = bits(4) UNKNOWN;
secondstage = TRUE;
return AArch64.PermissionFault(ipaddress, level, acctype,
!failedread, secondstage, s2fs1walk);
else
return AArch64.NoFault();
The AddrTop() function returns the bit number of the most significant valid bit of a VA in the current translation regime. If EL1 is using AArch64 and EL0 is using AArch32 then an address from EL0 is zero-extended to 64 bits.
integer AddrTop(bits(64) address);