X86: Separate out the page table walker into it's own cc and hh.
authorGabe Black <gblack@eecs.umich.edu>
Tue, 13 Nov 2007 02:06:57 +0000 (18:06 -0800)
committerGabe Black <gblack@eecs.umich.edu>
Tue, 13 Nov 2007 02:06:57 +0000 (18:06 -0800)
--HG--
extra : convert_revision : cbc3af01ca3dc911a59224a574007c5c0bcf6042

src/arch/x86/SConscript
src/arch/x86/X86TLB.py
src/arch/x86/faults.cc
src/arch/x86/pagetable_walker.cc [new file with mode: 0644]
src/arch/x86/pagetable_walker.hh [new file with mode: 0644]
src/arch/x86/tlb.cc
src/arch/x86/tlb.hh
src/cpu/BaseCPU.py

index 68a18d4c0e91f6f729eb41f31920e6c8564b6fe0..eef3956ee620e1943bcbc09d33a3e7d7d4f5c597 100644 (file)
@@ -112,6 +112,7 @@ if env['TARGET_ISA'] == 'x86':
         SimObject('X86System.py')
 
         # Full-system sources
+        Source('pagetable_walker.cc')
         Source('system.cc')
         Source('stacktrace.cc')
         Source('vtophys.cc')
index 5c174be59d7fa3ee9c24521925f473a1348609ca..dc080f37ea12c688b35cae1483703df10f3c4ce5 100644 (file)
 # Authors: Gabe Black
 
 from MemObject import MemObject
+from m5.SimObject import SimObject
 from m5.params import *
 from m5.proxy import *
+from m5 import build_env
 
-class X86TLB(MemObject):
+if build_env['FULL_SYSTEM']:
+    class X86PagetableWalker(MemObject):
+        type = 'X86PagetableWalker'
+        cxx_namespace = 'X86ISA'
+        cxx_class = 'Walker'
+        port = Port("Port for the hardware table walker")
+        system = Param.System(Parent.any, "system object")
+
+class X86TLB(SimObject):
     type = 'X86TLB'
+    cxx_namespace = 'X86ISA'
+    cxx_class = 'TLB'
     abstract = True
     size = Param.Int("TLB size")
-    walker_port = Port("Port for the hardware table walker")
-    system = Param.System(Parent.any, "system object")
+    if build_env['FULL_SYSTEM']:
+        walker = Param.X86PagetableWalker(\
+                X86PagetableWalker(), "page table walker")
 
 class X86DTB(X86TLB):
     type = 'X86DTB'
index abb5d98d74ee2e74a344c293c3d516b581c721c4..1c94a12512ec2a8f5947845e20a3409b37332bb8 100644 (file)
@@ -118,13 +118,13 @@ namespace X86ISA
     void FakeITLBFault::invoke(ThreadContext * tc)
     {
         // Start the page table walker.
-        tc->getITBPtr()->walker.start(tc, vaddr);
+        tc->getITBPtr()->walk(tc, vaddr);
     }
 
     void FakeDTLBFault::invoke(ThreadContext * tc)
     {
         // Start the page table walker.
-        tc->getDTBPtr()->walker.start(tc, vaddr);
+        tc->getDTBPtr()->walk(tc, vaddr);
     }
 
 #else // !FULL_SYSTEM
