| /* |
| * Copyright (c) 2010-2016, 2019, 2021-2022 Arm Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to 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. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef __ARCH_ARM_TABLE_WALKER_HH__ |
| #define __ARCH_ARM_TABLE_WALKER_HH__ |
| |
| #include <list> |
| |
| #include "arch/arm/faults.hh" |
| #include "arch/arm/mmu.hh" |
| #include "arch/arm/regs/misc.hh" |
| #include "arch/arm/system.hh" |
| #include "arch/arm/tlb.hh" |
| #include "arch/arm/types.hh" |
| #include "arch/generic/mmu.hh" |
| #include "mem/packet_queue.hh" |
| #include "mem/qport.hh" |
| #include "mem/request.hh" |
| #include "params/ArmTableWalker.hh" |
| #include "sim/clocked_object.hh" |
| #include "sim/eventq.hh" |
| |
| namespace gem5 |
| { |
| |
| class ThreadContext; |
| |
| namespace ArmISA { |
| class Translation; |
| class TLB; |
| |
| class TableWalker : public ClockedObject |
| { |
| using LookupLevel = enums::ArmLookupLevel; |
| |
| public: |
| class WalkerState; |
| |
| class DescriptorBase |
| { |
| public: |
| DescriptorBase() : lookupLevel(LookupLevel::L0) {} |
| |
| /** Current lookup level for this descriptor */ |
| LookupLevel lookupLevel; |
| |
| virtual Addr pfn() const = 0; |
| virtual TlbEntry::DomainType domain() const = 0; |
| virtual bool xn() const = 0; |
| virtual uint8_t ap() const = 0; |
| virtual bool global(WalkerState *currState) const = 0; |
| virtual uint8_t offsetBits() const = 0; |
| virtual bool secure(bool have_security, WalkerState *currState) const = 0; |
| virtual std::string dbgHeader() const = 0; |
| virtual uint64_t getRawData() const = 0; |
| virtual uint8_t texcb() const |
| { |
| panic("texcb() not implemented for this class\n"); |
| } |
| virtual bool shareable() const |
| { |
| panic("shareable() not implemented for this class\n"); |
| } |
| }; |
| |
| class L1Descriptor : public DescriptorBase |
| { |
| public: |
| /** Type of page table entry ARM DDI 0406B: B3-8*/ |
| enum EntryType |
| { |
| Ignore, |
| PageTable, |
| Section, |
| Reserved |
| }; |
| |
| /** The raw bits of the entry */ |
| uint32_t data; |
| |
| /** This entry has been modified (access flag set) and needs to be |
| * written back to memory */ |
| bool _dirty; |
| |
| /** Default ctor */ |
| L1Descriptor() : data(0), _dirty(false) |
| { |
| lookupLevel = LookupLevel::L1; |
| } |
| |
| uint64_t |
| getRawData() const override |
| { |
| return (data); |
| } |
| |
| std::string |
| dbgHeader() const override |
| { |
| return "Inserting Section Descriptor into TLB\n"; |
| } |
| |
| uint8_t |
| offsetBits() const override |
| { |
| return 20; |
| } |
| |
| EntryType |
| type() const |
| { |
| return (EntryType)(data & 0x3); |
| } |
| |
| /** Is the page a Supersection (16 MiB)?*/ |
| bool |
| supersection() const |
| { |
| return bits(data, 18); |
| } |
| |
| /** Return the physcal address of the entry, bits in position*/ |
| Addr |
| paddr() const |
| { |
| if (supersection()) |
| panic("Super sections not implemented\n"); |
| return mbits(data, 31, 20); |
| } |
| |
| /** Return the physcal address of the entry, bits in position*/ |
| Addr |
| paddr(Addr va) const |
| { |
| if (supersection()) |
| panic("Super sections not implemented\n"); |
| return mbits(data, 31, 20) | mbits(va, 19, 0); |
| } |
| |
| /** Return the physical frame, bits shifted right */ |
| Addr |
| pfn() const override |
| { |
| if (supersection()) |
| panic("Super sections not implemented\n"); |
| return bits(data, 31, 20); |
| } |
| |
| /** Is the translation global (no asid used)? */ |
| bool |
| global(WalkerState *currState) const override |
| { |
| return !bits(data, 17); |
| } |
| |
| /** Is the translation not allow execution? */ |
| bool |
| xn() const override |
| { |
| return bits(data, 4); |
| } |
| |
| /** Three bit access protection flags */ |
| uint8_t |
| ap() const override |
| { |
| return (bits(data, 15) << 2) | bits(data, 11, 10); |
| } |
| |
| /** Domain Client/Manager: ARM DDI 0406B: B3-31 */ |
| TlbEntry::DomainType |
| domain() const override |
| { |
| return static_cast<TlbEntry::DomainType>(bits(data, 8, 5)); |
| } |
| |
| /** Address of L2 descriptor if it exists */ |
| Addr |
| l2Addr() const |
| { |
| return mbits(data, 31, 10); |
| } |
| |
| /** Memory region attributes: ARM DDI 0406B: B3-32. |
| * These bits are largly ignored by M5 and only used to |
| * provide the illusion that the memory system cares about |
| * anything but cachable vs. uncachable. |
| */ |
| uint8_t |
| texcb() const override |
| { |
| return bits(data, 2) | bits(data, 3) << 1 | bits(data, 14, 12) << 2; |
| } |
| |
| /** If the section is shareable. See texcb() comment. */ |
| bool |
| shareable() const override |
| { |
| return bits(data, 16); |
| } |
| |
| /** Set access flag that this entry has been touched. Mark |
| * the entry as requiring a writeback, in the future. |
| */ |
| void |
| setAp0() |
| { |
| data |= 1 << 10; |
| _dirty = true; |
| } |
| |
| /** This entry needs to be written back to memory */ |
| bool |
| dirty() const |
| { |
| return _dirty; |
| } |
| |
| /** |
| * Returns true if this entry targets the secure physical address |
| * map. |
| */ |
| bool |
| secure(bool have_security, WalkerState *currState) const override |
| { |
| if (have_security && currState->secureLookup) { |
| if (type() == PageTable) |
| return !bits(data, 3); |
| else |
| return !bits(data, 19); |
| } |
| return false; |
| } |
| }; |
| |
| /** Level 2 page table descriptor */ |
| class L2Descriptor : public DescriptorBase |
| { |
| public: |
| /** The raw bits of the entry. */ |
| uint32_t data; |
| L1Descriptor *l1Parent; |
| |
| /** This entry has been modified (access flag set) and needs to be |
| * written back to memory */ |
| bool _dirty; |
| |
| /** Default ctor */ |
| L2Descriptor() : data(0), l1Parent(nullptr), _dirty(false) |
| { |
| lookupLevel = LookupLevel::L2; |
| } |
| |
| L2Descriptor(L1Descriptor &parent) : data(0), l1Parent(&parent), |
| _dirty(false) |
| { |
| lookupLevel = LookupLevel::L2; |
| } |
| |
| uint64_t |
| getRawData() const override |
| { |
| return (data); |
| } |
| |
| std::string |
| dbgHeader() const override |
| { |
| return "Inserting L2 Descriptor into TLB\n"; |
| } |
| |
| TlbEntry::DomainType |
| domain() const override |
| { |
| return l1Parent->domain(); |
| } |
| |
| bool |
| secure(bool have_security, WalkerState *currState) const override |
| { |
| return l1Parent->secure(have_security, currState); |
| } |
| |
| uint8_t |
| offsetBits() const override |
| { |
| return large() ? 16 : 12; |
| } |
| |
| /** Is the entry invalid */ |
| bool |
| invalid() const |
| { |
| return bits(data, 1, 0) == 0; |
| } |
| |
| /** What is the size of the mapping? */ |
| bool |
| large() const |
| { |
| return bits(data, 1) == 0; |
| } |
| |
| /** Is execution allowed on this mapping? */ |
| bool |
| xn() const override |
| { |
| return large() ? bits(data, 15) : bits(data, 0); |
| } |
| |
| /** Is the translation global (no asid used)? */ |
| bool |
| global(WalkerState *currState) const override |
| { |
| return !bits(data, 11); |
| } |
| |
| /** Three bit access protection flags */ |
| uint8_t |
| ap() const override |
| { |
| return bits(data, 5, 4) | (bits(data, 9) << 2); |
| } |
| |
| /** Memory region attributes: ARM DDI 0406B: B3-32 */ |
| uint8_t |
| texcb() const override |
| { |
| return large() ? |
| (bits(data, 2) | (bits(data, 3) << 1) | (bits(data, 14, 12) << 2)) : |
| (bits(data, 2) | (bits(data, 3) << 1) | (bits(data, 8, 6) << 2)); |
| } |
| |
| /** Return the physical frame, bits shifted right */ |
| Addr |
| pfn() const override |
| { |
| return large() ? bits(data, 31, 16) : bits(data, 31, 12); |
| } |
| |
| /** Return complete physical address given a VA */ |
| Addr |
| paddr(Addr va) const |
| { |
| if (large()) |
| return mbits(data, 31, 16) | mbits(va, 15, 0); |
| else |
| return mbits(data, 31, 12) | mbits(va, 11, 0); |
| } |
| |
| /** If the section is shareable. See texcb() comment. */ |
| bool |
| shareable() const override |
| { |
| return bits(data, 10); |
| } |
| |
| /** Set access flag that this entry has been touched. Mark |
| * the entry as requiring a writeback, in the future. |
| */ |
| void |
| setAp0() |
| { |
| data |= 1 << 4; |
| _dirty = true; |
| } |
| |
| /** This entry needs to be written back to memory */ |
| bool |
| dirty() const |
| { |
| return _dirty; |
| } |
| |
| }; |
| |
| /** Long-descriptor format (LPAE) */ |
| class LongDescriptor : public DescriptorBase |
| { |
| public: |
| /** Descriptor type */ |
| enum EntryType |
| { |
| Invalid, |
| Table, |
| Block, |
| Page |
| }; |
| |
| LongDescriptor() |
| : data(0), _dirty(false), aarch64(false), grainSize(Grain4KB), |
| physAddrRange(0) |
| {} |
| |
| /** The raw bits of the entry */ |
| uint64_t data; |
| |
| /** This entry has been modified (access flag set) and needs to be |
| * written back to memory */ |
| bool _dirty; |
| |
| /** True if the current lookup is performed in AArch64 state */ |
| bool aarch64; |
| |
| /** Width of the granule size in bits */ |
| GrainSize grainSize; |
| |
| uint8_t physAddrRange; |
| |
| uint64_t |
| getRawData() const override |
| { |
| return (data); |
| } |
| |
| std::string |
| dbgHeader() const override |
| { |
| switch (type()) { |
| case LongDescriptor::Page: |
| assert(lookupLevel == LookupLevel::L3); |
| return "Inserting Page descriptor into TLB\n"; |
| case LongDescriptor::Block: |
| assert(lookupLevel < LookupLevel::L3); |
| return "Inserting Block descriptor into TLB\n"; |
| case LongDescriptor::Table: |
| return "Inserting Table descriptor into TLB\n"; |
| default: |
| panic("Trying to insert and invalid descriptor\n"); |
| } |
| } |
| |
| /** |
| * Returns true if this entry targets the secure physical address |
| * map. |
| */ |
| bool |
| secure(bool have_security, WalkerState *currState) const override |
| { |
| if (type() == Block || type() == Page) { |
| return have_security && |
| (currState->secureLookup && !bits(data, 5)); |
| } else { |
| return have_security && currState->secureLookup; |
| } |
| } |
| |
| /** Return the descriptor type */ |
| EntryType |
| type() const |
| { |
| switch (bits(data, 1, 0)) { |
| case 0x1: |
| // In AArch64 blocks are not allowed at L0 for the |
| // 4 KiB granule and at L1 for 16/64 KiB granules |
| switch (grainSize) { |
| case Grain4KB: |
| if (lookupLevel == LookupLevel::L0 || |
| lookupLevel == LookupLevel::L3) |
| return Invalid; |
| else |
| return Block; |
| |
| case Grain16KB: |
| if (lookupLevel == LookupLevel::L2) |
| return Block; |
| else |
| return Invalid; |
| |
| case Grain64KB: |
| // With Armv8.2-LPA (52bit PA) L1 Block descriptors |
| // are allowed for 64KiB granule |
| if ((lookupLevel == LookupLevel::L1 && physAddrRange == 52) || |
| lookupLevel == LookupLevel::L2) |
| return Block; |
| else |
| return Invalid; |
| |
| default: |
| return Invalid; |
| } |
| case 0x3: |
| return lookupLevel == LookupLevel::L3 ? Page : Table; |
| default: |
| return Invalid; |
| } |
| } |
| |
| /** Return the bit width of the page/block offset */ |
| uint8_t |
| offsetBits() const override |
| { |
| if (type() == Block) { |
| switch (grainSize) { |
| case Grain4KB: |
| return lookupLevel == LookupLevel::L1 ? |
| 30 /* 1 GiB */ : 21 /* 2 MiB */; |
| case Grain16KB: |
| return 25 /* 32 MiB */; |
| case Grain64KB: |
| return lookupLevel == LookupLevel::L1 ? |
| 42 /* 4 TiB */ : 29 /* 512 MiB */; |
| default: |
| panic("Invalid AArch64 VM granule size\n"); |
| } |
| } else if (type() == Page) { |
| switch (grainSize) { |
| case Grain4KB: |
| case Grain16KB: |
| case Grain64KB: |
| return grainSize; /* enum -> uint okay */ |
| default: |
| panic("Invalid AArch64 VM granule size\n"); |
| } |
| } else if (type() == Table) { |
| const auto* ptops = getPageTableOps(grainSize); |
| return ptops->walkBits(lookupLevel); |
| } |
| panic("AArch64 page table entry must be block or page\n"); |
| } |
| |
| /** Return the physical frame, bits shifted right */ |
| Addr |
| pfn() const override |
| { |
| return paddr() >> offsetBits(); |
| } |
| |
| /** Return the physical address of the entry */ |
| Addr |
| paddr() const |
| { |
| Addr addr = 0; |
| if (aarch64) { |
| addr = mbits(data, 47, offsetBits()); |
| if (physAddrRange == 52 && grainSize == Grain64KB) { |
| addr |= bits(data, 15, 12) << 48; |
| } |
| } else { |
| addr = mbits(data, 39, offsetBits()); |
| } |
| return addr; |
| } |
| |
| /** Return the address of the next page table */ |
| Addr |
| nextTableAddr() const |
| { |
| assert(type() == Table); |
| Addr table_address = 0; |
| if (aarch64) { |
| table_address = mbits(data, 47, grainSize); |
| // Using 52bit if Armv8.2-LPA is implemented |
| if (physAddrRange == 52 && grainSize == Grain64KB) |
| table_address |= bits(data, 15, 12) << 48; |
| } else { |
| table_address = mbits(data, 39, 12); |
| } |
| |
| return table_address; |
| } |
| |
| /** Return the address of the next descriptor */ |
| Addr |
| nextDescAddr(Addr va) const |
| { |
| assert(type() == Table); |
| Addr pa = 0; |
| if (aarch64) { |
| int stride = grainSize - 3; |
| int va_lo = stride * (3 - (lookupLevel + 1)) + grainSize; |
| int va_hi = va_lo + stride - 1; |
| pa = nextTableAddr() | (bits(va, va_hi, va_lo) << 3); |
| } else { |
| if (lookupLevel == LookupLevel::L1) |
| pa = nextTableAddr() | (bits(va, 29, 21) << 3); |
| else // lookupLevel == L2 |
| pa = nextTableAddr() | (bits(va, 20, 12) << 3); |
| } |
| return pa; |
| } |
| |
| /** Is execution allowed on this mapping? */ |
| bool |
| xn() const override |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 54); |
| } |
| |
| /** Is privileged execution allowed on this mapping? (LPAE only) */ |
| bool |
| pxn() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 53); |
| } |
| |
| /** Contiguous hint bit. */ |
| bool |
| contiguousHint() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 52); |
| } |
| |
| /** Is the translation global (no asid used)? */ |
| bool |
| global(WalkerState *currState) const override |
| { |
| assert(currState && (type() == Block || type() == Page)); |
| if (!currState->aarch64 && (currState->isSecure && |
| !currState->secureLookup)) { |
| return false; // ARM ARM issue C B3.6.3 |
| } else if (currState->aarch64) { |
| if (!MMU::hasUnprivRegime(currState->el, currState->hcr.e2h)) { |
| // By default translations are treated as global |
| // in AArch64 for regimes without an unpriviledged |
| // component |
| return true; |
| } else if (currState->isSecure && !currState->secureLookup) { |
| return false; |
| } |
| } |
| return !bits(data, 11); |
| } |
| |
| /** Returns true if the access flag (AF) is set. */ |
| bool |
| af() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 10); |
| } |
| |
| /** 2-bit shareability field */ |
| uint8_t |
| sh() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 9, 8); |
| } |
| |
| /** 2-bit access protection flags */ |
| uint8_t |
| ap() const override |
| { |
| assert(type() == Block || type() == Page); |
| // Long descriptors only support the AP[2:1] scheme |
| return bits(data, 7, 6); |
| } |
| |
| /** Read/write access protection flag */ |
| bool |
| rw() const |
| { |
| assert(type() == Block || type() == Page); |
| return !bits(data, 7); |
| } |
| |
| /** User/privileged level access protection flag */ |
| bool |
| user() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 6); |
| } |
| |
| /** Return the AP bits as compatible with the AP[2:0] format. Utility |
| * function used to simplify the code in the TLB for performing |
| * permission checks. */ |
| static uint8_t |
| ap(bool rw, bool user) |
| { |
| return ((!rw) << 2) | (user << 1); |
| } |
| |
| TlbEntry::DomainType |
| domain() const override |
| { |
| // Long-desc. format only supports Client domain |
| return TlbEntry::DomainType::Client; |
| } |
| |
| /** Attribute index */ |
| uint8_t |
| attrIndx() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 4, 2); |
| } |
| |
| /** Memory attributes, only used by stage 2 translations */ |
| uint8_t |
| memAttr() const |
| { |
| assert(type() == Block || type() == Page); |
| return bits(data, 5, 2); |
| } |
| |
| /** Set access flag that this entry has been touched. Mark the entry as |
| * requiring a writeback, in the future. */ |
| void |
| setAf() |
| { |
| data |= 1 << 10; |
| _dirty = true; |
| } |
| |
| /** This entry needs to be written back to memory */ |
| bool |
| dirty() const |
| { |
| return _dirty; |
| } |
| |
| /** Whether the subsequent levels of lookup are secure */ |
| bool |
| secureTable() const |
| { |
| assert(type() == Table); |
| return !bits(data, 63); |
| } |
| |
| /** Two bit access protection flags for subsequent levels of lookup */ |
| uint8_t |
| apTable() const |
| { |
| assert(type() == Table); |
| return bits(data, 62, 61); |
| } |
| |
| /** R/W protection flag for subsequent levels of lookup */ |
| uint8_t |
| rwTable() const |
| { |
| assert(type() == Table); |
| return !bits(data, 62); |
| } |
| |
| /** User/privileged mode protection flag for subsequent levels of |
| * lookup */ |
| uint8_t |
| userTable() const |
| { |
| assert(type() == Table); |
| return !bits(data, 61); |
| } |
| |
| /** Is execution allowed on subsequent lookup levels? */ |
| bool |
| xnTable() const |
| { |
| assert(type() == Table); |
| return bits(data, 60); |
| } |
| |
| /** Is privileged execution allowed on subsequent lookup levels? */ |
| bool |
| pxnTable() const |
| { |
| assert(type() == Table); |
| return bits(data, 59); |
| } |
| }; |
| |
| class WalkerState |
| { |
| public: |
| /** Thread context that we're doing the walk for */ |
| ThreadContext *tc; |
| |
| /** If the access is performed in AArch64 state */ |
| bool aarch64; |
| |
| /** Current exception level */ |
| ExceptionLevel el; |
| |
| /** Current physical address range in bits */ |
| int physAddrRange; |
| |
| /** Request that is currently being serviced */ |
| RequestPtr req; |
| |
| /** Initial walk entry allowing to skip lookup levels */ |
| TlbEntry walkEntry; |
| |
| /** ASID that we're servicing the request under */ |
| uint16_t asid; |
| vmid_t vmid; |
| bool isHyp; |
| |
| /** Translation state for delayed requests */ |
| BaseMMU::Translation *transState; |
| |
| /** The fault that we are going to return */ |
| Fault fault; |
| |
| /** The virtual address that is being translated with tagging removed.*/ |
| Addr vaddr; |
| |
| /** The virtual address that is being translated */ |
| Addr vaddr_tainted; |
| |
| /** Cached copy of the sctlr as it existed when translation began */ |
| SCTLR sctlr; |
| |
| /** Cached copy of the scr as it existed when translation began */ |
| SCR scr; |
| |
| /** Cached copy of the cpsr as it existed when translation began */ |
| CPSR cpsr; |
| |
| /** Cached copy of ttbcr/tcr as it existed when translation began */ |
| union |
| { |
| TTBCR ttbcr; // AArch32 translations |
| TCR tcr; // AArch64 translations |
| }; |
| |
| /** Cached copy of the htcr as it existed when translation began. */ |
| HTCR htcr; |
| |
| /** Cached copy of the htcr as it existed when translation began. */ |
| HCR hcr; |
| |
| /** Cached copy of the vtcr as it existed when translation began. */ |
| VTCR_t vtcr; |
| |
| /** If the access is a write */ |
| bool isWrite; |
| |
| /** If the access is a fetch (for execution, and no-exec) must be checked?*/ |
| bool isFetch; |
| |
| /** If the access comes from the secure state. */ |
| bool isSecure; |
| |
| /** True if table walks are uncacheable (for table descriptors) */ |
| bool isUncacheable; |
| |
| /** Helper variables used to implement hierarchical access permissions |
| * when the long-desc. format is used (LPAE only) */ |
| bool secureLookup; |
| bool rwTable; |
| bool userTable; |
| bool xnTable; |
| bool pxnTable; |
| |
| /** Hierarchical access permission disable */ |
| bool hpd; |
| |
| /** Flag indicating if a second stage of lookup is required */ |
| bool stage2Req; |
| |
| /** A pointer to the stage 2 translation that's in progress */ |
| BaseMMU::Translation *stage2Tran; |
| |
| /** If the mode is timing or atomic */ |
| bool timing; |
| |
| /** If the atomic mode should be functional */ |
| bool functional; |
| |
| /** Save mode for use in delayed response */ |
| BaseMMU::Mode mode; |
| |
| /** The translation type that has been requested */ |
| MMU::ArmTranslationType tranType; |
| |
| /** Short-format descriptors */ |
| L1Descriptor l1Desc; |
| L2Descriptor l2Desc; |
| |
| /** Long-format descriptor (LPAE and AArch64) */ |
| LongDescriptor longDesc; |
| |
| /** Whether the response is delayed in timing mode due to additional |
| * lookups */ |
| bool delayed; |
| |
| TableWalker *tableWalker; |
| |
| /** Timestamp for calculating elapsed time in service (for stats) */ |
| Tick startTime; |
| |
| /** Page entries walked during service (for stats) */ |
| unsigned levels; |
| |
| void doL1Descriptor(); |
| void doL2Descriptor(); |
| |
| void doLongDescriptor(); |
| |
| WalkerState(); |
| |
| std::string name() const { return tableWalker->name(); } |
| }; |
| |
| class TableWalkerState : public Packet::SenderState |
| { |
| public: |
| Tick delay = 0; |
| Event *event = nullptr; |
| }; |
| |
| class Port : public QueuedRequestPort |
| { |
| public: |
| Port(TableWalker* _walker, RequestorID id); |
| |
| void sendFunctionalReq(Addr desc_addr, int size, |
| uint8_t *data, Request::Flags flag); |
| void sendAtomicReq(Addr desc_addr, int size, |
| uint8_t *data, Request::Flags flag, Tick delay); |
| void sendTimingReq(Addr desc_addr, int size, |
| uint8_t *data, Request::Flags flag, Tick delay, |
| Event *event); |
| |
| bool recvTimingResp(PacketPtr pkt) override; |
| |
| private: |
| void handleRespPacket(PacketPtr pkt, Tick delay=0); |
| void handleResp(TableWalkerState *state, Addr addr, |
| Addr size, Tick delay=0); |
| |
| PacketPtr createPacket(Addr desc_addr, int size, |
| uint8_t *data, Request::Flags flag, |
| Tick delay, Event *event); |
| |
| private: |
| /** Packet queue used to store outgoing requests. */ |
| ReqPacketQueue reqQueue; |
| |
| /** Packet queue used to store outgoing snoop responses. */ |
| SnoopRespPacketQueue snoopRespQueue; |
| |
| /** Cached requestorId of the table walker */ |
| RequestorID requestorId; |
| }; |
| |
| /** This translation class is used to trigger the data fetch once a timing |
| translation returns the translated physical address */ |
| class Stage2Walk : public BaseMMU::Translation |
| { |
| private: |
| uint8_t *data; |
| int numBytes; |
| RequestPtr req; |
| Event *event; |
| TableWalker &parent; |
| Addr oVAddr; |
| BaseMMU::Mode mode; |
| MMU::ArmTranslationType tranType; |
| |
| public: |
| Fault fault; |
| |
| Stage2Walk(TableWalker &_parent, uint8_t *_data, Event *_event, |
| Addr vaddr, BaseMMU::Mode mode, |
| MMU::ArmTranslationType tran_type); |
| |
| void markDelayed() {} |
| |
| void finish(const Fault &fault, const RequestPtr &req, |
| ThreadContext *tc, BaseMMU::Mode mode); |
| |
| void |
| setVirt(Addr vaddr, int size, Request::Flags flags, |
| int requestorId) |
| { |
| numBytes = size; |
| req->setVirt(vaddr, size, flags, requestorId, 0); |
| } |
| |
| void translateTiming(ThreadContext *tc); |
| }; |
| |
| Fault readDataUntimed(ThreadContext *tc, Addr vaddr, Addr desc_addr, |
| uint8_t *data, int num_bytes, Request::Flags flags, |
| BaseMMU::Mode mode, MMU::ArmTranslationType tran_type, |
| bool functional); |
| void readDataTimed(ThreadContext *tc, Addr desc_addr, |
| Stage2Walk *translation, int num_bytes, |
| Request::Flags flags); |
| |
| protected: |
| |
| /** Queues of requests for all the different lookup levels */ |
| std::list<WalkerState *> stateQueues[LookupLevel::Num_ArmLookupLevel]; |
| |
| /** Queue of requests that have passed are waiting because the walker is |
| * currently busy. */ |
| std::list<WalkerState *> pendingQueue; |
| |
| /** The MMU to forward second stage look upts to */ |
| MMU *mmu; |
| |
| /** Requestor id assigned by the MMU. */ |
| RequestorID requestorId; |
| |
| /** Port shared by the two table walkers. */ |
| Port* port; |
| |
| /** Indicates whether this table walker is part of the stage 2 mmu */ |
| const bool isStage2; |
| |
| /** TLB that is initiating these table walks */ |
| TLB *tlb; |
| |
| /** Cached copy of the sctlr as it existed when translation began */ |
| SCTLR sctlr; |
| |
| WalkerState *currState; |
| |
| /** If a timing translation is currently in progress */ |
| bool pending; |
| |
| /** The number of walks belonging to squashed instructions that can be |
| * removed from the pendingQueue per cycle. */ |
| unsigned numSquashable; |
| |
| /** Cached copies of system-level properties */ |
| const ArmRelease *release; |
| uint8_t _physAddrRange; |
| bool _haveLargeAsid64; |
| |
| /** Statistics */ |
| struct TableWalkerStats : public statistics::Group |
| { |
| TableWalkerStats(statistics::Group *parent); |
| statistics::Scalar walks; |
| statistics::Scalar walksShortDescriptor; |
| statistics::Scalar walksLongDescriptor; |
| statistics::Vector walksShortTerminatedAtLevel; |
| statistics::Vector walksLongTerminatedAtLevel; |
| statistics::Scalar squashedBefore; |
| statistics::Scalar squashedAfter; |
| statistics::Histogram walkWaitTime; |
| statistics::Histogram walkServiceTime; |
| // Essentially "L" of queueing theory |
| statistics::Histogram pendingWalks; |
| statistics::Vector pageSizes; |
| statistics::Vector2d requestOrigin; |
| } stats; |
| |
| mutable unsigned pendingReqs; |
| mutable Tick pendingChangeTick; |
| |
| static const unsigned REQUESTED = 0; |
| static const unsigned COMPLETED = 1; |
| |
| public: |
| PARAMS(ArmTableWalker); |
| TableWalker(const Params &p); |
| virtual ~TableWalker(); |
| |
| bool haveLargeAsid64() const { return _haveLargeAsid64; } |
| uint8_t physAddrRange() const { return _physAddrRange; } |
| /** Checks if all state is cleared and if so, completes drain */ |
| void completeDrain(); |
| DrainState drain() override; |
| void drainResume() override; |
| |
| gem5::Port &getPort(const std::string &if_name, |
| PortID idx=InvalidPortID) override; |
| |
| Port &getTableWalkerPort(); |
| |
| Fault walk(const RequestPtr &req, ThreadContext *tc, |
| uint16_t asid, vmid_t _vmid, |
| bool hyp, BaseMMU::Mode mode, BaseMMU::Translation *_trans, |
| bool timing, bool functional, bool secure, |
| MMU::ArmTranslationType tran_type, bool stage2, |
| const TlbEntry *walk_entry); |
| |
| void setMmu(MMU *_mmu); |
| void setTlb(TLB *_tlb) { tlb = _tlb; } |
| TLB* getTlb() { return tlb; } |
| void memAttrs(ThreadContext *tc, TlbEntry &te, SCTLR sctlr, |
| uint8_t texcb, bool s); |
| void memAttrsLPAE(ThreadContext *tc, TlbEntry &te, |
| LongDescriptor &lDescriptor); |
| void memAttrsAArch64(ThreadContext *tc, TlbEntry &te, |
| LongDescriptor &lDescriptor); |
| |
| static LookupLevel toLookupLevel(uint8_t lookup_level_as_int); |
| |
| private: |
| |
| void doL1Descriptor(); |
| void doL1DescriptorWrapper(); |
| EventFunctionWrapper doL1DescEvent; |
| |
| void doL2Descriptor(); |
| void doL2DescriptorWrapper(); |
| EventFunctionWrapper doL2DescEvent; |
| |
| void doLongDescriptor(); |
| |
| void doL0LongDescriptorWrapper(); |
| EventFunctionWrapper doL0LongDescEvent; |
| void doL1LongDescriptorWrapper(); |
| EventFunctionWrapper doL1LongDescEvent; |
| void doL2LongDescriptorWrapper(); |
| EventFunctionWrapper doL2LongDescEvent; |
| void doL3LongDescriptorWrapper(); |
| EventFunctionWrapper doL3LongDescEvent; |
| |
| void doLongDescriptorWrapper(LookupLevel curr_lookup_level); |
| Event* LongDescEventByLevel[4]; |
| |
| bool fetchDescriptor(Addr descAddr, uint8_t *data, int numBytes, |
| Request::Flags flags, int queueIndex, Event *event, |
| void (TableWalker::*doDescriptor)()); |
| |
| Fault generateLongDescFault(ArmFault::FaultSource src); |
| |
| void insertTableEntry(DescriptorBase &descriptor, bool longDescriptor); |
| void insertPartialTableEntry(LongDescriptor &descriptor); |
| |
| /** Returns a tuple made of: |
| * 1) The address of the first page table |
| * 2) The address of the first descriptor within the table |
| * 3) The page table level |
| */ |
| std::tuple<Addr, Addr, LookupLevel> walkAddresses( |
| Addr ttbr, GrainSize tg, int tsz, int pa_range); |
| |
| Fault processWalk(); |
| Fault processWalkLPAE(); |
| |
| bool checkVAddrSizeFaultAArch64(Addr addr, int top_bit, |
| GrainSize granule, int tsz, bool low_range); |
| |
| /// Returns true if the address exceeds the range permitted by the |
| /// system-wide setting or by the TCR_ELx IPS/PS setting |
| bool checkAddrSizeFaultAArch64(Addr addr, int pa_range); |
| |
| Fault processWalkAArch64(); |
| void processWalkWrapper(); |
| EventFunctionWrapper doProcessEvent; |
| |
| void nextWalk(ThreadContext *tc); |
| |
| void pendingChange(); |
| |
| static uint8_t pageSizeNtoStatBin(uint8_t N); |
| |
| Fault testWalk(Addr pa, Addr size, TlbEntry::DomainType domain, |
| LookupLevel lookup_level, bool stage2); |
| }; |
| |
| } // namespace ArmISA |
| } // namespace gem5 |
| |
| #endif //__ARCH_ARM_TABLE_WALKER_HH__ |