From f17f3d20be08d25f176138691a29897df54e5cc0 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Mon, 12 Nov 2007 14:38:24 -0800 Subject: X86: Implement a page table walker. --HG-- extra : convert_revision : 36bab5750100318faa9ba7178dc2e38590053aec --- src/arch/x86/X86TLB.py | 7 ++- src/arch/x86/tlb.cc | 136 +++++++++++++++++++++++++++++++++++++++++++++- src/arch/x86/tlb.hh | 144 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 281 insertions(+), 6 deletions(-) (limited to 'src/arch/x86') diff --git a/src/arch/x86/X86TLB.py b/src/arch/x86/X86TLB.py index ce4db4f4c..2d562ba9a 100644 --- a/src/arch/x86/X86TLB.py +++ b/src/arch/x86/X86TLB.py @@ -53,12 +53,15 @@ # # Authors: Gabe Black -from m5.SimObject import SimObject +from MemObject import MemObject from m5.params import * -class X86TLB(SimObject): + +class X86TLB(MemObject): type = 'X86TLB' abstract = True size = Param.Int("TLB size") + walker_port = Port("Port for the hardware table walker") + system = Param.System(Parent.any, "system object") class X86DTB(X86TLB): type = 'X86DTB' diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc index bf5a8434b..e30e820b4 100644 --- a/src/arch/x86/tlb.cc +++ b/src/arch/x86/tlb.cc @@ -72,7 +72,7 @@ namespace X86ISA { -TLB::TLB(const Params *p) : SimObject(p), size(p->size) +TLB::TLB(const Params *p) : MemObject(p), walker(name(), this), size(p->size) { tlb = new TlbEntry[size]; std::memset(tlb, 0, sizeof(TlbEntry) * size); @@ -81,6 +81,140 @@ TLB::TLB(const Params *p) : SimObject(p), size(p->size) freeList.push_back(&tlb[x]); } +bool +TLB::Walker::doNext(uint64_t data, PacketPtr &write) +{ + assert(state != Ready && state != Waiting); + write = NULL; + switch(state) { + case LongPML4: + nextState = LongPDP; + break; + case LongPDP: + nextState = LongPD; + break; + case LongPD: + nextState = LongPTE; + break; + case LongPTE: + nextState = Ready; + return false; + case PAEPDP: + nextState = PAEPD; + break; + case PAEPD: + break; + case PAEPTE: + nextState = Ready; + return false; + case PSEPD: + break; + case PD: + nextState = PTE; + break; + case PTE: + nextState = Ready; + return false; + default: + panic("Unknown page table walker state %d!\n"); + } + return true; +} + +void +TLB::Walker::buildReadPacket(Addr addr) +{ + readRequest.setPhys(addr, size, PHYSICAL | uncachable ? UNCACHEABLE : 0); + readPacket.reinitFromRequest(); +} + +TLB::walker::buildWritePacket(Addr addr) +{ + writeRequest.setPhys(addr, size, PHYSICAL | uncachable ? UNCACHEABLE : 0); + writePacket.reinitFromRequest(); + +bool +TLB::Walker::WalkerPort::recvTiming(PacketPtr pkt) +{ + if (pkt->isResponse() && !pkt->wasNacked()) { + if (pkt->isRead()) { + assert(packet); + assert(walker->state == Waiting); + packet = NULL; + walker->state = walker->nextState; + walker->nextState = Ready; + PacketPtr write; + if (walker->doNext(pkt, write)) { + packet = &walker->packet; + port->sendTiming(packet); + } + if (write) { + writes.push_back(write); + } + while (!port->blocked() && writes.size()) { + if (port->sendTiming(writes.front())) { + writes.pop_front(); + outstandingWrites++; + } + } + } else { + outstandingWrites--; + } + } else if (pkt->wasNacked()) { + pkt->reinitNacked(); + if (!sendTiming(pkt)) { + if (pkt->isWrite()) { + writes.push_front(pkt); + } + } + } + return true; +} + +Tick +TLB::Walker::WalkerPort::recvAtomic(PacketPtr pkt) +{ + return 0; +} + +void +TLB::Walker::WalkerPort::recvFunctional(PacketPtr pkt) +{ + return; +} + +void +TLB::Walker::WalkerPort::recvStatusChange(Status status) +{ + if (status == RangeChange) { + if (!snoopRangeSent) { + snoopRangeSent = true; + sendStatusChange(Port::RangeChange); + } + return; + } + + panic("Unexpected recvStatusChange.\n"); +} + +void +TLB::Walker::WalkerPort::recvRetry() +{ + retrying = false; + if (!sendTiming(packet)) { + retrying = true; + } +} + +Port * +TLB::getPort(const std::string &if_name, int idx) +{ + if (if_name == "walker_port") + return &walker.port; + else + panic("No tlb port named %s!\n", if_name); +} + void TLB::insert(Addr vpn, TlbEntry &entry) { diff --git a/src/arch/x86/tlb.hh b/src/arch/x86/tlb.hh index 12739379c..726c25374 100644 --- a/src/arch/x86/tlb.hh +++ b/src/arch/x86/tlb.hh @@ -59,10 +59,12 @@ #define __ARCH_X86_TLB_HH__ #include +#include #include "arch/x86/pagetable.hh" #include "arch/x86/segmentregs.hh" #include "config/full_system.hh" +#include "mem/mem_object.hh" #include "mem/request.hh" #include "params/X86DTB.hh" #include "params/X86ITB.hh" @@ -76,13 +78,16 @@ namespace X86ISA { static const unsigned StoreCheck = 1 << NUM_SEGMENTREGS; - class TLB : public SimObject + class TLB; + + class TLB : public MemObject { -#if !FULL_SYSTEM protected: friend class FakeITLBFault; friend class FakeDTLBFault; -#endif + + System * sys; + public: typedef X86TLBParams Params; TLB(const Params *p); @@ -91,6 +96,137 @@ namespace X86ISA TlbEntry *lookup(Addr va, bool update_lru = true); +#if FULL_SYSTEM + protected: + class Walker + { + public: + enum State { + Ready, + Waiting, + LongPML4, + LongPDP, + LongPD, + LongPTE, + PAEPDP, + PAEPD, + PAEPTE, + PSEPD, + PD, + PTE + }; + + // Act on the current state and determine what to do next. If the + // walker has finished updating the TLB, this will return false. + bool doNext(PacketPtr read, PacketPtr &write); + + // This does an actual load to feed the walker. If we're in + // atomic mode, this will drive the state machine itself until + // the TLB is filled. If we're in timing mode, the port getting + // a reply will drive the machine using this function which will + // return after starting the memory operation. + void doMemory(Addr addr); + + // Kick off the state machine. + void start(bool _uncachable, Addr _vaddr, Addr cr3, State next) + { + assert(state == Ready); + state = Waiting; + nextState = next; + // If PAE isn't being used, entries are 4 bytes. Otherwise + // they're 8. + if (next == PSEPD || next == PD || next == PTE) + size = 4; + else + size = 8; + vaddr = _vaddr; + uncachable = _uncacheable; + buildPacket(cr3); + if (state == Enums::timing) { + port->sendTiming(&packet); + } else if (state == Enums::atomic) { + port->sendAtomic(&packet); + Addr addr; + while(doNext(packet.get(), addr)) { + buildPacket(addr); + port->sendAtomic(&packet); + } + } else { + panic("Unrecognized memory system mode.\n"); + } + }; + + protected: + friend class TLB; + + class WalkerPort : public Port + { + public: + WalkerPort(const std::string &_name, Walker * _walker) : + Port(_name, _walker->tlb), walker(_walker), + packet(NULL), snoopRangeSent(false), retrying(false) + {} + + protected: + Walker * walker; + + PacketPtr packet; + vector writes; + + bool snoopRangeSent; + bool retrying; + + bool recvTiming(PacketPtr pkt); + Tick recvAtomic(PacketPtr pkt); + void recvFunctional(PacketPtr pkt); + void recvStatusChange(Status status); + void recvRetry(); + void getDeviceAddressRanges(AddrRangeList &resp, + bool &snoop) + { + resp.clear(); + snoop = true; + } + + public: + bool sendTiming(PacketPtr pkt) + { + retrying = !Port::sendTiming(pkt); + return !retrying; + } + + bool blocked() { return retrying; } + }; + + friend class WalkerPort; + + WalkerPort port; + + Packet packet; + Request request; + + TLB * tlb; + + State state; + State nextState; + int size; + + Addr vaddr; + + public: + Walker(const std::string &_name, TLB * _tlb) : + port(_name + "-walker_port", this), + packet(&request, ReadExReq, Broadcast), + tlb(_tlb), state(Ready), nextState(Ready) + { + } + + + }; + + Walker walker; +#endif + protected: int size; @@ -100,6 +236,8 @@ namespace X86ISA EntryList freeList; EntryList entryList; + Port *getPort(const std::string &if_name, int idx = -1); + void insert(Addr vpn, TlbEntry &entry); void invalidateAll(); -- cgit v1.2.3