Change-Id: I29214278c3dd4829c89a6f7c93214b8123912e74 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/67452 Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com> Maintainer: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Bobby Bruce <bbruce@ucdavis.edu> Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
1194 lines
35 KiB
C++
1194 lines
35 KiB
C++
/*
|
|
* 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:
|
|
TableWalker& owner;
|
|
|
|
/** 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__
|