diff --git a/src/arch/x86/pagetable_walker.cc b/src/arch/x86/pagetable_walker.cc
new file mode 100644 (file)
index 0000000..0472dcd
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * All rights reserved.
+ *
+ * Redistribution and use of this software in source and binary forms,
+ * with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * The software must be used only for Non-Commercial Use which means any
+ * use which is NOT directed to receiving any direct monetary
+ * compensation for, or commercial advantage from such use.  Illustrative
+ * examples of non-commercial use are academic research, personal study,
+ * teaching, education and corporate research & development.
+ * Illustrative examples of commercial use are distributing products for
+ * commercial advantage and providing services using the software for
+ * commercial advantage.
+ *
+ * If you wish to use this software or functionality therein that may be
+ * covered by patents for commercial use, please contact:
+ *     Director of Intellectual Property Licensing
+ *     Office of Strategy and Technology
+ *     Hewlett-Packard Company
+ *     1501 Page Mill Road
+ *     Palo Alto, California  94304
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.  Redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.  Neither the name of
+ * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.  No right of
+ * sublicense is granted herewith.  Derivatives of the software and
+ * output created using the software may be prepared, but only for
+ * Non-Commercial Uses.  Derivatives of the software may be shared with
+ * others provided: (i) the others agree to abide by the list of
+ * conditions herein which includes the Non-Commercial Use restrictions;
+ * and (ii) such Derivatives of the software include the above copyright
+ * notice to acknowledge the contribution from this software where
+ * applicable, this list of conditions and the disclaimer below.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#include "arch/x86/pagetable.hh"
+#include "arch/x86/pagetable_walker.hh"
+#include "arch/x86/tlb.hh"
+#include "base/bitfield.hh"
+#include "cpu/thread_context.hh"
+#include "cpu/base.hh"
+#include "mem/packet_access.hh"
+#include "mem/request.hh"
+#include "sim/system.hh"
+
+namespace X86ISA {
+
+// Unfortunately, the placement of the base field in a page table entry is
+// very erratic and would make a mess here. It might be moved here at some
+// point in the future.
+BitUnion64(PageTableEntry)
+    Bitfield<63> nx;
+    Bitfield<11, 9> avl;
+    Bitfield<8> g;
+    Bitfield<7> ps;
+    Bitfield<6> d;
+    Bitfield<5> a;
+    Bitfield<4> pcd;
+    Bitfield<3> pwt;
+    Bitfield<2> u;
+    Bitfield<1> w;
+    Bitfield<0> p;
+EndBitUnion(PageTableEntry)
+
+void
+Walker::doNext(PacketPtr &read, PacketPtr &write)
+{
+    assert(state != Ready && state != Waiting);
+    write = NULL;
+    PageTableEntry pte;
+    if (size == 8)
+        pte = read->get<uint64_t>();
+    else
+        pte = read->get<uint32_t>();
+    VAddr vaddr = entry.vaddr;
+    bool uncacheable = pte.pcd;
+    Addr nextRead = 0;
+    bool doWrite = false;
+    bool badNX = pte.nx && (!tlb->allowNX() || !enableNX);
+    switch(state) {
+      case LongPML4:
+        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl3 * size;
+        doWrite = !pte.a;
+        pte.a = 1;
+        entry.writable = pte.w;
+        entry.user = pte.u;
+        if (badNX)
+            panic("NX violation!\n");
+        entry.noExec = pte.nx;
+        if (!pte.p)
+            panic("Page not present!\n");
+        nextState = LongPDP;
+        break;
+      case LongPDP:
+        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl2 * size;
+        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 not present!\n");
+        nextState = LongPD;
+        break;
+      case LongPD:
+        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 not present!\n");
+        if (!pte.ps) {
+            // 4 KB page
+            entry.size = 4 * (1 << 10);
+            nextRead =
+                ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl1 * size;
+            nextState = LongPTE;
+            break;
+        } else {
+            // 2 MB page
+            entry.size = 2 * (1 << 20);
+            entry.paddr = (uint64_t)pte & (mask(31) << 21);
+            entry.uncacheable = uncacheable;
+            entry.global = pte.g;
+            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;
+        }
+      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 not present!\n");
+        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;
+      case PAEPDP:
+        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael2 * size;
+        if (!pte.p)
+            panic("Page not present!\n");
+        nextState = PAEPD;
+        break;
+      case PAEPD:
+        doWrite = !pte.a;
+        pte.a = 1;
+        entry.writable = pte.w;
+        entry.user = pte.u;
+        if (badNX)
+            panic("NX violation!\n");
+        if (!pte.p)
+            panic("Page not present!\n");
+        if (!pte.ps) {
+            // 4 KB page
+            entry.size = 4 * (1 << 10);
+            nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael1 * size;
+            nextState = PAEPTE;
+            break;
+        } else {
+            // 2 MB page
+            entry.size = 2 * (1 << 20);
+            entry.paddr = (uint64_t)pte & (mask(31) << 21);
+            entry.uncacheable = uncacheable;
+            entry.global = pte.g;
+            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;
+        }
+      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 not present!\n");
+        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;
+      case PSEPD:
+        doWrite = !pte.a;
+        pte.a = 1;
+        entry.writable = pte.w;
+        entry.user = pte.u;
+        if (!pte.p)
+            panic("Page not present!\n");
+        if (!pte.ps) {
+            // 4 KB page
+            entry.size = 4 * (1 << 10);
+            nextRead =
+                ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * size;
+            nextState = PTE;
+            break;
+        } else {
+            // 4 MB page
+            entry.size = 4 * (1 << 20);
+            entry.paddr = bits(pte, 20, 13) << 32 | bits(pte, 31, 22) << 22;
+            entry.uncacheable = uncacheable;
+            entry.global = pte.g;
+            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;
+        }
+      case PD:
+        doWrite = !pte.a;
+        pte.a = 1;
+        entry.writable = pte.w;
+        entry.user = pte.u;
+        if (!pte.p)
+            panic("Page not present!\n");
+        // 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 not present!\n");
+        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;
+      default:
+        panic("Unknown page table walker state %d!\n");
+    }
+    PacketPtr oldRead = read;
+    //If we didn't return, we're setting up another read.
+    uint32_t flags = oldRead->req->getFlags();
+    if (uncacheable)
+        flags |= UNCACHEABLE;
+    else
+        flags &= ~UNCACHEABLE;
+    RequestPtr request =
+        new Request(nextRead, oldRead->getSize(), flags);
+    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
+    read->allocate();
+    //If we need to write, adjust the read packet to write the modified value
+    //back to memory.
+    if (doWrite) {
+        write = oldRead;
+        write->set<uint64_t>(pte);
+        write->cmd = MemCmd::WriteReq;
+        write->setDest(Packet::Broadcast);
+    } else {
+        write = NULL;
+        delete oldRead->req;
+        delete oldRead;
+    }
+}
+
+void
+Walker::start(ThreadContext * _tc, Addr vaddr)
+{
+    assert(state == Ready);
+    assert(!tc);
+    tc = _tc;
+
+    VAddr addr = vaddr;
+
+    //Figure out what we're doing.
+    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
+    Addr top = 0;
+    // Check if we're in long mode or not
+    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
+    size = 8;
+    if (efer.lma) {
+        // Do long mode.
+        state = LongPML4;
+        top = (cr3.longPdtb << 12) + addr.longl4 * size;
+    } else {
+        // We're in some flavor of legacy mode.
+        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
+        if (cr4.pae) {
+            // Do legacy PAE.
+            state = PAEPDP;
+            top = (cr3.paePdtb << 5) + addr.pael3 * size;
+        } else {
+            size = 4;
+            top = (cr3.pdtb << 12) + addr.norml2 * size;
+            if (cr4.pse) {
+                // Do legacy PSE.
+                state = PSEPD;
+            } else {
+                // Do legacy non PSE.
+                state = PD;
+            }
+        }
+    }
+
+    nextState = Ready;
+    entry.vaddr = vaddr;
+
+    enableNX = efer.nxe;
+
+    RequestPtr request =
+        new Request(top, size, PHYSICAL | cr3.pcd ? UNCACHEABLE : 0);
+    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
+    read->allocate();
+    Enums::MemoryMode memMode = sys->getMemoryMode();
+    if (memMode == Enums::timing) {
+        tc->suspend();
+        port.sendTiming(read);
+    } else if (memMode == Enums::atomic) {
+        do {
+            port.sendAtomic(read);
+            PacketPtr write = NULL;
+            doNext(read, write);
+            state = nextState;
+            nextState = Ready;
+            if (write)
+                port.sendAtomic(write);
+        } while(read);
+        tc = NULL;
+        state = Ready;
+        nextState = Waiting;
+    } else {
+        panic("Unrecognized memory system mode.\n");
+    }
+}
+
+bool
+Walker::WalkerPort::recvTiming(PacketPtr pkt)
+{
+    return walker->recvTiming(pkt);
+}
+
+bool
+Walker::recvTiming(PacketPtr pkt)
+{
+    inflight--;
+    if (pkt->isResponse() && !pkt->wasNacked()) {
+        if (pkt->isRead()) {
+            assert(inflight);
+            assert(state == Waiting);
+            assert(!read);
+            state = nextState;
+            nextState = Ready;
+            PacketPtr write = NULL;
+            doNext(pkt, write);
+            state = Waiting;
+            read = pkt;
+            if (write) {
+                writes.push_back(write);
+            }
+            sendPackets();
+        } else {
+            sendPackets();
+        }
+        if (inflight == 0 && read == NULL && writes.size() == 0) {
+            tc->activate(0);
+            tc = NULL;
+            state = Ready;
+            nextState = Waiting;
+        }
+    } else if (pkt->wasNacked()) {
+        pkt->reinitNacked();
+        if (!port.sendTiming(pkt)) {
+            retrying = true;
+            if (pkt->isWrite()) {
+                writes.push_back(pkt);
+            } else {
+                assert(!read);
+                read = pkt;
+            }
+        } else {
+            inflight++;
+        }
+    }
+    return true;
+}
+
+Tick
+Walker::WalkerPort::recvAtomic(PacketPtr pkt)
+{
+    return 0;
+}
+
+void
+Walker::WalkerPort::recvFunctional(PacketPtr pkt)
+{
+    return;
+}
+
+void
+Walker::WalkerPort::recvStatusChange(Status status)
+{
+    if (status == RangeChange) {
+        if (!snoopRangeSent) {
+            snoopRangeSent = true;
+            sendStatusChange(Port::RangeChange);
+        }
+        return;
+    }
+
+    panic("Unexpected recvStatusChange.\n");
+}
+
+void
+Walker::WalkerPort::recvRetry()
+{
+    walker->recvRetry();
+}
+
+void
+Walker::recvRetry()
+{
+    retrying = false;
+    sendPackets();
+}
+
+void
+Walker::sendPackets()
+{
+    //If we're already waiting for the port to become available, just return.
+    if (retrying)
+        return;
+
+    //Reads always have priority
+    if (read) {
+        if (!port.sendTiming(read)) {
+            retrying = true;
+            return;
+        } else {
+            inflight++;
+            delete read->req;
+            delete read;
+            read = NULL;
+        }
+    }
+    //Send off as many of the writes as we can.
+    while (writes.size()) {
+        PacketPtr write = writes.back();
+        if (!port.sendTiming(write)) {
+            retrying = true;
+            return;
+        } else {
+            inflight++;
+            delete write->req;
+            delete write;
+            writes.pop_back();
+        }
+    }
+}
+
+Port *
+Walker::getPort(const std::string &if_name, int idx)
+{
+    if (if_name == "port")
+        return &port;
+    else
+        panic("No page table walker port named %s!\n", if_name);
+}
+
+}
+
+X86ISA::Walker *
+X86PagetableWalkerParams::create()
+{
+    return new X86ISA::Walker(this);
+}
diff --git a/src/arch/x86/pagetable_walker.hh b/src/arch/x86/pagetable_walker.hh
new file mode 100644 (file)
index 0000000..324f16f
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2007 The Hewlett-Packard Development Company
+ * All rights reserved.
+ *
+ * Redistribution and use of this software in source and binary forms,
+ * with or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * The software must be used only for Non-Commercial Use which means any
+ * use which is NOT directed to receiving any direct monetary
+ * compensation for, or commercial advantage from such use.  Illustrative
+ * examples of non-commercial use are academic research, personal study,
+ * teaching, education and corporate research & development.
+ * Illustrative examples of commercial use are distributing products for
+ * commercial advantage and providing services using the software for
+ * commercial advantage.
+ *
+ * If you wish to use this software or functionality therein that may be
+ * covered by patents for commercial use, please contact:
+ *     Director of Intellectual Property Licensing
+ *     Office of Strategy and Technology
+ *     Hewlett-Packard Company
+ *     1501 Page Mill Road
+ *     Palo Alto, California  94304
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.  Redistributions
+ * in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.  Neither the name of
+ * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.  No right of
+ * sublicense is granted herewith.  Derivatives of the software and
+ * output created using the software may be prepared, but only for
+ * Non-Commercial Uses.  Derivatives of the software may be shared with
+ * others provided: (i) the others agree to abide by the list of
+ * conditions herein which includes the Non-Commercial Use restrictions;
+ * and (ii) such Derivatives of the software include the above copyright
+ * notice to acknowledge the contribution from this software where
+ * applicable, this list of conditions and the disclaimer below.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Gabe Black
+ */
+
+#ifndef __ARCH_X86_PAGE_TABLE_WALKER_HH__
+#define __ARCH_X86_PAGE_TABLE_WALKER_HH__
+
+#include <vector>
+
+#include "arch/x86/pagetable.hh"
+#include "arch/x86/tlb.hh"
+#include "mem/mem_object.hh"
+#include "mem/packet.hh"
+#include "params/X86PagetableWalker.hh"
+#include "sim/host.hh"
+
+class ThreadContext;
+
+namespace X86ISA
+{
+    class Walker : public MemObject
+    {
+      public:
+        enum State {
+            Ready,
+            Waiting,
+            // Long mode
+            LongPML4, LongPDP, LongPD, LongPTE,
+            // PAE legacy mode
+            PAEPDP, PAEPD, PAEPTE,
+            // Non PAE legacy mode with and without PSE
+            PSEPD, PD, PTE
+        };
+
+        // Act on the current state and determine what to do next. read
+        // should be the packet that just came back from a read and write
+        // should be NULL. When the function returns, read is either NULL
+        // 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);
+
+        // Kick off the state machine.
+        void start(ThreadContext * _tc, Addr vaddr);
+
+      protected:
+
+        /*
+         * State having to do with sending packets.
+         */
+        PacketPtr read;
+        std::vector<PacketPtr> writes;
+
+        // How many memory operations are in flight.
+        unsigned inflight;
+
+        bool retrying;
+
+        /*
+         * Functions for dealing with packets.
+         */
+        bool recvTiming(PacketPtr pkt);
+        void recvRetry();
+
+        void sendPackets();
+
+        /*
+         * Port for accessing memory
+         */
+        class WalkerPort : public Port
+        {
+          public:
+            WalkerPort(const std::string &_name, Walker * _walker) :
+                  Port(_name, _walker), walker(_walker),
+                  snoopRangeSent(false)
+            {}
+
+          protected:
+            Walker * walker;
+
+            bool snoopRangeSent;
+
+            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;
+            }
+        };
+
+        Port *getPort(const std::string &if_name, int idx = -1);
+
+        friend class WalkerPort;
+
+        WalkerPort port;
+
+        // The TLB we're supposed to load.
+        TLB * tlb;
+        System * sys;
+
+        /*
+         * State machine state.
+         */
+        ThreadContext * tc;
+        State state;
+        State nextState;
+        int size;
+        bool enableNX;
+        TlbEntry entry;
+
+      public:
+
+        void setTLB(TLB * _tlb)
+        {
+            tlb = _tlb;
+        }
+
+        typedef X86PagetableWalkerParams Params;
+
+        Walker(const Params *params) :
+            MemObject(params),
+            read(NULL), inflight(0), retrying(false),
+            port(name() + ".port", this),
+            tlb(NULL), sys(params->system),
+            tc(NULL), state(Ready), nextState(Ready)
+        {
+        }
+    };
+}
+#endif // __ARCH_X86_PAGE_TABLE_WALKER_HH__
index dd516d2a0d50ded7deb44b7528a73f399b352a36..68a22bc1686d9581aa0586c00c2c1e1678acc342 100644 (file)
 #include "cpu/base.hh"
 #include "mem/packet_access.hh"
 #include "mem/request.hh"
