diff options
Diffstat (limited to 'arch/alpha/alpha_memory.cc')
-rw-r--r-- | arch/alpha/alpha_memory.cc | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/arch/alpha/alpha_memory.cc b/arch/alpha/alpha_memory.cc new file mode 100644 index 000000000..34c8e7f7d --- /dev/null +++ b/arch/alpha/alpha_memory.cc @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 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 holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 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. + */ + +#include <sstream> +#include <string> +#include <vector> + +#include "alpha_memory.hh" +#include "ev5.hh" +#include "exec_context.hh" +#include "trace.hh" +#include "inifile.hh" +#include "str.hh" + +using namespace std; + +/////////////////////////////////////////////////////////////////////// +// +// Alpha TLB +// +AlphaTlb::AlphaTlb(const string &name, int s) + : SimObject(name), size(s), nlu(0) +{ + table = new AlphaISA::PTE[size]; + memset(table, 0, sizeof(AlphaISA::PTE[size])); +} + +AlphaTlb::~AlphaTlb() +{ + if (table) + delete [] table; +} + +// look up an entry in the TLB +AlphaISA::PTE * +AlphaTlb::lookup(Addr vpn, uint8_t asn) const +{ + DPRINTF(TLB, "lookup %#x\n", vpn); + + PageTable::const_iterator i = lookupTable.find(vpn); + if (i == lookupTable.end()) + return NULL; + + while (i->first == vpn) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + if (vpn == pte->tag && (pte->asma || pte->asn == asn)) + return pte; + + ++i; + } + + // not found... + return NULL; +} + + +void +AlphaTlb::checkCacheability(MemReqPtr req) +{ + // in Alpha, cacheability is controlled by upper-level bits of the + // physical address + if (req->paddr & PA_UNCACHED_BIT) { + if (PA_IPR_SPACE(req->paddr)) { + // IPR memory space not implemented + if (!req->xc->misspeculating()) + panic("IPR memory space not implemented! PA=%x\n", req->paddr); + } else { + // mark request as uncacheable + req->flags |= UNCACHEABLE; + } + } +} + + +// insert a new TLB entry +void +AlphaTlb::insert(Addr vaddr, AlphaISA::PTE &pte) +{ + if (table[nlu].valid) { + Addr oldvpn = table[nlu].tag; + PageTable::iterator i = lookupTable.find(oldvpn); + + if (i == lookupTable.end()) + panic("TLB entry not found in lookupTable"); + + int index; + while ((index = i->second) != nlu) { + if (table[index].tag != oldvpn) + panic("TLB entry not found in lookupTable"); + + ++i; + } + + DPRINTF(TLB, "remove @%d: %#x -> %#x\n", nlu, oldvpn, table[nlu].ppn); + + lookupTable.erase(i); + } + + Addr vpn = VA_VPN(vaddr); + DPRINTF(TLB, "insert @%d: %#x -> %#x\n", nlu, vpn, pte.ppn); + + table[nlu] = pte; + table[nlu].tag = vpn; + table[nlu].valid = true; + + lookupTable.insert(make_pair(vpn, nlu)); + nextnlu(); +} + +void +AlphaTlb::flushAll() +{ + memset(table, 0, sizeof(AlphaISA::PTE[size])); + lookupTable.clear(); + nlu = 0; +} + +void +AlphaTlb::flushProcesses() +{ + PageTable::iterator i = lookupTable.begin(); + PageTable::iterator end = lookupTable.end(); + while (i != end) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + + if (!pte->asma) { + DPRINTF(TLB, "flush @%d: %#x -> %#x\n", index, pte->tag, pte->ppn); + pte->valid = false; + lookupTable.erase(i); + } + + ++i; + } +} + +void +AlphaTlb::flushAddr(Addr vaddr, uint8_t asn) +{ + Addr vpn = VA_VPN(vaddr); + + PageTable::iterator i = lookupTable.find(vpn); + if (i == lookupTable.end()) + return; + + while (i->first == vpn) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + + if (vpn == pte->tag && (pte->asma || pte->asn == asn)) { + DPRINTF(TLB, "flushaddr @%d: %#x -> %#x\n", index, vpn, pte->ppn); + + // invalidate this entry + pte->valid = false; + + lookupTable.erase(i); + } + + ++i; + } +} + + +void +AlphaTlb::serialize() +{ + nameOut(); + + paramOut("size", size); + paramOut("nlu", nlu); + + stringstream buf; + for (int i = 0; i < size; i++) { + buf.str(""); + ccprintf(buf, "pte%02d.valid", i); + paramOut(buf.str(), table[i].valid); + + buf.str(""); + ccprintf(buf, "pte%02d.tag", i); + paramOut(buf.str(), table[i].tag); + + buf.str(""); + ccprintf(buf, "pte%02d.ppn", i); + paramOut(buf.str(), table[i].ppn); + + buf.str(""); + ccprintf(buf, "pte%02d.xre", i); + paramOut(buf.str(), table[i].xre); + + buf.str(""); + ccprintf(buf, "pte%02d.xwe", i); + paramOut(buf.str(), table[i].xwe); + + buf.str(""); + ccprintf(buf, "pte%02d.fonr", i); + paramOut(buf.str(), table[i].fonr); + + buf.str(""); + ccprintf(buf, "pte%02d.fonw", i); + paramOut(buf.str(), table[i].fonw); + + buf.str(""); + ccprintf(buf, "pte%02d.asma", i); + paramOut(buf.str(), table[i].asma); + + buf.str(""); + ccprintf(buf, "pte%02d.asn", i); + paramOut(buf.str(), table[i].asn); + } +} + +void +AlphaTlb::unserialize(IniFile &db, const string &category, ConfigNode *node) +{ + string data; + stringstream buf; + + db.findDefault(category,"size",data); + to_number(data,size); + db.findDefault(category,"nlu",data); + to_number(data,nlu); + + for (int i = 0; i < size; i++) { + buf.str(""); + ccprintf(buf, "pte%02d.valid", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].valid); + + buf.str(""); + ccprintf(buf, "pte%02d.tag", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].tag); + + buf.str(""); + ccprintf(buf, "pte%02d.ppn", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].ppn); + + buf.str(""); + ccprintf(buf, "pte%02d.xre", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].xre); + + buf.str(""); + ccprintf(buf, "pte%02d.xwe", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].xwe); + + buf.str(""); + ccprintf(buf, "pte%02d.fonr", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].fonr); + + buf.str(""); + ccprintf(buf, "pte%02d.fonw", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].fonw); + + buf.str(""); + ccprintf(buf, "pte%02d.asma", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].asma); + + buf.str(""); + ccprintf(buf, "pte%02d.asn", i); + db.findDefault(category, buf.str(), data); + to_number(data, table[i].asn); + } +} + + +/////////////////////////////////////////////////////////////////////// +// +// Alpha ITB +// +AlphaItb::AlphaItb(const std::string &name, int size) + : AlphaTlb(name, size) +{} + + +void +AlphaItb::regStats() +{ + hits + .name(name() + ".hits") + .desc("ITB hits"); + misses + .name(name() + ".misses") + .desc("ITB misses"); + acv + .name(name() + ".acv") + .desc("ITB acv"); + accesses + .name(name() + ".accesses") + .desc("ITB accesses"); + + accesses = hits + misses; +} + +void +AlphaItb::fault(Addr pc, ExecContext *xc) const +{ + uint64_t *ipr = xc->regs.ipr; + + if (!xc->misspeculating()) { + ipr[AlphaISA::IPR_ITB_TAG] = pc; + ipr[AlphaISA::IPR_IFAULT_VA_FORM] = + ipr[AlphaISA::IPR_IVPTBR] | (VA_VPN(pc) << 3); + } +} + + +Fault +AlphaItb::translate(MemReqPtr req) const +{ + InternalProcReg *ipr = req->xc->regs.ipr; + + if (PC_PAL(req->vaddr)) { + // strip off PAL PC marker (lsb is 1) + req->paddr = (req->vaddr & ~3) & PA_IMPL_MASK; + hits++; + return No_Fault; + } + + // verify that this is a good virtual address + if (!validVirtualAddress(req->vaddr)) { + fault(req->vaddr, req->xc); + acv++; + return Itb_Acv_Fault; + } + + // Check for "superpage" mapping: when SP<1> is set, and + // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>. + if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) && + VA_SPACE(req->vaddr) == 2) { + // only valid in kernel mode + if (ICM_CM(ipr[AlphaISA::IPR_ICM]) != AlphaISA::mode_kernel) { + fault(req->vaddr, req->xc); + acv++; + return Itb_Acv_Fault; + } + + req->flags |= PHYSICAL; + } + + if (req->flags & PHYSICAL) { + req->paddr = req->vaddr & PA_IMPL_MASK; + } else { + // not a physical address: need to look up pte + + AlphaISA::PTE *pte = lookup(VA_VPN(req->vaddr), + DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); + + if (!pte) { + fault(req->vaddr, req->xc); + misses++; + return Itb_Fault_Fault; + } + + req->paddr = PA_PFN2PA(pte->ppn) + VA_POFS(req->vaddr & ~3); + + // check permissions for this access + if (!(pte->xre & (1 << ICM_CM(ipr[AlphaISA::IPR_ICM])))) { + // instruction access fault + fault(req->vaddr, req->xc); + acv++; + return Itb_Acv_Fault; + } + } + + checkCacheability(req); + + hits++; + return No_Fault; +} + +/////////////////////////////////////////////////////////////////////// +// +// Alpha DTB +// +AlphaDtb::AlphaDtb(const std::string &name, int size) + : AlphaTlb(name, size) +{} + +void +AlphaDtb::regStats() +{ + read_hits + .name(name() + ".read_hits") + .desc("DTB read hits") + ; + + read_misses + .name(name() + ".read_misses") + .desc("DTB read misses") + ; + + read_acv + .name(name() + ".read_acv") + .desc("DTB read access violations") + ; + + read_accesses + .name(name() + ".read_accesses") + .desc("DTB read accesses") + ; + + write_hits + .name(name() + ".write_hits") + .desc("DTB write hits") + ; + + write_misses + .name(name() + ".write_misses") + .desc("DTB write misses") + ; + + write_acv + .name(name() + ".write_acv") + .desc("DTB write access violations") + ; + + write_accesses + .name(name() + ".write_accesses") + .desc("DTB write accesses") + ; + + hits + .name(name() + ".hits") + .desc("DTB hits") + ; + + misses + .name(name() + ".misses") + .desc("DTB misses") + ; + + acv + .name(name() + ".acv") + .desc("DTB access violations") + ; + + accesses + .name(name() + ".accesses") + .desc("DTB accesses") + ; + + hits = read_hits + write_hits; + misses = read_misses + write_misses; + acv = read_acv + write_acv; + accesses = read_accesses + write_accesses; +} + +void +AlphaDtb::fault(Addr vaddr, uint64_t flags, ExecContext *xc) const +{ + uint64_t *ipr = xc->regs.ipr; + + // set fault address and flags + if (!xc->misspeculating() && !xc->regs.intrlock) { + // set VA register with faulting address + ipr[AlphaISA::IPR_VA] = vaddr; + + // set MM_STAT register flags + ipr[AlphaISA::IPR_MM_STAT] = (((xc->regs.opcode & 0x3f) << 11) + | ((xc->regs.ra & 0x1f) << 6) + | (flags & 0x3f)); + + // set VA_FORM register with faulting formatted address + ipr[AlphaISA::IPR_VA_FORM] = + ipr[AlphaISA::IPR_MVPTBR] | (VA_VPN(vaddr) << 3); + + // lock these registers until the VA register is read + xc->regs.intrlock = true; + } +} + +Fault +AlphaDtb::translate(MemReqPtr req, bool write) const +{ + RegFile *regs = &req->xc->regs; + Addr pc = regs->pc; + InternalProcReg *ipr = regs->ipr; + + if (write) + write_accesses++; + else + read_accesses++; + + AlphaISA::md_mode_type mode = + (AlphaISA::md_mode_type)DTB_CM_CM(ipr[AlphaISA::IPR_DTB_CM]); + + if (PC_PAL(pc)) { + mode = (req->flags & ALTMODE) ? (AlphaISA::md_mode_type) + (ALT_MODE_AM(ipr[AlphaISA::IPR_ALT_MODE])) + : AlphaISA::mode_kernel; + } + + // verify that this is a good virtual address + if (!validVirtualAddress(req->vaddr)) { + fault(req->vaddr, + ((write ? MM_STAT_WR_MASK : 0) | MM_STAT_BAD_VA_MASK | + MM_STAT_ACV_MASK), + req->xc); + + if (write) { write_acv++; } else { read_acv++; } + return Dtb_Fault_Fault; + } + + // Check for "superpage" mapping: when SP<1> is set, and + // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13>. + if ((MCSR_SP(ipr[AlphaISA::IPR_MCSR]) & 2) && VA_SPACE(req->vaddr) == 2) { + // only valid in kernel mode + if (DTB_CM_CM(ipr[AlphaISA::IPR_DTB_CM]) != AlphaISA::mode_kernel) { + fault(req->vaddr, + ((write ? MM_STAT_WR_MASK : 0) | MM_STAT_ACV_MASK), + req->xc); + if (write) { write_acv++; } else { read_acv++; } + return Dtb_Acv_Fault; + } + + req->flags |= PHYSICAL; + } + + if (req->flags & PHYSICAL) { + req->paddr = req->vaddr & PA_IMPL_MASK; + } else { + // not a physical address: need to look up pte + + AlphaISA::PTE *pte = lookup(VA_VPN(req->vaddr), + DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); + + if (!pte) { + // page fault + fault(req->vaddr, + ((write ? MM_STAT_WR_MASK : 0) | MM_STAT_DTB_MISS_MASK), + req->xc); + if (write) { write_misses++; } else { read_misses++; } + return (req->flags & VPTE) ? Pdtb_Miss_Fault : Ndtb_Miss_Fault; + } + + req->paddr = PA_PFN2PA(pte->ppn) | VA_POFS(req->vaddr); + + if (write) { + if (!(pte->xwe & MODE2MASK(mode))) { + // declare the instruction access fault + fault(req->vaddr, MM_STAT_WR_MASK | MM_STAT_ACV_MASK | + (pte->fonw ? MM_STAT_FONW_MASK : 0), + req->xc); + write_acv++; + return Dtb_Fault_Fault; + } + if (pte->fonw) { + fault(req->vaddr, MM_STAT_WR_MASK | MM_STAT_FONW_MASK, + req->xc); + write_acv++; + return Dtb_Fault_Fault; + } + } else { + if (!(pte->xre & MODE2MASK(mode))) { + fault(req->vaddr, + MM_STAT_ACV_MASK | (pte->fonr ? MM_STAT_FONR_MASK : 0), + req->xc); + read_acv++; + return Dtb_Acv_Fault; + } + if (pte->fonr) { + fault(req->vaddr, MM_STAT_FONR_MASK, req->xc); + read_acv++; + return Dtb_Fault_Fault; + } + } + } + + checkCacheability(req); + + if (write) + write_hits++; + else + read_hits++; + + return No_Fault; +} + +AlphaISA::PTE & +AlphaTlb::index() +{ + AlphaISA::PTE *pte = &table[nlu]; + nextnlu(); + + return *pte; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaItb) + + Param<int> size; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaItb) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaItb) + + INIT_PARAM_DFLT(size, "TLB size", 48) + +END_INIT_SIM_OBJECT_PARAMS(AlphaItb) + + +CREATE_SIM_OBJECT(AlphaItb) +{ + return new AlphaItb(getInstanceName(), size); +} + +REGISTER_SIM_OBJECT("AlphaITB", AlphaItb) + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaDtb) + + Param<int> size; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaDtb) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaDtb) + + INIT_PARAM_DFLT(size, "TLB size", 64) + +END_INIT_SIM_OBJECT_PARAMS(AlphaDtb) + + +CREATE_SIM_OBJECT(AlphaDtb) +{ + return new AlphaDtb(getInstanceName(), size); +} + +REGISTER_SIM_OBJECT("AlphaDTB", AlphaDtb) |