summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/arch/x86/X86TLB.py7
-rw-r--r--src/arch/x86/tlb.cc136
-rw-r--r--src/arch/x86/tlb.hh144
-rw-r--r--src/cpu/BaseCPU.py9
-rw-r--r--src/cpu/o3/O3CPU.py2
-rw-r--r--src/cpu/simple/AtomicSimpleCPU.py3
-rw-r--r--src/cpu/simple/TimingSimpleCPU.py2
7 files changed, 293 insertions, 10 deletions
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 <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();
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']