}
}
- void FakeITLBFault::invoke(ThreadContext * tc)
- {
- // Start the page table walker.
- tc->getITBPtr()->walk(tc, vaddr, write, execute);
- }
-
- void FakeDTLBFault::invoke(ThreadContext * tc)
- {
- // Start the page table walker.
- tc->getDTBPtr()->walk(tc, vaddr, write, execute);
- }
-
-#else // !FULL_SYSTEM
- void FakeITLBFault::invoke(ThreadContext * tc)
- {
- DPRINTF(TLB, "Invoking an ITLB fault for address %#x at pc %#x.\n",
- vaddr, tc->readPC());
- Process *p = tc->getProcessPtr();
- TlbEntry entry;
- bool success = p->pTable->lookup(vaddr, entry);
- if(!success) {
- panic("Tried to execute unmapped address %#x.\n", vaddr);
- } else {
- Addr alignedVaddr = p->pTable->pageAlign(vaddr);
- DPRINTF(TLB, "Mapping %#x to %#x\n", alignedVaddr,
- entry.pageStart());
- tc->getITBPtr()->insert(alignedVaddr, entry);
- }
- }
-
- void FakeDTLBFault::invoke(ThreadContext * tc)
- {
- DPRINTF(TLB, "Invoking an DTLB fault for address %#x at pc %#x.\n",
- vaddr, tc->readPC());
- Process *p = tc->getProcessPtr();
- TlbEntry entry;
- bool success = p->pTable->lookup(vaddr, entry);
- if(!success) {
- p->checkAndAllocNextPage(vaddr);
- success = p->pTable->lookup(vaddr, entry);
- }
- if(!success) {
- panic("Tried to access unmapped address %#x.\n", vaddr);
- } else {
- Addr alignedVaddr = p->pTable->pageAlign(vaddr);
- DPRINTF(TLB, "Mapping %#x to %#x\n", alignedVaddr,
- entry.pageStart());
- tc->getDTBPtr()->insert(alignedVaddr, entry);
- }
- }
#endif
} // namespace X86ISA
return true;
}
};
-
- // These faults aren't part of the ISA definition. They trigger filling
- // the tlb on a miss and are to take the place of a hardware table walker.
- class FakeITLBFault : public X86Fault
- {
- protected:
- Addr vaddr;
- bool write;
- bool execute;
- public:
- FakeITLBFault(Addr _vaddr, bool _write, bool _execute) :
- X86Fault("fake instruction tlb fault", "itlb", 0),
- vaddr(_vaddr), write(_write), execute(_execute)
- {}
-
- void invoke(ThreadContext * tc);
- };
-
- class FakeDTLBFault : public X86Fault
- {
- protected:
- Addr vaddr;
- bool write;
- bool execute;
- public:
- FakeDTLBFault(Addr _vaddr, bool _write, bool _execute) :
- X86Fault("fake data tlb fault", "dtlb", 0),
- vaddr(_vaddr), write(_write), execute(_execute)
- {}
-
- void invoke(ThreadContext * tc);
- };
};
#endif // __ARCH_X86_FAULTS_HH__
Bitfield<0> p;
EndBitUnion(PageTableEntry)
-void
+Fault
Walker::doNext(PacketPtr &read, PacketPtr &write)
{
assert(state != Ready && state != Waiting);
pte.a = 1;
entry.writable = pte.w;
entry.user = pte.u;
- if (badNX)
- panic("NX violation!\n");
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
entry.noExec = pte.nx;
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
nextState = LongPDP;
break;
case LongPDP:
pte.a = 1;
entry.writable = entry.writable && pte.w;
entry.user = entry.user && pte.u;
- if (badNX)
- panic("NX violation!\n");
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
nextState = LongPD;
break;
case LongPD:
pte.a = 1;
entry.writable = entry.writable && pte.w;
entry.user = entry.user && pte.u;
- if (badNX)
- panic("NX violation!\n");
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
if (!pte.ps) {
// 4 KB page
entry.size = 4 * (1 << 10);
entry.patBit = bits(pte, 12);
entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
}
case LongPTE:
doWrite = !pte.a;
pte.a = 1;
entry.writable = entry.writable && pte.w;
entry.user = entry.user && pte.u;
- if (badNX)
- panic("NX violation!\n");
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
entry.paddr = (uint64_t)pte & (mask(40) << 12);
entry.uncacheable = uncacheable;
entry.global = pte.g;
entry.patBit = bits(pte, 12);
entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
case PAEPDP:
nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael2 * size;
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (!pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
nextState = PAEPD;
break;
case PAEPD:
pte.a = 1;
entry.writable = pte.w;
entry.user = pte.u;
- if (badNX)
- panic("NX violation!\n");
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
if (!pte.ps) {
// 4 KB page
entry.size = 4 * (1 << 10);
entry.patBit = bits(pte, 12);
entry.vaddr = entry.vaddr & ~((2 * (1 << 20)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
}
case PAEPTE:
doWrite = !pte.a;
pte.a = 1;
entry.writable = entry.writable && pte.w;
entry.user = entry.user && pte.u;
- if (badNX)
- panic("NX violation!\n");
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (badNX || !pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
entry.paddr = (uint64_t)pte & (mask(40) << 12);
entry.uncacheable = uncacheable;
entry.global = pte.g;
entry.patBit = bits(pte, 7);
entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
case PSEPD:
doWrite = !pte.a;
pte.a = 1;
entry.writable = pte.w;
entry.user = pte.u;
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (!pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
if (!pte.ps) {
// 4 KB page
entry.size = 4 * (1 << 10);
entry.patBit = bits(pte, 12);
entry.vaddr = entry.vaddr & ~((4 * (1 << 20)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
}
case PD:
doWrite = !pte.a;
pte.a = 1;
entry.writable = pte.w;
entry.user = pte.u;
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (!pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
// 4 KB page
entry.size = 4 * (1 << 10);
nextRead = ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * size;
nextState = PTE;
break;
- nextState = PTE;
- break;
case PTE:
doWrite = !pte.a;
pte.a = 1;
entry.writable = pte.w;
entry.user = pte.u;
- if (!pte.p)
- panic("Page at %#x not present!\n", entry.vaddr);
+ if (!pte.p) {
+ stop();
+ return pageFault(pte.p);
+ }
entry.paddr = (uint64_t)pte & (mask(20) << 12);
entry.uncacheable = uncacheable;
entry.global = pte.g;
entry.patBit = bits(pte, 7);
entry.vaddr = entry.vaddr & ~((4 * (1 << 10)) - 1);
tlb->insert(entry.vaddr, entry);
- nextState = Ready;
- delete read->req;
- delete read;
- read = NULL;
- return;
+ stop();
+ return NoFault;
default:
panic("Unknown page table walker state %d!\n");
}
delete oldRead->req;
delete oldRead;
}
+ return NoFault;
}
-void
-Walker::start(ThreadContext * _tc, Addr vaddr, bool _write, bool _execute)
+Fault
+Walker::start(ThreadContext * _tc, BaseTLB::Translation *_translation,
+ RequestPtr _req, bool _write, bool _execute)
{
assert(state == Ready);
assert(!tc);
tc = _tc;
+ req = _req;
+ Addr vaddr = req->getVaddr();
execute = _execute;
write = _write;
+ translation = _translation;
VAddr addr = vaddr;
// Do long mode.
state = LongPML4;
top = (cr3.longPdtb << 12) + addr.longl4 * size;
+ enableNX = efer.nxe;
} else {
// We're in some flavor of legacy mode.
CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
// Do legacy PAE.
state = PAEPDP;
top = (cr3.paePdtb << 5) + addr.pael3 * size;
+ enableNX = efer.nxe;
} else {
size = 4;
top = (cr3.pdtb << 12) + addr.norml2 * size;
// Do legacy non PSE.
state = PD;
}
+ enableNX = false;
}
}
nextState = Ready;
entry.vaddr = vaddr;
- enableNX = efer.nxe;
-
Request::Flags flags = Request::PHYSICAL;
if (cr3.pcd)
flags.set(Request::UNCACHEABLE);
read->allocate();
Enums::MemoryMode memMode = sys->getMemoryMode();
if (memMode == Enums::timing) {
- tc->suspend();
+ timingFault = NoFault;
port.sendTiming(read);
} else if (memMode == Enums::atomic) {
+ Fault fault;
do {
port.sendAtomic(read);
PacketPtr write = NULL;
- doNext(read, write);
+ fault = doNext(read, write);
+ assert(fault == NoFault || read == NULL);
state = nextState;
nextState = Ready;
if (write)
tc = NULL;
state = Ready;
nextState = Waiting;
+ return fault;
} else {
panic("Unrecognized memory system mode.\n");
}
+ return NoFault;
}
bool
state = nextState;
nextState = Ready;
PacketPtr write = NULL;
- doNext(pkt, write);
+ timingFault = doNext(pkt, write);
state = Waiting;
read = pkt;
+ assert(timingFault == NoFault || read == NULL);
if (write) {
writes.push_back(write);
}
sendPackets();
}
if (inflight == 0 && read == NULL && writes.size() == 0) {
- tc->activate(0);
tc = NULL;
state = Ready;
nextState = Waiting;
+ if (timingFault == NoFault) {
+ /*
+ * Finish the translation. Now that we now the right entry is
+ * in the TLB, this should work with no memory accesses.
+ * There could be new faults unrelated to the table walk like
+ * permissions violations, so we'll need the return value as
+ * well.
+ */
+ bool delayedResponse;
+ Fault fault = tlb->translate(req, tc, NULL, write, execute,
+ delayedResponse, true);
+ assert(!delayedResponse);
+ // Let the CPU continue.
+ translation->finish(fault, req, tc, write);
+ } else {
+ // There was a fault during the walk. Let the CPU know.
+ translation->finish(timingFault, req, tc, write);
+ }
}
} else if (pkt->wasNacked()) {
pkt->reinitNacked();
panic("No page table walker port named %s!\n", if_name);
}
+Fault
+Walker::pageFault(bool present)
+{
+ HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
+ return new PageFault(entry.vaddr, present, write,
+ m5reg.cpl == 3, false, execute && enableNX);
+}
+
}
X86ISA::Walker *
// if the machine is finished, or points to a packet to initiate
// the next read. If any write is required to update an "accessed"
// bit, write will point to a packet to do the write. Otherwise it
- // will be NULL.
- void doNext(PacketPtr &read, PacketPtr &write);
+ // will be NULL. The return value is whatever fault was incurred
+ // during this stage of the lookup.
+ Fault doNext(PacketPtr &read, PacketPtr &write);
// Kick off the state machine.
- void start(ThreadContext * _tc, Addr vaddr, bool write, bool execute);
+ Fault start(ThreadContext * _tc, BaseTLB::Translation *translation,
+ RequestPtr req, bool write, bool execute);
+ // Clean up after the state machine.
+ void
+ stop()
+ {
+ nextState = Ready;
+ delete read->req;
+ delete read;
+ read = NULL;
+ }
protected:
bool retrying;
+ /*
+ * The fault, if any, that's waiting to be delivered in timing mode.
+ */
+ Fault timingFault;
+
/*
* Functions for dealing with packets.
*/
// The TLB we're supposed to load.
TLB * tlb;
System * sys;
+ BaseTLB::Translation * translation;
/*
* State machine state.
*/
ThreadContext * tc;
+ RequestPtr req;
State state;
State nextState;
int size;
bool enableNX;
- bool write, execute;
+ bool write, execute, user;
TlbEntry entry;
Fault pageFault(bool present);
#if FULL_SYSTEM
#include "arch/x86/pagetable_walker.hh"
+#else
+#include "mem/page_table.hh"
+#include "sim/process.hh"
#endif
namespace X86ISA {
#endif
}
-void
+TlbEntry *
TLB::insert(Addr vpn, TlbEntry &entry)
{
//TODO Deal with conflicting entries
*newEntry = entry;
newEntry->vaddr = vpn;
entryList.push_front(newEntry);
+ return newEntry;
}
TLB::EntryList::iterator
return *entry;
}
-#if FULL_SYSTEM
-void
-TLB::walk(ThreadContext * _tc, Addr vaddr, bool write, bool execute)
-{
- walker->start(_tc, vaddr, write, execute);
-}
-#endif
-
void
TLB::invalidateAll()
{
}
}
-template<class TlbFault>
Fault
-TLB::translateAtomic(RequestPtr req, ThreadContext *tc,
- bool write, bool execute)
+TLB::translate(RequestPtr req, ThreadContext *tc,
+ Translation *translation, bool write, bool execute,
+ bool &delayedResponse, bool timing)
{
+ delayedResponse = false;
Addr vaddr = req->getVaddr();
DPRINTF(TLB, "Translating vaddr %#x.\n", vaddr);
uint32_t flags = req->getFlags();
// The vaddr already has the segment base applied.
TlbEntry *entry = lookup(vaddr);
if (!entry) {
- return new TlbFault(vaddr, write, execute);
- } else {
- // Do paging protection checks.
- DPRINTF(TLB, "Entry found with paddr %#x, doing protection checks.\n", entry->paddr);
- Addr paddr = entry->paddr | (vaddr & (entry->size-1));
- DPRINTF(TLB, "Translated %#x -> %#x.\n", vaddr, paddr);
- req->setPaddr(paddr);
+#if FULL_SYSTEM
+ Fault fault = walker->start(tc, translation, req,
+ write, execute);
+ if (timing || fault != NoFault) {
+ // This gets ignored in atomic mode.
+ delayedResponse = true;
+ return fault;
+ }
+ entry = lookup(vaddr);
+ assert(entry);
+#else
+ DPRINTF(TLB, "Handling a TLB miss for "
+ "address %#x at pc %#x.\n",
+ vaddr, tc->readPC());
+
+ Process *p = tc->getProcessPtr();
+ TlbEntry newEntry;
+ bool success = p->pTable->lookup(vaddr, newEntry);
+ if(!success && !execute) {
+ p->checkAndAllocNextPage(vaddr);
+ success = p->pTable->lookup(vaddr, newEntry);
+ }
+ if(!success) {
+ panic("Tried to execute unmapped address %#x.\n", vaddr);
+ } else {
+ Addr alignedVaddr = p->pTable->pageAlign(vaddr);
+ DPRINTF(TLB, "Mapping %#x to %#x\n", alignedVaddr,
+ newEntry.pageStart());
+ entry = insert(alignedVaddr, newEntry);
+ }
+ DPRINTF(TLB, "Miss was serviced.\n");
+#endif
}
+ // Do paging protection checks.
+ DPRINTF(TLB, "Entry found with paddr %#x, "
+ "doing protection checks.\n", entry->paddr);
+ Addr paddr = entry->paddr | (vaddr & (entry->size-1));
+ DPRINTF(TLB, "Translated %#x -> %#x.\n", vaddr, paddr);
+ req->setPaddr(paddr);
} else {
//Use the address which already has segmentation applied.
DPRINTF(TLB, "Paging disabled.\n");
Fault
DTB::translateAtomic(RequestPtr req, ThreadContext *tc, bool write)
{
- return TLB::translateAtomic<FakeDTLBFault>(req, tc, write, false);
+ bool delayedResponse;
+ return TLB::translate(req, tc, NULL, write,
+ false, delayedResponse, false);
}
void
DTB::translateTiming(RequestPtr req, ThreadContext *tc,
Translation *translation, bool write)
{
+ bool delayedResponse;
assert(translation);
- translation->finish(translateAtomic(req, tc, write), req, tc, write);
+ Fault fault = TLB::translate(req, tc, translation,
+ write, false, delayedResponse, true);
+ if (!delayedResponse)
+ translation->finish(fault, req, tc, write);
}
Fault
ITB::translateAtomic(RequestPtr req, ThreadContext *tc)
{
- return TLB::translateAtomic<FakeITLBFault>(req, tc, false, true);
+ bool delayedResponse;
+ return TLB::translate(req, tc, NULL, false,
+ true, delayedResponse, false);
}
void
ITB::translateTiming(RequestPtr req, ThreadContext *tc,
Translation *translation)
{
+ bool delayedResponse;
assert(translation);
- translation->finish(translateAtomic(req, tc), req, tc, false);
+ Fault fault = TLB::translate(req, tc, translation,
+ false, true, delayedResponse, true);
+ if (!delayedResponse)
+ translation->finish(fault, req, tc, false);
}
#if FULL_SYSTEM
class TLB : public BaseTLB
{
protected:
- friend class FakeITLBFault;
- friend class FakeDTLBFault;
+ friend class Walker;
typedef std::list<TlbEntry *> EntryList;
protected:
Walker * walker;
-
- void walk(ThreadContext * _tc, Addr vaddr, bool write, bool execute);
#endif
public:
EntryList freeList;
EntryList entryList;
- template<class TlbFault>
- Fault translateAtomic(RequestPtr req, ThreadContext *tc,
- bool write, bool execute);
- void translateTiming(RequestPtr req, ThreadContext *tc,
- Translation *translation, bool write, bool execute);
+ Fault translate(RequestPtr req, ThreadContext *tc,
+ Translation *translation, bool write, bool execute,
+ bool &delayedResponse, bool timing);
public:
- void insert(Addr vpn, TlbEntry &entry);
+ TlbEntry * insert(Addr vpn, TlbEntry &entry);
// Checkpointing
virtual void serialize(std::ostream &os);