X86: Implement a page table walker.
authorGabe Black <gblack@eecs.umich.edu>
Mon, 12 Nov 2007 22:38:24 +0000 (14:38 -0800)
committerGabe Black <gblack@eecs.umich.edu>
Mon, 12 Nov 2007 22:38:24 +0000 (14:38 -0800)
--HG--
extra : convert_revision : 36bab5750100318faa9ba7178dc2e38590053aec

src/arch/x86/X86TLB.py
src/arch/x86/tlb.cc
src/arch/x86/tlb.hh
src/cpu/BaseCPU.py
src/cpu/o3/O3CPU.py
src/cpu/simple/AtomicSimpleCPU.py
src/cpu/simple/TimingSimpleCPU.py

index ce4db4f4cfdcfb7182a50f230f9fe4ac691a2fee..2d562ba9a396a7a9864955d619ceb26f6c0d880d 100644 (file)
 #
 # 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'
index bf5a8434b44a3dcc428f4367461e16b82c4a8ea4..e30e820b4824fdc1d74adce909f87aa67222b08a 100644 (file)
@@ -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)
 {
index 12739379c1c2bc814276a929b75ca71e39ee4ca7..726c253746408bb1fa39f2df4493ba2909ef3b30 100644 (file)
 #define __ARCH_X86_TLB_HH__
 
 #include <list>
+#include <string>
 
 #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<uint64_t>(), 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<PacketPtr> 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();
index 9b2b99c5812957ae18fc2a108fdb262893a93e06..1af30a5328f0da19c0af9291dd34dd1523baa3cd 100644 (file)
@@ -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)
index 27ca8ce1eef8bb5b3cb5401a67f3fabb6505440f..f0284b2cff3d76b5dc726bf083a2a121ce6d2bb7 100644 (file)
@@ -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")
index bfd1825c27ecc91a21cf20689cd4acebc65794fc..28c2aa9c9e0f84932ad117e465c080e69b03339b 100644 (file)
@@ -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']
index 2fcde175ceadbe83d13445a447cbecb16eae6848..7e777e8138fe05ad1e0b5acf5b2a03c670552778 100644 (file)
@@ -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']