-#include "sim/system.hh"
-
-namespace X86ISA {
 
 #if FULL_SYSTEM
-TLB::TLB(const Params *p) : MemObject(p), walker(name(), this), size(p->size)
-#else
-TLB::TLB(const Params *p) : MemObject(p), size(p->size)
+#include "arch/x86/pagetable_walker.hh"
 #endif
+
+namespace X86ISA {
+
+TLB::TLB(const Params *p) : SimObject(p), size(p->size)
 {
     tlb = new TlbEntry[size];
     std::memset(tlb, 0, sizeof(TlbEntry) * size);
 
     for (int x = 0; x < size; x++)
         freeList.push_back(&tlb[x]);
-}
 
 #if FULL_SYSTEM
-
-// Unfortunately, the placement of the base field in a page table entry is
-// very erratic and would make a mess here. It might be moved here at some
-// point in the future.
-BitUnion64(PageTableEntry)
-    Bitfield<63> nx;
-    Bitfield<11, 9> avl;
-    Bitfield<8> g;
-    Bitfield<7> ps;
-    Bitfield<6> d;
-    Bitfield<5> a;
-    Bitfield<4> pcd;
-    Bitfield<3> pwt;
-    Bitfield<2> u;
-    Bitfield<1> w;
-    Bitfield<0> p;
-EndBitUnion(PageTableEntry)
-
-void
-TLB::Walker::doNext(PacketPtr &read, PacketPtr &write)
-{
-    assert(state != Ready && state != Waiting);
-    write = NULL;
-    PageTableEntry pte;
-    if (size == 8)
-        pte = read->get<uint64_t>();
-    else
-        pte = read->get<uint32_t>();
-    VAddr vaddr = entry.vaddr;
-    bool uncacheable = pte.pcd;
-    Addr nextRead = 0;
-    bool doWrite = false;
-    bool badNX = pte.nx && (!tlb->allowNX || !enableNX);
-    switch(state) {
-      case LongPML4:
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl3 * size;
-        doWrite = !pte.a;
-        pte.a = 1;
-        entry.writable = pte.w;
-        entry.user = pte.u;
-        if (badNX)
-            panic("NX violation!\n");
-        entry.noExec = pte.nx;
-        if (!pte.p)
-            panic("Page not present!\n");
-        nextState = LongPDP;
-        break;
-      case LongPDP:
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl2 * size;
-        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 not present!\n");
-        nextState = LongPD;
-        break;
-      case LongPD:
-        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 not present!\n");
-        if (!pte.ps) {
-            // 4 KB page
-            entry.size = 4 * (1 << 10);
-            nextRead =
-                ((uint64_t)pte & (mask(40) << 12)) + vaddr.longl1 * size;
-            nextState = LongPTE;
-            break;
-        } else {
-            // 2 MB page
-            entry.size = 2 * (1 << 20);
-            entry.paddr = (uint64_t)pte & (mask(31) << 21);
-            entry.uncacheable = uncacheable;
-            entry.global = pte.g;
-            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;
-        }
-      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 not present!\n");
-        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;
-      case PAEPDP:
-        nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael2 * size;
-        if (!pte.p)
-            panic("Page not present!\n");
-        nextState = PAEPD;
-        break;
-      case PAEPD:
-        doWrite = !pte.a;
-        pte.a = 1;
-        entry.writable = pte.w;
-        entry.user = pte.u;
-        if (badNX)
-            panic("NX violation!\n");
-        if (!pte.p)
-            panic("Page not present!\n");
-        if (!pte.ps) {
-            // 4 KB page
-            entry.size = 4 * (1 << 10);
-            nextRead = ((uint64_t)pte & (mask(40) << 12)) + vaddr.pael1 * size;
-            nextState = PAEPTE;
-            break;
-        } else {
-            // 2 MB page
-            entry.size = 2 * (1 << 20);
-            entry.paddr = (uint64_t)pte & (mask(31) << 21);
-            entry.uncacheable = uncacheable;
-            entry.global = pte.g;
-            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;
-        }
-      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 not present!\n");
-        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;
-      case PSEPD:
-        doWrite = !pte.a;
-        pte.a = 1;
-        entry.writable = pte.w;
-        entry.user = pte.u;
-        if (!pte.p)
-            panic("Page not present!\n");
-        if (!pte.ps) {
-            // 4 KB page
-            entry.size = 4 * (1 << 10);
-            nextRead =
-                ((uint64_t)pte & (mask(20) << 12)) + vaddr.norml2 * size;
-            nextState = PTE;
-            break;
-        } else {
-            // 4 MB page
-            entry.size = 4 * (1 << 20);
-            entry.paddr = bits(pte, 20, 13) << 32 | bits(pte, 31, 22) << 22;
-            entry.uncacheable = uncacheable;
-            entry.global = pte.g;
-            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;
-        }
-      case PD:
-        doWrite = !pte.a;
-        pte.a = 1;
-        entry.writable = pte.w;
-        entry.user = pte.u;
-        if (!pte.p)
-            panic("Page not present!\n");
-        // 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 not present!\n");
-        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;
-      default:
-        panic("Unknown page table walker state %d!\n");
-    }
-    PacketPtr oldRead = read;
-    //If we didn't return, we're setting up another read.
-    uint32_t flags = oldRead->req->getFlags();
-    if (uncacheable)
-        flags |= UNCACHEABLE;
-    else
-        flags &= ~UNCACHEABLE;
-    RequestPtr request =
-        new Request(nextRead, oldRead->getSize(), flags);
-    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
-    read->allocate();
-    //If we need to write, adjust the read packet to write the modified value
-    //back to memory.
-    if (doWrite) {
-        write = oldRead;
-        write->set<uint64_t>(pte);
-        write->cmd = MemCmd::WriteReq;
-        write->setDest(Packet::Broadcast);
-    } else {
-        write = NULL;
-        delete oldRead->req;
-        delete oldRead;
-    }
-}
-
-void
-TLB::Walker::start(ThreadContext * _tc, Addr vaddr)
-{
-    assert(state == Ready);
-    assert(!tc);
-    tc = _tc;
-
-    VAddr addr = vaddr;
-
-    //Figure out what we're doing.
-    CR3 cr3 = tc->readMiscRegNoEffect(MISCREG_CR3);
-    Addr top = 0;
-    // Check if we're in long mode or not
-    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
-    size = 8;
-    if (efer.lma) {
-        // Do long mode.
-        state = LongPML4;
-        top = (cr3.longPdtb << 12) + addr.longl4 * size;
-    } else {
-        // We're in some flavor of legacy mode.
-        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
-        if (cr4.pae) {
-            // Do legacy PAE.
-            state = PAEPDP;
-            top = (cr3.paePdtb << 5) + addr.pael3 * size;
-        } else {
-            size = 4;
-            top = (cr3.pdtb << 12) + addr.norml2 * size;
-            if (cr4.pse) {
-                // Do legacy PSE.
-                state = PSEPD;
-            } else {
-                // Do legacy non PSE.
-                state = PD;
-            }
-        }
-    }
-
-    nextState = Ready;
-    entry.vaddr = vaddr;
-
-    enableNX = efer.nxe;
-
-    RequestPtr request =
-        new Request(top, size, PHYSICAL | cr3.pcd ? UNCACHEABLE : 0);
-    read = new Packet(request, MemCmd::ReadExReq, Packet::Broadcast);
-    read->allocate();
-    Enums::MemoryMode memMode = tlb->sys->getMemoryMode();
-    if (memMode == Enums::timing) {
-        tc->suspend();
-        port.sendTiming(read);
-    } else if (memMode == Enums::atomic) {
-        do {
-            port.sendAtomic(read);
-            PacketPtr write = NULL;
-            doNext(read, write);
-            state = nextState;
-            nextState = Ready;
-            if (write)
-                port.sendAtomic(write);
-        } while(read);
-        tc = NULL;
-        state = Ready;
-        nextState = Waiting;
-    } else {
-        panic("Unrecognized memory system mode.\n");
-    }
-}
-
-bool
-TLB::Walker::WalkerPort::recvTiming(PacketPtr pkt)
-{
-    return walker->recvTiming(pkt);
-}
-
-bool
-TLB::Walker::recvTiming(PacketPtr pkt)
-{
-    inflight--;
-    if (pkt->isResponse() && !pkt->wasNacked()) {
-        if (pkt->isRead()) {
-            assert(inflight);
-            assert(state == Waiting);
-            assert(!read);
-            state = nextState;
-            nextState = Ready;
-            PacketPtr write = NULL;
-            doNext(pkt, write);
-            state = Waiting;
-            read = pkt;
-            if (write) {
-                writes.push_back(write);
-            }
-            sendPackets();
-        } else {
-            sendPackets();
-        }
-        if (inflight == 0 && read == NULL && writes.size() == 0) {
-            tc->activate(0);
-            tc = NULL;
-            state = Ready;
-            nextState = Waiting;
-        }
-    } else if (pkt->wasNacked()) {
-        pkt->reinitNacked();
-        if (!port.sendTiming(pkt)) {
-            retrying = true;
-            if (pkt->isWrite()) {
-                writes.push_back(pkt);
-            } else {
-                assert(!read);
-                read = pkt;
-            }
-        } else {
-            inflight++;
-        }
-    }
-    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()
-{
-    walker->recvRetry();
-}
-
-void
-TLB::Walker::recvRetry()
-{
-    retrying = false;
-    sendPackets();
-}
-
-void
-TLB::Walker::sendPackets()
-{
-    //If we're already waiting for the port to become available, just return.
-    if (retrying)
-        return;
-
-    //Reads always have priority
-    if (read) {
-        if (!port.sendTiming(read)) {
-            retrying = true;
-            return;
-        } else {
-            inflight++;
-            delete read->req;
-            delete read;
-            read = NULL;
-        }
-    }
-    //Send off as many of the writes as we can.
-    while (writes.size()) {
-        PacketPtr write = writes.back();
-        if (!port.sendTiming(write)) {
-            retrying = true;
-            return;
-        } else {
-            inflight++;
-            delete write->req;
-            delete write;
-            writes.pop_back();
-        }
-    }
-}
-
-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);
-}
-
-#else
-
-Port *
-TLB::getPort(const std::string &if_name, int idx)
-{
-    panic("No tlb ports in se!\n", if_name);
-}
-
+    walker = p->walker;
+    walker->setTLB(this);
 #endif
+}
 
 void
 TLB::insert(Addr vpn, TlbEntry &entry)
