X86: Implement a page table walker.
--HG-- extra : convert_revision : 36bab5750100318faa9ba7178dc2e38590053aec
This commit is contained in:
parent
7a39457d7f
commit
f17f3d20be
7 changed files with 293 additions and 10 deletions
|
@ -53,12 +53,15 @@
|
||||||
#
|
#
|
||||||
# Authors: Gabe Black
|
# Authors: Gabe Black
|
||||||
|
|
||||||
from m5.SimObject import SimObject
|
from MemObject import MemObject
|
||||||
from m5.params import *
|
from m5.params import *
|
||||||
class X86TLB(SimObject):
|
|
||||||
|
class X86TLB(MemObject):
|
||||||
type = 'X86TLB'
|
type = 'X86TLB'
|
||||||
abstract = True
|
abstract = True
|
||||||
size = Param.Int("TLB size")
|
size = Param.Int("TLB size")
|
||||||
|
walker_port = Port("Port for the hardware table walker")
|
||||||
|
system = Param.System(Parent.any, "system object")
|
||||||
|
|
||||||
class X86DTB(X86TLB):
|
class X86DTB(X86TLB):
|
||||||
type = 'X86DTB'
|
type = 'X86DTB'
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
|
|
||||||
namespace X86ISA {
|
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];
|
tlb = new TlbEntry[size];
|
||||||
std::memset(tlb, 0, sizeof(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]);
|
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
|
void
|
||||||
TLB::insert(Addr vpn, TlbEntry &entry)
|
TLB::insert(Addr vpn, TlbEntry &entry)
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,10 +59,12 @@
|
||||||
#define __ARCH_X86_TLB_HH__
|
#define __ARCH_X86_TLB_HH__
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "arch/x86/pagetable.hh"
|
#include "arch/x86/pagetable.hh"
|
||||||
#include "arch/x86/segmentregs.hh"
|
#include "arch/x86/segmentregs.hh"
|
||||||
#include "config/full_system.hh"
|
#include "config/full_system.hh"
|
||||||
|
#include "mem/mem_object.hh"
|
||||||
#include "mem/request.hh"
|
#include "mem/request.hh"
|
||||||
#include "params/X86DTB.hh"
|
#include "params/X86DTB.hh"
|
||||||
#include "params/X86ITB.hh"
|
#include "params/X86ITB.hh"
|
||||||
|
@ -76,13 +78,16 @@ namespace X86ISA
|
||||||
{
|
{
|
||||||
static const unsigned StoreCheck = 1 << NUM_SEGMENTREGS;
|
static const unsigned StoreCheck = 1 << NUM_SEGMENTREGS;
|
||||||
|
|
||||||
class TLB : public SimObject
|
class TLB;
|
||||||
|
|
||||||
|
class TLB : public MemObject
|
||||||
{
|
{
|
||||||
#if !FULL_SYSTEM
|
|
||||||
protected:
|
protected:
|
||||||
friend class FakeITLBFault;
|
friend class FakeITLBFault;
|
||||||
friend class FakeDTLBFault;
|
friend class FakeDTLBFault;
|
||||||
#endif
|
|
||||||
|
System * sys;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef X86TLBParams Params;
|
typedef X86TLBParams Params;
|
||||||
TLB(const Params *p);
|
TLB(const Params *p);
|
||||||
|
@ -91,6 +96,137 @@ namespace X86ISA
|
||||||
|
|
||||||
TlbEntry *lookup(Addr va, bool update_lru = true);
|
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:
|
protected:
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
|
@ -100,6 +236,8 @@ namespace X86ISA
|
||||||
EntryList freeList;
|
EntryList freeList;
|
||||||
EntryList entryList;
|
EntryList entryList;
|
||||||
|
|
||||||
|
Port *getPort(const std::string &if_name, int idx = -1);
|
||||||
|
|
||||||
void insert(Addr vpn, TlbEntry &entry);
|
void insert(Addr vpn, TlbEntry &entry);
|
||||||
|
|
||||||
void invalidateAll();
|
void invalidateAll();
|
||||||
|
|
|
@ -100,18 +100,25 @@ class BaseCPU(SimObject):
|
||||||
|
|
||||||
_mem_ports = []
|
_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):
|
def connectMemPorts(self, bus):
|
||||||
for p in self._mem_ports:
|
for p in self._mem_ports:
|
||||||
if p != 'physmem_port':
|
if p != 'physmem_port':
|
||||||
exec('self.%s = bus.port' % p)
|
exec('self.%s = bus.port' % p)
|
||||||
|
|
||||||
def addPrivateSplitL1Caches(self, ic, dc):
|
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.icache = ic
|
||||||
self.dcache = dc
|
self.dcache = dc
|
||||||
self.icache_port = ic.cpu_side
|
self.icache_port = ic.cpu_side
|
||||||
self.dcache_port = dc.cpu_side
|
self.dcache_port = dc.cpu_side
|
||||||
self._mem_ports = ['icache.mem_side', 'dcache.mem_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):
|
def addTwoLevelCacheHierarchy(self, ic, dc, l2c):
|
||||||
self.addPrivateSplitL1Caches(ic, dc)
|
self.addPrivateSplitL1Caches(ic, dc)
|
||||||
|
|
|
@ -58,7 +58,7 @@ class DerivO3CPU(BaseCPU):
|
||||||
cachePorts = Param.Unsigned(200, "Cache Ports")
|
cachePorts = Param.Unsigned(200, "Cache Ports")
|
||||||
icache_port = Port("Instruction Port")
|
icache_port = Port("Instruction Port")
|
||||||
dcache_port = Port("Data 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")
|
decodeToFetchDelay = Param.Unsigned(1, "Decode to fetch delay")
|
||||||
renameToFetchDelay = Param.Unsigned(1 ,"Rename to fetch delay")
|
renameToFetchDelay = Param.Unsigned(1 ,"Rename to fetch delay")
|
||||||
|
|
|
@ -41,4 +41,5 @@ class AtomicSimpleCPU(BaseCPU):
|
||||||
icache_port = Port("Instruction Port")
|
icache_port = Port("Instruction Port")
|
||||||
dcache_port = Port("Data Port")
|
dcache_port = Port("Data Port")
|
||||||
physmem_port = Port("Physical Memory 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']
|
||||||
|
|
|
@ -38,4 +38,4 @@ class TimingSimpleCPU(BaseCPU):
|
||||||
profile = Param.Latency('0ns', "trace the kernel stack")
|
profile = Param.Latency('0ns', "trace the kernel stack")
|
||||||
icache_port = Port("Instruction Port")
|
icache_port = Port("Instruction Port")
|
||||||
dcache_port = Port("Data Port")
|
dcache_port = Port("Data Port")
|
||||||
_mem_ports = ['icache_port', 'dcache_port']
|
_mem_ports = BaseCPU._mem_ports + ['icache_port', 'dcache_port']
|
||||||
|
|
Loading…
Reference in a new issue