You copied the Doc URL to your clipboard.
AArch32 Translation.Walk Pseudocode
Library pseudocode for aarch32/translation/walk/AArch32.TranslationTableWalkLD
// AArch32.TranslationTableWalkLD() // ================================ // Returns a result of a translation table walk using the Long-descriptor format // // 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 AArch32.TranslationTableWalkLD(bits(40) ipaddress, bits(32) vaddress, AccType acctype, boolean iswrite, boolean secondstage, boolean s2fs1walk, integer size) if !secondstage then assert ELUsingAArch32(S1TranslationRegime()); else assert HaveEL(EL2) && !IsSecure() && ELUsingAArch32(EL2) && HasS2Translation(); TLBRecord result; AddressDescriptor descaddr; bits(64) baseregister; bits(40) inputaddr; // Input Address is 'vaddress' for stage 1, 'ipaddress' for stage 2 bit nswalk; // Stage 2 translation table walks are to Secure or to Non-secure PA space result.descupdate.AF = FALSE; result.descupdate.AP = FALSE; domain = bits(4) UNKNOWN; descaddr.memattrs.memtype = MemType_Normal; // Fixed parameters for the page table walk: // grainsize = Log2(Size of Table) - Size of Table is 4KB in AArch32 // stride = Log2(Address per Level) - Bits of address consumed at each level constant integer grainsize = 12; // Log2(4KB page size) constant integer stride = grainsize - 3; // Log2(page size / 8 bytes) // Derived parameters for the page table walk: // 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); el = AArch32.AccessUsesEL(acctype); isprivileged = AArch32.AccessIsPrivileged(acctype); if el == EL2 then inputsize = 32 - UInt(HTCR.T0SZ); basefound = inputsize == 32 || IsZero(inputaddr<31:inputsize>); disabled = FALSE; baseregister = HTTBR; descaddr.memattrs = WalkAttrDecode(HTCR.SH0, HTCR.ORGN0, HTCR.IRGN0, secondstage); reversedescriptors = HSCTLR.EE == '1'; lookupsecure = FALSE; singlepriv = TRUE; hierattrsdisabled = AArch32.HaveHPDExt() && HTCR.HPD == '1'; else basefound = FALSE; disabled = FALSE; t0size = UInt(TTBCR.T0SZ); if t0size == 0 || IsZero(inputaddr<31:(32-t0size)>) then inputsize = 32 - t0size; basefound = TRUE; baseregister = TTBR0; descaddr.memattrs = WalkAttrDecode(TTBCR.SH0, TTBCR.ORGN0, TTBCR.IRGN0, secondstage); hierattrsdisabled = AArch32.HaveHPDExt() && TTBCR.T2E == '1' && TTBCR2.HPD0 == '1'; t1size = UInt(TTBCR.T1SZ); if (t1size == 0 && !basefound) || (t1size > 0 && IsOnes(inputaddr<31:(32-t1size)>)) then inputsize = 32 - t1size; basefound = TRUE; baseregister = TTBR1; descaddr.memattrs = WalkAttrDecode(TTBCR.SH1, TTBCR.ORGN1, TTBCR.IRGN1, secondstage); hierattrsdisabled = AArch32.HaveHPDExt() && TTBCR.T2E == '1' && TTBCR2.HPD1 == '1'; reversedescriptors = SCTLR.EE == '1'; lookupsecure = IsSecure(); singlepriv = FALSE; // The starting level is the number of strides needed to consume the input address level = 4 - (1 + ((inputsize - grainsize - 1) DIV stride)); else // Second stage translation inputaddr = ipaddress; inputsize = 32 - SInt(VTCR.T0SZ); // VTCR.S must match VTCR.T0SZ[3] if VTCR.S != VTCR.T0SZ<3> then (-, inputsize) = ConstrainUnpredictableInteger(32-7, 32+8, Unpredictable_RESVTCRS); basefound = inputsize == 40 || IsZero(inputaddr<39:inputsize>); disabled = FALSE; descaddr.memattrs = WalkAttrDecode(VTCR.SH0, VTCR.ORGN0, VTCR.IRGN0, secondstage); reversedescriptors = HSCTLR.EE == '1'; singlepriv = TRUE; lookupsecure = FALSE; baseregister = VTTBR; startlevel = UInt(VTCR.SL0); level = 2 - startlevel; if level <= 0 then basefound = FALSE; // Number of entries in the starting level table = // (Size of Input Address)/((Address per level)^(Num levels remaining)*(Size of Table)) startsizecheck = inputsize - ((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) // That is, VTCR.SL0 == '00' and SInt(VTCR.T0SZ) > 1, Size of Input Address < 2^31 bytes // Upper bound check is: startsizecheck > Log2(pagesize/8*16) // That is, VTCR.SL0 == '01' and SInt(VTCR.T0SZ) < -2, Size of Input Address > 2^34 bytes if startsizecheck < 1 || startsizecheck > stride + 4 then basefound = FALSE; if !basefound || disabled then level = 1; // AArch64 reports this as a level 0 fault result.addrdesc.fault = AArch32.TranslationFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; if !IsZero(baseregister<47:40>) then level = 0; result.addrdesc.fault = AArch32.AddressSizeFault(ipaddress, domain, 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 per level)^(Num levels remaining)*(Size of Table)) baselowerbound = 3 + inputsize - ((3-level)*stride + grainsize); // Log2(Num of entries*8) baseaddress = baseregister<39: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(40) index = ZeroExtend(inputaddr<addrselecttop:addrselectbottom>:'000'); descaddr.paddress.address = ZeroExtend(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 secondstage || !HasS2Translation() || (HaveNV2Ext() && acctype == AccType_NV2REGISTER) then descaddr2 = descaddr; else descaddr2 = AArch32.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); accdesc = CreateAccessDescriptorPTW(acctype, secondstage, s2fs1walk, level); desc = _Mem[descaddr2, 8, accdesc]; 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 = AArch32.TranslationFault(ipaddress, domain, 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 !IsZero(desc<47:40>) then result.addrdesc.fault = AArch32.AddressSizeFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; baseaddress = desc<39:grainsize>:Zeros(grainsize); if !secondstage then // Unpack the upper and lower table attributes ns_table = ns_table OR desc<63>; if !secondstage && !hierattrsdisabled then 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 pxn_table = pxn_table OR desc<59>; ap_table<0> = ap_table<0> OR desc<61>; // privileged level = level + 1; addrselecttop = addrselectbottom - 1; blocktranslate = FALSE; until blocktranslate; // Unpack the descriptor into address and upper and lower block attributes outputaddress = desc<39:addrselectbottom>:inputaddr<addrselectbottom-1:0>; // Check the output address is inside the supported range if !IsZero(desc<47:40>) then result.addrdesc.fault = AArch32.AddressSizeFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; // Check the access flag if desc<10> == '0' then result.addrdesc.fault = AArch32.AccessFlagFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; xn = desc<54>; // Bit[54] of the block/page descriptor holds UXN pxn = desc<53>; // Bit[53] of the block/page descriptor holds PXN ap = desc<7:6>:'1'; // Bits[7:6] of the block/page descriptor hold AP[2:1] contiguousbit = desc<52>; nG = desc<11>; sh = desc<9:8>; 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.GP = desc<50>; // Stage 1 block or pages might be guarded result.perms.ap<0> = '1'; result.addrdesc.memattrs = AArch32.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; if HaveExtendedExecuteNeverExt() then result.perms.xxn = desc<53>; result.perms.pxn = '0'; result.nG = '0'; if s2fs1walk then result.addrdesc.memattrs = S2AttrDecode(sh, memattr, AccType_PTW); else result.addrdesc.memattrs = S2AttrDecode(sh, memattr, acctype); result.addrdesc.paddress.NS = '1'; result.addrdesc.paddress.address = ZeroExtend(outputaddress); result.addrdesc.fault = AArch32.NoFault(); result.contiguous = contiguousbit == '1'; if HaveCommonNotPrivateTransExt() then result.CnP = baseregister<0>; return result;
Library pseudocode for aarch32/translation/walk/AArch32.TranslationTableWalkSD
// AArch32.TranslationTableWalkSD() // ================================ // Returns a result of a translation table walk using the Short-descriptor format // // 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 AArch32.TranslationTableWalkSD(bits(32) vaddress, AccType acctype, boolean iswrite, integer size) assert ELUsingAArch32(S1TranslationRegime()); // This is only called when address translation is enabled TLBRecord result; AddressDescriptor l1descaddr; AddressDescriptor l2descaddr; bits(40) outputaddress; // Variables for Abort functions ipaddress = bits(40) UNKNOWN; secondstage = FALSE; s2fs1walk = FALSE; NS = bit UNKNOWN; // Default setting of the domain and level. domain = bits(4) UNKNOWN; level = 1; // Determine correct Translation Table Base Register to use. bits(64) ttbr; n = UInt(TTBCR.N); if n == 0 || IsZero(vaddress<31:(32-n)>) then ttbr = TTBR0; disabled = (TTBCR.PD0 == '1'); else ttbr = TTBR1; disabled = (TTBCR.PD1 == '1'); n = 0; // TTBR1 translation always works like N=0 TTBR0 translation // Check if Translation table walk disabled for translations with this Base register. if disabled then result.addrdesc.fault = AArch32.TranslationFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; // Obtain descriptor from initial lookup. l1descaddr.paddress.address = ZeroExtend(ttbr<31:14-n>:vaddress<31-n:20>:'00'); l1descaddr.paddress.NS = if IsSecure() then '0' else '1'; IRGN = ttbr<0>:ttbr<6>; // TTBR.IRGN RGN = ttbr<4:3>; // TTBR.RGN SH = ttbr<1>:ttbr<5>; // TTBR.S:TTBR.NOS l1descaddr.memattrs = WalkAttrDecode(SH, RGN, IRGN, secondstage); if !HaveEL(EL2) || (IsSecure() && !IsSecureEL2Enabled()) then // if only 1 stage of translation l1descaddr2 = l1descaddr; else l1descaddr2 = AArch32.SecondStageWalk(l1descaddr, vaddress, acctype, iswrite, 4); // Check for a fault on the stage 2 walk if IsFault(l1descaddr2) then result.addrdesc.fault = l1descaddr2.fault; return result; // Update virtual address for abort functions l1descaddr2.vaddress = ZeroExtend(vaddress); accdesc = CreateAccessDescriptorPTW(acctype, secondstage, s2fs1walk, level); l1desc = _Mem[l1descaddr2, 4,accdesc]; if SCTLR.EE == '1' then l1desc = BigEndianReverse(l1desc); // Process descriptor from initial lookup. case l1desc<1:0> of when '00' // Fault, Reserved result.addrdesc.fault = AArch32.TranslationFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; when '01' // Large page or Small page domain = l1desc<8:5>; level = 2; pxn = l1desc<2>; NS = l1desc<3>; // Obtain descriptor from level 2 lookup. l2descaddr.paddress.address = ZeroExtend(l1desc<31:10>:vaddress<19:12>:'00'); l2descaddr.paddress.NS = if IsSecure() then '0' else '1'; l2descaddr.memattrs = l1descaddr.memattrs; if !HaveEL(EL2) || (IsSecure() && !IsSecureEL2Enabled()) then // if only 1 stage of translation l2descaddr2 = l2descaddr; else l2descaddr2 = AArch32.SecondStageWalk(l2descaddr, vaddress, acctype, iswrite, 4); // Check for a fault on the stage 2 walk if IsFault(l2descaddr2) then result.addrdesc.fault = l2descaddr2.fault; return result; // Update virtual address for abort functions l2descaddr2.vaddress = ZeroExtend(vaddress); accdesc = CreateAccessDescriptorPTW(acctype, secondstage, s2fs1walk, level); l2desc = _Mem[l2descaddr2, 4, accdesc]; if SCTLR.EE == '1' then l2desc = BigEndianReverse(l2desc); // Process descriptor from level 2 lookup. if l2desc<1:0> == '00' then result.addrdesc.fault = AArch32.TranslationFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; nG = l2desc<11>; S = l2desc<10>; ap = l2desc<9,5:4>; if SCTLR.AFE == '1' && l2desc<4> == '0' then // Armv8 VMSAv8-32 does not support hardware management of the Access flag. result.addrdesc.fault = AArch32.AccessFlagFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; if l2desc<1> == '0' then // Large page xn = l2desc<15>; tex = l2desc<14:12>; c = l2desc<3>; b = l2desc<2>; blocksize = 64; outputaddress = ZeroExtend(l2desc<31:16>:vaddress<15:0>); else // Small page tex = l2desc<8:6>; c = l2desc<3>; b = l2desc<2>; xn = l2desc<0>; blocksize = 4; outputaddress = ZeroExtend(l2desc<31:12>:vaddress<11:0>); when '1x' // Section or Supersection NS = l1desc<19>; nG = l1desc<17>; S = l1desc<16>; ap = l1desc<15,11:10>; tex = l1desc<14:12>; xn = l1desc<4>; c = l1desc<3>; b = l1desc<2>; pxn = l1desc<0>; level = 1; if SCTLR.AFE == '1' && l1desc<10> == '0' then // Armv8 VMSAv8-32 does not support hardware management of the Access flag. result.addrdesc.fault = AArch32.AccessFlagFault(ipaddress, domain, level, acctype, iswrite, secondstage, s2fs1walk); return result; if l1desc<18> == '0' then // Section domain = l1desc<8:5>; blocksize = 1024; outputaddress = ZeroExtend(l1desc<31:20>:vaddress<19:0>); else // Supersection domain = '0000'; blocksize = 16384; outputaddress = l1desc<8:5>:l1desc<23:20>:l1desc<31:24>:vaddress<23:0>; // Decode the TEX, C, B and S bits to produce the TLBRecord's memory attributes if SCTLR.TRE == '0' then if RemapRegsHaveResetValues() then result.addrdesc.memattrs = AArch32.DefaultTEXDecode(tex, c, b, S, acctype); else result.addrdesc.memattrs = MemoryAttributes IMPLEMENTATION_DEFINED; else result.addrdesc.memattrs = AArch32.RemappedTEXDecode(tex, c, b, S, acctype); // Set the rest of the TLBRecord, try to add it to the TLB, and return it. result.perms.ap = ap; result.perms.xn = xn; result.perms.pxn = pxn; result.nG = nG; result.domain = domain; result.level = level; result.blocksize = blocksize; result.addrdesc.paddress.address = ZeroExtend(outputaddress); result.addrdesc.paddress.NS = if IsSecure() then NS else '1'; result.addrdesc.fault = AArch32.NoFault(); return result;