From 1048b548fabfb7af2113f226f2151d3eb0e63289 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Mon, 12 Nov 2007 18:06:57 -0800 Subject: [PATCH] X86: Separate out the page table walker into it's own cc and hh. --HG-- extra : convert_revision : cbc3af01ca3dc911a59224a574007c5c0bcf6042 --- src/arch/x86/SConscript | 1 + src/arch/x86/X86TLB.py | 19 +- src/arch/x86/faults.cc | 4 +- src/arch/x86/pagetable_walker.cc | 533 +++++++++++++++++++++++++++++++ src/arch/x86/pagetable_walker.hh | 189 +++++++++++ src/arch/x86/tlb.cc | 489 +--------------------------- src/arch/x86/tlb.hh | 138 +------- src/cpu/BaseCPU.py | 4 +- 8 files changed, 775 insertions(+), 602 deletions(-) create mode 100644 src/arch/x86/pagetable_walker.cc create mode 100644 src/arch/x86/pagetable_walker.hh diff --git a/src/arch/x86/SConscript b/src/arch/x86/SConscript index 68a18d4c0..eef3956ee 100644 --- a/src/arch/x86/SConscript +++ b/src/arch/x86/SConscript @@ -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') diff --git a/src/arch/x86/X86TLB.py b/src/arch/x86/X86TLB.py index 5c174be59..dc080f37e 100644 --- a/src/arch/x86/X86TLB.py +++ b/src/arch/x86/X86TLB.py @@ -54,15 +54,28 @@ # 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' diff --git a/src/arch/x86/faults.cc b/src/arch/x86/faults.cc index abb5d98d7..1c94a1251 100644 --- a/src/arch/x86/faults.cc +++ b/src/arch/x86/faults.cc @@ -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 index 000000000..0472dcd21 --- /dev/null +++ b/src/arch/x86/pagetable_walker.cc @@ -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(); + else + pte = read->get(); + 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(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 index 000000000..324f16f3c --- /dev/null +++ b/src/arch/x86/pagetable_walker.hh @@ -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 + +#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 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__ diff --git a/src/arch/x86/tlb.cc b/src/arch/x86/tlb.cc index dd516d2a0..68a22bc16 100644 --- a/src/arch/x86/tlb.cc +++ b/src/arch/x86/tlb.cc @@ -69,491 +69,26 @@ #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(); - else - pte = read->get(); - 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(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() { diff --git a/src/arch/x86/tlb.hh b/src/arch/x86/tlb.hh index 93bbf2c9d..a361c2291 100644 --- a/src/arch/x86/tlb.hh +++ b/src/arch/x86/tlb.hh @@ -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 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 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 §ion); @@ -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 diff --git a/src/cpu/BaseCPU.py b/src/cpu/BaseCPU.py index cb5793e57..9fc1db9f1 100644 --- a/src/cpu/BaseCPU.py +++ b/src/cpu/BaseCPU.py @@ -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: -- 2.30.2