/*
- * Copyright (c) 2010-2014 ARM Limited
+ * Copyright (c) 2010-2016, 2019 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* 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.
- *
- * Authors: Ali Saidi
- * Giacomo Gabrielli
*/
#ifndef __ARCH_ARM_TABLE_WALKER_HH__
#include <list>
+#include "arch/arm/faults.hh"
#include "arch/arm/miscregs.hh"
#include "arch/arm/system.hh"
#include "arch/arm/tlb.hh"
-#include "dev/dma_device.hh"
-#include "mem/mem_object.hh"
#include "mem/request.hh"
#include "params/ArmTableWalker.hh"
+#include "sim/clocked_object.hh"
#include "sim/eventq.hh"
class ThreadContext;
+class DmaPort;
+
namespace ArmISA {
class Translation;
class TLB;
class Stage2MMU;
-class TableWalker : public MemObject
+class TableWalker : public ClockedObject
{
public:
class WalkerState;
class DescriptorBase {
public:
+ DescriptorBase() : lookupLevel(L0) {}
+
/** Current lookup level for this descriptor */
LookupLevel lookupLevel;
bool _dirty;
/** Default ctor */
- L1Descriptor()
+ L1Descriptor() : data(0), _dirty(false)
{
lookupLevel = L1;
}
*/
bool secure(bool have_security, WalkerState *currState) const
{
- if (have_security) {
+ if (have_security && currState->secureLookup) {
if (type() == PageTable)
return !bits(data, 3);
else
bool _dirty;
/** Default ctor */
- L2Descriptor()
+ L2Descriptor() : data(0), l1Parent(nullptr), _dirty(false)
{
lookupLevel = L2;
}
- L2Descriptor(L1Descriptor &parent) : l1Parent(&parent)
+ L2Descriptor(L1Descriptor &parent) : data(0), l1Parent(&parent),
+ _dirty(false)
{
lookupLevel = L2;
}
Page
};
+ LongDescriptor() : data(0), _dirty(false) {}
+
/** The raw bits of the entry */
uint64_t data;
/** 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 xnTable;
bool pxnTable;
+ /** Hierarchical access permission disable */
+ bool hpd;
+
/** Flag indicating if a second stage of lookup is required */
bool stage2Req;
- /** Indicates whether the translation has been passed onto the second
- * stage mmu, and no more work is required from the first stage.
- */
- bool doingStage2;
-
/** A pointer to the stage 2 translation that's in progress */
TLB::Translation *stage2Tran;
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();
protected:
- /**
- * A snooping DMA port that currently does nothing besides
- * extending the DMA port to accept snoops without complaining.
- */
- class SnoopingDmaPort : public DmaPort
- {
-
- protected:
-
- virtual void recvTimingSnoopReq(PacketPtr pkt)
- { }
-
- virtual Tick recvAtomicSnoop(PacketPtr pkt)
- { return 0; }
-
- virtual void recvFunctionalSnoop(PacketPtr pkt)
- { }
-
- virtual bool isSnooping() const { return true; }
-
- public:
-
- /**
- * A snooping DMA port merely calls the construtor of the DMA
- * port.
- */
- SnoopingDmaPort(MemObject *dev, System *s) :
- DmaPort(dev, s)
- { }
- };
-
/** Queues of requests for all the different lookup levels */
std::list<WalkerState *> stateQueues[MAX_LOOKUP_LEVELS];
* currently busy. */
std::list<WalkerState *> pendingQueue;
-
- /** Port to issue translation requests from */
- SnoopingDmaPort port;
-
- /** If we're draining keep the drain event around until we're drained */
- DrainManager *drainManager;
-
/** The MMU to forward second stage look upts to */
Stage2MMU *stage2Mmu;
+ /** Port shared by the two table walkers. */
+ DmaPort* port;
+
+ /** Requestor id assigned by the MMU. */
+ RequestorID requestorId;
+
/** Indicates whether this table walker is part of the stage 2 mmu */
const bool isStage2;
/** If a timing translation is currently in progress */
bool pending;
- /** Request id for requests generated by this walker */
- MasterID masterId;
-
/** The number of walks belonging to squashed instructions that can be
* removed from the pendingQueue per cycle. */
unsigned numSquashable;
bool _haveVirtualization;
uint8_t physAddrRange;
bool _haveLargeAsid64;
- ArmSystem *armSys;
+
+ /** Statistics */
+ struct TableWalkerStats : public Stats::Group {
+ TableWalkerStats(Stats::Group *parent);
+ Stats::Scalar walks;
+ Stats::Scalar walksShortDescriptor;
+ Stats::Scalar walksLongDescriptor;
+ Stats::Vector walksShortTerminatedAtLevel;
+ Stats::Vector walksLongTerminatedAtLevel;
+ Stats::Scalar squashedBefore;
+ Stats::Scalar squashedAfter;
+ Stats::Histogram walkWaitTime;
+ Stats::Histogram walkServiceTime;
+ Stats::Histogram pendingWalks; // essentially "L" of queueing theory
+ Stats::Vector pageSizes;
+ Stats::Vector2d requestOrigin;
+ } stats;
+
+ mutable unsigned pendingReqs;
+ mutable Tick pendingChangeTick;
+
+ static const unsigned REQUESTED = 0;
+ static const unsigned COMPLETED = 1;
public:
typedef ArmTableWalkerParams Params;
return dynamic_cast<const Params *>(_params);
}
+ void init() override;
+
bool haveLPAE() const { return _haveLPAE; }
bool haveVirtualization() const { return _haveVirtualization; }
bool haveLargeAsid64() const { return _haveLargeAsid64; }
/** Checks if all state is cleared and if so, completes drain */
void completeDrain();
- unsigned int drain(DrainManager *dm);
- virtual void drainResume();
- virtual BaseMasterPort& getMasterPort(const std::string &if_name,
- PortID idx = InvalidPortID);
-
- /**
- * Allow the MMU (overseeing both stage 1 and stage 2 TLBs) to
- * access the table walker port through the TLB so that it can
- * orchestrate staged translations.
- *
- * @return Our DMA port
- */
- DmaPort& getWalkerPort() { return port; }
-
- Fault walk(RequestPtr req, ThreadContext *tc, uint16_t asid, uint8_t _vmid,
+ DrainState drain() override;
+ void drainResume() override;
+
+ Port &getPort(const std::string &if_name,
+ PortID idx=InvalidPortID) override;
+
+ Fault walk(const RequestPtr &req, ThreadContext *tc,
+ uint16_t asid, uint8_t _vmid,
bool _isHyp, TLB::Mode mode, TLB::Translation *_trans,
bool timing, bool functional, bool secure,
- TLB::ArmTranslationType tranType);
+ TLB::ArmTranslationType tranType, bool _stage2Req);
void setTlb(TLB *_tlb) { tlb = _tlb; }
TLB* getTlb() { return tlb; }
- void setMMU(Stage2MMU *m) { stage2Mmu = m; }
+ void setMMU(Stage2MMU *m, RequestorID requestor_id);
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, uint8_t attrIndx,
- uint8_t sh);
+ void memAttrsAArch64(ThreadContext *tc, TlbEntry &te,
+ LongDescriptor &lDescriptor);
static LookupLevel toLookupLevel(uint8_t lookup_level_as_int);
void doL1Descriptor();
void doL1DescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL1DescriptorWrapper> doL1DescEvent;
+ EventFunctionWrapper doL1DescEvent;
void doL2Descriptor();
void doL2DescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL2DescriptorWrapper> doL2DescEvent;
+ EventFunctionWrapper doL2DescEvent;
void doLongDescriptor();
void doL0LongDescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL0LongDescriptorWrapper> doL0LongDescEvent;
+ EventFunctionWrapper doL0LongDescEvent;
void doL1LongDescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL1LongDescriptorWrapper> doL1LongDescEvent;
+ EventFunctionWrapper doL1LongDescEvent;
void doL2LongDescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL2LongDescriptorWrapper> doL2LongDescEvent;
+ EventFunctionWrapper doL2LongDescEvent;
void doL3LongDescriptorWrapper();
- EventWrapper<TableWalker,
- &TableWalker::doL3LongDescriptorWrapper> doL3LongDescEvent;
+ 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);
Fault processWalk();
static bool checkAddrSizeFaultAArch64(Addr addr, int currPhysAddrRange);
Fault processWalkAArch64();
void processWalkWrapper();
- EventWrapper<TableWalker, &TableWalker::processWalkWrapper> doProcessEvent;
+ 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);
};
} // namespace ArmISA