X86: Implement a page table walker.

--HG--
extra : convert_revision : 36bab5750100318faa9ba7178dc2e38590053aec
This commit is contained in:
Gabe Black 2007-11-12 14:38:24 -08:00
parent 7a39457d7f
commit f17f3d20be
7 changed files with 293 additions and 10 deletions

View file

@ -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'

View 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)
{

View file

@ -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();

View 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)

View 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")

View 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']

View 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']