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(); diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index 9b2b99c58..1af30a532 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -100,18 +100,25 @@ class BaseCPU(SimObject): _mem_ports = [] + if build_env['TARGET_ISA'] == 'x86': + itb.walker_port = Port("ITB page table walker port") + dtb.walker_port = Port("ITB page table walker port") + _mem_ports = ["itb.walker_port", "dtb.walker_port"] + def connectMemPorts(self, bus): for p in self._mem_ports: if p != 'physmem_port': exec('self.%s = bus.port' % p) def addPrivateSplitL1Caches(self, ic, dc): - assert(len(self._mem_ports) == 2 or len(self._mem_ports) == 3) + assert(len(self._mem_ports) < 6) self.icache = ic self.dcache = dc self.icache_port = ic.cpu_side self.dcache_port = dc.cpu_side self._mem_ports = ['icache.mem_side', 'dcache.mem_side'] + if build_env['TARGET_ISA'] == 'x86': + self._mem_ports += ["itb.walker_port", "dtb.walker_port"] def addTwoLevelCacheHierarchy(self, ic, dc, l2c): self.addPrivateSplitL1Caches(ic, dc) diff --git a/src/cpu/o3/O3CPU.py b/src/cpu/o3/O3CPU.py index 27ca8ce1e..f0284b2cf 100644 --- a/src/cpu/o3/O3CPU.py +++ b/src/cpu/o3/O3CPU.py @@ -58,7 +58,7 @@ class DerivO3CPU(BaseCPU): cachePorts = Param.Unsigned(200, "Cache Ports") icache_port = Port("Instruction Port") dcache_port = Port("Data Port") - _mem_ports = ['icache_port', 'dcache_port'] + _mem_ports = BaseCPU._mem_ports + ['icache_port', 'dcache_port'] decodeToFetchDelay = Param.Unsigned(1, "Decode to fetch delay") renameToFetchDelay = Param.Unsigned(1 ,"Rename to fetch delay") diff --git a/src/cpu/simple/AtomicSimpleCPU.py b/src/cpu/simple/AtomicSimpleCPU.py index bfd1825c2..28c2aa9c9 100644 --- a/src/cpu/simple/AtomicSimpleCPU.py +++ b/src/cpu/simple/AtomicSimpleCPU.py @@ -41,4 +41,5 @@ class AtomicSimpleCPU(BaseCPU): icache_port = Port("Instruction Port") dcache_port = Port("Data Port") physmem_port = Port("Physical Memory Port") - _mem_ports = ['icache_port', 'dcache_port', 'physmem_port'] + _mem_ports = BaseCPU._mem_ports + \ + ['icache_port', 'dcache_port', 'physmem_port'] diff --git a/src/cpu/simple/TimingSimpleCPU.py b/src/cpu/simple/TimingSimpleCPU.py index 2fcde175c..7e777e813 100644 --- a/src/cpu/simple/TimingSimpleCPU.py +++ b/src/cpu/simple/TimingSimpleCPU.py @@ -38,4 +38,4 @@ class TimingSimpleCPU(BaseCPU): profile = Param.Latency('0ns', "trace the kernel stack") icache_port = Port("Instruction Port") dcache_port = Port("Data Port") - _mem_ports = ['icache_port', 'dcache_port'] + _mem_ports = BaseCPU._mem_ports + ['icache_port', 'dcache_port']