@@ -593,6 +128,14 @@ TLB::lookup(Addr va, bool update_lru)
     return NULL;
 }
 
+#if FULL_SYSTEM
+void
+TLB::walk(ThreadContext * _tc, Addr vaddr)
+{
+    walker->start(_tc, vaddr);
+}
+#endif
+
 void
 TLB::invalidateAll()
 {
index 93bbf2c9d82a07d7b3c64480eecd2f404e8ec4df..a361c22917be1967786a63d3cb0345bec2449a50 100644 (file)
@@ -77,21 +77,26 @@ class Packet;
 
 namespace X86ISA
 {
+    class Walker;
+
     static const unsigned StoreCheck = 1 << NUM_SEGMENTREGS;
 
     class TLB;
 
-    class TLB : public MemObject
+    class TLB : public SimObject
     {
       protected:
         friend class FakeITLBFault;
         friend class FakeDTLBFault;
 
-        System * sys;
-
-        bool allowNX;
+        bool _allowNX;
 
       public:
+        bool allowNX() const
+        {
+            return _allowNX;
+        }
+
         typedef X86TLBParams Params;
         TLB(const Params *p);
 
@@ -101,119 +106,11 @@ namespace X86ISA
 
 #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. read
-            // should be the packet that just came back from a read and write
-            // should be NULL. When the function returns, read is either NULL
-            // 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);
-
-            // Kick off the state machine.
-            void start(ThreadContext * _tc, Addr vaddr);
-
-          protected:
-            friend class TLB;
-
-            /*
-             * State having to do with sending packets.
-             */
-            PacketPtr read;
-            std::vector<PacketPtr> writes;
-
-            // How many memory operations are in flight.
-            unsigned inflight;
-
-            bool retrying;
-
-            /*
-             * Functions for dealing with packets.
-             */
-            bool recvTiming(PacketPtr pkt);
-            void recvRetry();
-
-            void sendPackets();
-
-            /*
-             * Port for accessing memory
-             */
-            class WalkerPort : public Port
-            {
-              public:
-                WalkerPort(const std::string &_name, Walker * _walker) :
-                      Port(_name, _walker->tlb), walker(_walker),
-                      snoopRangeSent(false)
-                {}
-
-              protected:
-                Walker * walker;
-
-                bool snoopRangeSent;
-
-                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;
-                }
-            };
-
-            friend class WalkerPort;
-
-            WalkerPort port;
-
-            // The TLB we're supposed to load.
-            TLB * tlb;
-
-            /*
-             * State machine state.
-             */
-            ThreadContext * tc;
-            State state;
-            State nextState;
-            int size;
-            bool enableNX;
-            TlbEntry entry;
-
-          public:
-            Walker(const std::string &_name, TLB * _tlb) :
-                read(NULL), inflight(0), retrying(false),
-                port(_name + "-walker_port", this),
-                tlb(_tlb),
-                tc(NULL), state(Ready), nextState(Ready)
-            {
-            }
-        };
-
-        Walker walker;
 
-#endif
+        Walker * walker;
 
-        Port *getPort(const std::string &if_name, int idx = -1);
+        void walk(ThreadContext * _tc, Addr vaddr);
+#endif
 
       public:
         void invalidateAll();
@@ -231,13 +128,14 @@ namespace X86ISA
         EntryList freeList;
         EntryList entryList;
 
-        void insert(Addr vpn, TlbEntry &entry);
-
         template<class TlbFault>
         Fault translate(RequestPtr &req, ThreadContext *tc,
                 bool write, bool execute);
 
       public:
+
+        void insert(Addr vpn, TlbEntry &entry);
+
         // Checkpointing
         virtual void serialize(std::ostream &os);
         virtual void unserialize(Checkpoint *cp, const std::string &section);
@@ -249,8 +147,7 @@ namespace X86ISA
         typedef X86ITBParams Params;
         ITB(const Params *p) : TLB(p)
         {
-            sys = p->system;
-            allowNX = false;
+            _allowNX = false;
         }
 
         Fault translate(RequestPtr &req, ThreadContext *tc);
@@ -264,8 +161,7 @@ namespace X86ISA
         typedef X86DTBParams Params;
         DTB(const Params *p) : TLB(p)
         {
-            sys = p->system;
-            allowNX = true;
+            _allowNX = true;
         }
         Fault translate(RequestPtr &req, ThreadContext *tc, bool write);
 #if FULL_SYSTEM
index cb5793e5747d4a55f7f3cffc438c85ea794b84cd..9fc1db9f1790ab9b4dca376f561de647210e323f 100644 (file)
@@ -101,9 +101,7 @@ class BaseCPU(SimObject):
     _mem_ports = []
 
     if build_env['TARGET_ISA'] == 'x86' and build_env['FULL_SYSTEM']:
-        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"]
+        _mem_ports = ["itb.walker.port", "dtb.walker.port"]
 
     def connectMemPorts(self, bus):
         for p in self._mem_ports: