table_walker.hh revision 13784
13691SN/A/* 23691SN/A * Copyright (c) 2010-2016 ARM Limited 39449SAli.Saidi@ARM.com * All rights reserved 49729Sandreas.hansson@arm.com * 59729Sandreas.hansson@arm.com * The license below extends only to copyright in the software and shall 68721SN/A * not be construed as granting a license to any other intellectual 79729Sandreas.hansson@arm.com * property including but not limited to intellectual property relating 89729Sandreas.hansson@arm.com * to a hardware implementation of the functionality of the software 99729Sandreas.hansson@arm.com * licensed hereunder. You may use the software subject to the license 109729Sandreas.hansson@arm.com * terms below provided that you ensure that this notice is replicated 119729Sandreas.hansson@arm.com * unmodified and in its entirety in all distributions of the software, 129729Sandreas.hansson@arm.com * modified or unmodified, in source code or in binary form. 139729Sandreas.hansson@arm.com * 149729Sandreas.hansson@arm.com * Redistribution and use in source and binary forms, with or without 159729Sandreas.hansson@arm.com * modification, are permitted provided that the following conditions are 169729Sandreas.hansson@arm.com * met: redistributions of source code must retain the above copyright 179729Sandreas.hansson@arm.com * notice, this list of conditions and the following disclaimer; 189729Sandreas.hansson@arm.com * redistributions in binary form must reproduce the above copyright 199729Sandreas.hansson@arm.com * notice, this list of conditions and the following disclaimer in the 209729Sandreas.hansson@arm.com * documentation and/or other materials provided with the distribution; 219055Ssaidi@eecs.umich.edu * neither the name of the copyright holders nor the names of its 229729Sandreas.hansson@arm.com * contributors may be used to endorse or promote products derived from 239729Sandreas.hansson@arm.com * this software without specific prior written permission. 249729Sandreas.hansson@arm.com * 259729Sandreas.hansson@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 269729Sandreas.hansson@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 279729Sandreas.hansson@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 289055Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 299729Sandreas.hansson@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 309729Sandreas.hansson@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 319729Sandreas.hansson@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 329729Sandreas.hansson@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 339729Sandreas.hansson@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 349729Sandreas.hansson@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 359729Sandreas.hansson@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 369729Sandreas.hansson@arm.com * 379247Sandreas.hansson@arm.com * Authors: Ali Saidi 389729Sandreas.hansson@arm.com * Giacomo Gabrielli 399729Sandreas.hansson@arm.com */ 409729Sandreas.hansson@arm.com 419729Sandreas.hansson@arm.com#ifndef __ARCH_ARM_TABLE_WALKER_HH__ 429729Sandreas.hansson@arm.com#define __ARCH_ARM_TABLE_WALKER_HH__ 439729Sandreas.hansson@arm.com 449729Sandreas.hansson@arm.com#include <list> 459729Sandreas.hansson@arm.com 468721SN/A#include "arch/arm/miscregs.hh" 478721SN/A#include "arch/arm/system.hh" 488721SN/A#include "arch/arm/tlb.hh" 498721SN/A#include "mem/request.hh" 508721SN/A#include "params/ArmTableWalker.hh" 518721SN/A#include "sim/eventq.hh" 528721SN/A 538721SN/Aclass ThreadContext; 548721SN/A 558721SN/Aclass DmaPort; 568721SN/A 578721SN/Anamespace ArmISA { 588721SN/Aclass Translation; 598721SN/Aclass TLB; 608721SN/Aclass Stage2MMU; 618721SN/A 629729Sandreas.hansson@arm.comclass TableWalker : public MemObject 638721SN/A{ 648721SN/A public: 658721SN/A class WalkerState; 669729Sandreas.hansson@arm.com 678721SN/A class DescriptorBase { 688721SN/A public: 698721SN/A /** Current lookup level for this descriptor */ 709729Sandreas.hansson@arm.com LookupLevel lookupLevel; 716024SN/A 728721SN/A virtual Addr pfn() const = 0; 738721SN/A virtual TlbEntry::DomainType domain() const = 0; 749729Sandreas.hansson@arm.com virtual bool xn() const = 0; 758721SN/A virtual uint8_t ap() const = 0; 768721SN/A virtual bool global(WalkerState *currState) const = 0; 779729Sandreas.hansson@arm.com virtual uint8_t offsetBits() const = 0; 788721SN/A virtual bool secure(bool have_security, WalkerState *currState) const = 0; 798721SN/A virtual std::string dbgHeader() const = 0; 808721SN/A virtual uint64_t getRawData() const = 0; 818721SN/A virtual uint8_t texcb() const 828721SN/A { 838721SN/A panic("texcb() not implemented for this class\n"); 848721SN/A } 858721SN/A virtual bool shareable() const 866024SN/A { 876024SN/A panic("shareable() not implemented for this class\n"); 888721SN/A } 898721SN/A }; 909729Sandreas.hansson@arm.com 918721SN/A class L1Descriptor : public DescriptorBase { 928721SN/A public: 939729Sandreas.hansson@arm.com /** Type of page table entry ARM DDI 0406B: B3-8*/ 949729Sandreas.hansson@arm.com enum EntryType { 959729Sandreas.hansson@arm.com Ignore, 969729Sandreas.hansson@arm.com PageTable, 979729Sandreas.hansson@arm.com Section, 989729Sandreas.hansson@arm.com Reserved 999729Sandreas.hansson@arm.com }; 1009729Sandreas.hansson@arm.com 1019729Sandreas.hansson@arm.com /** The raw bits of the entry */ 1029729Sandreas.hansson@arm.com uint32_t data; 1039729Sandreas.hansson@arm.com 1049729Sandreas.hansson@arm.com /** This entry has been modified (access flag set) and needs to be 1059729Sandreas.hansson@arm.com * written back to memory */ 1069729Sandreas.hansson@arm.com bool _dirty; 1079729Sandreas.hansson@arm.com 1089729Sandreas.hansson@arm.com /** Default ctor */ 1099729Sandreas.hansson@arm.com L1Descriptor() : data(0), _dirty(false) 1109729Sandreas.hansson@arm.com { 1119729Sandreas.hansson@arm.com lookupLevel = L1; 1123691SN/A } 1139729Sandreas.hansson@arm.com 1149729Sandreas.hansson@arm.com virtual uint64_t getRawData() const 1159729Sandreas.hansson@arm.com { 1169729Sandreas.hansson@arm.com return (data); 1179449SAli.Saidi@ARM.com } 1189729Sandreas.hansson@arm.com 1199729Sandreas.hansson@arm.com virtual std::string dbgHeader() const 1209729Sandreas.hansson@arm.com { 1219729Sandreas.hansson@arm.com return "Inserting Section Descriptor into TLB\n"; 1229729Sandreas.hansson@arm.com } 1239729Sandreas.hansson@arm.com 1249729Sandreas.hansson@arm.com virtual uint8_t offsetBits() const 1259729Sandreas.hansson@arm.com { 1269729Sandreas.hansson@arm.com return 20; 1279449SAli.Saidi@ARM.com } 1289729Sandreas.hansson@arm.com 1299729Sandreas.hansson@arm.com EntryType type() const 1309449SAli.Saidi@ARM.com { 1316127SN/A return (EntryType)(data & 0x3); 1326127SN/A } 1339729Sandreas.hansson@arm.com 1349729Sandreas.hansson@arm.com /** Is the page a Supersection (16MB)?*/ 1356291SN/A bool supersection() const 1366291SN/A { 1376291SN/A return bits(data, 18); 1386291SN/A } 1396291SN/A 1406291SN/A /** Return the physcal address of the entry, bits in position*/ 1416291SN/A Addr paddr() const 1426291SN/A { 1436291SN/A if (supersection()) 1446291SN/A panic("Super sections not implemented\n"); 1456291SN/A return mbits(data, 31, 20); 1466291SN/A } 1476291SN/A /** Return the physcal address of the entry, bits in position*/ 1486291SN/A Addr paddr(Addr va) const 1496291SN/A { 1506291SN/A if (supersection()) 1516291SN/A panic("Super sections not implemented\n"); 1526291SN/A return mbits(data, 31, 20) | mbits(va, 19, 0); 1536291SN/A } 1546291SN/A 1556291SN/A 1566127SN/A /** Return the physical frame, bits shifted right */ 1579729Sandreas.hansson@arm.com Addr pfn() const 1589449SAli.Saidi@ARM.com { 1599729Sandreas.hansson@arm.com if (supersection()) 1609449SAli.Saidi@ARM.com panic("Super sections not implemented\n"); 1619449SAli.Saidi@ARM.com return bits(data, 31, 20); 1629490Sandreas.hansson@arm.com } 1639490Sandreas.hansson@arm.com 1649449SAli.Saidi@ARM.com /** Is the translation global (no asid used)? */ 1659449SAli.Saidi@ARM.com bool global(WalkerState *currState) const 1669729Sandreas.hansson@arm.com { 1679729Sandreas.hansson@arm.com return !bits(data, 17); 1689729Sandreas.hansson@arm.com } 1699729Sandreas.hansson@arm.com 1709449SAli.Saidi@ARM.com /** Is the translation not allow execution? */ 1719729Sandreas.hansson@arm.com bool xn() const 1729729Sandreas.hansson@arm.com { 1739729Sandreas.hansson@arm.com return bits(data, 4); 1748721SN/A } 1759729Sandreas.hansson@arm.com 1769490Sandreas.hansson@arm.com /** Three bit access protection flags */ 1779729Sandreas.hansson@arm.com uint8_t ap() const 1789729Sandreas.hansson@arm.com { 1799729Sandreas.hansson@arm.com return (bits(data, 15) << 2) | bits(data, 11, 10); 1809729Sandreas.hansson@arm.com } 1818721SN/A 1828721SN/A /** Domain Client/Manager: ARM DDI 0406B: B3-31 */ 1838721SN/A TlbEntry::DomainType domain() const 1848721SN/A { 1858721SN/A return static_cast<TlbEntry::DomainType>(bits(data, 8, 5)); 1868721SN/A } 1878721SN/A 1888721SN/A /** Address of L2 descriptor if it exists */ 1898721SN/A Addr l2Addr() const 1908721SN/A { 1919729Sandreas.hansson@arm.com return mbits(data, 31, 10); 1923691SN/A } 1939729Sandreas.hansson@arm.com 1943691SN/A /** Memory region attributes: ARM DDI 0406B: B3-32. 1959449SAli.Saidi@ARM.com * These bits are largly ignored by M5 and only used to 1968721SN/A * provide the illusion that the memory system cares about 1973691SN/A * anything but cachable vs. uncachable. 1987461SN/A */ 1999449SAli.Saidi@ARM.com uint8_t texcb() const 2009449SAli.Saidi@ARM.com { 2018721SN/A return bits(data, 2) | bits(data, 3) << 1 | bits(data, 14, 12) << 2; 2028721SN/A } 2038721SN/A 2048721SN/A /** If the section is shareable. See texcb() comment. */ 2058721SN/A bool shareable() const 2068721SN/A { 2078721SN/A return bits(data, 16); 2088721SN/A } 2098721SN/A 2108721SN/A /** Set access flag that this entry has been touched. Mark 2118721SN/A * the entry as requiring a writeback, in the future. 2129247Sandreas.hansson@arm.com */ 2138721SN/A void setAp0() 2143691SN/A { 2158721SN/A data |= 1 << 10; 2168721SN/A _dirty = true; 2178721SN/A } 2189490Sandreas.hansson@arm.com 2199247Sandreas.hansson@arm.com /** This entry needs to be written back to memory */ 2209729Sandreas.hansson@arm.com bool dirty() const 2218721SN/A { 2228721SN/A return _dirty; 2238721SN/A } 2248721SN/A 2258721SN/A /** 2263691SN/A * Returns true if this entry targets the secure physical address 2278721SN/A * map. 2289729Sandreas.hansson@arm.com */ 2298721SN/A bool secure(bool have_security, WalkerState *currState) const 2309729Sandreas.hansson@arm.com { 2319729Sandreas.hansson@arm.com if (have_security) { 2329729Sandreas.hansson@arm.com if (type() == PageTable) 2339729Sandreas.hansson@arm.com return !bits(data, 3); 2349729Sandreas.hansson@arm.com else 2359729Sandreas.hansson@arm.com return !bits(data, 19); 2369729Sandreas.hansson@arm.com } 2379729Sandreas.hansson@arm.com return false; 2389729Sandreas.hansson@arm.com } 2399055Ssaidi@eecs.umich.edu }; 2409729Sandreas.hansson@arm.com 2419729Sandreas.hansson@arm.com /** Level 2 page table descriptor */ 2429729Sandreas.hansson@arm.com class L2Descriptor : public DescriptorBase { 2439729Sandreas.hansson@arm.com public: 2449729Sandreas.hansson@arm.com /** The raw bits of the entry. */ 2459729Sandreas.hansson@arm.com uint32_t data; 2469055Ssaidi@eecs.umich.edu L1Descriptor *l1Parent; 2479729Sandreas.hansson@arm.com 2489729Sandreas.hansson@arm.com /** This entry has been modified (access flag set) and needs to be 2499729Sandreas.hansson@arm.com * written back to memory */ 2509729Sandreas.hansson@arm.com bool _dirty; 2519729Sandreas.hansson@arm.com 2529729Sandreas.hansson@arm.com /** Default ctor */ 2539729Sandreas.hansson@arm.com L2Descriptor() : data(0), l1Parent(nullptr), _dirty(false) 2549729Sandreas.hansson@arm.com { 2559449SAli.Saidi@ARM.com lookupLevel = L2; 2569729Sandreas.hansson@arm.com } 2579729Sandreas.hansson@arm.com 2589729Sandreas.hansson@arm.com L2Descriptor(L1Descriptor &parent) : data(0), l1Parent(&parent), 2599729Sandreas.hansson@arm.com _dirty(false) 2609729Sandreas.hansson@arm.com { 2619729Sandreas.hansson@arm.com lookupLevel = L2; 2629729Sandreas.hansson@arm.com } 2639729Sandreas.hansson@arm.com 2648721SN/A virtual uint64_t getRawData() const 2658721SN/A { 2668721SN/A return (data); 2678721SN/A } 2688721SN/A 2698721SN/A virtual std::string dbgHeader() const 2708721SN/A { 2718721SN/A return "Inserting L2 Descriptor into TLB\n"; 2728721SN/A } 2738721SN/A 2748721SN/A virtual TlbEntry::DomainType domain() const 2758721SN/A { 2768721SN/A return l1Parent->domain(); 2778721SN/A } 2788721SN/A 2798721SN/A bool secure(bool have_security, WalkerState *currState) const 2809729Sandreas.hansson@arm.com { 2818721SN/A return l1Parent->secure(have_security, currState); 2828721SN/A } 2839247Sandreas.hansson@arm.com 2849729Sandreas.hansson@arm.com virtual uint8_t offsetBits() const 2858721SN/A { 2868721SN/A return large() ? 16 : 12; 2879247Sandreas.hansson@arm.com } 2889729Sandreas.hansson@arm.com 2898721SN/A /** Is the entry invalid */ 2908721SN/A bool invalid() const 2919247Sandreas.hansson@arm.com { 2929729Sandreas.hansson@arm.com return bits(data, 1, 0) == 0; 2938721SN/A } 2948721SN/A 2959729Sandreas.hansson@arm.com /** What is the size of the mapping? */ 2968721SN/A bool large() const 2978721SN/A { 2988721SN/A return bits(data, 1) == 0; 2998721SN/A } 3008721SN/A 3018721SN/A /** Is execution allowed on this mapping? */ 3028721SN/A bool xn() const 3038721SN/A { 3048721SN/A return large() ? bits(data, 15) : bits(data, 0); 3058721SN/A } 3068721SN/A 3078721SN/A /** Is the translation global (no asid used)? */ 3089729Sandreas.hansson@arm.com bool global(WalkerState *currState) const 3098721SN/A { 3108721SN/A return !bits(data, 11); 3119729Sandreas.hansson@arm.com } 3129729Sandreas.hansson@arm.com 3139729Sandreas.hansson@arm.com /** Three bit access protection flags */ 3149247Sandreas.hansson@arm.com uint8_t ap() const 3159729Sandreas.hansson@arm.com { 3169729Sandreas.hansson@arm.com return bits(data, 5, 4) | (bits(data, 9) << 2); 3179729Sandreas.hansson@arm.com } 3189247Sandreas.hansson@arm.com 3199729Sandreas.hansson@arm.com /** Memory region attributes: ARM DDI 0406B: B3-32 */ 3209729Sandreas.hansson@arm.com uint8_t texcb() const 3219247Sandreas.hansson@arm.com { 3229247Sandreas.hansson@arm.com return large() ? 3239729Sandreas.hansson@arm.com (bits(data, 2) | (bits(data, 3) << 1) | (bits(data, 14, 12) << 2)) : 3249729Sandreas.hansson@arm.com (bits(data, 2) | (bits(data, 3) << 1) | (bits(data, 8, 6) << 2)); 3259729Sandreas.hansson@arm.com } 3269729Sandreas.hansson@arm.com 3279729Sandreas.hansson@arm.com /** Return the physical frame, bits shifted right */ 3289729Sandreas.hansson@arm.com Addr pfn() const 3299729Sandreas.hansson@arm.com { 3308721SN/A return large() ? bits(data, 31, 16) : bits(data, 31, 12); 3319729Sandreas.hansson@arm.com } 3329729Sandreas.hansson@arm.com 3339729Sandreas.hansson@arm.com /** Return complete physical address given a VA */ 3349729Sandreas.hansson@arm.com Addr paddr(Addr va) const 3359449SAli.Saidi@ARM.com { 3369729Sandreas.hansson@arm.com if (large()) 3379729Sandreas.hansson@arm.com return mbits(data, 31, 16) | mbits(va, 15, 0); 3389729Sandreas.hansson@arm.com else 3399729Sandreas.hansson@arm.com return mbits(data, 31, 12) | mbits(va, 11, 0); 3409449SAli.Saidi@ARM.com } 3419729Sandreas.hansson@arm.com 3429729Sandreas.hansson@arm.com /** If the section is shareable. See texcb() comment. */ 3439729Sandreas.hansson@arm.com bool shareable() const 3449729Sandreas.hansson@arm.com { 3459449SAli.Saidi@ARM.com return bits(data, 10); 3469729Sandreas.hansson@arm.com } 3479729Sandreas.hansson@arm.com 3488721SN/A /** Set access flag that this entry has been touched. Mark 3498721SN/A * the entry as requiring a writeback, in the future. 3508721SN/A */ 3519729Sandreas.hansson@arm.com void setAp0() 3529729Sandreas.hansson@arm.com { 3538721SN/A data |= 1 << 4; 3548721SN/A _dirty = true; 3558721SN/A } 3568721SN/A 3578721SN/A /** This entry needs to be written back to memory */ 3588721SN/A bool dirty() const 3598721SN/A { 3608721SN/A return _dirty; 3618721SN/A } 3628721SN/A 3638721SN/A }; 3648721SN/A 3658721SN/A // Granule sizes for AArch64 long descriptors 3669449SAli.Saidi@ARM.com enum GrainSize { 3679449SAli.Saidi@ARM.com Grain4KB = 12, 3689729Sandreas.hansson@arm.com Grain16KB = 14, 3699449SAli.Saidi@ARM.com Grain64KB = 16, 3709449SAli.Saidi@ARM.com ReservedGrain = 0 3719729Sandreas.hansson@arm.com }; 3729449SAli.Saidi@ARM.com 3739449SAli.Saidi@ARM.com /** Long-descriptor format (LPAE) */ 3749729Sandreas.hansson@arm.com class LongDescriptor : public DescriptorBase { 3759449SAli.Saidi@ARM.com public: 3769729Sandreas.hansson@arm.com /** Descriptor type */ 3779729Sandreas.hansson@arm.com enum EntryType { 3789729Sandreas.hansson@arm.com Invalid, 3799729Sandreas.hansson@arm.com Table, 3809247Sandreas.hansson@arm.com Block, 3819729Sandreas.hansson@arm.com Page 3828721SN/A }; 3839449SAli.Saidi@ARM.com 3849729Sandreas.hansson@arm.com /** The raw bits of the entry */ 3859729Sandreas.hansson@arm.com uint64_t data; 3869729Sandreas.hansson@arm.com 3879729Sandreas.hansson@arm.com /** This entry has been modified (access flag set) and needs to be 3889247Sandreas.hansson@arm.com * written back to memory */ 3898721SN/A bool _dirty; 3908721SN/A 3918721SN/A virtual uint64_t getRawData() const 3928721SN/A { 3938721SN/A return (data); 3948721SN/A } 3958721SN/A 3968721SN/A virtual std::string dbgHeader() const 3978721SN/A { 3988721SN/A if (type() == LongDescriptor::Page) { 3999729Sandreas.hansson@arm.com assert(lookupLevel == L3); 4008721SN/A return "Inserting Page descriptor into TLB\n"; 4019729Sandreas.hansson@arm.com } else { 4028721SN/A assert(lookupLevel < L3); 4039449SAli.Saidi@ARM.com return "Inserting Block descriptor into TLB\n"; 4048721SN/A } 4058721SN/A } 4068721SN/A 4079449SAli.Saidi@ARM.com /** 4089449SAli.Saidi@ARM.com * Returns true if this entry targets the secure physical address 4098721SN/A * map. 4108721SN/A */ 4118721SN/A bool secure(bool have_security, WalkerState *currState) const 4128721SN/A { 4138721SN/A assert(type() == Block || type() == Page); 4148721SN/A return have_security && (currState->secureLookup && !bits(data, 5)); 4158721SN/A } 4168721SN/A 4178721SN/A /** True if the current lookup is performed in AArch64 state */ 4188721SN/A bool aarch64; 4198721SN/A 4209449SAli.Saidi@ARM.com /** Width of the granule size in bits */ 4219449SAli.Saidi@ARM.com GrainSize grainSize; 4228721SN/A 4238721SN/A /** Return the descriptor type */ 4248721SN/A EntryType type() const 4258721SN/A { 4269729Sandreas.hansson@arm.com switch (bits(data, 1, 0)) { 4279449SAli.Saidi@ARM.com case 0x1: 4289729Sandreas.hansson@arm.com // In AArch64 blocks are not allowed at L0 for the 4 KB granule 4298721SN/A // and at L1 for 16/64 KB granules 4308721SN/A if (grainSize > Grain4KB) 4318721SN/A return lookupLevel == L2 ? Block : Invalid; 4328721SN/A return lookupLevel == L0 || lookupLevel == L3 ? Invalid : Block; 4338721SN/A case 0x3: 4348721SN/A return lookupLevel == L3 ? Page : Table; 4358721SN/A default: 4369729Sandreas.hansson@arm.com return Invalid; 4378721SN/A } 4389729Sandreas.hansson@arm.com } 4399729Sandreas.hansson@arm.com 4403691SN/A /** Return the bit width of the page/block offset */ 4413691SN/A uint8_t offsetBits() const 4423691SN/A { 4433691SN/A if (type() == Block) { 4449449SAli.Saidi@ARM.com switch (grainSize) { 4459729Sandreas.hansson@arm.com case Grain4KB: 4469729Sandreas.hansson@arm.com return lookupLevel == L1 ? 30 /* 1 GB */ 4478721SN/A : 21 /* 2 MB */; 4489729Sandreas.hansson@arm.com case Grain16KB: 4499729Sandreas.hansson@arm.com return 25 /* 32 MB */; 4509729Sandreas.hansson@arm.com case Grain64KB: 4519729Sandreas.hansson@arm.com return 29 /* 512 MB */; 4529729Sandreas.hansson@arm.com default: 4539729Sandreas.hansson@arm.com panic("Invalid AArch64 VM granule size\n"); 4549729Sandreas.hansson@arm.com } 4559729Sandreas.hansson@arm.com } else if (type() == Page) { 4569729Sandreas.hansson@arm.com switch (grainSize) { 4579729Sandreas.hansson@arm.com case Grain4KB: 4589729Sandreas.hansson@arm.com case Grain16KB: 4599729Sandreas.hansson@arm.com case Grain64KB: 4609729Sandreas.hansson@arm.com return grainSize; /* enum -> uint okay */ 4619729Sandreas.hansson@arm.com default: 4629729Sandreas.hansson@arm.com panic("Invalid AArch64 VM granule size\n"); 4639729Sandreas.hansson@arm.com } 4649729Sandreas.hansson@arm.com } else { 4659729Sandreas.hansson@arm.com panic("AArch64 page table entry must be block or page\n"); 4669729Sandreas.hansson@arm.com } 4679729Sandreas.hansson@arm.com } 4689729Sandreas.hansson@arm.com 4699729Sandreas.hansson@arm.com /** Return the physical frame, bits shifted right */ 4709729Sandreas.hansson@arm.com Addr pfn() const 4719729Sandreas.hansson@arm.com { 4729729Sandreas.hansson@arm.com if (aarch64) 4739729Sandreas.hansson@arm.com return bits(data, 47, offsetBits()); 4749729Sandreas.hansson@arm.com return bits(data, 39, offsetBits()); 4759729Sandreas.hansson@arm.com } 4769729Sandreas.hansson@arm.com 4779729Sandreas.hansson@arm.com /** Return the complete physical address given a VA */ 4789729Sandreas.hansson@arm.com Addr paddr(Addr va) const 4799729Sandreas.hansson@arm.com { 4809729Sandreas.hansson@arm.com int n = offsetBits(); 4819729Sandreas.hansson@arm.com if (aarch64) 4829729Sandreas.hansson@arm.com return mbits(data, 47, n) | mbits(va, n - 1, 0); 4839729Sandreas.hansson@arm.com return mbits(data, 39, n) | mbits(va, n - 1, 0); 4848721SN/A } 4858721SN/A 4868721SN/A /** Return the physical address of the entry */ 4878721SN/A Addr paddr() const 4888721SN/A { 4898721SN/A if (aarch64) 4908721SN/A return mbits(data, 47, offsetBits()); 4918721SN/A return mbits(data, 39, offsetBits()); 4928721SN/A } 4938721SN/A 4948721SN/A /** Return the address of the next page table */ 4958721SN/A Addr nextTableAddr() const 4968721SN/A { 4978721SN/A assert(type() == Table); 4988721SN/A if (aarch64) 4998721SN/A return mbits(data, 47, grainSize); 5009729Sandreas.hansson@arm.com else 5018721SN/A return mbits(data, 39, 12); 5028721SN/A } 5038721SN/A 5049729Sandreas.hansson@arm.com /** Return the address of the next descriptor */ 5058721SN/A Addr nextDescAddr(Addr va) const 5068721SN/A { 5078721SN/A assert(type() == Table); 5089729Sandreas.hansson@arm.com Addr pa = 0; 5098721SN/A if (aarch64) { 5108721SN/A int stride = grainSize - 3; 5118721SN/A int va_lo = stride * (3 - (lookupLevel + 1)) + grainSize; 5129729Sandreas.hansson@arm.com int va_hi = va_lo + stride - 1; 5138721SN/A pa = nextTableAddr() | (bits(va, va_hi, va_lo) << 3); 5148721SN/A } else { 5159729Sandreas.hansson@arm.com if (lookupLevel == L1) 5168721SN/A pa = nextTableAddr() | (bits(va, 29, 21) << 3); 5178721SN/A else // lookupLevel == L2 5188721SN/A pa = nextTableAddr() | (bits(va, 20, 12) << 3); 5198721SN/A } 5208721SN/A return pa; 5218721SN/A } 5228721SN/A 5238721SN/A /** Is execution allowed on this mapping? */ 5248721SN/A bool xn() const 5258721SN/A { 5268721SN/A assert(type() == Block || type() == Page); 5278721SN/A return bits(data, 54); 5289729Sandreas.hansson@arm.com } 5298721SN/A 5308721SN/A /** Is privileged execution allowed on this mapping? (LPAE only) */ 5319729Sandreas.hansson@arm.com bool pxn() const 5329729Sandreas.hansson@arm.com { 5339729Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 5348721SN/A return bits(data, 53); 5359729Sandreas.hansson@arm.com } 5369729Sandreas.hansson@arm.com 5379729Sandreas.hansson@arm.com /** Contiguous hint bit. */ 5388721SN/A bool contiguousHint() const 5399729Sandreas.hansson@arm.com { 5409729Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 5418721SN/A return bits(data, 52); 5428721SN/A } 5439729Sandreas.hansson@arm.com 5449729Sandreas.hansson@arm.com /** Is the translation global (no asid used)? */ 5459729Sandreas.hansson@arm.com bool global(WalkerState *currState) const 5469729Sandreas.hansson@arm.com { 5479729Sandreas.hansson@arm.com assert(currState && (type() == Block || type() == Page)); 5489729Sandreas.hansson@arm.com if (!currState->aarch64 && (currState->isSecure && 5499729Sandreas.hansson@arm.com !currState->secureLookup)) { 5508721SN/A return false; // ARM ARM issue C B3.6.3 5519490Sandreas.hansson@arm.com } else if (currState->aarch64) { 5529729Sandreas.hansson@arm.com if (currState->el == EL2 || currState->el == EL3) { 5539729Sandreas.hansson@arm.com return true; // By default translations are treated as global 5549729Sandreas.hansson@arm.com // in AArch64 EL2 and EL3 5559729Sandreas.hansson@arm.com } else if (currState->isSecure && !currState->secureLookup) { 5569729Sandreas.hansson@arm.com return false; 5579729Sandreas.hansson@arm.com } 5589729Sandreas.hansson@arm.com } 5599729Sandreas.hansson@arm.com return !bits(data, 11); 5609729Sandreas.hansson@arm.com } 5619729Sandreas.hansson@arm.com 5629729Sandreas.hansson@arm.com /** Returns true if the access flag (AF) is set. */ 5639729Sandreas.hansson@arm.com bool af() const 5649729Sandreas.hansson@arm.com { 5659490Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 5669729Sandreas.hansson@arm.com return bits(data, 10); 5679729Sandreas.hansson@arm.com } 5689247Sandreas.hansson@arm.com 5699449SAli.Saidi@ARM.com /** 2-bit shareability field */ 5709247Sandreas.hansson@arm.com uint8_t sh() const 5719729Sandreas.hansson@arm.com { 5729729Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 5739729Sandreas.hansson@arm.com return bits(data, 9, 8); 5749729Sandreas.hansson@arm.com } 5759729Sandreas.hansson@arm.com 5769729Sandreas.hansson@arm.com /** 2-bit access protection flags */ 5778721SN/A uint8_t ap() const 5788721SN/A { 5799729Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 5808721SN/A // Long descriptors only support the AP[2:1] scheme 5818721SN/A return bits(data, 7, 6); 5828721SN/A } 5838983Snate@binkert.org 5848983Snate@binkert.org /** Read/write access protection flag */ 5859247Sandreas.hansson@arm.com bool rw() const 5869247Sandreas.hansson@arm.com { 5878721SN/A assert(type() == Block || type() == Page); 5888721SN/A return !bits(data, 7); 5898721SN/A } 5908721SN/A 5919729Sandreas.hansson@arm.com /** User/privileged level access protection flag */ 5928721SN/A bool user() const 5939729Sandreas.hansson@arm.com { 5948721SN/A assert(type() == Block || type() == Page); 5958721SN/A return bits(data, 6); 5969449SAli.Saidi@ARM.com } 5978721SN/A 5988721SN/A /** Return the AP bits as compatible with the AP[2:0] format. Utility 5999449SAli.Saidi@ARM.com * function used to simplify the code in the TLB for performing 6008721SN/A * permission checks. */ 6018721SN/A static uint8_t ap(bool rw, bool user) 6029449SAli.Saidi@ARM.com { 6038721SN/A return ((!rw) << 2) | (user << 1); 6048721SN/A } 6059449SAli.Saidi@ARM.com 6068721SN/A TlbEntry::DomainType domain() const 6078721SN/A { 6089449SAli.Saidi@ARM.com // Long-desc. format only supports Client domain 6098721SN/A assert(type() == Block || type() == Page); 6109729Sandreas.hansson@arm.com return TlbEntry::DomainType::Client; 6119449SAli.Saidi@ARM.com } 6129729Sandreas.hansson@arm.com 6138721SN/A /** Attribute index */ 6149449SAli.Saidi@ARM.com uint8_t attrIndx() const 6158721SN/A { 6168721SN/A assert(type() == Block || type() == Page); 6179449SAli.Saidi@ARM.com return bits(data, 4, 2); 6188721SN/A } 6199449SAli.Saidi@ARM.com 6209729Sandreas.hansson@arm.com /** Memory attributes, only used by stage 2 translations */ 6218721SN/A uint8_t memAttr() const 6229729Sandreas.hansson@arm.com { 6239729Sandreas.hansson@arm.com assert(type() == Block || type() == Page); 6249449SAli.Saidi@ARM.com return bits(data, 5, 2); 6259449SAli.Saidi@ARM.com } 6269729Sandreas.hansson@arm.com 6279729Sandreas.hansson@arm.com /** Set access flag that this entry has been touched. Mark the entry as 6289449SAli.Saidi@ARM.com * requiring a writeback, in the future. */ 6299449SAli.Saidi@ARM.com void setAf() 6309449SAli.Saidi@ARM.com { 6319449SAli.Saidi@ARM.com data |= 1 << 10; 6329449SAli.Saidi@ARM.com _dirty = true; 6339449SAli.Saidi@ARM.com } 6349729Sandreas.hansson@arm.com 6359729Sandreas.hansson@arm.com /** This entry needs to be written back to memory */ 6369449SAli.Saidi@ARM.com bool dirty() const 6379449SAli.Saidi@ARM.com { 6389729Sandreas.hansson@arm.com return _dirty; 6399729Sandreas.hansson@arm.com } 6409729Sandreas.hansson@arm.com 6419729Sandreas.hansson@arm.com /** Whether the subsequent levels of lookup are secure */ 6429729Sandreas.hansson@arm.com bool secureTable() const 6439729Sandreas.hansson@arm.com { 6449729Sandreas.hansson@arm.com assert(type() == Table); 6459729Sandreas.hansson@arm.com return !bits(data, 63); 6469729Sandreas.hansson@arm.com } 6479729Sandreas.hansson@arm.com 6489729Sandreas.hansson@arm.com /** Two bit access protection flags for subsequent levels of lookup */ 6499729Sandreas.hansson@arm.com uint8_t apTable() const 6509729Sandreas.hansson@arm.com { 6519729Sandreas.hansson@arm.com assert(type() == Table); 6529729Sandreas.hansson@arm.com return bits(data, 62, 61); 6538721SN/A } 6548721SN/A 6558721SN/A /** R/W protection flag for subsequent levels of lookup */ 6568721SN/A uint8_t rwTable() const 6578721SN/A { 6588721SN/A assert(type() == Table); 6598721SN/A return !bits(data, 62); 6608721SN/A } 6618721SN/A 6628721SN/A /** User/privileged mode protection flag for subsequent levels of 6638721SN/A * lookup */ 6648721SN/A uint8_t userTable() const 6658721SN/A { 6668721SN/A assert(type() == Table); 6678721SN/A return !bits(data, 61); 6688721SN/A } 6699449SAli.Saidi@ARM.com 6708721SN/A /** Is execution allowed on subsequent lookup levels? */ 6718721SN/A bool xnTable() const 6728721SN/A { 6739449SAli.Saidi@ARM.com assert(type() == Table); 6748721SN/A return bits(data, 60); 6758721SN/A } 6768721SN/A 6779449SAli.Saidi@ARM.com /** Is privileged execution allowed on subsequent lookup levels? */ 6786024SN/A bool pxnTable() const 6798721SN/A { 6808721SN/A assert(type() == Table); 6819449SAli.Saidi@ARM.com return bits(data, 59); 6828721SN/A } 6838721SN/A }; 6849449SAli.Saidi@ARM.com 6858721SN/A class WalkerState 6868721SN/A { 6878721SN/A public: 6888721SN/A /** Thread context that we're doing the walk for */ 6898721SN/A ThreadContext *tc; 6908721SN/A 6918721SN/A /** If the access is performed in AArch64 state */ 6928721SN/A bool aarch64; 6936024SN/A 6946024SN/A /** Current exception level */ 6958721SN/A ExceptionLevel el; 6968721SN/A 6979729Sandreas.hansson@arm.com /** Current physical address range in bits */ 6988721SN/A int physAddrRange; 6998721SN/A 7009449SAli.Saidi@ARM.com /** Request that is currently being serviced */ 7019449SAli.Saidi@ARM.com RequestPtr req; 7029449SAli.Saidi@ARM.com 7038721SN/A /** ASID that we're servicing the request under */ 7049449SAli.Saidi@ARM.com uint16_t asid; 7059449SAli.Saidi@ARM.com uint8_t vmid; 7069449SAli.Saidi@ARM.com bool isHyp; 7078721SN/A 7089449SAli.Saidi@ARM.com /** Translation state for delayed requests */ 7099449SAli.Saidi@ARM.com TLB::Translation *transState; 7108721SN/A 7118721SN/A /** The fault that we are going to return */ 7129449SAli.Saidi@ARM.com Fault fault; 7139449SAli.Saidi@ARM.com 7149449SAli.Saidi@ARM.com /** The virtual address that is being translated with tagging removed.*/ 7159729Sandreas.hansson@arm.com Addr vaddr; 7169729Sandreas.hansson@arm.com 7179729Sandreas.hansson@arm.com /** The virtual address that is being translated */ 7189729Sandreas.hansson@arm.com Addr vaddr_tainted; 7193691SN/A 7209449SAli.Saidi@ARM.com /** Cached copy of the sctlr as it existed when translation began */ 7219449SAli.Saidi@ARM.com SCTLR sctlr; 7229449SAli.Saidi@ARM.com 7239449SAli.Saidi@ARM.com /** Cached copy of the scr as it existed when translation began */ 7249449SAli.Saidi@ARM.com SCR scr; 7259449SAli.Saidi@ARM.com 7269449SAli.Saidi@ARM.com /** Cached copy of the cpsr as it existed when translation began */ 7279449SAli.Saidi@ARM.com CPSR cpsr; 7289449SAli.Saidi@ARM.com 7299449SAli.Saidi@ARM.com /** Cached copy of ttbcr/tcr as it existed when translation began */ 7309449SAli.Saidi@ARM.com union { 7319449SAli.Saidi@ARM.com TTBCR ttbcr; // AArch32 translations 7329729Sandreas.hansson@arm.com TCR tcr; // AArch64 translations 7339729Sandreas.hansson@arm.com }; 7349449SAli.Saidi@ARM.com 7359449SAli.Saidi@ARM.com /** Cached copy of the htcr as it existed when translation began. */ 7369729Sandreas.hansson@arm.com HTCR htcr; 7379247Sandreas.hansson@arm.com 7389449SAli.Saidi@ARM.com /** Cached copy of the htcr as it existed when translation began. */ 7399247Sandreas.hansson@arm.com HCR hcr; 7409449SAli.Saidi@ARM.com 7419449SAli.Saidi@ARM.com /** Cached copy of the vtcr as it existed when translation began. */ 7429449SAli.Saidi@ARM.com VTCR_t vtcr; 7439449SAli.Saidi@ARM.com 7449449SAli.Saidi@ARM.com /** If the access is a write */ 7459449SAli.Saidi@ARM.com bool isWrite; 7468721SN/A 7478721SN/A /** If the access is a fetch (for execution, and no-exec) must be checked?*/ 7489449SAli.Saidi@ARM.com bool isFetch; 7496127SN/A 7506127SN/A /** If the access comes from the secure state. */ 7516127SN/A bool isSecure; 7528983Snate@binkert.org 7538983Snate@binkert.org /** Helper variables used to implement hierarchical access permissions 7549247Sandreas.hansson@arm.com * when the long-desc. format is used (LPAE only) */ 7559247Sandreas.hansson@arm.com bool secureLookup; 7566127SN/A bool rwTable; 7576127SN/A bool userTable; 7586127SN/A bool xnTable; 7593691SN/A bool pxnTable; 7609729Sandreas.hansson@arm.com 7613691SN/A /** Flag indicating if a second stage of lookup is required */ 7629729Sandreas.hansson@arm.com bool stage2Req; 7633691SN/A 7648721SN/A /** A pointer to the stage 2 translation that's in progress */ 7659449SAli.Saidi@ARM.com TLB::Translation *stage2Tran; 7668721SN/A 7678721SN/A /** If the mode is timing or atomic */ 7689449SAli.Saidi@ARM.com bool timing; 7698721SN/A 7708721SN/A /** If the atomic mode should be functional */ 7719449SAli.Saidi@ARM.com bool functional; 7728721SN/A 7738721SN/A /** Save mode for use in delayed response */ 7749449SAli.Saidi@ARM.com BaseTLB::Mode mode; 7758721SN/A 7768721SN/A /** The translation type that has been requested */ 7779449SAli.Saidi@ARM.com TLB::ArmTranslationType tranType; 7788721SN/A 7799449SAli.Saidi@ARM.com /** Short-format descriptors */ 7809449SAli.Saidi@ARM.com L1Descriptor l1Desc; 7819729Sandreas.hansson@arm.com L2Descriptor l2Desc; 7828721SN/A 7839449SAli.Saidi@ARM.com /** Long-format descriptor (LPAE and AArch64) */ 7848721SN/A LongDescriptor longDesc; 7858721SN/A 7869449SAli.Saidi@ARM.com /** Whether the response is delayed in timing mode due to additional 7878721SN/A * lookups */ 7889449SAli.Saidi@ARM.com bool delayed; 7899729Sandreas.hansson@arm.com 7903691SN/A TableWalker *tableWalker; 7919729Sandreas.hansson@arm.com 7929729Sandreas.hansson@arm.com /** Timestamp for calculating elapsed time in service (for stats) */ 7933691SN/A Tick startTime; 7943691SN/A 795 /** Page entries walked during service (for stats) */ 796 unsigned levels; 797 798 void doL1Descriptor(); 799 void doL2Descriptor(); 800 801 void doLongDescriptor(); 802 803 WalkerState(); 804 805 std::string name() const { return tableWalker->name(); } 806 }; 807 808 protected: 809 810 /** Queues of requests for all the different lookup levels */ 811 std::list<WalkerState *> stateQueues[MAX_LOOKUP_LEVELS]; 812 813 /** Queue of requests that have passed are waiting because the walker is 814 * currently busy. */ 815 std::list<WalkerState *> pendingQueue; 816 817 /** The MMU to forward second stage look upts to */ 818 Stage2MMU *stage2Mmu; 819 820 /** Port shared by the two table walkers. */ 821 DmaPort* port; 822 823 /** Master id assigned by the MMU. */ 824 MasterID masterId; 825 826 /** Indicates whether this table walker is part of the stage 2 mmu */ 827 const bool isStage2; 828 829 /** TLB that is initiating these table walks */ 830 TLB *tlb; 831 832 /** Cached copy of the sctlr as it existed when translation began */ 833 SCTLR sctlr; 834 835 WalkerState *currState; 836 837 /** If a timing translation is currently in progress */ 838 bool pending; 839 840 /** The number of walks belonging to squashed instructions that can be 841 * removed from the pendingQueue per cycle. */ 842 unsigned numSquashable; 843 844 /** Cached copies of system-level properties */ 845 bool haveSecurity; 846 bool _haveLPAE; 847 bool _haveVirtualization; 848 uint8_t physAddrRange; 849 bool _haveLargeAsid64; 850 851 /** Statistics */ 852 Stats::Scalar statWalks; 853 Stats::Scalar statWalksShortDescriptor; 854 Stats::Scalar statWalksLongDescriptor; 855 Stats::Vector statWalksShortTerminatedAtLevel; 856 Stats::Vector statWalksLongTerminatedAtLevel; 857 Stats::Scalar statSquashedBefore; 858 Stats::Scalar statSquashedAfter; 859 Stats::Histogram statWalkWaitTime; 860 Stats::Histogram statWalkServiceTime; 861 Stats::Histogram statPendingWalks; // essentially "L" of queueing theory 862 Stats::Vector statPageSizes; 863 Stats::Vector2d statRequestOrigin; 864 865 mutable unsigned pendingReqs; 866 mutable Tick pendingChangeTick; 867 868 static const unsigned REQUESTED = 0; 869 static const unsigned COMPLETED = 1; 870 871 public: 872 typedef ArmTableWalkerParams Params; 873 TableWalker(const Params *p); 874 virtual ~TableWalker(); 875 876 const Params * 877 params() const 878 { 879 return dynamic_cast<const Params *>(_params); 880 } 881 882 void init() override; 883 884 bool haveLPAE() const { return _haveLPAE; } 885 bool haveVirtualization() const { return _haveVirtualization; } 886 bool haveLargeAsid64() const { return _haveLargeAsid64; } 887 /** Checks if all state is cleared and if so, completes drain */ 888 void completeDrain(); 889 DrainState drain() override; 890 void drainResume() override; 891 892 Port &getPort(const std::string &if_name, 893 PortID idx=InvalidPortID) override; 894 895 void regStats() override; 896 897 Fault walk(const RequestPtr &req, ThreadContext *tc, 898 uint16_t asid, uint8_t _vmid, 899 bool _isHyp, TLB::Mode mode, TLB::Translation *_trans, 900 bool timing, bool functional, bool secure, 901 TLB::ArmTranslationType tranType, bool _stage2Req); 902 903 void setTlb(TLB *_tlb) { tlb = _tlb; } 904 TLB* getTlb() { return tlb; } 905 void setMMU(Stage2MMU *m, MasterID master_id); 906 void memAttrs(ThreadContext *tc, TlbEntry &te, SCTLR sctlr, 907 uint8_t texcb, bool s); 908 void memAttrsLPAE(ThreadContext *tc, TlbEntry &te, 909 LongDescriptor &lDescriptor); 910 void memAttrsAArch64(ThreadContext *tc, TlbEntry &te, 911 LongDescriptor &lDescriptor); 912 913 static LookupLevel toLookupLevel(uint8_t lookup_level_as_int); 914 915 private: 916 917 void doL1Descriptor(); 918 void doL1DescriptorWrapper(); 919 EventFunctionWrapper doL1DescEvent; 920 921 void doL2Descriptor(); 922 void doL2DescriptorWrapper(); 923 EventFunctionWrapper doL2DescEvent; 924 925 void doLongDescriptor(); 926 927 void doL0LongDescriptorWrapper(); 928 EventFunctionWrapper doL0LongDescEvent; 929 void doL1LongDescriptorWrapper(); 930 EventFunctionWrapper doL1LongDescEvent; 931 void doL2LongDescriptorWrapper(); 932 EventFunctionWrapper doL2LongDescEvent; 933 void doL3LongDescriptorWrapper(); 934 EventFunctionWrapper doL3LongDescEvent; 935 936 void doLongDescriptorWrapper(LookupLevel curr_lookup_level); 937 Event* LongDescEventByLevel[4]; 938 939 bool fetchDescriptor(Addr descAddr, uint8_t *data, int numBytes, 940 Request::Flags flags, int queueIndex, Event *event, 941 void (TableWalker::*doDescriptor)()); 942 943 void insertTableEntry(DescriptorBase &descriptor, bool longDescriptor); 944 945 Fault processWalk(); 946 Fault processWalkLPAE(); 947 static unsigned adjustTableSizeAArch64(unsigned tsz); 948 /// Returns true if the address exceeds the range permitted by the 949 /// system-wide setting or by the TCR_ELx IPS/PS setting 950 static bool checkAddrSizeFaultAArch64(Addr addr, int currPhysAddrRange); 951 Fault processWalkAArch64(); 952 void processWalkWrapper(); 953 EventFunctionWrapper doProcessEvent; 954 955 void nextWalk(ThreadContext *tc); 956 957 void pendingChange(); 958 959 static uint8_t pageSizeNtoStatBin(uint8_t N); 960 961 Fault testWalk(Addr pa, Addr size, TlbEntry::DomainType domain, 962 LookupLevel lookup_level); 963}; 964 965} // namespace ArmISA 966 967#endif //__ARCH_ARM_TABLE_WALKER_HH__ 968 969