diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/alpha/alpha_memory.cc | 661 | ||||
-rw-r--r-- | arch/alpha/alpha_memory.hh | 126 | ||||
-rw-r--r-- | arch/alpha/arguments.cc | 64 | ||||
-rw-r--r-- | arch/alpha/arguments.hh | 143 | ||||
-rw-r--r-- | arch/alpha/ev5.cc | 571 | ||||
-rw-r--r-- | arch/alpha/ev5.hh | 104 | ||||
-rw-r--r-- | arch/alpha/fake_syscall.cc | 1736 | ||||
-rw-r--r-- | arch/alpha/faults.cc | 61 | ||||
-rw-r--r-- | arch/alpha/faults.hh | 56 | ||||
-rw-r--r-- | arch/alpha/isa_desc | 2427 | ||||
-rw-r--r-- | arch/alpha/isa_traits.hh | 282 | ||||
-rw-r--r-- | arch/alpha/osfpal.cc | 304 | ||||
-rw-r--r-- | arch/alpha/osfpal.hh | 79 | ||||
-rw-r--r-- | arch/alpha/vtophys.cc | 123 | ||||
-rw-r--r-- | arch/alpha/vtophys.hh | 46 | ||||
-rw-r--r-- | arch/isa_parser.py | 1446 |
16 files changed, 8229 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) diff --git a/arch/alpha/alpha_memory.hh b/arch/alpha/alpha_memory.hh new file mode 100644 index 000000000..06fea32e4 --- /dev/null +++ b/arch/alpha/alpha_memory.hh @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#ifndef __ALPHA_MEMORY_HH__ +#define __ALPHA_MEMORY_HH__ + +#include <map> + +#include "mem_req.hh" +#include "sim_object.hh" +#include "statistics.hh" + +class ExecContext; + +class AlphaTlb : public SimObject +{ + protected: + typedef std::multimap<Addr, int> PageTable; + PageTable lookupTable; // Quick lookup into page table + + AlphaISA::PTE *table; // the Page Table + int size; // TLB Size + int nlu; // not last used entry (for replacement) + + void nextnlu() { if (++nlu >= size) nlu = 0; } + AlphaISA::PTE *lookup(Addr vpn, uint8_t asn) const; + + public: + AlphaTlb(const std::string &name, int size); + virtual ~AlphaTlb(); + + int getsize() const { return size; } + + AlphaISA::PTE &index(); + void insert(Addr vaddr, AlphaISA::PTE &pte); + + void flushAll(); + void flushProcesses(); + void flushAddr(Addr addr, uint8_t asn); + + // static helper functions... really EV5 VM traits + static bool validVirtualAddress(Addr vaddr) { + // unimplemented bits must be all 0 or all 1 + Addr unimplBits = vaddr & VA_UNIMPL_MASK; + return (unimplBits == 0) || (unimplBits == VA_UNIMPL_MASK); + } + + static void checkCacheability(MemReqPtr req); + + // Checkpointing + virtual void serialize(); + virtual void unserialize(IniFile &db, const std::string &category, + ConfigNode *node); + +}; + +class AlphaItb : public AlphaTlb +{ + protected: + mutable Statistics::Scalar<> hits; + mutable Statistics::Scalar<> misses; + mutable Statistics::Scalar<> acv; + mutable Statistics::Formula accesses; + + protected: + void fault(Addr pc, ExecContext *xc) const; + + public: + AlphaItb(const std::string &name, int size); + virtual void regStats(); + + Fault translate(MemReqPtr req) const; +}; + +class AlphaDtb : public AlphaTlb +{ + protected: + mutable Statistics::Scalar<> read_hits; + mutable Statistics::Scalar<> read_misses; + mutable Statistics::Scalar<> read_acv; + mutable Statistics::Scalar<> read_accesses; + mutable Statistics::Scalar<> write_hits; + mutable Statistics::Scalar<> write_misses; + mutable Statistics::Scalar<> write_acv; + mutable Statistics::Scalar<> write_accesses; + Statistics::Formula hits; + Statistics::Formula misses; + Statistics::Formula acv; + Statistics::Formula accesses; + + protected: + void fault(Addr pc, uint64_t flags, ExecContext *xc) const; + + public: + AlphaDtb(const std::string &name, int size); + virtual void regStats(); + + Fault translate(MemReqPtr req, bool write) const; +}; + +#endif // __ALPHA_MEMORY_HH__ diff --git a/arch/alpha/arguments.cc b/arch/alpha/arguments.cc new file mode 100644 index 000000000..91e0576f5 --- /dev/null +++ b/arch/alpha/arguments.cc @@ -0,0 +1,64 @@ +/* + * 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 "arguments.hh" +#include "exec_context.hh" +#include "physical_memory.hh" +#include "vtophys.hh" + +AlphaArguments::Data::~Data() +{ + while (!data.empty()) { + delete [] data.front(); + data.pop_front(); + } +} + +char * +AlphaArguments::Data::alloc(size_t size) +{ + char *buf = new char[size]; + data.push_back(buf); + return buf; +} + +uint64_t +AlphaArguments::getArg(bool fp) +{ + if (number < 6) { + if (fp) + return xc->regs.floatRegFile.q[16 + number]; + else + return xc->regs.intRegFile[16 + number]; + } else { + Addr sp = xc->regs.intRegFile[30]; + Addr paddr = vtophys(xc, sp + (number-6) * sizeof(uint64_t)); + return xc->physmem->phys_read_qword(paddr); + } +} + diff --git a/arch/alpha/arguments.hh b/arch/alpha/arguments.hh new file mode 100644 index 000000000..c5fdb60ad --- /dev/null +++ b/arch/alpha/arguments.hh @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#ifndef __ARGUMENTS_HH__ +#define __ARGUMENTS_HH__ + +#include <assert.h> + +#include "host.hh" +#include "kernel.hh" +#include "refcnt.hh" + +class ExecContext; + +class AlphaArguments +{ + protected: + ExecContext *xc; + int number; + uint64_t getArg(bool fp = false); + + protected: + class Data : public RefCounted + { + public: + Data(){} + ~Data(); + + private: + std::list<char *> data; + + public: + char *alloc(size_t size); + }; + + RefCountingPtr<Data> data; + + public: + AlphaArguments(ExecContext *ctx, int n = 0) + : xc(ctx), number(n), data(NULL) + { assert(number >= 0); data = new Data;} + AlphaArguments(const AlphaArguments &args) + : xc(args.xc), number(args.number), data(args.data) {} + ~AlphaArguments() {} + + ExecContext *getExecContext() const { return xc; } + + const AlphaArguments &operator=(const AlphaArguments &args) { + xc = args.xc; + number = args.number; + data = args.data; + return *this; + } + + AlphaArguments &operator++() { + ++number; + assert(number >= 0); + return *this; + } + + AlphaArguments operator++(int) { + AlphaArguments args = *this; + ++number; + assert(number >= 0); + return args; + } + + AlphaArguments &operator--() { + --number; + assert(number >= 0); + return *this; + } + + AlphaArguments operator--(int) { + AlphaArguments args = *this; + --number; + assert(number >= 0); + return args; + } + + const AlphaArguments &operator+=(int index) { + number += index; + assert(number >= 0); + return *this; + } + + const AlphaArguments &operator-=(int index) { + number -= index; + assert(number >= 0); + return *this; + } + + AlphaArguments operator[](int index) { + return AlphaArguments(xc, index); + } + + template <class T> + operator T() { + assert(sizeof(T) <= sizeof(uint64_t)); + T data = static_cast<T>(getArg()); + return data; + } + + template <class T> + operator T *() { + T *buf = (T *)data->alloc(sizeof(T)); + Kernel::CopyData(xc, buf, getArg(), sizeof(T)); + return buf; + } + + operator char *() { + char *buf = data->alloc(2048); + Kernel::CopyString(xc, buf, getArg(), 2048); + return buf; + } +}; + +#endif // __ARGUMENTS_HH__ diff --git a/arch/alpha/ev5.cc b/arch/alpha/ev5.cc new file mode 100644 index 000000000..c1631872a --- /dev/null +++ b/arch/alpha/ev5.cc @@ -0,0 +1,571 @@ +/* $Id$ */ + +#include "alpha_memory.hh" +#include "annotation.hh" +#ifdef DEBUG +#include "debug.hh" +#endif +#include "exec_context.hh" +#include "sim_events.hh" +#include "isa_traits.hh" +#include "remote_gdb.hh" +#include "kgdb.h" // for ALPHA_KENTRY_IF +#include "osfpal.hh" + +#ifdef FULL_SYSTEM + +#ifndef SYSTEM_EV5 +#error This code is only valid for EV5 systems +#endif + +//////////////////////////////////////////////////////////////////////// +// +// +// +void +AlphaISA::swap_palshadow(RegFile *regs, bool use_shadow) +{ + if (regs->pal_shadow == use_shadow) + panic("swap_palshadow: wrong PAL shadow state"); + + regs->pal_shadow = use_shadow; + + for (int i = 0; i < NumIntRegs; i++) { + if (reg_redir[i]) { + IntReg temp = regs->intRegFile[i]; + regs->intRegFile[i] = regs->palregs[i]; + regs->palregs[i] = temp; + } + } +} + +//////////////////////////////////////////////////////////////////////// +// +// Machine dependent functions +// +void +AlphaISA::init(void *mem, RegFile *regs) +{ + ipr_init(mem, regs); +} + +void +m5_exit() +{ + static SimExitEvent event("m5_exit instruction encountered"); +} + +//////////////////////////////////////////////////////////////////////// +// +// alpha exceptions - value equals trap address, update with MD_FAULT_TYPE +// +Addr +AlphaISA::fault_addr[Num_Faults] = { + 0x0000, /* No_Fault */ + 0x0001, /* Reset_Fault */ + 0x0401, /* Machine_Check_Fault */ + 0x0501, /* Arithmetic_Fault */ + 0x0101, /* Interrupt_Fault */ + 0x0201, /* Ndtb_Miss_Fault */ + 0x0281, /* Pdtb_Miss_Fault */ + 0x0301, /* Alignment_Fault */ + 0x0381, /* Dtb_Fault_Fault */ + 0x0381, /* Dtb_Acv_Fault */ + 0x0181, /* Itb_Miss_Fault */ + 0x0181, /* Itb_Fault_Fault */ + 0x0081, /* Itb_Acv_Fault */ + 0x0481, /* Unimplemented_Opcode_Fault */ + 0x0581, /* Fen_Fault */ + 0x2001, /* Pal_Fault */ + 0x0501, /* Integer_Overflow_Fault: maps to Arithmetic_Fault */ +}; + +const int AlphaISA::reg_redir[AlphaISA::NumIntRegs] = { + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 8 */ 1, 1, 1, 1, 1, 1, 1, 0, + /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 24 */ 0, 1, 0, 0, 0, 0, 0, 0 }; + +//////////////////////////////////////////////////////////////////////// +// +// +// +void +AlphaISA::ipr_init(void *mem, RegFile *regs) +{ + uint64_t *ipr = regs->ipr; + + bzero((char *)ipr, NumInternalProcRegs * sizeof(InternalProcReg)); + ipr[IPR_PAL_BASE] = PAL_BASE; +} + + +void +ExecContext::ev5_trap(Fault fault) +{ + assert(!misspeculating()); + kernelStats.fault(fault); + + if (fault == Arithmetic_Fault) + panic("Arithmetic traps are unimplemented!"); + + AlphaISA::InternalProcReg *ipr = regs.ipr; + + // exception restart address + if (fault != Interrupt_Fault || !PC_PAL(regs.pc)) + ipr[AlphaISA::IPR_EXC_ADDR] = regs.pc; + + if (fault == Pal_Fault || fault == Arithmetic_Fault /* || + fault == Interrupt_Fault && !PC_PAL(regs.pc) */) { + // traps... skip faulting instruction + ipr[AlphaISA::IPR_EXC_ADDR] += 4; + } + + if (!PC_PAL(regs.pc)) + AlphaISA::swap_palshadow(®s, true); + + regs.pc = ipr[AlphaISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault]; + regs.npc = regs.pc + sizeof(MachInst); + + Annotate::Ev5Trap(this, fault); +} + + +void +AlphaISA::intr_post(RegFile *regs, Fault fault, Addr pc) +{ + InternalProcReg *ipr = regs->ipr; + bool use_pc = (fault == No_Fault); + + if (fault == Arithmetic_Fault) + panic("arithmetic faults NYI..."); + + // compute exception restart address + if (use_pc || fault == Pal_Fault || fault == Arithmetic_Fault) { + // traps... skip faulting instruction + ipr[IPR_EXC_ADDR] = regs->pc + 4; + } else { + // fault, post fault at excepting instruction + ipr[IPR_EXC_ADDR] = regs->pc; + } + + // jump to expection address (PAL PC bit set here as well...) + if (!use_pc) + regs->npc = ipr[IPR_PAL_BASE] + fault_addr[fault]; + else + regs->npc = ipr[IPR_PAL_BASE] + pc; + + // that's it! (orders of magnitude less painful than x86) +} + +bool AlphaISA::check_interrupts = false; + +Fault +ExecContext::hwrei() +{ + uint64_t *ipr = regs.ipr; + + if (!PC_PAL(regs.pc)) + return Unimplemented_Opcode_Fault; + + kernelStats.hwrei(); + + regs.npc = ipr[AlphaISA::IPR_EXC_ADDR]; + + if (!misspeculating()) { + if ((ipr[AlphaISA::IPR_EXC_ADDR] & 1) == 0) + AlphaISA::swap_palshadow(®s, false); + + AlphaISA::check_interrupts = true; + } + + // FIXME: XXX check for interrupts? XXX + return No_Fault; +} + +uint64_t +ExecContext::readIpr(int idx, Fault &fault) +{ + uint64_t *ipr = regs.ipr; + uint64_t retval = 0; // return value, default 0 + + switch (idx) { + case AlphaISA::IPR_PALtemp0: + case AlphaISA::IPR_PALtemp1: + case AlphaISA::IPR_PALtemp2: + case AlphaISA::IPR_PALtemp3: + case AlphaISA::IPR_PALtemp4: + case AlphaISA::IPR_PALtemp5: + case AlphaISA::IPR_PALtemp6: + case AlphaISA::IPR_PALtemp7: + case AlphaISA::IPR_PALtemp8: + case AlphaISA::IPR_PALtemp9: + case AlphaISA::IPR_PALtemp10: + case AlphaISA::IPR_PALtemp11: + case AlphaISA::IPR_PALtemp12: + case AlphaISA::IPR_PALtemp13: + case AlphaISA::IPR_PALtemp14: + case AlphaISA::IPR_PALtemp15: + case AlphaISA::IPR_PALtemp16: + case AlphaISA::IPR_PALtemp17: + case AlphaISA::IPR_PALtemp18: + case AlphaISA::IPR_PALtemp19: + case AlphaISA::IPR_PALtemp20: + case AlphaISA::IPR_PALtemp21: + case AlphaISA::IPR_PALtemp22: + case AlphaISA::IPR_PALtemp23: + case AlphaISA::IPR_PAL_BASE: + + case AlphaISA::IPR_IVPTBR: + case AlphaISA::IPR_DC_MODE: + case AlphaISA::IPR_MAF_MODE: + case AlphaISA::IPR_ISR: + case AlphaISA::IPR_EXC_ADDR: + case AlphaISA::IPR_IC_PERR_STAT: + case AlphaISA::IPR_DC_PERR_STAT: + case AlphaISA::IPR_MCSR: + case AlphaISA::IPR_ASTRR: + case AlphaISA::IPR_ASTER: + case AlphaISA::IPR_SIRR: + case AlphaISA::IPR_ICSR: + case AlphaISA::IPR_ICM: + case AlphaISA::IPR_DTB_CM: + case AlphaISA::IPR_IPLR: + case AlphaISA::IPR_INTID: + case AlphaISA::IPR_PMCTR: + // no side-effect + retval = ipr[idx]; + break; + + case AlphaISA::IPR_VA: + // SFX: unlocks interrupt status registers + retval = ipr[idx]; + regs.intrlock = false; + break; + + case AlphaISA::IPR_VA_FORM: + case AlphaISA::IPR_MM_STAT: + case AlphaISA::IPR_IFAULT_VA_FORM: + case AlphaISA::IPR_EXC_MASK: + case AlphaISA::IPR_EXC_SUM: + retval = ipr[idx]; + break; + + case AlphaISA::IPR_DTB_PTE: + { + AlphaISA::PTE &pte = dtb->index(); + + retval |= ((u_int64_t)pte.ppn & ULL(0x7ffffff)) << 32; + retval |= ((u_int64_t)pte.xre & ULL(0xf)) << 8; + retval |= ((u_int64_t)pte.xwe & ULL(0xf)) << 12; + retval |= ((u_int64_t)pte.fonr & ULL(0x1)) << 1; + retval |= ((u_int64_t)pte.fonw & ULL(0x1))<< 2; + retval |= ((u_int64_t)pte.asma & ULL(0x1)) << 4; + retval |= ((u_int64_t)pte.asn & ULL(0x7f)) << 57; + } + break; + + // write only registers + case AlphaISA::IPR_HWINT_CLR: + case AlphaISA::IPR_SL_XMIT: + case AlphaISA::IPR_DC_FLUSH: + case AlphaISA::IPR_IC_FLUSH: + case AlphaISA::IPR_ALT_MODE: + case AlphaISA::IPR_DTB_IA: + case AlphaISA::IPR_DTB_IAP: + case AlphaISA::IPR_ITB_IA: + case AlphaISA::IPR_ITB_IAP: + fault = Unimplemented_Opcode_Fault; + break; + + default: + // invalid IPR + fault = Unimplemented_Opcode_Fault; + break; + } + + return retval; +} + +#ifdef DEBUG +// Cause the simulator to break when changing to the following IPL +int break_ipl = -1; +#endif + +Fault +ExecContext::setIpr(int idx, uint64_t val) +{ + uint64_t *ipr = regs.ipr; + + if (misspeculating()) + return No_Fault; + + switch (idx) { + case AlphaISA::IPR_PALtemp0: + case AlphaISA::IPR_PALtemp1: + case AlphaISA::IPR_PALtemp2: + case AlphaISA::IPR_PALtemp3: + case AlphaISA::IPR_PALtemp4: + case AlphaISA::IPR_PALtemp5: + case AlphaISA::IPR_PALtemp6: + case AlphaISA::IPR_PALtemp7: + case AlphaISA::IPR_PALtemp8: + case AlphaISA::IPR_PALtemp9: + case AlphaISA::IPR_PALtemp10: + case AlphaISA::IPR_PALtemp11: + case AlphaISA::IPR_PALtemp12: + case AlphaISA::IPR_PALtemp13: + case AlphaISA::IPR_PALtemp14: + case AlphaISA::IPR_PALtemp15: + case AlphaISA::IPR_PALtemp16: + case AlphaISA::IPR_PALtemp17: + case AlphaISA::IPR_PALtemp18: + case AlphaISA::IPR_PALtemp19: + case AlphaISA::IPR_PALtemp20: + case AlphaISA::IPR_PALtemp21: + case AlphaISA::IPR_PALtemp22: + case AlphaISA::IPR_PAL_BASE: + case AlphaISA::IPR_IC_PERR_STAT: + case AlphaISA::IPR_DC_PERR_STAT: + case AlphaISA::IPR_CC_CTL: + case AlphaISA::IPR_CC: + case AlphaISA::IPR_PMCTR: + // write entire quad w/ no side-effect + ipr[idx] = val; + break; + + case AlphaISA::IPR_PALtemp23: + // write entire quad w/ no side-effect + ipr[idx] = val; + kernelStats.context(ipr[idx]); + Annotate::Context(this); + break; + + case AlphaISA::IPR_DTB_PTE: + // write entire quad w/ no side-effect, tag is forthcoming + ipr[idx] = val; + break; + + case AlphaISA::IPR_EXC_ADDR: + // second least significant bit in PC is always zero + ipr[idx] = val & ~2; + break; + + case AlphaISA::IPR_ASTRR: + case AlphaISA::IPR_ASTER: + // only write least significant four bits - privilege mask + ipr[idx] = val & 0xf; + break; + + case AlphaISA::IPR_IPLR: +#ifdef DEBUG + if (break_ipl != -1 && break_ipl == (val & 0x1f)) + debug_break(); +#endif + + // only write least significant five bits - interrupt level + ipr[idx] = val & 0x1f; + kernelStats.swpipl(ipr[idx]); + Annotate::IPL(this, val & 0x1f); + break; + + case AlphaISA::IPR_DTB_CM: + Annotate::ChangeMode(this, (val & 0x18) != 0); + kernelStats.mode((val & 0x18) != 0); + + case AlphaISA::IPR_ICM: + // only write two mode bits - processor mode + ipr[idx] = val & 0x18; + break; + + case AlphaISA::IPR_ALT_MODE: + // only write two mode bits - processor mode + ipr[idx] = val & 0x18; + break; + + case AlphaISA::IPR_MCSR: + // more here after optimization... + ipr[idx] = val; + break; + + case AlphaISA::IPR_SIRR: + // only write software interrupt mask + ipr[idx] = val & 0x7fff0; + break; + + case AlphaISA::IPR_ICSR: + ipr[idx] = val & ULL(0xffffff0300); + break; + + case AlphaISA::IPR_IVPTBR: + case AlphaISA::IPR_MVPTBR: + ipr[idx] = val & ULL(0xffffffffc0000000); + break; + + case AlphaISA::IPR_DC_TEST_CTL: + ipr[idx] = val & 0x1ffb; + break; + + case AlphaISA::IPR_DC_MODE: + case AlphaISA::IPR_MAF_MODE: + ipr[idx] = val & 0x3f; + break; + + case AlphaISA::IPR_ITB_ASN: + ipr[idx] = val & 0x7f0; + break; + + case AlphaISA::IPR_DTB_ASN: + ipr[idx] = val & ULL(0xfe00000000000000); + break; + + case AlphaISA::IPR_EXC_SUM: + case AlphaISA::IPR_EXC_MASK: + // any write to this register clears it + ipr[idx] = 0; + break; + + case AlphaISA::IPR_INTID: + case AlphaISA::IPR_SL_RCV: + case AlphaISA::IPR_MM_STAT: + case AlphaISA::IPR_ITB_PTE_TEMP: + case AlphaISA::IPR_DTB_PTE_TEMP: + // read-only registers + return Unimplemented_Opcode_Fault; + + case AlphaISA::IPR_HWINT_CLR: + case AlphaISA::IPR_SL_XMIT: + case AlphaISA::IPR_DC_FLUSH: + case AlphaISA::IPR_IC_FLUSH: + // the following are write only + ipr[idx] = val; + break; + + case AlphaISA::IPR_DTB_IA: + // really a control write + ipr[idx] = 0; + + dtb->flushAll(); + break; + + case AlphaISA::IPR_DTB_IAP: + // really a control write + ipr[idx] = 0; + + dtb->flushProcesses(); + break; + + case AlphaISA::IPR_DTB_IS: + // really a control write + ipr[idx] = val; + + dtb->flushAddr(val, DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); + break; + + case AlphaISA::IPR_DTB_TAG: { + struct AlphaISA::PTE pte; + + // FIXME: granularity hints NYI... + if (DTB_PTE_GH(ipr[AlphaISA::IPR_DTB_PTE]) != 0) + panic("PTE GH field != 0"); + + // write entire quad + ipr[idx] = val; + + // construct PTE for new entry + pte.ppn = DTB_PTE_PPN(ipr[AlphaISA::IPR_DTB_PTE]); + pte.xre = DTB_PTE_XRE(ipr[AlphaISA::IPR_DTB_PTE]); + pte.xwe = DTB_PTE_XWE(ipr[AlphaISA::IPR_DTB_PTE]); + pte.fonr = DTB_PTE_FONR(ipr[AlphaISA::IPR_DTB_PTE]); + pte.fonw = DTB_PTE_FONW(ipr[AlphaISA::IPR_DTB_PTE]); + pte.asma = DTB_PTE_ASMA(ipr[AlphaISA::IPR_DTB_PTE]); + pte.asn = DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]); + + // insert new TAG/PTE value into data TLB + dtb->insert(val, pte); + } + break; + + case AlphaISA::IPR_ITB_PTE: { + struct AlphaISA::PTE pte; + + // FIXME: granularity hints NYI... + if (ITB_PTE_GH(val) != 0) + panic("PTE GH field != 0"); + + // write entire quad + ipr[idx] = val; + + // construct PTE for new entry + pte.ppn = ITB_PTE_PPN(val); + pte.xre = ITB_PTE_XRE(val); + pte.xwe = 0; + pte.fonr = ITB_PTE_FONR(val); + pte.fonw = ITB_PTE_FONW(val); + pte.asma = ITB_PTE_ASMA(val); + pte.asn = ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN]); + + // insert new TAG/PTE value into data TLB + itb->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); + } + break; + + case AlphaISA::IPR_ITB_IA: + // really a control write + ipr[idx] = 0; + + itb->flushAll(); + break; + + case AlphaISA::IPR_ITB_IAP: + // really a control write + ipr[idx] = 0; + + itb->flushProcesses(); + break; + + case AlphaISA::IPR_ITB_IS: + // really a control write + ipr[idx] = val; + + itb->flushAddr(val, ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN])); + break; + + default: + // invalid IPR + return Unimplemented_Opcode_Fault; + } + + // no error... + return No_Fault; +} + +/** + * Check for special simulator handling of specific PAL calls. + * If return value is false, actual PAL call will be suppressed. + */ +bool +ExecContext::simPalCheck(int palFunc) +{ + kernelStats.callpal(palFunc); + + switch (palFunc) { + case PAL::halt: + if (!misspeculating()) { + setStatus(Halted); + if (--System::numSystemsRunning == 0) + new SimExitEvent("all cpus halted"); + } + break; + + case PAL::bpt: + case PAL::bugchk: + if (system->remoteGDB->trap(ALPHA_KENTRY_IF)) + return false; + break; + } + + return true; +} + +#endif // FULL_SYSTEM diff --git a/arch/alpha/ev5.hh b/arch/alpha/ev5.hh new file mode 100644 index 000000000..c3330bc01 --- /dev/null +++ b/arch/alpha/ev5.hh @@ -0,0 +1,104 @@ +/* $Id$ */ + +#ifndef __EV5_H__ +#define __EV5_H__ + +#ifndef SYSTEM_EV5 +#error This code is only valid for EV5 systems +#endif + +#include "isa_traits.hh" + +void m5_exit(); + +//////////////////////////////////////////////////////////////////////// +// +// +// + +//////////////////////////////////////////////////////////////////////// +// +// +// + +#define MODE2MASK(X) (1 << (X)) + +// Alpha IPR register accessors +#define PC_PAL(X) ((X) & 0x1) +#define MCSR_SP(X) (((X) >> 1) & 0x3) + +#define ICSR_SDE(X) (((X) >> 30) & 0x1) +#define ICSR_SPE(X) (((X) >> 28) & 0x3) +#define ICSR_FPE(X) (((X) >> 26) & 0x1) + +#define ALT_MODE_AM(X) (((X) >> 3) & 0x3) + +#define DTB_CM_CM(X) (((X) >> 3) & 0x3) +#define DTB_ASN_ASN(X) (((X) >> 57) & 0x7f) +#define DTB_PTE_PPN(X) (((X) >> 32) & 0x07ffffff) +#define DTB_PTE_XRE(X) (((X) >> 8) & 0xf) +#define DTB_PTE_XWE(X) (((X) >> 12) & 0xf) +#define DTB_PTE_FONR(X) (((X) >> 1) & 0x1) +#define DTB_PTE_FONW(X) (((X) >> 2) & 0x1) +#define DTB_PTE_GH(X) (((X) >> 5) & 0x3) +#define DTB_PTE_ASMA(X) (((X) >> 4) & 0x1) + +#define ICM_CM(X) (((X) >> 3) & 0x3) +#define ITB_ASN_ASN(X) (((X) >> 4) & 0x7f) +#define ITB_PTE_PPN(X) (((X) >> 32) & 0x07ffffff) +#define ITB_PTE_XRE(X) (((X) >> 8) & 0xf) +#define ITB_PTE_FONR(X) (((X) >> 1) & 0x1) +#define ITB_PTE_FONW(X) (((X) >> 2) & 0x1) +#define ITB_PTE_GH(X) (((X) >> 5) & 0x3) +#define ITB_PTE_ASMA(X) (((X) >> 4) & 0x1) + +#define VA_UNIMPL_MASK ULL(0xfffff80000000000) +#define VA_IMPL_MASK ULL(0x000007ffffffffff) +#define VA_IMPL(X) ((X) & VA_IMPL_MASK) +#define VA_VPN(X) (VA_IMPL(X) >> 13) +#define VA_SPACE(X) (((X) >> 41) & 0x3) +#define VA_POFS(X) ((X) & 0x1fff) + +#define PA_IMPL_MASK ULL(0xffffffffff) +#define PA_UNCACHED_BIT ULL(0x8000000000) +#define PA_IPR_SPACE(X) ((X) >= ULL(0xFFFFF00000)) + +#define PA_PFN2PA(X) ((X) << 13) + + +#define MM_STAT_BAD_VA_MASK 0x0020 +#define MM_STAT_DTB_MISS_MASK 0x0010 +#define MM_STAT_FONW_MASK 0x0008 +#define MM_STAT_FONR_MASK 0x0004 +#define MM_STAT_ACV_MASK 0x0002 +#define MM_STAT_WR_MASK 0x0001 + + +//////////////////////////////////////////////////////////////////////// +// +// +// + +// VPTE size for HW_LD/HW_ST +#define HW_VPTE ((inst >> 11) & 0x1) + +// QWORD size for HW_LD/HW_ST +#define HW_QWORD ((inst >> 12) & 0x1) + +// ALT mode for HW_LD/HW_ST +#define HW_ALT (((inst >> 14) & 0x1) ? ALTMODE : 0) + +// LOCK/COND mode for HW_LD/HW_ST +#define HW_LOCK (((inst >> 10) & 0x1) ? LOCKED : 0) +#define HW_COND (((inst >> 10) & 0x1) ? LOCKED : 0) + +// PHY size for HW_LD/HW_ST +#define HW_PHY (((inst >> 15) & 0x1) ? PHYSICAL : 0) + +// OFFSET for HW_LD/HW_ST +#define HW_OFS (inst & 0x3ff) + + +#define PAL_BASE 0x4000 + +#endif //__EV5_H__ diff --git a/arch/alpha/fake_syscall.cc b/arch/alpha/fake_syscall.cc new file mode 100644 index 000000000..ad3c86515 --- /dev/null +++ b/arch/alpha/fake_syscall.cc @@ -0,0 +1,1736 @@ +/* + * 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 <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> // for memset() + +#include "host.hh" +#include "base_cpu.hh" +#include "functional_memory.hh" +#include "prog.hh" +#include "exec_context.hh" +#include "fake_syscall.hh" +#include "sim_events.hh" + +#include "osf_syscalls.h" +#include "universe.hh" // for curTick & ticksPerSecond + +#include "trace.hh" + +using namespace std; + +// +// System call descriptor +// +class SyscallDesc { + + public: + + typedef int (*FuncPtr)(SyscallDesc *, int num, + Process *, ExecContext *); + + const char *name; + FuncPtr funcPtr; + int flags; + + SyscallDesc(const char *_name, FuncPtr _funcPtr, int _flags = 0) + : name(_name), funcPtr(_funcPtr), flags(_flags) + {} + + int doFunc(int num, Process *proc, ExecContext *xc) { + return (*funcPtr)(this, num, proc, xc); + } +}; + + +class BaseBufferArg { + + public: + + BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size) { + bufPtr = new uint8_t[size]; + // clear out buffer: in case we only partially populate this, + // and then do a copyOut(), we want to make sure we don't + // introduce any random junk into the simulated address space + memset(bufPtr, 0, size); + } + + virtual ~BaseBufferArg() { delete [] bufPtr; } + + // + // copy data into simulator space (read from target memory) + // + virtual bool copyIn(FunctionalMemory *mem) { + mem->access(Read, addr, bufPtr, size); + return true; // no EFAULT detection for now + } + + // + // copy data out of simulator space (write to target memory) + // + virtual bool copyOut(FunctionalMemory *mem) { + mem->access(Write, addr, bufPtr, size); + return true; // no EFAULT detection for now + } + + protected: + Addr addr; + int size; + uint8_t *bufPtr; +}; + + +class BufferArg : public BaseBufferArg +{ + public: + BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { } + void *bufferPtr() { return bufPtr; } +}; + +template <class T> +class TypedBufferArg : public BaseBufferArg +{ + public: + // user can optionally specify a specific number of bytes to + // allocate to deal with those structs that have variable-size + // arrays at the end + TypedBufferArg(Addr _addr, int _size = sizeof(T)) + : BaseBufferArg(_addr, _size) + { } + + // type case + operator T*() { return (T *)bufPtr; } + + // dereference operators + T& operator*() { return *((T *)bufPtr); } + T* operator->() { return (T *)bufPtr; } + T& operator[](int i) { return ((T *)bufPtr)[i]; } +}; + + +static IntReg +getArg(ExecContext *xc, int i) +{ + return xc->regs.intRegFile[ArgumentReg0 + i]; +} + + +// +// used to shift args for indirect syscall +// +static void +setArg(ExecContext *xc, int i, IntReg val) +{ + xc->regs.intRegFile[ArgumentReg0 + i] = val; +} + + +static void +set_return_value(ExecContext *xc, IntReg return_value) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + const int RegA3 = 19; // only place this is used + if (return_value >= 0) { + // no error + xc->regs.intRegFile[RegA3] = 0; + xc->regs.intRegFile[ReturnValueReg] = return_value; + } else { + // got an error, return details + xc->regs.intRegFile[RegA3] = (IntReg) -1; + xc->regs.intRegFile[ReturnValueReg] = -return_value; + } +} + + +int +getpagesizeFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + return VMPageSize; +} + + +int +obreakFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + // change brk addr to first arg + process->brk_point = getArg(xc, 0); + return process->brk_point; +} + + +int +ioctlFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + unsigned req = getArg(xc, 1); + + switch (req) { + case OSF::TIOCGETP: { + // get tty parameters: the main use of this call is by + // isatty(), which really just wants to see whether it + // succeeds or returns ENOTTY to determine whether this is + // a terminal or not. This call is in turn used by the + // stdio library to determine whether to do line buffering + // or block buffering on a specific file descriptor. + TypedBufferArg<OSF::sgttyb> buf(getArg(xc, 2)); + + if (fd < 0) { + // bad file descriptor + return -EBADF; + } else if (0 <= fd < 3) { + // stdin/stdout/stderr: make it look like a terminal + // so we get line buffering & not block buffering + buf->sg_ispeed = 0xf; + buf->sg_ospeed = 0xf; + buf->sg_erase = 0x7f; + buf->sg_kill = 0x15; + buf->sg_flags = 0x18; + buf.copyOut(xc->mem); + return 0; + } else { + // any other file descriptor: assume it's a file or + // pipe and not a terminal + return -ENOTTY; + } + break; + } + + case OSF::TIOCISATTY: + if (fd < 0) { + // bad file descriptor + return -EBADF; + } else if (0 <= fd < 3) { + // stdin/stdout/stderr: make it look like a terminal + // so we get line buffering & not block buffering + return 0; + } else { + // any other file descriptor: assume it's a file or + // pipe and not a terminal + return -ENOTTY; + } + break; + + default: + cerr << "Unsupported ioctl call: ioctl(" + << fd << ", " << req << ", ...)" << endl; + abort(); + break; + } +} + + +int +openFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + string path; + + if (xc->mem->readString(path, getArg(xc, 0)) != No_Fault) + return -EFAULT; + + if (path == "/dev/sysdev0") { + // This is a memory-mapped high-resolution timer device on Alpha. + // We don't support it, so just punt. + DCOUT(SyscallWarnings) << "Ignoring open(" << path << ", ...)" << endl; + return -ENOENT; + } + + int osfFlags = getArg(xc, 1); + int mode = getArg(xc, 2); + int hostFlags = 0; + + // translate open flags + for (int i = 0; i < OSF::NUM_OPEN_FLAGS; i++) { + if (osfFlags & OSF::openFlagTable[i].osfFlag) { + osfFlags &= ~OSF::openFlagTable[i].osfFlag; + hostFlags |= OSF::openFlagTable[i].hostFlag; + } + } + + // any target flags left? + if (osfFlags != 0) + cerr << "Syscall: open: cannot decode flags: " << osfFlags << endl; + +#ifdef __CYGWIN32__ + hostFlags |= O_BINARY; +#endif + + // open the file + int fd = open(path.c_str(), hostFlags, mode); + + return (fd == -1) ? -errno : process->open_fd(fd); +} + + +int +closeFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + return close(fd); +} + + +int +readFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + int nbytes = getArg(xc, 2); + BufferArg bufArg(getArg(xc, 1), nbytes); + + int bytes_read = read(fd, bufArg.bufferPtr(), nbytes); + + if (bytes_read != -1) + bufArg.copyOut(xc->mem); + + return bytes_read; +} + +int +writeFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + int nbytes = getArg(xc, 2); + BufferArg bufArg(getArg(xc, 1), nbytes); + + bufArg.copyIn(xc->mem); + + int bytes_written = write(fd, bufArg.bufferPtr(), nbytes); + + fsync(fd); + + return bytes_written; +} + + +int +lseekFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + uint64_t offs = getArg(xc, 1); + int whence = getArg(xc, 2); + + off_t result = lseek(fd, offs, whence); + + return (result == (off_t)-1) ? -errno : result; +} + +static +void +copyOutStatBuf(FunctionalMemory *mem, Addr addr, struct stat *host) +{ + TypedBufferArg<OSF::F64_stat> tgt(addr); + + tgt->st_dev = host->st_dev; + tgt->st_ino = host->st_ino; + tgt->st_mode = host->st_mode; + tgt->st_nlink = host->st_nlink; + tgt->st_uid = host->st_uid; + tgt->st_gid = host->st_gid; + tgt->st_rdev = host->st_rdev; + tgt->st_size = host->st_size; + tgt->st_atimeX = host->st_atime; + tgt->st_mtimeX = host->st_mtime; + tgt->st_ctimeX = host->st_ctime; + tgt->st_blksize = host->st_blksize; + tgt->st_blocks = host->st_blocks; + + tgt.copyOut(mem); +} + +int +statFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + string path; + + if (xc->mem->readString(path, getArg(xc, 0)) != No_Fault) + return -EFAULT; + + struct stat hostBuf; + int result = stat(path.c_str(), &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf(xc->mem, getArg(xc, 1), &hostBuf); + + return 0; +} + + +int +lstatFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + string path; + + if (xc->mem->readString(path, getArg(xc, 0)) != No_Fault) + return -EFAULT; + + struct stat hostBuf; + int result = lstat(path.c_str(), &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf(xc->mem, getArg(xc, 1), &hostBuf); + + return 0; +} + +int +fstatFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int fd = process->sim_fd(getArg(xc, 0)); + + if (fd < 0) + return -EBADF; + + struct stat hostBuf; + int result = fstat(fd, &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf(xc->mem, getArg(xc, 1), &hostBuf); + + return 0; +} + + +// +// We don't handle mmap(). If the target is really mmaping /dev/zero, +// we can get away with doing nothing (since the simulator doesn't +// really check addresses anyway). Always print a warning, since this +// could be seriously broken if we're not mapping /dev/zero. +// +// Someday we should explicitly check for /dev/zero in open, flag the +// file descriptor, and fail an mmap to anything else. +// +int +mmapFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr start = getArg(xc, 0); + uint64_t length = getArg(xc, 1); + int prot = getArg(xc, 2); + int flags = getArg(xc, 3); + int fd = process->sim_fd(getArg(xc, 4)); + int offset = getArg(xc, 5); + + cerr << "Warning: ignoring syscall mmap(" + << start << ", " << length << ", " + << prot << ", " << flags << ", " + << fd << " " << getArg(xc, 4) << ", " + << offset << ")" << endl; + + return start; +} + + +const char *hostname = "m5.eecs.umich.edu"; + +int +unameFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + TypedBufferArg<OSF::utsname> name(getArg(xc, 0)); + + strcpy(name->sysname, "OSF1"); + strcpy(name->nodename, hostname); + strcpy(name->release, "V5.1"); + strcpy(name->version, "732"); + strcpy(name->machine, "alpha"); + + name.copyOut(xc->mem); + return 0; +} + + +int +gethostnameFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int name_len = getArg(xc, 1); + BufferArg name(getArg(xc, 0), name_len); + + strncpy((char *)name.bufferPtr(), hostname, name_len); + + name.copyOut(xc->mem); + + return 0; +} + + +int +getsysinfoFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + unsigned op = getArg(xc, 0); + unsigned nbytes = getArg(xc, 2); + + switch (op) { + + case OSF::GSI_MAX_CPU: { + TypedBufferArg<uint32_t> max_cpu(getArg(xc, 1)); + *max_cpu = process->numCpus; + max_cpu.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_CPUS_IN_BOX: { + TypedBufferArg<uint32_t> cpus_in_box(getArg(xc, 1)); + *cpus_in_box = process->numCpus; + cpus_in_box.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_PHYSMEM: { + TypedBufferArg<uint64_t> physmem(getArg(xc, 1)); + *physmem = 1024 * 1024; // physical memory in KB + physmem.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_CPU_INFO: { + TypedBufferArg<OSF::cpu_info> infop(getArg(xc, 1)); + + infop->current_cpu = 0; + infop->cpus_in_box = process->numCpus; + infop->cpu_type = 57; + infop->ncpus = process->numCpus; + int cpumask = (1 << process->numCpus) - 1; + infop->cpus_present = infop->cpus_running = cpumask; + infop->cpu_binding = 0; + infop->cpu_ex_binding = 0; + infop->mhz = 667; + + infop.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_PROC_TYPE: { + TypedBufferArg<uint64_t> proc_type(getArg(xc, 1)); + *proc_type = 11; + proc_type.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_PLATFORM_NAME: { + BufferArg bufArg(getArg(xc, 1), nbytes); + strncpy((char *)bufArg.bufferPtr(), + "COMPAQ Professional Workstation XP1000", + nbytes); + bufArg.copyOut(xc->mem); + return 1; + } + + case OSF::GSI_CLK_TCK: { + TypedBufferArg<uint64_t> clk_hz(getArg(xc, 1)); + *clk_hz = 1024; + clk_hz.copyOut(xc->mem); + return 1; + } + + default: + cerr << "getsysinfo: unknown op " << op << endl; + abort(); + break; + } + + return 0; +} + +int +getpidFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + // Make up a PID. There's no interprocess communication in + // fake_syscall mode, so there's no way for a process to know it's + // not getting a unique value. + return 100; +} + +int +getuidFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + // Make up a UID. + return 100; +} + +int +getrlimitFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + unsigned resource = getArg(xc, 0); + TypedBufferArg<OSF::rlimit> rlp(getArg(xc, 1)); + + switch (resource) { + case OSF::RLIMIT_STACK: + // max stack size in bytes: make up a number (2MB for now) + rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024; + break; + + default: + cerr << "getrlimitFunc: unimplemented resource " << resource << endl; + abort(); + break; + } + + rlp.copyOut(xc->mem); + return 0; +} + +// 1M usecs in 1 sec, for readability +static const int one_million = 1000000; + +// seconds since the epoch (1/1/1970)... about a billion, by my reckoning +static const unsigned seconds_since_epoch = 1000000000; + +// +// helper function: populate struct timeval with approximation of +// current elapsed time +// +static void +getElapsedTime(OSF::timeval *tp) +{ + int cycles_per_usec = ticksPerSecond / one_million; + + int elapsed_usecs = curTick / cycles_per_usec; + tp->tv_sec = elapsed_usecs / one_million; + tp->tv_usec = elapsed_usecs % one_million; +} + + +int +gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + TypedBufferArg<OSF::timeval> tp(getArg(xc, 0)); + + getElapsedTime(tp); + tp->tv_sec += seconds_since_epoch; + + tp.copyOut(xc->mem); + + return 0; +} + + +int +getrusageFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int who = getArg(xc, 0); // THREAD, SELF, or CHILDREN + TypedBufferArg<OSF::rusage> rup(getArg(xc, 1)); + + if (who != OSF::RUSAGE_SELF) { + // don't really handle THREAD or CHILDREN, but just warn and + // plow ahead + DCOUT(SyscallWarnings) + << "Warning: getrusage() only supports RUSAGE_SELF." + << " Parameter " << who << " ignored." << endl; + } + + getElapsedTime(&rup->ru_utime); + rup->ru_stime.tv_sec = 0; + rup->ru_stime.tv_usec = 0; + rup->ru_maxrss = 0; + rup->ru_ixrss = 0; + rup->ru_idrss = 0; + rup->ru_isrss = 0; + rup->ru_minflt = 0; + rup->ru_majflt = 0; + rup->ru_nswap = 0; + rup->ru_inblock = 0; + rup->ru_oublock = 0; + rup->ru_msgsnd = 0; + rup->ru_msgrcv = 0; + rup->ru_nsignals = 0; + rup->ru_nvcsw = 0; + rup->ru_nivcsw = 0; + + rup.copyOut(xc->mem); + + return 0; +} + + +int +sigreturnFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + RegFile *regs = &xc->regs; + TypedBufferArg<OSF::sigcontext> sc(getArg(xc, 0)); + + sc.copyIn(xc->mem); + + // restore state from sigcontext structure + regs->pc = sc->sc_pc; + regs->npc = regs->pc + sizeof(MachInst); + + for (int i = 0; i < 31; ++i) { + regs->intRegFile[i] = sc->sc_regs[i]; + regs->floatRegFile.q[i] = sc->sc_fpregs[i]; + } + + regs->miscRegs.fpcr = sc->sc_fpcr; + + return 0; +} + +int +tableFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int id = getArg(xc, 0); // table ID + int index = getArg(xc, 1); // index into table + // arg 2 is buffer pointer; type depends on table ID + int nel = getArg(xc, 3); // number of elements + int lel = getArg(xc, 4); // expected element size + + switch (id) { + case OSF::TBL_SYSINFO: { + if (index != 0 || nel != 1 || lel != sizeof(OSF::tbl_sysinfo)) + return -EINVAL; + TypedBufferArg<OSF::tbl_sysinfo> elp(getArg(xc, 2)); + + const int clk_hz = one_million; + elp->si_user = curTick / (ticksPerSecond / clk_hz); + elp->si_nice = 0; + elp->si_sys = 0; + elp->si_idle = 0; + elp->wait = 0; + elp->si_hz = clk_hz; + elp->si_phz = clk_hz; + elp->si_boottime = seconds_since_epoch; // seconds since epoch? + elp->si_max_procs = process->numCpus; + elp.copyOut(xc->mem); + return 0; + } + + default: + cerr << "table(): id " << id << " unknown." << endl; + return -EINVAL; + } +} + +// +// forward declaration... defined below table +// +int +indirectSyscallFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc); + +// +// Handler for unimplemented syscalls that we haven't thought about. +// +int +unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + cerr << "Error: syscall " << desc->name + << " (#" << callnum << ") unimplemented."; + cerr << " Args: " << getArg(xc, 0) << ", " << getArg(xc, 1) + << ", ..." << endl; + + abort(); +} + + +// +// Handler for unimplemented syscalls that we never intend to +// implement (signal handling, etc.) and should not affect the correct +// behavior of the program. Print a warning only if the appropriate +// trace flag is enabled. Return success to the target program. +// +int +ignoreFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + DCOUT(SyscallWarnings) << "Warning: ignoring syscall " << desc->name + << "(" << getArg(xc, 0) + << ", " << getArg(xc, 1) + << ", ...)" << endl; + + return 0; +} + + +int +exitFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + new SimExitEvent("syscall caused exit", getArg(xc, 0) & 0xff); + + return 1; +} + + +SyscallDesc syscallDescs[] = { + /* 0 */ SyscallDesc("syscall (#0)", indirectSyscallFunc), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("old_open", unimplementedFunc), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("wait4", unimplementedFunc), + /* 8 */ SyscallDesc("old_creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unimplementedFunc), + /* 11 */ SyscallDesc("execv", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("fchdir", unimplementedFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", unimplementedFunc), + /* 16 */ SyscallDesc("chown", unimplementedFunc), + /* 17 */ SyscallDesc("obreak", obreakFunc), + /* 18 */ SyscallDesc("pre_F64_getfsstat", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getpid", getpidFunc), + /* 21 */ SyscallDesc("mount", unimplementedFunc), + /* 22 */ SyscallDesc("unmount", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", unimplementedFunc), + /* 24 */ SyscallDesc("getuid", getuidFunc), + /* 25 */ SyscallDesc("exec_with_loader", unimplementedFunc), + /* 26 */ SyscallDesc("ptrace", unimplementedFunc), + /* 27 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 28 */ SyscallDesc("sendmsg", unimplementedFunc), + /* 29 */ SyscallDesc("recvfrom", unimplementedFunc), + /* 30 */ SyscallDesc("accept", unimplementedFunc), + /* 31 */ SyscallDesc("getpeername", unimplementedFunc), + /* 32 */ SyscallDesc("getsockname", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("chflags", unimplementedFunc), + /* 35 */ SyscallDesc("fchflags", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", unimplementedFunc), + /* 38 */ SyscallDesc("old_stat", unimplementedFunc), + /* 39 */ SyscallDesc("setpgid", unimplementedFunc), + /* 40 */ SyscallDesc("old_lstat", unimplementedFunc), + /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 42 */ SyscallDesc("pipe", unimplementedFunc), + /* 43 */ SyscallDesc("set_program_attributes", unimplementedFunc), + /* 44 */ SyscallDesc("profil", unimplementedFunc), + /* 45 */ SyscallDesc("open", openFunc), + /* 46 */ SyscallDesc("obsolete osigaction", unimplementedFunc), + /* 47 */ SyscallDesc("getgid", unimplementedFunc), + /* 48 */ SyscallDesc("sigprocmask", ignoreFunc), + /* 49 */ SyscallDesc("getlogin", unimplementedFunc), + /* 50 */ SyscallDesc("setlogin", unimplementedFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("sigpending", unimplementedFunc), + /* 53 */ SyscallDesc("classcntl", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", ioctlFunc), + /* 55 */ SyscallDesc("reboot", unimplementedFunc), + /* 56 */ SyscallDesc("revoke", unimplementedFunc), + /* 57 */ SyscallDesc("symlink", unimplementedFunc), + /* 58 */ SyscallDesc("readlink", unimplementedFunc), + /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("old_fstat", unimplementedFunc), + /* 63 */ SyscallDesc("getpgrp", unimplementedFunc), + /* 64 */ SyscallDesc("getpagesize", getpagesizeFunc), + /* 65 */ SyscallDesc("mremap", unimplementedFunc), + /* 66 */ SyscallDesc("vfork", unimplementedFunc), + /* 67 */ SyscallDesc("pre_F64_stat", unimplementedFunc), + /* 68 */ SyscallDesc("pre_F64_lstat", unimplementedFunc), + /* 69 */ SyscallDesc("sbrk", unimplementedFunc), + /* 70 */ SyscallDesc("sstk", unimplementedFunc), + /* 71 */ SyscallDesc("mmap", mmapFunc), + /* 72 */ SyscallDesc("ovadvise", unimplementedFunc), + /* 73 */ SyscallDesc("munmap", unimplementedFunc), + /* 74 */ SyscallDesc("mprotect", ignoreFunc), + /* 75 */ SyscallDesc("madvise", unimplementedFunc), + /* 76 */ SyscallDesc("old_vhangup", unimplementedFunc), + /* 77 */ SyscallDesc("kmodcall", unimplementedFunc), + /* 78 */ SyscallDesc("mincore", unimplementedFunc), + /* 79 */ SyscallDesc("getgroups", unimplementedFunc), + /* 80 */ SyscallDesc("setgroups", unimplementedFunc), + /* 81 */ SyscallDesc("old_getpgrp", unimplementedFunc), + /* 82 */ SyscallDesc("setpgrp", unimplementedFunc), + /* 83 */ SyscallDesc("setitimer", unimplementedFunc), + /* 84 */ SyscallDesc("old_wait", unimplementedFunc), + /* 85 */ SyscallDesc("table", tableFunc), + /* 86 */ SyscallDesc("getitimer", unimplementedFunc), + /* 87 */ SyscallDesc("gethostname", gethostnameFunc), + /* 88 */ SyscallDesc("sethostname", unimplementedFunc), + /* 89 */ SyscallDesc("getdtablesize", unimplementedFunc), + /* 90 */ SyscallDesc("dup2", unimplementedFunc), + /* 91 */ SyscallDesc("pre_F64_fstat", unimplementedFunc), + /* 92 */ SyscallDesc("fcntl", unimplementedFunc), + /* 93 */ SyscallDesc("select", unimplementedFunc), + /* 94 */ SyscallDesc("poll", unimplementedFunc), + /* 95 */ SyscallDesc("fsync", unimplementedFunc), + /* 96 */ SyscallDesc("setpriority", unimplementedFunc), + /* 97 */ SyscallDesc("socket", unimplementedFunc), + /* 98 */ SyscallDesc("connect", unimplementedFunc), + /* 99 */ SyscallDesc("old_accept", unimplementedFunc), + /* 100 */ SyscallDesc("getpriority", unimplementedFunc), + /* 101 */ SyscallDesc("old_send", unimplementedFunc), + /* 102 */ SyscallDesc("old_recv", unimplementedFunc), + /* 103 */ SyscallDesc("sigreturn", sigreturnFunc), + /* 104 */ SyscallDesc("bind", unimplementedFunc), + /* 105 */ SyscallDesc("setsockopt", unimplementedFunc), + /* 106 */ SyscallDesc("listen", unimplementedFunc), + /* 107 */ SyscallDesc("plock", unimplementedFunc), + /* 108 */ SyscallDesc("old_sigvec", unimplementedFunc), + /* 109 */ SyscallDesc("old_sigblock", unimplementedFunc), + /* 110 */ SyscallDesc("old_sigsetmask", unimplementedFunc), + /* 111 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 112 */ SyscallDesc("sigstack", ignoreFunc), + /* 113 */ SyscallDesc("old_recvmsg", unimplementedFunc), + /* 114 */ SyscallDesc("old_sendmsg", unimplementedFunc), + /* 115 */ SyscallDesc("obsolete vtrace", unimplementedFunc), + /* 116 */ SyscallDesc("gettimeofday", gettimeofdayFunc), + /* 117 */ SyscallDesc("getrusage", getrusageFunc), + /* 118 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 119 */ SyscallDesc("numa_syscalls", unimplementedFunc), + /* 120 */ SyscallDesc("readv", unimplementedFunc), + /* 121 */ SyscallDesc("writev", unimplementedFunc), + /* 122 */ SyscallDesc("settimeofday", unimplementedFunc), + /* 123 */ SyscallDesc("fchown", unimplementedFunc), + /* 124 */ SyscallDesc("fchmod", unimplementedFunc), + /* 125 */ SyscallDesc("old_recvfrom", unimplementedFunc), + /* 126 */ SyscallDesc("setreuid", unimplementedFunc), + /* 127 */ SyscallDesc("setregid", unimplementedFunc), + /* 128 */ SyscallDesc("rename", unimplementedFunc), + /* 129 */ SyscallDesc("truncate", unimplementedFunc), + /* 130 */ SyscallDesc("ftruncate", unimplementedFunc), + /* 131 */ SyscallDesc("flock", unimplementedFunc), + /* 132 */ SyscallDesc("setgid", unimplementedFunc), + /* 133 */ SyscallDesc("sendto", unimplementedFunc), + /* 134 */ SyscallDesc("shutdown", unimplementedFunc), + /* 135 */ SyscallDesc("socketpair", unimplementedFunc), + /* 136 */ SyscallDesc("mkdir", unimplementedFunc), + /* 137 */ SyscallDesc("rmdir", unimplementedFunc), + /* 138 */ SyscallDesc("utimes", unimplementedFunc), + /* 139 */ SyscallDesc("obsolete 4.2 sigreturn", unimplementedFunc), + /* 140 */ SyscallDesc("adjtime", unimplementedFunc), + /* 141 */ SyscallDesc("old_getpeername", unimplementedFunc), + /* 142 */ SyscallDesc("gethostid", unimplementedFunc), + /* 143 */ SyscallDesc("sethostid", unimplementedFunc), + /* 144 */ SyscallDesc("getrlimit", getrlimitFunc), + /* 145 */ SyscallDesc("setrlimit", unimplementedFunc), + /* 146 */ SyscallDesc("old_killpg", unimplementedFunc), + /* 147 */ SyscallDesc("setsid", unimplementedFunc), + /* 148 */ SyscallDesc("quotactl", unimplementedFunc), + /* 149 */ SyscallDesc("oldquota", unimplementedFunc), + /* 150 */ SyscallDesc("old_getsockname", unimplementedFunc), + /* 151 */ SyscallDesc("pread", unimplementedFunc), + /* 152 */ SyscallDesc("pwrite", unimplementedFunc), + /* 153 */ SyscallDesc("pid_block", unimplementedFunc), + /* 154 */ SyscallDesc("pid_unblock", unimplementedFunc), + /* 155 */ SyscallDesc("signal_urti", unimplementedFunc), + /* 156 */ SyscallDesc("sigaction", ignoreFunc), + /* 157 */ SyscallDesc("sigwaitprim", unimplementedFunc), + /* 158 */ SyscallDesc("nfssvc", unimplementedFunc), + /* 159 */ SyscallDesc("getdirentries", unimplementedFunc), + /* 160 */ SyscallDesc("pre_F64_statfs", unimplementedFunc), + /* 161 */ SyscallDesc("pre_F64_fstatfs", unimplementedFunc), + /* 162 */ SyscallDesc("unknown #162", unimplementedFunc), + /* 163 */ SyscallDesc("async_daemon", unimplementedFunc), + /* 164 */ SyscallDesc("getfh", unimplementedFunc), + /* 165 */ SyscallDesc("getdomainname", unimplementedFunc), + /* 166 */ SyscallDesc("setdomainname", unimplementedFunc), + /* 167 */ SyscallDesc("unknown #167", unimplementedFunc), + /* 168 */ SyscallDesc("unknown #168", unimplementedFunc), + /* 169 */ SyscallDesc("exportfs", unimplementedFunc), + /* 170 */ SyscallDesc("unknown #170", unimplementedFunc), + /* 171 */ SyscallDesc("unknown #171", unimplementedFunc), + /* 172 */ SyscallDesc("unknown #172", unimplementedFunc), + /* 173 */ SyscallDesc("unknown #173", unimplementedFunc), + /* 174 */ SyscallDesc("unknown #174", unimplementedFunc), + /* 175 */ SyscallDesc("unknown #175", unimplementedFunc), + /* 176 */ SyscallDesc("unknown #176", unimplementedFunc), + /* 177 */ SyscallDesc("unknown #177", unimplementedFunc), + /* 178 */ SyscallDesc("unknown #178", unimplementedFunc), + /* 179 */ SyscallDesc("unknown #179", unimplementedFunc), + /* 180 */ SyscallDesc("unknown #180", unimplementedFunc), + /* 181 */ SyscallDesc("alt_plock", unimplementedFunc), + /* 182 */ SyscallDesc("unknown #182", unimplementedFunc), + /* 183 */ SyscallDesc("unknown #183", unimplementedFunc), + /* 184 */ SyscallDesc("getmnt", unimplementedFunc), + /* 185 */ SyscallDesc("unknown #185", unimplementedFunc), + /* 186 */ SyscallDesc("unknown #186", unimplementedFunc), + /* 187 */ SyscallDesc("alt_sigpending", unimplementedFunc), + /* 188 */ SyscallDesc("alt_setsid", unimplementedFunc), + /* 189 */ SyscallDesc("unknown #189", unimplementedFunc), + /* 190 */ SyscallDesc("unknown #190", unimplementedFunc), + /* 191 */ SyscallDesc("unknown #191", unimplementedFunc), + /* 192 */ SyscallDesc("unknown #192", unimplementedFunc), + /* 193 */ SyscallDesc("unknown #193", unimplementedFunc), + /* 194 */ SyscallDesc("unknown #194", unimplementedFunc), + /* 195 */ SyscallDesc("unknown #195", unimplementedFunc), + /* 196 */ SyscallDesc("unknown #196", unimplementedFunc), + /* 197 */ SyscallDesc("unknown #197", unimplementedFunc), + /* 198 */ SyscallDesc("unknown #198", unimplementedFunc), + /* 199 */ SyscallDesc("swapon", unimplementedFunc), + /* 200 */ SyscallDesc("msgctl", unimplementedFunc), + /* 201 */ SyscallDesc("msgget", unimplementedFunc), + /* 202 */ SyscallDesc("msgrcv", unimplementedFunc), + /* 203 */ SyscallDesc("msgsnd", unimplementedFunc), + /* 204 */ SyscallDesc("semctl", unimplementedFunc), + /* 205 */ SyscallDesc("semget", unimplementedFunc), + /* 206 */ SyscallDesc("semop", unimplementedFunc), + /* 207 */ SyscallDesc("uname", unameFunc), + /* 208 */ SyscallDesc("lchown", unimplementedFunc), + /* 209 */ SyscallDesc("shmat", unimplementedFunc), + /* 210 */ SyscallDesc("shmctl", unimplementedFunc), + /* 211 */ SyscallDesc("shmdt", unimplementedFunc), + /* 212 */ SyscallDesc("shmget", unimplementedFunc), + /* 213 */ SyscallDesc("mvalid", unimplementedFunc), + /* 214 */ SyscallDesc("getaddressconf", unimplementedFunc), + /* 215 */ SyscallDesc("msleep", unimplementedFunc), + /* 216 */ SyscallDesc("mwakeup", unimplementedFunc), + /* 217 */ SyscallDesc("msync", unimplementedFunc), + /* 218 */ SyscallDesc("signal", unimplementedFunc), + /* 219 */ SyscallDesc("utc_gettime", unimplementedFunc), + /* 220 */ SyscallDesc("utc_adjtime", unimplementedFunc), + /* 221 */ SyscallDesc("unknown #221", unimplementedFunc), + /* 222 */ SyscallDesc("security", unimplementedFunc), + /* 223 */ SyscallDesc("kloadcall", unimplementedFunc), + /* 224 */ SyscallDesc("stat", statFunc), + /* 225 */ SyscallDesc("lstat", lstatFunc), + /* 226 */ SyscallDesc("fstat", fstatFunc), + /* 227 */ SyscallDesc("statfs", unimplementedFunc), + /* 228 */ SyscallDesc("fstatfs", unimplementedFunc), + /* 229 */ SyscallDesc("getfsstat", unimplementedFunc), + /* 230 */ SyscallDesc("gettimeofday64", unimplementedFunc), + /* 231 */ SyscallDesc("settimeofday64", unimplementedFunc), + /* 232 */ SyscallDesc("unknown #232", unimplementedFunc), + /* 233 */ SyscallDesc("getpgid", unimplementedFunc), + /* 234 */ SyscallDesc("getsid", unimplementedFunc), + /* 235 */ SyscallDesc("sigaltstack", ignoreFunc), + /* 236 */ SyscallDesc("waitid", unimplementedFunc), + /* 237 */ SyscallDesc("priocntlset", unimplementedFunc), + /* 238 */ SyscallDesc("sigsendset", unimplementedFunc), + /* 239 */ SyscallDesc("set_speculative", unimplementedFunc), + /* 240 */ SyscallDesc("msfs_syscall", unimplementedFunc), + /* 241 */ SyscallDesc("sysinfo", unimplementedFunc), + /* 242 */ SyscallDesc("uadmin", unimplementedFunc), + /* 243 */ SyscallDesc("fuser", unimplementedFunc), + /* 244 */ SyscallDesc("proplist_syscall", unimplementedFunc), + /* 245 */ SyscallDesc("ntp_adjtime", unimplementedFunc), + /* 246 */ SyscallDesc("ntp_gettime", unimplementedFunc), + /* 247 */ SyscallDesc("pathconf", unimplementedFunc), + /* 248 */ SyscallDesc("fpathconf", unimplementedFunc), + /* 249 */ SyscallDesc("sync2", unimplementedFunc), + /* 250 */ SyscallDesc("uswitch", unimplementedFunc), + /* 251 */ SyscallDesc("usleep_thread", unimplementedFunc), + /* 252 */ SyscallDesc("audcntl", unimplementedFunc), + /* 253 */ SyscallDesc("audgen", unimplementedFunc), + /* 254 */ SyscallDesc("sysfs", unimplementedFunc), + /* 255 */ SyscallDesc("subsys_info", unimplementedFunc), + /* 256 */ SyscallDesc("getsysinfo", getsysinfoFunc), + /* 257 */ SyscallDesc("setsysinfo", unimplementedFunc), + /* 258 */ SyscallDesc("afs_syscall", unimplementedFunc), + /* 259 */ SyscallDesc("swapctl", unimplementedFunc), + /* 260 */ SyscallDesc("memcntl", unimplementedFunc), + /* 261 */ SyscallDesc("fdatasync", unimplementedFunc), + /* 262 */ SyscallDesc("oflock", unimplementedFunc), + /* 263 */ SyscallDesc("F64_readv", unimplementedFunc), + /* 264 */ SyscallDesc("F64_writev", unimplementedFunc), + /* 265 */ SyscallDesc("cdslxlate", unimplementedFunc), + /* 266 */ SyscallDesc("sendfile", unimplementedFunc), +}; + +const int Num_Syscall_Descs = sizeof(syscallDescs) / sizeof(SyscallDesc); + +const int Max_Syscall_Desc = Num_Syscall_Descs - 1; + +// +// Mach syscalls -- identified by negated syscall numbers +// + +// Create a stack region for a thread. +int +stack_createFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + TypedBufferArg<OSF::vm_stack> argp(getArg(xc, 0)); + + argp.copyIn(xc->mem); + + // if the user chose an address, just let them have it. Otherwise + // pick one for them. + if (argp->address == 0) { + argp->address = process->next_thread_stack_base; + int stack_size = (argp->rsize + argp->ysize + argp->gsize); + process->next_thread_stack_base -= stack_size; + argp.copyOut(xc->mem); + } + + return 0; +} + +const int NXM_LIB_VERSION = 301003; + +// +// This call sets up the interface between the user and kernel +// schedulers by creating a shared-memory region. The shared memory +// region has several structs, some global, some per-RAD, some per-VP. +// +int +nxm_task_initFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + TypedBufferArg<OSF::nxm_task_attr> attrp(getArg(xc, 0)); + TypedBufferArg<Addr> configptr_ptr(getArg(xc, 1)); + + attrp.copyIn(xc->mem); + + if (attrp->nxm_version != NXM_LIB_VERSION) { + cerr << "nxm_task_init: thread library version mismatch! " + << "got " << attrp->nxm_version + << ", expected " << NXM_LIB_VERSION << endl; + abort(); + } + + if (attrp->flags != OSF::NXM_TASK_INIT_VP) { + cerr << "nxm_task_init: bad flag value " << attrp->flags + << " (expected " << OSF::NXM_TASK_INIT_VP << ")" << endl; + abort(); + } + + const Addr base_addr = 0x12000; // was 0x3f0000000LL; + Addr cur_addr = base_addr; // next addresses to use + // first comes the config_info struct + Addr config_addr = cur_addr; + cur_addr += sizeof(OSF::nxm_config_info); + // next comes the per-cpu state vector + Addr slot_state_addr = cur_addr; + int slot_state_size = process->numCpus * sizeof(OSF::nxm_slot_state_t); + cur_addr += slot_state_size; + // now the per-RAD state struct (we only support one RAD) + cur_addr = 0x14000; // bump up addr for alignment + Addr rad_state_addr = cur_addr; + int rad_state_size = + (sizeof(OSF::nxm_shared) + + (process->numCpus-1) * sizeof(OSF::nxm_sched_state)); + cur_addr += rad_state_size; + + // now initialize a config_info struct and copy it out to user space + TypedBufferArg<OSF::nxm_config_info> config(config_addr); + + config->nxm_nslots_per_rad = process->numCpus; + config->nxm_nrads = 1; // only one RAD in our system! + config->nxm_slot_state = slot_state_addr; + config->nxm_rad[0] = rad_state_addr; + + config.copyOut(xc->mem); + + // initialize the slot_state array and copy it out + TypedBufferArg<OSF::nxm_slot_state_t> slot_state(slot_state_addr, + slot_state_size); + for (int i = 0; i < process->numCpus; ++i) { + // CPU 0 is bound to the calling process; all others are available + slot_state[i] = (i == 0) ? OSF::NXM_SLOT_BOUND : OSF::NXM_SLOT_AVAIL; + } + + slot_state.copyOut(xc->mem); + + // same for the per-RAD "shared" struct. Note that we need to + // allocate extra bytes for the per-VP array which is embedded at + // the end. + TypedBufferArg<OSF::nxm_shared> rad_state(rad_state_addr, + rad_state_size); + + rad_state->nxm_callback = attrp->nxm_callback; + rad_state->nxm_version = attrp->nxm_version; + rad_state->nxm_uniq_offset = attrp->nxm_uniq_offset; + for (int i = 0; i < process->numCpus; ++i) { + OSF::nxm_sched_state *ssp = &rad_state->nxm_ss[i]; + ssp->nxm_u.sigmask = 0; + ssp->nxm_u.sig = 0; + ssp->nxm_u.flags = 0; + ssp->nxm_u.cancel_state = 0; + ssp->nxm_u.nxm_ssig = 0; + ssp->nxm_bits = 0; + ssp->nxm_quantum = attrp->nxm_quantum; + ssp->nxm_set_quantum = attrp->nxm_quantum; + ssp->nxm_sysevent = 0; + + if (i == 0) { + uint64_t uniq = xc->regs.miscRegs.uniq; + ssp->nxm_u.pth_id = uniq + attrp->nxm_uniq_offset; + ssp->nxm_u.nxm_active = uniq | 1; + } + else { + ssp->nxm_u.pth_id = 0; + ssp->nxm_u.nxm_active = 0; + } + } + + rad_state.copyOut(xc->mem); + + // + // copy pointer to shared config area out to user + // + *configptr_ptr = config_addr; + configptr_ptr.copyOut(xc->mem); + + return 0; +} + + +static void +init_exec_context(ExecContext *ec, + OSF::nxm_thread_attr *attrp, uint64_t uniq_val) +{ + memset(&ec->regs, 0, sizeof(ec->regs)); + + ec->regs.intRegFile[ArgumentReg0] = attrp->registers.a0; + ec->regs.intRegFile[27/*t12*/] = attrp->registers.pc; + ec->regs.intRegFile[StackPointerReg] = attrp->registers.sp; + ec->regs.miscRegs.uniq = uniq_val; + + ec->regs.pc = attrp->registers.pc; + ec->regs.npc = attrp->registers.pc + sizeof(MachInst); + + ec->setStatus(ExecContext::Active); +} + +int +nxm_thread_createFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + TypedBufferArg<OSF::nxm_thread_attr> attrp(getArg(xc, 0)); + TypedBufferArg<uint64_t> kidp(getArg(xc, 1)); + int thread_index = getArg(xc, 2); + + // get attribute args + attrp.copyIn(xc->mem); + + if (attrp->version != NXM_LIB_VERSION) { + cerr << "nxm_thread_create: thread library version mismatch! " + << "got " << attrp->version + << ", expected " << NXM_LIB_VERSION << endl; + abort(); + } + + if (thread_index < 0 | thread_index > process->numCpus) { + cerr << "nxm_thread_create: bad thread index " << thread_index + << endl; + abort(); + } + + // On a real machine, the per-RAD shared structure is in + // shared memory, so both the user and kernel can get at it. + // We don't have that luxury, so we just copy it in and then + // back out again. + int rad_state_size = + (sizeof(OSF::nxm_shared) + + (process->numCpus-1) * sizeof(OSF::nxm_sched_state)); + + TypedBufferArg<OSF::nxm_shared> rad_state(0x14000, + rad_state_size); + rad_state.copyIn(xc->mem); + + uint64_t uniq_val = attrp->pthid - rad_state->nxm_uniq_offset; + + if (attrp->type == OSF::NXM_TYPE_MANAGER) { + // DEC pthreads seems to always create one of these (in + // addition to N application threads), but we don't use it, + // so don't bother creating it. + + // This is supposed to be a port number. Make something up. + *kidp = 99; + kidp.copyOut(xc->mem); + + return 0; + } else if (attrp->type == OSF::NXM_TYPE_VP) { + // A real "virtual processor" kernel thread. Need to fork + // this thread on another CPU. + OSF::nxm_sched_state *ssp = &rad_state->nxm_ss[thread_index]; + + if (ssp->nxm_u.nxm_active != 0) + return OSF::KERN_NOT_RECEIVER; + + ssp->nxm_u.pth_id = attrp->pthid; + ssp->nxm_u.nxm_active = uniq_val | 1; + + rad_state.copyOut(xc->mem); + + Addr slot_state_addr = 0x12000 + sizeof(OSF::nxm_config_info); + int slot_state_size = process->numCpus * sizeof(OSF::nxm_slot_state_t); + + TypedBufferArg<OSF::nxm_slot_state_t> slot_state(slot_state_addr, + slot_state_size); + + slot_state.copyIn(xc->mem); + + if (slot_state[thread_index] != OSF::NXM_SLOT_AVAIL) { + cerr << "nxm_thread_createFunc: requested VP slot " + << thread_index << " not available!" << endl; + fatal(""); + } + + slot_state[thread_index] = OSF::NXM_SLOT_BOUND; + + slot_state.copyOut(xc->mem); + + // Find a free simulator execution context. + list<ExecContext *> &ecList = process->execContexts; + list<ExecContext *>::iterator i = ecList.begin(); + list<ExecContext *>::iterator end = ecList.end(); + for (; i != end; ++i) { + ExecContext *xc = *i; + + if (xc->status() == ExecContext::Unallocated) { + // inactive context... grab it + init_exec_context(xc, attrp, uniq_val); + + // This is supposed to be a port number, but we'll try + // and get away with just sticking the thread index + // here. + *kidp = thread_index; + kidp.copyOut(xc->mem); + + return 0; + } + } + + // fell out of loop... no available inactive context + cerr << "nxm_thread_create: no idle contexts available." << endl; + abort(); + } else { + cerr << "nxm_thread_create: can't handle thread type " + << attrp->type << endl; + abort(); + } + + return 0; +} + + +int +nxm_idleFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + return 0; +} + +int +nxm_thread_blockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + uint64_t tid = getArg(xc, 0); + uint64_t secs = getArg(xc, 1); + uint64_t flags = getArg(xc, 2); + uint64_t action = getArg(xc, 3); + uint64_t usecs = getArg(xc, 4); + + cout << xc->cpu->name() << ": nxm_thread_block " << tid << " " << secs + << " " << flags << " " << action << " " << usecs << endl; + + return 0; +} + + +int +nxm_blockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr uaddr = getArg(xc, 0); + uint64_t val = getArg(xc, 1); + uint64_t secs = getArg(xc, 2); + uint64_t usecs = getArg(xc, 3); + uint64_t flags = getArg(xc, 4); + + BaseCPU *cpu = xc->cpu; + + cout << cpu->name() << ": nxm_block " << hex << uaddr << dec << " " << val + << " " << secs << " " << usecs + << " " << flags << endl; + + return 0; +} + + +int +nxm_unblockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr uaddr = getArg(xc, 0); + + cout << xc->cpu->name() << ": nxm_unblock " + << hex << uaddr << dec << endl; + + return 0; +} + + +int +swtch_priFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + // Attempts to switch to another runnable thread (if there is + // one). Returns false if there are no other threads to run + // (i.e., the thread can reasonably spin-wait) or true if there + // are other threads. + // + // Since we assume at most one "kernel" thread per CPU, it's + // always safe to return false here. + return false; +} + + +// just activate one by default +static int +activate_waiting_context(Addr uaddr, Process *process, + bool activate_all = false) +{ + int num_activated = 0; + + list<Process::WaitRec>::iterator i = process->waitList.begin(); + list<Process::WaitRec>::iterator end = process->waitList.end(); + + while (i != end && (num_activated == 0 || activate_all)) { + if (i->waitChan == uaddr) { + // found waiting process: make it active + ExecContext *newCtx = i->waitingContext; + assert(newCtx->status() == ExecContext::Suspended); + newCtx->setStatus(ExecContext::Active); + + // get rid of this record + i = process->waitList.erase(i); + + ++num_activated; + } else { + ++i; + } + } + + return num_activated; +} + + +static void +m5_lock_mutex(Addr uaddr, Process *process, ExecContext *xc) +{ + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(xc->mem); + + if (*lockp == 0) { + // lock is free: grab it + *lockp = 1; + lockp.copyOut(xc->mem); + } else { + // lock is busy: disable until free + process->waitList.push_back(Process::WaitRec(uaddr, xc)); + xc->setStatus(ExecContext::Suspended); + } +} + +static void +m5_unlock_mutex(Addr uaddr, Process *process, ExecContext *xc) +{ + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(xc->mem); + assert(*lockp != 0); + + // Check for a process waiting on the lock. + int num_waiting = activate_waiting_context(uaddr, process); + + // clear lock field if no waiting context is taking over the lock + if (num_waiting == 0) { + *lockp = 0; + lockp.copyOut(xc->mem); + } +} + + +int +m5_mutex_lockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr uaddr = getArg(xc, 0); + + m5_lock_mutex(uaddr, process, xc); + + // Return 0 since we will always return to the user with the lock + // acquired. We will just keep the context inactive until that is + // true. + return 0; +} + + +int +m5_mutex_trylockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr uaddr = getArg(xc, 0); + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(xc->mem); + + if (*lockp == 0) { + // lock is free: grab it + *lockp = 1; + lockp.copyOut(xc->mem); + return 0; + } else { + return 1; + } +} + + +int +m5_mutex_unlockFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr uaddr = getArg(xc, 0); + + m5_unlock_mutex(uaddr, process, xc); + + return 0; +} + + +int +m5_cond_signalFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr cond_addr = getArg(xc, 0); + + // Wqake up one process waiting on the condition variable. + activate_waiting_context(cond_addr, process); + + return 0; +} + + +int +m5_cond_broadcastFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr cond_addr = getArg(xc, 0); + + // Wake up all processes waiting on the condition variable. + activate_waiting_context(cond_addr, process, true); + + return 0; +} + + +int +m5_cond_waitFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + Addr cond_addr = getArg(xc, 0); + Addr lock_addr = getArg(xc, 1); + TypedBufferArg<uint64_t> condp(cond_addr); + TypedBufferArg<uint64_t> lockp(lock_addr); + + // user is supposed to acquire lock before entering + lockp.copyIn(xc->mem); + assert(*lockp != 0); + + m5_unlock_mutex(lock_addr, process, xc); + + process->waitList.push_back(Process::WaitRec(cond_addr, xc)); + xc->setStatus(ExecContext::Suspended); + + return 0; +} + + +int +m5_thread_exitFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + assert(xc->status() == ExecContext::Active); + xc->setStatus(ExecContext::Unallocated); + + return 0; +} + + +SyscallDesc machSyscallDescs[] = { + /* 0 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 1 */ SyscallDesc("m5_mutex_lock", m5_mutex_lockFunc), + /* 2 */ SyscallDesc("m5_mutex_trylock", m5_mutex_trylockFunc), + /* 3 */ SyscallDesc("m5_mutex_unlock", m5_mutex_unlockFunc), + /* 4 */ SyscallDesc("m5_cond_signal", m5_cond_signalFunc), + /* 5 */ SyscallDesc("m5_cond_broadcast", m5_cond_broadcastFunc), + /* 6 */ SyscallDesc("m5_cond_wait", m5_cond_waitFunc), + /* 7 */ SyscallDesc("m5_thread_exit", m5_thread_exitFunc), + /* 8 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 9 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 10 */ SyscallDesc("task_self", unimplementedFunc), + /* 11 */ SyscallDesc("thread_reply", unimplementedFunc), + /* 12 */ SyscallDesc("task_notify", unimplementedFunc), + /* 13 */ SyscallDesc("thread_self", unimplementedFunc), + /* 14 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 15 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 16 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 17 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 18 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 19 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 20 */ SyscallDesc("msg_send_trap", unimplementedFunc), + /* 21 */ SyscallDesc("msg_receive_trap", unimplementedFunc), + /* 22 */ SyscallDesc("msg_rpc_trap", unimplementedFunc), + /* 23 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 24 */ SyscallDesc("nxm_block", nxm_blockFunc), + /* 25 */ SyscallDesc("nxm_unblock", nxm_unblockFunc), + /* 26 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 27 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 28 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 29 */ SyscallDesc("nxm_thread_destroy", unimplementedFunc), + /* 30 */ SyscallDesc("lw_wire", unimplementedFunc), + /* 31 */ SyscallDesc("lw_unwire", unimplementedFunc), + /* 32 */ SyscallDesc("nxm_thread_create", nxm_thread_createFunc), + /* 33 */ SyscallDesc("nxm_task_init", nxm_task_initFunc), + /* 34 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 35 */ SyscallDesc("nxm_idle", nxm_idleFunc), + /* 36 */ SyscallDesc("nxm_wakeup_idle", unimplementedFunc), + /* 37 */ SyscallDesc("nxm_set_pthid", unimplementedFunc), + /* 38 */ SyscallDesc("nxm_thread_kill", unimplementedFunc), + /* 39 */ SyscallDesc("nxm_thread_block", nxm_thread_blockFunc), + /* 40 */ SyscallDesc("nxm_thread_wakeup", unimplementedFunc), + /* 41 */ SyscallDesc("init_process", unimplementedFunc), + /* 42 */ SyscallDesc("nxm_get_binding", unimplementedFunc), + /* 43 */ SyscallDesc("map_fd", unimplementedFunc), + /* 44 */ SyscallDesc("nxm_resched", unimplementedFunc), + /* 45 */ SyscallDesc("nxm_set_cancel", unimplementedFunc), + /* 46 */ SyscallDesc("nxm_set_binding", unimplementedFunc), + /* 47 */ SyscallDesc("stack_create", stack_createFunc), + /* 48 */ SyscallDesc("nxm_get_state", unimplementedFunc), + /* 49 */ SyscallDesc("nxm_thread_suspend", unimplementedFunc), + /* 50 */ SyscallDesc("nxm_thread_resume", unimplementedFunc), + /* 51 */ SyscallDesc("nxm_signal_check", unimplementedFunc), + /* 52 */ SyscallDesc("htg_unix_syscall", unimplementedFunc), + /* 53 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 54 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 55 */ SyscallDesc("host_self", unimplementedFunc), + /* 56 */ SyscallDesc("host_priv_self", unimplementedFunc), + /* 57 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 58 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 59 */ SyscallDesc("swtch_pri", swtch_priFunc), + /* 60 */ SyscallDesc("swtch", unimplementedFunc), + /* 61 */ SyscallDesc("thread_switch", unimplementedFunc), + /* 62 */ SyscallDesc("semop_fast", unimplementedFunc), + /* 63 */ SyscallDesc("nxm_pshared_init", unimplementedFunc), + /* 64 */ SyscallDesc("nxm_pshared_block", unimplementedFunc), + /* 65 */ SyscallDesc("nxm_pshared_unblock", unimplementedFunc), + /* 66 */ SyscallDesc("nxm_pshared_destroy", unimplementedFunc), + /* 67 */ SyscallDesc("nxm_swtch_pri", swtch_priFunc), + /* 68 */ SyscallDesc("lw_syscall", unimplementedFunc), + /* 69 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 70 */ SyscallDesc("mach_sctimes_0", unimplementedFunc), + /* 71 */ SyscallDesc("mach_sctimes_1", unimplementedFunc), + /* 72 */ SyscallDesc("mach_sctimes_2", unimplementedFunc), + /* 73 */ SyscallDesc("mach_sctimes_3", unimplementedFunc), + /* 74 */ SyscallDesc("mach_sctimes_4", unimplementedFunc), + /* 75 */ SyscallDesc("mach_sctimes_5", unimplementedFunc), + /* 76 */ SyscallDesc("mach_sctimes_6", unimplementedFunc), + /* 77 */ SyscallDesc("mach_sctimes_7", unimplementedFunc), + /* 78 */ SyscallDesc("mach_sctimes_8", unimplementedFunc), + /* 79 */ SyscallDesc("mach_sctimes_9", unimplementedFunc), + /* 80 */ SyscallDesc("mach_sctimes_10", unimplementedFunc), + /* 81 */ SyscallDesc("mach_sctimes_11", unimplementedFunc), + /* 82 */ SyscallDesc("mach_sctimes_port_alloc_dealloc", unimplementedFunc) +}; + +const int Num_Mach_Syscall_Descs = + sizeof(machSyscallDescs) / sizeof(SyscallDesc); + +const int Max_Mach_Syscall_Desc = Num_Mach_Syscall_Descs - 1; + +// Since negated values are used to identify Mach syscalls, the +// minimum (signed) valid syscall number is the negated max Mach +// syscall number. +const int Min_Syscall_Desc = -Max_Mach_Syscall_Desc; + + +// +// helper function for invoking syscalls +// +static +int +doSyscall(int callnum, Process *process, + ExecContext *xc) +{ + if (callnum < Min_Syscall_Desc || callnum > Max_Syscall_Desc) { + cerr << "Syscall " << callnum << " out of range" << endl; + abort(); + } + + SyscallDesc *desc = + (callnum < 0) ? &machSyscallDescs[-callnum] : &syscallDescs[callnum]; + + DCOUT(SyscallVerbose) << xc->cpu->name() << ": syscall " << desc->name + << " called @ " << curTick << endl; + + return desc->doFunc(callnum, process, xc); +} + +// +// Indirect syscall invocation (call #0) +// +int +indirectSyscallFunc(SyscallDesc *desc, int callnum, Process *process, + ExecContext *xc) +{ + int new_callnum = getArg(xc, 0); + + for (int i = 0; i < 5; ++i) + setArg(xc, i, getArg(xc, i+1)); + + return doSyscall(new_callnum, process, xc); +} + + +void +fake_syscall(Process *process, ExecContext *xc) +{ + int64_t callnum = xc->regs.intRegFile[ReturnValueReg]; + + int retval = doSyscall(callnum, process, xc); + + set_return_value(xc, retval); +} diff --git a/arch/alpha/faults.cc b/arch/alpha/faults.cc new file mode 100644 index 000000000..c3c19eb58 --- /dev/null +++ b/arch/alpha/faults.cc @@ -0,0 +1,61 @@ +/* + * 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 "faults.hh" + +namespace { + const char * + fault_name[Num_Faults] = { + "none", + "reset", + "mchk", + "arith", + "interrupt", + "dtb_miss_single", + "dtb_miss_double", + "unalign", + "dfault", + "dfault", + "itbmiss", + "itbmiss", + "iaccvio", + "opdec", + "fen", + "pal", + }; +} + +const char * +FaultName(int index) +{ + if (index < 0 || index >= Num_Faults) + return 0; + + return fault_name[index]; +} + diff --git a/arch/alpha/faults.hh b/arch/alpha/faults.hh new file mode 100644 index 000000000..bc8a4da0e --- /dev/null +++ b/arch/alpha/faults.hh @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef __FAULTS_HH__ +#define __FAULTS_HH__ + +enum Fault { + No_Fault, + Reset_Fault, // processor reset + Machine_Check_Fault, // machine check (also internal S/W fault) + Arithmetic_Fault, // FP exception + Interrupt_Fault, // external interrupt + Ndtb_Miss_Fault, // DTB miss + Pdtb_Miss_Fault, // nested DTB miss + Alignment_Fault, // unaligned access + Dtb_Fault_Fault, // DTB page fault + Dtb_Acv_Fault, // DTB access violation + Itb_Miss_Fault, // ITB miss + Itb_Fault_Fault, // ITB page fault + Itb_Acv_Fault, // ITB access violation + Unimplemented_Opcode_Fault, // invalid/unimplemented instruction + Fen_Fault, // FP not-enabled fault + Pal_Fault, // call_pal S/W interrupt + Integer_Overflow_Fault, + Num_Faults // number of faults +}; + +const char * +FaultName(int index); + +#endif // __FAULTS_HH__ diff --git a/arch/alpha/isa_desc b/arch/alpha/isa_desc new file mode 100644 index 000000000..e6ac01f28 --- /dev/null +++ b/arch/alpha/isa_desc @@ -0,0 +1,2427 @@ +// -*- mode:c++ -*- +// +// Alpha ISA description file. +// + +let {{ + global rcs_id + rcs_id = "$Id$" +}}; + + +#include <sstream> +#include <iostream> +#include <iomanip> + +#include <math.h> +#if defined(linux) +#include <fenv.h> +#endif + +#include "static_inst.hh" +#include "cprintf.hh" +#include "misc.hh" +#include "op_class.hh" + +#include "exec_context.hh" +#include "simple_cpu.hh" +#include "spec_state.hh" +#include "cpu.hh" +#include "exetrace.hh" +#include "annotation.hh" + +#ifdef FULL_SYSTEM +#include "ev5.hh" +#endif + +namespace AlphaISA; + +// Universal (format-independent) fields +def bitfield OPCODE <31:26>; +def bitfield RA <25:21>; +def bitfield RB <20:16>; + +// Memory format +def signed bitfield MEMDISP <15: 0>; // displacement +def bitfield MEMFUNC <15: 0>; // function code (same field, unsigned) + +// Memory-format jumps +def bitfield JMPFUNC <15:14>; // function code (disp<15:14>) +def bitfield JMPHINT <13: 0>; // tgt Icache idx hint (disp<13:0>) + +// Branch format +def signed bitfield BRDISP <20: 0>; // displacement + +// Integer operate format(s>; +def bitfield INTIMM <20:13>; // integer immediate (literal) +def bitfield IMM <12:12>; // immediate flag +def bitfield INTFUNC <11: 5>; // function code +def bitfield RC < 4: 0>; // dest reg + +// Floating-point operate format +def bitfield FA <25:21>; +def bitfield FB <20:16>; +def bitfield FP_FULLFUNC <15: 5>; // complete function code + def bitfield FP_TRAPMODE <15:13>; // trapping mode + def bitfield FP_ROUNDMODE <12:11>; // rounding mode + def bitfield FP_TYPEFUNC <10: 5>; // type+func: handiest for decoding + def bitfield FP_SRCTYPE <10: 9>; // source reg type + def bitfield FP_SHORTFUNC < 8: 5>; // short function code + def bitfield FP_SHORTFUNC_TOP2 <8:7>; // top 2 bits of short func code +def bitfield FC < 4: 0>; // dest reg + +// PALcode format +def bitfield PALFUNC <25: 0>; // function code + +// EV5 PAL instructions: +// HW_LD/HW_ST +def bitfield HW_LDST_PHYS <15>; // address is physical +def bitfield HW_LDST_ALT <14>; // use ALT_MODE IPR +def bitfield HW_LDST_WRTCK <13>; // HW_LD only: fault if no write acc +def bitfield HW_LDST_QUAD <12>; // size: 0=32b, 1=64b +def bitfield HW_LDST_VPTE <11>; // HW_LD only: is PTE fetch +def bitfield HW_LDST_LOCK <10>; // HW_LD only: is load locked +def bitfield HW_LDST_COND <10>; // HW_ST only: is store conditional +def signed bitfield HW_LDST_DISP <9:0>; // signed displacement + +// HW_REI +def bitfield HW_REI_TYP <15:14>; // type: stalling vs. non-stallingk +def bitfield HW_REI_MBZ <13: 0>; // must be zero + +// HW_MTPR/MW_MFPR +def bitfield HW_IPR_IDX <15:0>; // IPR index + +// M5 instructions +def bitfield M5FUNC <7:0>; + +let {{ + global operandTypeMap + operandTypeMap = { + 'sb' : ('signed int', 8), + 'ub' : ('unsigned int', 8), + 'sw' : ('signed int', 16), + 'uw' : ('unsigned int', 16), + 'sl' : ('signed int', 32), + 'ul' : ('unsigned int', 32), + 'sq' : ('signed int', 64), + 'uq' : ('unsigned int', 64), + 'sf' : ('float', 32), + 'df' : ('float', 64) + } + + global operandTraitsMap + operandTraitsMap = { + # Int regs default to unsigned, but code should not count on this. + # For clarity, descriptions that depend on unsigned behavior should + # explicitly specify '.uq'. + 'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1), + 'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2), + 'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3), + 'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1), + 'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2), + 'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3), + 'Mem': MemOperandTraits('uq', None, + ('IsMemRef', 'IsLoad', 'IsStore'), 4), + 'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4), + 'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1), + 'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1), + # The next two are hacks for non-full-system call-pal emulation + 'R0': IntRegOperandTraits('uq', '0', None, 1), + 'R16': IntRegOperandTraits('uq', '16', None, 1), + } + + defineDerivedOperandVars() +}}; + +declare {{ +// just temporary, while comparing with old code for debugging +// #define SS_COMPATIBLE_DISASSEMBLY + + /// Check "FP enabled" machine status bit. Called when executing any FP + /// instruction in full-system mode. + /// @retval Full-system mode: No_Fault if FP is enabled, Fen_Fault + /// if not. Non-full-system mode: always returns No_Fault. +#ifdef FULL_SYSTEM + inline Fault checkFpEnableFault(ExecContext *xc) + { + Fault fault = No_Fault; // dummy... this ipr access should not fault + if (!ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) { + fault = Fen_Fault; + } + return fault; + } +#else + inline Fault checkFpEnableFault(ExecContext *xc) + { + return No_Fault; + } +#endif + + /** + * Base class for all Alpha static instructions. + */ + class AlphaStaticInst : public StaticInst<AlphaISA> + { + protected: + + /// Make AlphaISA register dependence tags directly visible in + /// this class and derived classes. Maybe these should really + /// live here and not in the AlphaISA namespace. + enum DependenceTags { + FP_Base_DepTag = AlphaISA::FP_Base_DepTag, + Fpcr_DepTag = AlphaISA::Fpcr_DepTag, + Uniq_DepTag = AlphaISA::Uniq_DepTag, + IPR_Base_DepTag = AlphaISA::IPR_Base_DepTag + }; + + /// Constructor. + AlphaStaticInst(const char *mnem, MachInst _machInst, + OpClass __opClass) + : StaticInst<AlphaISA>(mnem, _machInst, __opClass) + { + } + + /// Print a register name for disassembly given the unique + /// dependence tag number (FP or int). + void printReg(std::ostream &os, int reg) + { + if (reg < FP_Base_DepTag) { + ccprintf(os, "r%d", reg); + } + else { + ccprintf(os, "f%d", reg - FP_Base_DepTag); + } + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + }; +}}; + + +def template BasicDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + /// Constructor. + %(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + SimpleCPU *memAccessObj __attribute__((unused)) = cpu; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(simple_rd)s; + %(code)s; + + if (fault == No_Fault) { + %(simple_wb)s; + } + + return fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + DynInst *memAccessObj __attribute__((unused)) = dynInst; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(dtld_rd)s; + %(code)s; + + if (fault == No_Fault) { + %(dtld_wb)s; + } + + return fault; + } + }; +}}; + +def template BasicDecode {{ + return new %(class_name)s(machInst); +}}; + +def template BasicDecodeWithMnemonic {{ + return new %(class_name)s("%(mnemonic)s", machInst); +}}; + +// The most basic instruction format... used only for a few misc. insts +def format BasicOperate(code, *flags) {{ + iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), flags) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + + + +//////////////////////////////////////////////////////////////////// + +declare {{ + /** + * Static instruction class for no-ops. This is a leaf class. + */ + class Nop : public AlphaStaticInst + { + /// Disassembly of original instruction. + const std::string originalDisassembly; + + public: + /// Constructor + Nop(const std::string _originalDisassembly, MachInst _machInst) + : AlphaStaticInst("nop", _machInst, No_OpClass), + originalDisassembly(_originalDisassembly) + { + flags[IsNop] = true; + } + + ~Nop() { } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + return No_Fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + return No_Fault; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return originalDisassembly; +#else + return csprintf("%-10s (%s)", "nop", originalDisassembly); +#endif + } + }; + + /// Helper function for decoding nops. Substitute Nop object + /// for original inst passed in as arg (and delete latter). + inline + AlphaStaticInst * + makeNop(AlphaStaticInst *inst) + { + AlphaStaticInst *nop = new Nop(inst->disassemble(0), inst->machInst); + delete inst; + return nop; + } +}}; + +def format Nop() {{ + return ('', 'return new Nop("%s", machInst);\n' % name) +}}; + + +// integer & FP operate instructions use Rc as dest, so check for +// Rc == 31 to detect nops +def template OperateNopCheckDecode {{ + { + AlphaStaticInst *i = new %(class_name)s(machInst); + if (RC == 31) { + i = makeNop(i); + } + return i; + } +}}; + +// Like BasicOperate format, but generates NOP if RC/FC == 31 +def format BasicOperateWithNopCheck(code, *opt_args) {{ + iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), + opt_args) + return iop.subst('BasicDeclare', 'OperateNopCheckDecode') +}}; + + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +declare {{ + /** + * Base class for integer immediate instructions. + */ + class IntegerImm : public AlphaStaticInst + { + protected: + /// Immediate operand value (unsigned 8-bit int). + uint8_t imm; + + /// Constructor + IntegerImm(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), imm(INTIMM) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first source reg... if there's + // a second one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + + ss << (int)imm; + + if (_numDestRegs > 0) { + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + }; +}}; + +def template RegOrImmDecode {{ + { + AlphaStaticInst *i = + (IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst) + : (AlphaStaticInst *)new %(class_name)s(machInst); + if (RC == 31) { + i = makeNop(i); + } + return i; + } +}}; + +// Primary format for integer operate instructions: +// - Generates both reg-reg and reg-imm versions if Rb_or_imm is used. +// - Generates NOP if RC == 31. +def format IntegerOperate(code, *opt_flags) {{ + # If the code block contains 'Rb_or_imm', we define two instructions, + # one using 'Rb' and one using 'imm', and have the decoder select + # the right one. + uses_imm = (code.find('Rb_or_imm') != -1) + if uses_imm: + orig_code = code + # base code is reg version: + # rewrite by substituting 'Rb' for 'Rb_or_imm' + code = re.sub(r'Rb_or_imm', 'Rb', orig_code) + # generate immediate version by substituting 'imm' + # note that imm takes no extenstion, so we extend + # the regexp to replace any extension as well + imm_code = re.sub(r'Rb_or_imm(\.\w+)?', 'imm', orig_code) + + # generate declaration for register version + cblk = CodeBlock(code) + iop = InstObjParams(name, Name, 'AlphaStaticInst', cblk, opt_flags) + decls = iop.subst('BasicDeclare') + + if uses_imm: + # append declaration for imm version + imm_cblk = CodeBlock(imm_code) + imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk, + opt_flags) + decls += imm_iop.subst('BasicDeclare') + # decode checks IMM bit to pick correct version + decode = iop.subst('RegOrImmDecode') + else: + # no imm version: just check for nop + decode = iop.subst('OperateNopCheckDecode') + + return (decls, decode) +}}; + + +//////////////////////////////////////////////////////////////////// +// +// Floating-point instructions +// +// Note that many FP-type instructions which do not support all the +// various rounding & trapping modes use the simpler format +// BasicOperateWithNopCheck. +// + +declare {{ + /** + * Base class for general floating-point instructions. Includes + * support for various Alpha rounding and trapping modes. Only FP + * instructions that require this support are derived from this + * class; the rest derive directly from AlphaStaticInst. + */ + class AlphaFP : public AlphaStaticInst + { + public: + /// Alpha FP rounding modes. + enum RoundingMode { + Chopped = 0, ///< round toward zero + Minus_Infinity = 1, ///< round toward minus infinity + Normal = 2, ///< round to nearest (default) + Dynamic = 3, ///< use FPCR setting (in instruction) + Plus_Infinity = 3 ///< round to plus inifinity (in FPCR) + }; + + /// Alpha FP trapping modes. + /// For instructions that produce integer results, the + /// "Underflow Enable" modes really mean "Overflow Enable", and + /// the assembly modifier is V rather than U. + enum TrappingMode { + /// default: nothing enabled + Imprecise = 0, ///< no modifier + /// underflow/overflow traps enabled, inexact disabled + Underflow_Imprecise = 1, ///< /U or /V + Underflow_Precise = 5, ///< /SU or /SV + /// underflow/overflow and inexact traps enabled + Underflow_Inexact_Precise = 7 ///< /SUI or /SVI + }; + + protected: +#if defined(linux) + static const int alphaToC99RoundingMode[]; +#endif + + /// Map enum RoundingMode values to disassembly suffixes. + static const char *roundingModeSuffix[]; + /// Map enum TrappingMode values to FP disassembly suffixes. + static const char *fpTrappingModeSuffix[]; + /// Map enum TrappingMode values to integer disassembly suffixes. + static const char *intTrappingModeSuffix[]; + + /// This instruction's rounding mode. + RoundingMode roundingMode; + /// This instruction's trapping mode. + TrappingMode trappingMode; + + /// Constructor + AlphaFP(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + roundingMode((enum RoundingMode)FP_ROUNDMODE), + trappingMode((enum TrappingMode)FP_TRAPMODE) + { + if (trappingMode != Imprecise) { + warn("Warning: precise FP traps unimplemented\n"); + } + } + +#if defined(linux) + int + getC99RoundingMode(ExecContext *xc) + { + if (roundingMode == Dynamic) { + return alphaToC99RoundingMode[bits(xc->readFpcr(), 59, 58)]; + } + else { + return alphaToC99RoundingMode[roundingMode]; + } + } +#endif + + // This differs from the AlphaStaticInst version only in + // printing suffixes for non-default rounding & trapping modes. + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::string mnem_str(mnemonic); + + mnem_str += ((_destRegIdx[0] >= FP_Base_DepTag) + ? fpTrappingModeSuffix[trappingMode] + : intTrappingModeSuffix[trappingMode]); + mnem_str += roundingModeSuffix[roundingMode]; + + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnem_str.c_str()); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + }; + +#if defined(linux) + const int AlphaFP::alphaToC99RoundingMode[] = { + FE_TOWARDZERO, // Chopped + FE_DOWNWARD, // Minus_Infinity + FE_TONEAREST, // Normal + FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR + }; +#endif + + const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" }; + // mark invalid trapping modes, but don't fail on them, because + // you could decode anything on a misspeculated path + const char *AlphaFP::fpTrappingModeSuffix[] = + { "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" }; + const char *AlphaFP::intTrappingModeSuffix[] = + { "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" }; +}}; + + +def template FloatingPointDeclare {{ + /** + * "Fast" static instruction class for "%(mnemonic)s" (imprecise + * trapping mode, normal rounding mode). + */ + class %(class_name)sFast : public %(base_class)s + { + public: + /// Constructor. + %(class_name)sFast(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(simple_rd)s; + %(code)s; + + if (fault == No_Fault) { + %(simple_wb)s; + } + + return fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(dtld_rd)s; + %(code)s; + + if (fault == No_Fault) { + %(dtld_wb)s; + } + + return fault; + } + }; + + /** + * General static instruction class for "%(mnemonic)s". Supports + * all the various rounding and trapping modes. + */ + class %(class_name)sGeneral : public %(base_class)s + { + public: + /// Constructor. + %(class_name)sGeneral(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(simple_rd)s; + +#if defined(linux) + fesetround(getC99RoundingMode(xc)); +#endif + + %(code)s; + +#if defined(linux) + fesetround(FE_TONEAREST); +#endif + + if (fault == No_Fault) { + %(simple_wb)s; + } + + return fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(dtld_rd)s; + +#if defined(linux) + fesetround(getC99RoundingMode(xc)); +#endif + + %(code)s; + +#if defined(linux) + fesetround(FE_TONEAREST); +#endif + + if (fault == No_Fault) { + %(dtld_wb)s; + } + + return fault; + } + }; +}}; + +def template FloatingPointDecode {{ + { + bool fast = (FP_TRAPMODE == AlphaFP::Imprecise + && FP_ROUNDMODE == AlphaFP::Normal); + AlphaStaticInst *i = + fast ? (AlphaStaticInst *)new %(class_name)sFast(machInst) : + (AlphaStaticInst *)new %(class_name)sGeneral(machInst); + + if (FC == 31) { + i = makeNop(i); + } + + return i; + } +}}; + + +// General format for floating-point operate instructions: +// - Checks trapping and rounding mode flags. Trapping modes +// currently unimplemented (will fail). +// - Generates NOP if FC == 31. +def format FloatingPointOperate(code, *opt_args) {{ + iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), + opt_args) + return iop.subst('FloatingPointDeclare', 'FloatingPointDecode') +}}; + + +//////////////////////////////////////////////////////////////////// +// +// Memory-format instructions: LoadAddress, Load, Store +// + +declare {{ + /** + * Base class for general Alpha memory-format instructions. + */ + class Memory : public AlphaStaticInst + { + protected: + + /// Displacement for EA calculation (signed). + int32_t disp; + /// Memory request flags. See mem_req_base.hh. + unsigned memAccessFlags; + + /// Constructor + Memory(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + disp(MEMDISP), memAccessFlags(0) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s %c%d,%d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB); + } + }; + + /** + * Base class for a few miscellaneous memory-format insts + * that don't interpret the disp field: wh64, fetch, fetch_m, ecb. + * None of these instructions has a destination register either. + */ + class MemoryNoDisp : public AlphaStaticInst + { + protected: + /// Memory request flags. See mem_req_base.hh. + unsigned memAccessFlags; + + /// Constructor + MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + memAccessFlags(0) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (r%d)", mnemonic, RB); + } + }; + + /** + * Base class for "fake" effective-address computation + * instructions returnded by eaCompInst(). + */ + class EACompBase : public AlphaStaticInst + { + public: + /// Constructor + EACompBase(MachInst machInst) + : AlphaStaticInst("(eacomp)", machInst, IntALU) + { + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { panic("attempt to execute eacomp"); } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { panic("attempt to execute eacomp"); } + }; + + /** + * Base class for "fake" memory-access instructions returnded by + * memAccInst(). + */ + class MemAccBase : public AlphaStaticInst + { + public: + /// Constructor + MemAccBase(MachInst machInst, OpClass __opClass) + : AlphaStaticInst("(memacc)", machInst, __opClass) + { + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { panic("attempt to execute memacc"); } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { panic("attempt to execute memacc"); } + }; + +}}; + + +def format LoadAddress(code) {{ + iop = InstObjParams(name, Name, 'Memory', CodeBlock(code)) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + + +def template LoadStoreDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + protected: + + /** + * "Fake" effective address computation class for "%(mnemonic)s". + */ + class EAComp : public EACompBase + { + public: + /// Constructor + EAComp(MachInst machInst) + : EACompBase(machInst) + { + %(ea_constructor)s; + } + }; + + /** + * "Fake" memory access instruction class for "%(mnemonic)s". + */ + class MemAcc : public MemAccBase + { + public: + /// Constructor + MemAcc(MachInst machInst) + : MemAccBase(machInst, %(op_class)s) + { + %(memacc_constructor)s; + } + }; + + /// Pointer to EAComp object. + StaticInstPtr<AlphaISA> eaCompPtr; + /// Pointer to MemAcc object. + StaticInstPtr<AlphaISA> memAccPtr; + + public: + + StaticInstPtr<AlphaISA> eaCompInst() { return eaCompPtr; } + StaticInstPtr<AlphaISA> memAccInst() { return memAccPtr; } + + /// Constructor. + %(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s), + eaCompPtr(new EAComp(machInst)), memAccPtr(new MemAcc(machInst)) + { + %(constructor)s; + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + SimpleCPU *memAccessObj = cpu; + Addr EA; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(simple_nonmem_rd)s; + %(ea_code)s; + + if (fault == No_Fault) { + %(simple_mem_rd)s; + %(memacc_code)s; + } + + if (fault == No_Fault) { + %(simple_mem_wb)s; + } + + if (fault == No_Fault) { + %(postacc_code)s; + } + + if (fault == No_Fault) { + %(simple_nonmem_wb)s; + } + + return fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + DynInst *memAccessObj = dynInst; + Addr EA; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(dtld_nonmem_rd)s; + %(ea_code)s; + + if (fault == No_Fault) { + %(dtld_mem_rd)s; + %(memacc_code)s; + } + + if (fault == No_Fault) { + %(dtld_mem_wb)s; + } + + if (fault == No_Fault) { + %(postacc_code)s; + } + + if (fault == No_Fault) { + %(dtld_nonmem_wb)s; + } + + return fault; + } + }; +}}; + + +def template PrefetchDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + /// Constructor + %(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + Addr EA; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(simple_nonmem_rd)s; + %(ea_code)s; + + if (fault == No_Fault) { + cpu->prefetch(EA, memAccessFlags); + } + + return No_Fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + Addr EA; + Fault fault = No_Fault; + + %(fp_enable_check)s; + %(exec_decl)s; + %(dtld_nonmem_rd)s; + %(ea_code)s; + + if (fault == No_Fault) { + dynInst->prefetch(EA, memAccessFlags); + } + + return No_Fault; + } + }; +}}; + + +// load instructions use Ra as dest, so check for +// Ra == 31 to detect nops +def template LoadNopCheckDecode {{ + { + AlphaStaticInst *i = new %(class_name)s(machInst); + if (RA == 31) { + i = makeNop(i); + } + return i; + } +}}; + + +// for some load instructions, Ra == 31 indicates a prefetch (not a nop) +def template LoadPrefetchCheckDecode {{ + { + if (RA != 31) { + return new %(class_name)s(machInst); + } + else { + return new %(class_name)sPrefetch(machInst); + } + } +}}; + + +let {{ +global LoadStoreBase +def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '', + base_class = 'Memory', flags = [], + declare_template = 'LoadStoreDeclare', + decode_template = 'BasicDecode'): + # Segregate flags into instruction flags (handled by InstObjParams) + # and memory access flags (handled here). + + # Would be nice to autogenerate this list, but oh well. + valid_mem_flags = ['LOCKED', 'EVICT_NEXT', 'PF_EXCLUSIVE'] + inst_flags = [] + mem_flags = [] + for f in flags: + if f in valid_mem_flags: + mem_flags.append(f) + else: + inst_flags.append(f) + + ea_cblk = CodeBlock(ea_code) + memacc_cblk = CodeBlock(memacc_code) + postacc_cblk = CodeBlock(postacc_code) + + cblk = CodeBlock(ea_code + memacc_code + postacc_code) + iop = InstObjParams(name, Name, base_class, cblk, inst_flags) + + iop.ea_constructor = ea_cblk.constructor + iop.ea_code = ea_cblk.code + iop.memacc_constructor = memacc_cblk.constructor + iop.memacc_code = memacc_cblk.code + iop.postacc_code = postacc_cblk.code + + mem_flags = string.join(mem_flags, '|') + if mem_flags != '': + iop.constructor += '\n\tmemAccessFlags = ' + mem_flags + ';' + + return iop.subst(declare_template, decode_template) +}}; + + +def format LoadOrNop(ea_code, memacc_code, *flags) {{ + return LoadStoreBase(name, Name, ea_code, memacc_code, + flags = flags, + decode_template = 'LoadNopCheckDecode') +}}; + + +// Note that the flags passed in apply only to the prefetch version +def format LoadOrPrefetch(ea_code, memacc_code, *pf_flags) {{ + # declare the load instruction object and generate the decode block + (decls, decode) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, + decode_template = 'LoadPrefetchCheckDecode') + + # Declare the prefetch instruction object. + + # convert flags from tuple to list to make them mutable + pf_flags = list(pf_flags) + ['IsMemRef', 'IsLoad', 'IsDataPrefetch', 'RdPort'] + + (pfdecls, pfdecode) = \ + LoadStoreBase(name, Name + 'Prefetch', ea_code, '', + flags = pf_flags, + declare_template = 'PrefetchDeclare') + + return (decls + pfdecls, decode) +}}; + + +def format Store(ea_code, memacc_code, *flags) {{ + return LoadStoreBase(name, Name, ea_code, memacc_code, + flags = flags) +}}; + + +def format StoreCond(ea_code, memacc_code, postacc_code, *flags) {{ + return LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code, + flags = flags) +}}; + + +// Use 'MemoryNoDisp' as base: for wh64, fetch, ecb +def format MiscPrefetch(ea_code, memacc_code, *flags) {{ + return LoadStoreBase(name, Name, ea_code, memacc_code, + flags = flags, base_class = 'MemoryNoDisp') +}}; + + +//////////////////////////////////////////////////////////////////// + + +declare {{ + + /** + * Base class for instructions whose disassembly is not purely a + * function of the machine instruction (i.e., it depends on the + * PC). This class overrides the disassemble() method to check + * the PC and symbol table values before re-using a cached + * disassembly string. This is necessary for branches and jumps, + * where the disassembly string includes the target address (which + * may depend on the PC and/or symbol table). + */ + class PCDependentDisassembly : public AlphaStaticInst + { + protected: + /// Cached program counter from last disassembly + Addr cachedPC; + /// Cached symbol table pointer from last disassembly + const SymbolTable *cachedSymtab; + + /// Constructor + PCDependentDisassembly(const char *mnem, MachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + cachedPC(0), cachedSymtab(0) + { + } + + const std::string &disassemble(Addr pc, const SymbolTable *symtab) + { + if (!cachedDisassembly || + pc != cachedPC || symtab != cachedSymtab) + { + if (cachedDisassembly) + delete cachedDisassembly; + + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + cachedPC = pc; + cachedSymtab = symtab; + } + + return *cachedDisassembly; + } + }; + + /** + * Base class for branches (PC-relative control transfers), + * conditional or unconditional. + */ + class Branch : public PCDependentDisassembly + { + protected: + /// Displacement to target address (signed). + int32_t disp; + + /// Constructor. + Branch(const char *mnem, MachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(BRDISP << 2) + { + } + + Addr branchTarget(Addr branchPC) + { + return branchPC + 4 + disp; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // There's only one register arg (RA), but it could be + // either a source (the condition for conditional + // branches) or a destination (the link reg for + // unconditional branches) + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + else if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } + +#ifdef SS_COMPATIBLE_DISASSEMBLY + if (_numSrcRegs == 0 && _numDestRegs == 0) { + printReg(ss, 31); + ss << ","; + } +#endif + + Addr target = pc + 4 + disp; + + std::string str; + if (symtab && symtab->findSymbol(target, str)) + ss << str; + else + ccprintf(ss, "0x%x", target); + + return ss.str(); + } + }; + + /** + * Base class for jumps (register-indirect control transfers). In + * the Alpha ISA, these are always unconditional. + */ + class Jump : public PCDependentDisassembly + { + protected: + + /// Displacement to target address (signed). + int32_t disp; + + public: + /// Constructor + Jump(const char *mnem, MachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(BRDISP) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + +#ifdef SS_COMPATIBLE_DISASSEMBLY + if (_numDestRegs == 0) { + printReg(ss, 31); + ss << ","; + } +#endif + + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } + + ccprintf(ss, "(r%d)", RB); + + return ss.str(); + } + }; +}}; + +def template JumpOrBranchDecode {{ + return (RA == 31) + ? (StaticInst<AlphaISA> *)new %(class_name)s(machInst) + : (StaticInst<AlphaISA> *)new %(class_name)sAndLink(machInst); +}}; + +def format CondBranch(code) {{ + code = 'bool cond;\n' + code + '\nif (cond) NPC = NPC + disp;\n'; + iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), + ('IsDirectControl', 'IsCondControl')) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + +let {{ +global UncondCtrlBase +def UncondCtrlBase(name, Name, base_class, npc_expr, flags): + # Declare basic control transfer w/o link (i.e. link reg is R31) + nolink_code = 'NPC = %s;\n' % npc_expr + nolink_iop = InstObjParams(name, Name, base_class, + CodeBlock(nolink_code), flags) + decls = nolink_iop.subst('BasicDeclare') + + # Generate declaration of '*AndLink' version, append to decls + link_code = 'Ra = NPC & ~3;\n' + nolink_code + link_iop = InstObjParams(name, Name + 'AndLink', base_class, + CodeBlock(link_code), flags) + decls += link_iop.subst('BasicDeclare') + + # need to use link_iop for the decode template since it is expecting + # the shorter version of class_name (w/o "AndLink") + return (decls, nolink_iop.subst('JumpOrBranchDecode')) +}}; + +def format UncondBranch(*flags) {{ + flags += ('IsUncondControl', 'IsDirectControl') + return UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags) +}}; + +def format Jump(*flags) {{ + flags += ('IsUncondControl', 'IsIndirectControl') + return UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags) +}}; + + +declare {{ + /** + * Base class for emulated call_pal calls (used only in + * non-full-system mode). + */ + class EmulatedCallPal : public AlphaStaticInst + { + protected: + + /// Constructor. + EmulatedCallPal(const char *mnem, MachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%s %s", "call_pal", mnemonic); +#else + return csprintf("%-10s %s", "call_pal", mnemonic); +#endif + } + }; +}}; + +def format EmulatedCallPal(code) {{ + iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code)) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + +declare {{ + /** + * Base class for full-system-mode call_pal instructions. + * Probably could turn this into a leaf class and get rid of the + * parser template. + */ + class CallPalBase : public AlphaStaticInst + { + protected: + int palFunc; ///< Function code part of instruction + int palOffset; ///< Target PC, offset from IPR_PAL_BASE + + /// Constructor. + CallPalBase(const char *mnem, MachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + palFunc(PALFUNC) + { + int palPriv = ((machInst & 0x80) != 0); + int shortPalFunc = (machInst & 0x3f); + palOffset = 0x2001 + (palPriv << 12) + (shortPalFunc << 6); + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s %#x", "call_pal", palFunc); + } + }; +}}; + + +def format CallPal(code) {{ + iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code)) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + +// +// hw_ld, hw_st +// +declare {{ + /** + * Base class for hw_ld and hw_st. + */ + class HwLoadStore : public AlphaStaticInst + { + protected: + + /// Displacement for EA calculation (signed). + int16_t disp; + /// Memory request flags. See mem_req_base.hh. + unsigned memAccessFlags; + + /// Constructor + HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), disp(HW_LDST_DISP) + { + memAccessFlags = 0; + if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL; + if (HW_LDST_ALT) memAccessFlags |= ALTMODE; + if (HW_LDST_VPTE) memAccessFlags |= VPTE; + if (HW_LDST_LOCK) memAccessFlags |= LOCKED; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB); +#else + // HW_LDST_LOCK and HW_LDST_COND are the same bit. + const char *lock_str = + (HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : ""; + + return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s", + mnemonic, RA, disp, RB, + HW_LDST_PHYS ? ",PHYS" : "", + HW_LDST_ALT ? ",ALT" : "", + HW_LDST_QUAD ? ",QUAD" : "", + HW_LDST_VPTE ? ",VPTE" : "", + lock_str); +#endif + } + }; +}}; + + +def format HwLoadStore(ea_code, memacc_code, class_ext, *flags) {{ + return LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + flags = flags, + base_class = 'HwLoadStore') +}}; + + +def format HwStoreCond(ea_code, memacc_code, postacc_code, class_ext, *flags) {{ + return LoadStoreBase(name, Name + class_ext, + ea_code, memacc_code, postacc_code, + flags = flags, + base_class = 'HwLoadStore') +}}; + + +declare {{ + /** + * Base class for hw_mfpr and hw_mtpr. + */ + class HwMoveIPR : public AlphaStaticInst + { + protected: + /// Index of internal processor register. + int ipr_index; + + /// Constructor + HwMoveIPR(const char *mnem, MachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + ipr_index(HW_IPR_IDX) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + if (_numSrcRegs > 0) { + // must be mtpr + return csprintf("%-10s r%d,IPR(%#x)", + mnemonic, RA, ipr_index); + } + else { + // must be mfpr + return csprintf("%-10s IPR(%#x),r%d", + mnemonic, ipr_index, RA); + } + } + }; +}}; + +def format HwMoveIPR(code) {{ + iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code)) + return iop.subst('BasicDeclare', 'BasicDecode') +}}; + +declare {{ + /** + * Static instruction class for unimplemented instructions that + * cause simulator termination. Note that these are recognized + * (legal) instructions that the simulator does not support; the + * 'Unknown' class is used for unrecognized/illegal instructions. + * This is a leaf class. + */ + class FailUnimplemented : public AlphaStaticInst + { + public: + /// Constructor + FailUnimplemented(const char *_mnemonic, MachInst _machInst) + : AlphaStaticInst(_mnemonic, _machInst, No_OpClass) + { + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + panic("attempt to execute unimplemented instruction '%s' " + "(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + // don't panic if this is a misspeculated instruction + if (!xc->spec_mode) + panic("attempt to execute unimplemented instruction '%s' " + "(inst 0x%08x, opcode 0x%x)", + mnemonic, machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (unimplemented)", mnemonic); + } + }; + + /** + * Base class for unimplemented instructions that cause a warning + * to be printed (but do not terminate simulation). This + * implementation is a little screwy in that it will print a + * warning for each instance of a particular unimplemented machine + * instruction, not just for each unimplemented opcode. Should + * probably make the 'warned' flag a static member of the derived + * class. + */ + class WarnUnimplemented : public AlphaStaticInst + { + private: + /// Have we warned on this instruction yet? + bool warned; + + public: + /// Constructor + WarnUnimplemented(const char *_mnemonic, MachInst _machInst) + : AlphaStaticInst(_mnemonic, _machInst, No_OpClass), warned(false) + { + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + if (!warned) { + warn("Warning: instruction '%s' unimplemented\n", mnemonic); + warned = true; + } + + return No_Fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + if (!xc->spec_mode && !warned) { + warn("Warning: instruction '%s' unimplemented\n", mnemonic); + warned = true; + } + + return No_Fault; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s", mnemonic); +#else + return csprintf("%-10s (unimplemented)", mnemonic); +#endif + } + }; +}}; + +def template WarnUnimplDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + /// Constructor + %(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst) + { + } + }; +}}; + + +def format FailUnimpl() {{ + iop = InstObjParams(name, 'FailUnimplemented') + return ('', iop.subst('BasicDecodeWithMnemonic')) +}}; + +def format WarnUnimpl() {{ + iop = InstObjParams(name, Name, 'WarnUnimplemented') + return iop.subst('WarnUnimplDeclare', 'BasicDecode') +}}; + +declare {{ + /** + * Static instruction class for unknown (illegal) instructions. + * These cause simulator termination if they are executed in a + * non-speculative mode. This is a leaf class. + */ + class Unknown : public AlphaStaticInst + { + public: + /// Constructor + Unknown(MachInst _machInst) + : AlphaStaticInst("unknown", _machInst, No_OpClass) + { + } + + Fault execute(SimpleCPU *cpu, ExecContext *xc, + Trace::InstRecord *traceData) + { + panic("attempt to execute unknown instruction " + "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } + + Fault execute(CPU *cpu, SpecExecContext *xc, DynInst *dynInst, + Trace::InstRecord *traceData) + { + // don't panic if this is a misspeculated instruction + if (!xc->spec_mode) + panic("attempt to execute unknown instruction " + "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (inst 0x%x, opcode 0x%x)", + "unknown", machInst, OPCODE); + } + }; +}}; + +def format Unknown() {{ + return ('', 'return new Unknown(machInst);\n') +}}; + +declare {{ + + /// Return opa + opb, summing carry into third arg. + inline uint64_t + addc(uint64_t opa, uint64_t opb, int &carry) + { + uint64_t res = opa + opb; + if (res < opa || res < opb) + ++carry; + return res; + } + + /// Multiply two 64-bit values (opa * opb), returning the 128-bit + /// product in res_hi and res_lo. + void + mul128(uint64_t opa, uint64_t opb, uint64_t &res_hi, uint64_t &res_lo) + { + // do a 64x64 --> 128 multiply using four 32x32 --> 64 multiplies + uint64_t opa_hi = opa<63:32>; + uint64_t opa_lo = opa<31:0>; + uint64_t opb_hi = opb<63:32>; + uint64_t opb_lo = opb<31:0>; + + res_lo = opa_lo * opb_lo; + + // The middle partial products logically belong in bit + // positions 95 to 32. Thus the lower 32 bits of each product + // sum into the upper 32 bits of the low result, while the + // upper 32 sum into the low 32 bits of the upper result. + uint64_t partial1 = opa_hi * opb_lo; + uint64_t partial2 = opa_lo * opb_hi; + + uint64_t partial1_lo = partial1<31:0> << 32; + uint64_t partial1_hi = partial1<63:32>; + uint64_t partial2_lo = partial2<31:0> << 32; + uint64_t partial2_hi = partial2<63:32>; + + // Add partial1_lo and partial2_lo to res_lo, keeping track + // of any carries out + int carry_out = 0; + res_lo = addc(partial1_lo, res_lo, carry_out); + res_lo = addc(partial2_lo, res_lo, carry_out); + + // Now calculate the high 64 bits... + res_hi = (opa_hi * opb_hi) + partial1_hi + partial2_hi + carry_out; + } + + /// Map 8-bit S-floating exponent to 11-bit T-floating exponent. + /// See Table 2-2 of Alpha AHB. + inline int + map_s(int old_exp) + { + int hibit = old_exp<7:>; + int lobits = old_exp<6:0>; + + if (hibit == 1) { + return (lobits == 0x7f) ? 0x7ff : (0x400 | lobits); + } + else { + return (lobits == 0) ? 0 : (0x380 | lobits); + } + } + + /// Convert a 32-bit S-floating value to the equivalent 64-bit + /// representation to be stored in an FP reg. + inline uint64_t + s_to_t(uint32_t s_val) + { + uint64_t tmp = s_val; + return (tmp<31:> << 63 // sign bit + | (uint64_t)map_s(tmp<30:23>) << 52 // exponent + | tmp<22:0> << 29); // fraction + } + + /// Convert a 64-bit T-floating value to the equivalent 32-bit + /// S-floating representation to be stored in memory. + inline int32_t + t_to_s(uint64_t t_val) + { + return (t_val<63:62> << 30 // sign bit & hi exp bit + | t_val<58:29>); // rest of exp & fraction + } +}}; + +decode OPCODE default Unknown::unknown() { + + format LoadAddress { + 0x08: lda({{ Ra = Rb + disp; }}); + 0x09: ldah({{ Ra = Rb + (disp << 16); }}); + } + + format LoadOrNop { + 0x0a: ldbu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.ub; }}); + 0x0c: ldwu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uw; }}); + 0x0b: ldq_u({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }}); + 0x23: ldt({{ EA = Rb + disp; }}, {{ Fa = Mem.df; }}); + 0x2a: ldl_l({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }}, LOCKED); + 0x2b: ldq_l({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, LOCKED); + } + + format LoadOrPrefetch { + 0x28: ldl({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }}); + 0x29: ldq({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, EVICT_NEXT); + 0x22: lds({{ EA = Rb + disp; }}, {{ Fa.uq = s_to_t(Mem.ul); }}, + PF_EXCLUSIVE); + } + + format Store { + 0x0e: stb({{ EA = Rb + disp; }}, {{ Mem.ub = Ra<7:0>; }}); + 0x0d: stw({{ EA = Rb + disp; }}, {{ Mem.uw = Ra<15:0>; }}); + 0x2c: stl({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }}); + 0x2d: stq({{ EA = Rb + disp; }}, {{ Mem.uq = Ra.uq; }}); + 0x0f: stq_u({{ EA = (Rb + disp) & ~7; }}, {{ Mem.uq = Ra.uq; }}); + 0x26: sts({{ EA = Rb + disp; }}, {{ Mem.ul = t_to_s(Fa.uq); }}); + 0x27: stt({{ EA = Rb + disp; }}, {{ Mem.df = Fa; }}); + } + + format StoreCond { + 0x2e: stl_c({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }}, + {{ + uint64_t tmp = Mem_write_result; + Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; + }}, LOCKED); + 0x2f: stq_c({{ EA = Rb + disp; }}, {{ Mem.uq = Ra; }}, + {{ + uint64_t tmp = Mem_write_result; + Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; + }}, LOCKED); + } + + format IntegerOperate { + + 0x10: decode INTFUNC { // integer arithmetic operations + + 0x00: addl({{ Rc.sl = Ra.sl + Rb_or_imm.sl; }}); + 0x40: addlv({{ + uint32_t tmp = Ra.sl + Rb_or_imm.sl; + // signed overflow occurs when operands have same sign + // and sign of result does not match. + if (Ra.sl<31:> == Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>) + fault = Integer_Overflow_Fault; + Rc.sl = tmp; + }}); + 0x02: s4addl({{ Rc.sl = (Ra.sl << 2) + Rb_or_imm.sl; }}); + 0x12: s8addl({{ Rc.sl = (Ra.sl << 3) + Rb_or_imm.sl; }}); + + 0x20: addq({{ Rc = Ra + Rb_or_imm; }}); + 0x60: addqv({{ + uint64_t tmp = Ra + Rb_or_imm; + // signed overflow occurs when operands have same sign + // and sign of result does not match. + if (Ra<63:> == Rb_or_imm<63:> && tmp<63:> != Ra<63:>) + fault = Integer_Overflow_Fault; + Rc = tmp; + }}); + 0x22: s4addq({{ Rc = (Ra << 2) + Rb_or_imm; }}); + 0x32: s8addq({{ Rc = (Ra << 3) + Rb_or_imm; }}); + + 0x09: subl({{ Rc.sl = Ra.sl - Rb_or_imm.sl; }}); + 0x49: sublv({{ + uint32_t tmp = Ra.sl - Rb_or_imm.sl; + // signed overflow detection is same as for add, + // except we need to look at the *complemented* + // sign bit of the subtrahend (Rb), i.e., if the initial + // signs are the *same* then no overflow can occur + if (Ra.sl<31:> != Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>) + fault = Integer_Overflow_Fault; + Rc.sl = tmp; + }}); + 0x0b: s4subl({{ Rc.sl = (Ra.sl << 2) - Rb_or_imm.sl; }}); + 0x1b: s8subl({{ Rc.sl = (Ra.sl << 3) - Rb_or_imm.sl; }}); + + 0x29: subq({{ Rc = Ra - Rb_or_imm; }}); + 0x69: subqv({{ + uint64_t tmp = Ra - Rb_or_imm; + // signed overflow detection is same as for add, + // except we need to look at the *complemented* + // sign bit of the subtrahend (Rb), i.e., if the initial + // signs are the *same* then no overflow can occur + if (Ra<63:> != Rb_or_imm<63:> && tmp<63:> != Ra<63:>) + fault = Integer_Overflow_Fault; + Rc = tmp; + }}); + 0x2b: s4subq({{ Rc = (Ra << 2) - Rb_or_imm; }}); + 0x3b: s8subq({{ Rc = (Ra << 3) - Rb_or_imm; }}); + + 0x2d: cmpeq({{ Rc = (Ra == Rb_or_imm); }}); + 0x6d: cmple({{ Rc = (Ra.sq <= Rb_or_imm.sq); }}); + 0x4d: cmplt({{ Rc = (Ra.sq < Rb_or_imm.sq); }}); + 0x3d: cmpule({{ Rc = (Ra.uq <= Rb_or_imm.uq); }}); + 0x1d: cmpult({{ Rc = (Ra.uq < Rb_or_imm.uq); }}); + + 0x0f: cmpbge({{ + int hi = 7; + int lo = 0; + uint64_t tmp = 0; + for (int i = 0; i < 8; ++i) { + tmp |= (Ra.uq<hi:lo> >= Rb_or_imm.uq<hi:lo>) << i; + hi += 8; + lo += 8; + } + Rc = tmp; + }}); + } + + 0x11: decode INTFUNC { // integer logical operations + + 0x00: and({{ Rc = Ra & Rb_or_imm; }}); + 0x08: bic({{ Rc = Ra & ~Rb_or_imm; }}); + 0x20: bis({{ Rc = Ra | Rb_or_imm; }}); + 0x28: ornot({{ Rc = Ra | ~Rb_or_imm; }}); + 0x40: xor({{ Rc = Ra ^ Rb_or_imm; }}); + 0x48: eqv({{ Rc = Ra ^ ~Rb_or_imm; }}); + + // conditional moves + 0x14: cmovlbs({{ Rc = ((Ra & 1) == 1) ? Rb_or_imm : Rc; }}); + 0x16: cmovlbc({{ Rc = ((Ra & 1) == 0) ? Rb_or_imm : Rc; }}); + 0x24: cmoveq({{ Rc = (Ra == 0) ? Rb_or_imm : Rc; }}); + 0x26: cmovne({{ Rc = (Ra != 0) ? Rb_or_imm : Rc; }}); + 0x44: cmovlt({{ Rc = (Ra.sq < 0) ? Rb_or_imm : Rc; }}); + 0x46: cmovge({{ Rc = (Ra.sq >= 0) ? Rb_or_imm : Rc; }}); + 0x64: cmovle({{ Rc = (Ra.sq <= 0) ? Rb_or_imm : Rc; }}); + 0x66: cmovgt({{ Rc = (Ra.sq > 0) ? Rb_or_imm : Rc; }}); + + // For AMASK, RA must be R31. + 0x61: decode RA { + 31: amask({{ Rc = Rb_or_imm & ~ULL(0x17); }}); + } + + // For IMPLVER, RA must be R31 and the B operand + // must be the immediate value 1. + 0x6c: decode RA { + 31: decode IMM { + 1: decode INTIMM { + // return EV5 for FULL_SYSTEM and EV6 otherwise + 1: implver({{ +#ifdef FULL_SYSTEM + Rc = 1; +#else + Rc = 2; +#endif + }}); + } + } + } + +#ifdef FULL_SYSTEM + // The mysterious 11.25... + 0x25: WarnUnimpl::eleven25(); +#endif + } + + 0x12: decode INTFUNC { + 0x39: sll({{ Rc = Ra << Rb_or_imm<5:0>; }}); + 0x34: srl({{ Rc = Ra.uq >> Rb_or_imm<5:0>; }}); + 0x3c: sra({{ Rc = Ra.sq >> Rb_or_imm<5:0>; }}); + + 0x02: mskbl({{ Rc = Ra & ~(mask( 8) << (Rb_or_imm<2:0> * 8)); }}); + 0x12: mskwl({{ Rc = Ra & ~(mask(16) << (Rb_or_imm<2:0> * 8)); }}); + 0x22: mskll({{ Rc = Ra & ~(mask(32) << (Rb_or_imm<2:0> * 8)); }}); + 0x32: mskql({{ Rc = Ra & ~(mask(64) << (Rb_or_imm<2:0> * 8)); }}); + + 0x52: mskwh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(16) >> (64 - 8 * bv))) : Ra; + }}); + 0x62: msklh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(32) >> (64 - 8 * bv))) : Ra; + }}); + 0x72: mskqh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(64) >> (64 - 8 * bv))) : Ra; + }}); + + 0x06: extbl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))< 7:0>; }}); + 0x16: extwl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<15:0>; }}); + 0x26: extll({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<31:0>; }}); + 0x36: extql({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8)); }}); + + 0x5a: extwh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<15:0>; }}); + 0x6a: extlh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<31:0>; }}); + 0x7a: extqh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>); }}); + + 0x0b: insbl({{ Rc = Ra< 7:0> << (Rb_or_imm<2:0> * 8); }}); + 0x1b: inswl({{ Rc = Ra<15:0> << (Rb_or_imm<2:0> * 8); }}); + 0x2b: insll({{ Rc = Ra<31:0> << (Rb_or_imm<2:0> * 8); }}); + 0x3b: insql({{ Rc = Ra << (Rb_or_imm<2:0> * 8); }}); + + 0x57: inswh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq<15:0> >> (64 - 8 * bv)) : 0; + }}); + 0x67: inslh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq<31:0> >> (64 - 8 * bv)) : 0; + }}); + 0x77: insqh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq >> (64 - 8 * bv)) : 0; + }}); + + 0x30: zap({{ + uint64_t zapmask = 0; + for (int i = 0; i < 8; ++i) { + if (Rb_or_imm<i:>) + zapmask |= (mask(8) << (i * 8)); + } + Rc = Ra & ~zapmask; + }}); + 0x31: zapnot({{ + uint64_t zapmask = 0; + for (int i = 0; i < 8; ++i) { + if (!Rb_or_imm<i:>) + zapmask |= (mask(8) << (i * 8)); + } + Rc = Ra & ~zapmask; + }}); + } + + 0x13: decode INTFUNC { // integer multiplies + 0x00: mull({{ Rc.sl = Ra.sl * Rb_or_imm.sl; }}, IntMULT); + 0x20: mulq({{ Rc = Ra * Rb_or_imm; }}, IntMULT); + 0x30: umulh({{ + uint64_t hi, lo; + mul128(Ra, Rb_or_imm, hi, lo); + Rc = hi; + }}, IntMULT); + 0x40: mullv({{ + // 32-bit multiply with trap on overflow + int64_t Rax = Ra.sl; // sign extended version of Ra.sl + int64_t Rbx = Rb_or_imm.sl; + int64_t tmp = Rax * Rbx; + // To avoid overflow, all the upper 32 bits must match + // the sign bit of the lower 32. We code this as + // checking the upper 33 bits for all 0s or all 1s. + uint64_t sign_bits = tmp<63:31>; + if (sign_bits != 0 && sign_bits != mask(33)) + fault = Integer_Overflow_Fault; + Rc.sl = tmp<31:0>; + }}, IntMULT); + 0x60: mulqv({{ + // 64-bit multiply with trap on overflow + uint64_t hi, lo; + mul128(Ra, Rb_or_imm, hi, lo); + // all the upper 64 bits must match the sign bit of + // the lower 64 + if (!((hi == 0 && lo<63:> == 0) || + (hi == mask(64) && lo<63:> == 1))) + fault = Integer_Overflow_Fault; + Rc = lo; + }}, IntMULT); + } + + 0x1c: decode INTFUNC { + 0x00: decode RA { 31: sextb({{ Rc.sb = Rb_or_imm< 7:0>; }}); } + 0x01: decode RA { 31: sextw({{ Rc.sw = Rb_or_imm<15:0>; }}); } + + format FailUnimpl { + 0x30: ctpop(); + 0x31: perr(); + 0x32: ctlz(); + 0x33: cttz(); + 0x34: unpkbw(); + 0x35: unpkbl(); + 0x36: pkwb(); + 0x37: pklb(); + 0x38: minsb8(); + 0x39: minsw4(); + 0x3a: minub8(); + 0x3b: minuw4(); + 0x3c: maxub8(); + 0x3d: maxuw4(); + 0x3e: maxsb8(); + 0x3f: maxsw4(); + } + + format BasicOperateWithNopCheck { + 0x70: decode RB { + 31: ftoit({{ Rc = Fa.uq; }}, FloatCVT); + } + 0x78: decode RB { + 31: ftois({{ Rc.sl = t_to_s(Fa.uq); }}, + FloatCVT); + } + } + } + } + + // Conditional branches. + format CondBranch { + 0x39: beq({{ cond = (Ra == 0); }}); + 0x3d: bne({{ cond = (Ra != 0); }}); + 0x3e: bge({{ cond = (Ra.sq >= 0); }}); + 0x3f: bgt({{ cond = (Ra.sq > 0); }}); + 0x3b: ble({{ cond = (Ra.sq <= 0); }}); + 0x3a: blt({{ cond = (Ra.sq < 0); }}); + 0x38: blbc({{ cond = ((Ra & 1) == 0); }}); + 0x3c: blbs({{ cond = ((Ra & 1) == 1); }}); + + 0x31: fbeq({{ cond = (Fa == 0); }}); + 0x35: fbne({{ cond = (Fa != 0); }}); + 0x36: fbge({{ cond = (Fa >= 0); }}); + 0x37: fbgt({{ cond = (Fa > 0); }}); + 0x33: fble({{ cond = (Fa <= 0); }}); + 0x32: fblt({{ cond = (Fa < 0); }}); + } + + // unconditional branches + format UncondBranch { + 0x30: br(); + 0x34: bsr(IsCall); + } + + // indirect branches + 0x1a: decode JMPFUNC { + format Jump { + 0: jmp(); + 1: jsr(IsCall); + 2: ret(IsReturn); + 3: jsr_coroutine(IsCall, IsReturn); + } + } + + // IEEE floating point + 0x14: decode FP_SHORTFUNC { + // Integer to FP register moves must have RB == 31 + 0x4: decode RB { + 31: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x004: itofs({{ Fc.uq = s_to_t(Ra.ul); }}, FloatCVT); + 0x024: itoft({{ Fc.uq = Ra.uq; }}, FloatCVT); + 0x014: FailUnimpl::itoff(); // VAX-format conversion + } + } + } + + // Square root instructions must have FA == 31 + 0xb: decode FA { + 31: decode FP_TYPEFUNC { + format FloatingPointOperate { +#ifdef SS_COMPATIBLE_FP + 0x0b: sqrts({{ + if (Fb < 0.0) + fault = Arithmetic_Fault; + Fc = sqrt(Fb); + }}, FloatSQRT); +#else + 0x0b: sqrts({{ + if (Fb.sf < 0.0) + fault = Arithmetic_Fault; + Fc.sf = sqrt(Fb.sf); + }}, FloatSQRT); +#endif + 0x2b: sqrtt({{ + if (Fb < 0.0) + fault = Arithmetic_Fault; + Fc = sqrt(Fb); + }}, FloatSQRT); + } + } + } + + // VAX-format sqrtf and sqrtg are not implemented + 0xa: FailUnimpl::sqrtfg(); + } + + // IEEE floating point + 0x16: decode FP_SHORTFUNC_TOP2 { + // The top two bits of the short function code break this space + // into four groups: binary ops, compares, reserved, and conversions. + // See Table 4-12 of AHB. + // Most of these instructions may have various trapping and + // rounding mode flags set; these are decoded in the + // FloatingPointDecode template used by the + // FloatingPointOperate format. + + // add/sub/mul/div: just decode on the short function code + // and source type. + 0: decode FP_TYPEFUNC { + format FloatingPointOperate { +#ifdef SS_COMPATIBLE_FP + 0x00: adds({{ Fc = Fa + Fb; }}); + 0x01: subs({{ Fc = Fa - Fb; }}); + 0x02: muls({{ Fc = Fa * Fb; }}, FloatMULT); + 0x03: divs({{ Fc = Fa / Fb; }}, FloatDIV); +#else + 0x00: adds({{ Fc.sf = Fa.sf + Fb.sf; }}); + 0x01: subs({{ Fc.sf = Fa.sf - Fb.sf; }}); + 0x02: muls({{ Fc.sf = Fa.sf * Fb.sf; }}, FloatMULT); + 0x03: divs({{ Fc.sf = Fa.sf / Fb.sf; }}, FloatDIV); +#endif + + 0x20: addt({{ Fc = Fa + Fb; }}); + 0x21: subt({{ Fc = Fa - Fb; }}); + 0x22: mult({{ Fc = Fa * Fb; }}, FloatMULT); + 0x23: divt({{ Fc = Fa / Fb; }}, FloatDIV); + } + } + + // Floating-point compare instructions must have the default + // rounding mode, and may use the default trapping mode or + // /SU. Both trapping modes are treated the same by M5; the + // only difference on the real hardware (as far a I can tell) + // is that without /SU you'd get an imprecise trap if you + // tried to compare a NaN with something else (instead of an + // "unordered" result). + 1: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x0a5, 0x5a5: cmpteq({{ Fc = (Fa == Fb) ? 2.0 : 0.0; }}, + FloatCMP); + 0x0a7, 0x5a7: cmptle({{ Fc = (Fa <= Fb) ? 2.0 : 0.0; }}, + FloatCMP); + 0x0a6, 0x5a6: cmptlt({{ Fc = (Fa < Fb) ? 2.0 : 0.0; }}, + FloatCMP); + 0x0a4, 0x5a4: cmptun({{ // unordered + Fc = (!(Fa < Fb) && !(Fa == Fb) && !(Fa > Fb)) ? 2.0 : 0.0; + }}, FloatCMP); + } + } + + // The FP-to-integer and integer-to-FP conversion insts + // require that FA be 31. + 3: decode FA { + 31: decode FP_TYPEFUNC { + format FloatingPointOperate { + 0x2f: cvttq({{ Fc.sq = (int64_t)rint(Fb); }}); + + // The cvtts opcode is overloaded to be cvtst if the trap + // mode is 2 or 6 (which are not valid otherwise) + 0x2c: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + // trap on denorm version "cvtst/s" is + // simulated same as cvtst + 0x2ac, 0x6ac: cvtst({{ Fc = Fb.sf; }}); + } + default: cvtts({{ Fc.sf = Fb; }}); + } + + // The trapping mode for integer-to-FP conversions + // must be /SUI or nothing; /U and /SU are not + // allowed. The full set of rounding modes are + // supported though. + 0x3c: decode FP_TRAPMODE { + 0,7: cvtqs({{ Fc.sf = Fb.sq; }}); + } + 0x3e: decode FP_TRAPMODE { + 0,7: cvtqt({{ Fc = Fb.sq; }}); + } + } + } + } + } + + // misc FP operate + 0x17: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x010: cvtlq({{ + Fc.sl = (Fb.uq<63:62> << 30) | Fb.uq<58:29>; + }}); + 0x030: cvtql({{ + Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29); + }}); + + // We treat the precise & imprecise trapping versions of + // cvtql identically. + 0x130, 0x530: cvtqlv({{ + // To avoid overflow, all the upper 32 bits must match + // the sign bit of the lower 32. We code this as + // checking the upper 33 bits for all 0s or all 1s. + uint64_t sign_bits = Fb.uq<63:31>; + if (sign_bits != 0 && sign_bits != mask(33)) + fault = Integer_Overflow_Fault; + Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29); + }}); + + 0x020: cpys({{ // copy sign + Fc.uq = (Fa.uq<63:> << 63) | Fb.uq<62:0>; + }}); + 0x021: cpysn({{ // copy sign negated + Fc.uq = (~Fa.uq<63:> << 63) | Fb.uq<62:0>; + }}); + 0x022: cpyse({{ // copy sign and exponent + Fc.uq = (Fa.uq<63:52> << 52) | Fb.uq<51:0>; + }}); + + 0x02a: fcmoveq({{ Fc = (Fa == 0) ? Fb : Fc; }}); + 0x02b: fcmovne({{ Fc = (Fa != 0) ? Fb : Fc; }}); + 0x02c: fcmovlt({{ Fc = (Fa < 0) ? Fb : Fc; }}); + 0x02d: fcmovge({{ Fc = (Fa >= 0) ? Fb : Fc; }}); + 0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }}); + 0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }}); + + 0x024: mt_fpcr({{ FPCR = Fa.uq; }}); + 0x025: mf_fpcr({{ Fa.uq = FPCR; }}); + } + } + + // miscellaneous mem-format ops + 0x18: decode MEMFUNC { + format WarnUnimpl { + 0x0000: trapb(); + 0x0400: excb(); + 0x4000: mb(); + 0x4400: wmb(); + 0x8000: fetch(); + 0xa000: fetch_m(); + 0xe800: ecb(); + } + + format MiscPrefetch { + 0xf800: wh64({{ EA = Rb; }}, + {{ memAccessObj->writeHint(EA, 64); }}, + IsMemRef, IsStore, WrPort); + } + + format BasicOperate { + 0xc000: rpcc({{ Ra = curTick; }}); + } + +#ifdef FULL_SYSTEM + format BasicOperate { + 0xe000: rc({{ + Ra = xc->regs.intrflag; + xc->regs.intrflag = 0; + }}, No_OpClass); + 0xf000: rs({{ + Ra = xc->regs.intrflag; + xc->regs.intrflag = 1; + }}, No_OpClass); + } +#else + format FailUnimpl { + 0xe000: rc(); + 0xf000: rs(); + } +#endif + } + +#ifdef FULL_SYSTEM + 0x00: CallPal::call_pal({{ + // check to see if simulator wants to do something special + // on this PAL call (including maybe suppress it) + bool dopal = xc->simPalCheck(palFunc); + + Annotate::Callpal(xc, palFunc); + + if (dopal) { + if (!xc->misspeculating()) { + AlphaISA::swap_palshadow(&xc->regs, true); + } + xc->setIpr(AlphaISA::IPR_EXC_ADDR, NPC); + NPC = xc->readIpr(AlphaISA::IPR_PAL_BASE, fault) + palOffset; + } + }}); +#else + 0x00: decode PALFUNC { + format EmulatedCallPal { + 0x83: callsys({{ xc->syscall(); }}); + // Read uniq reg into ABI return value register (r0) + 0x9e: rduniq({{ R0 = Runiq; }}); + // Write uniq reg with value from ABI arg register (r16) + 0x9f: wruniq({{ Runiq = R16; }}); + } + } +#endif + +#ifdef FULL_SYSTEM + format HwLoadStore { + 0x1b: decode HW_LDST_QUAD { + 0: hw_ld({{ EA = (Rb + disp) & ~3; }}, {{ Ra = Mem.ul; }}, L); + 1: hw_ld({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }}, Q); + } + + 0x1f: decode HW_LDST_COND { + 0: decode HW_LDST_QUAD { + 0: hw_st({{ EA = (Rb + disp) & ~3; }}, + {{ Mem.ul = Ra<31:0>; }}, L); + 1: hw_st({{ EA = (Rb + disp) & ~7; }}, + {{ Mem.uq = Ra.uq; }}, Q); + } + + 1: FailUnimpl::hw_st_cond(); + } + } + + format BasicOperate { + 0x1e: hw_rei({{ xc->hwrei(); }}); + + // M5 special opcodes use the reserved 0x01 opcode space + 0x01: decode M5FUNC { + 0x00: arm({{ + Annotate::ARM(xc); + xc->kernelStats.arm(); + }}); + 0x01: quiesce({{ + Annotate::QUIESCE(xc); + xc->setStatus(ExecContext::Suspended); + xc->kernelStats.quiesce(); + }}); + 0x10: ivlb({{ + Annotate::BeginInterval(xc); + xc->kernelStats.ivlb(); + }}, No_OpClass); + 0x11: ivle({{ Annotate::EndInterval(xc); }}, No_OpClass); + 0x20: m5exit({{ + if (!xc->misspeculating()) + m5_exit(); + }}, No_OpClass); + } + } + + format HwMoveIPR { + 0x19: hw_mfpr({{ + // this instruction is only valid in PAL mode + if (!PC_PAL(xc->regs.pc)) { + fault = Unimplemented_Opcode_Fault; + } + else { + Ra = xc->readIpr(ipr_index, fault); + } + }}); + 0x1d: hw_mtpr({{ + // this instruction is only valid in PAL mode + if (!PC_PAL(xc->regs.pc)) { + fault = Unimplemented_Opcode_Fault; + } + else { + xc->setIpr(ipr_index, Ra); + if (traceData) { traceData->setData(Ra); } + } + }}); + } +#endif +} diff --git a/arch/alpha/isa_traits.hh b/arch/alpha/isa_traits.hh new file mode 100644 index 000000000..d77505651 --- /dev/null +++ b/arch/alpha/isa_traits.hh @@ -0,0 +1,282 @@ +/* + * 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. + */ + +#ifndef __ISA_TRAITS_HH__ +#define __ISA_TRAITS_HH__ + +#include "host.hh" +#include "faults.hh" +#include "misc.hh" + +class CPU; +class IniFile; + +#define TARGET_ALPHA + +template <class ISA> class StaticInst; +template <class ISA> class StaticInstPtr; + +class AlphaISA +{ + public: + + typedef uint32_t MachInst; + typedef uint64_t Addr; + typedef uint8_t RegIndex; + + enum { + MemoryEnd = 0xffffffffffffffffULL, + + NumIntRegs = 32, + NumFloatRegs = 32, + NumMiscRegs = 32, + + MaxRegsOfAnyType = 32, + // Static instruction parameters + MaxInstSrcRegs = 3, + MaxInstDestRegs = 2, + + // semantically meaningful register indices + ZeroReg = 31, // architecturally meaningful + // the rest of these depend on the ABI + StackPointerReg = 30, + GlobalPointerReg = 29, + ReturnAddressReg = 26, + ReturnValueReg = 0, + ArgumentReg0 = 16, + ArgumentReg1 = 17, + ArgumentReg2 = 18, + ArgumentReg3 = 19, + ArgumentReg4 = 20, + ArgumentReg5 = 21, + + LogVMPageSize = 13, // 8K bytes + VMPageSize = (1 << LogVMPageSize), + + BranchPredAddrShiftAmt = 2, // instructions are 4-byte aligned + + WordBytes = 4, + HalfwordBytes = 2, + ByteBytes = 1, + DepNA = 0, + }; + + // These enumerate all the registers for dependence tracking. + enum DependenceTags { + // 0..31 are the integer regs 0..31 + // 32..63 are the FP regs 0..31, i.e. use (reg + FP_Base_DepTag) + FP_Base_DepTag = 32, + Ctrl_Base_DepTag = 64, + Fpcr_DepTag = 64, // floating point control register + Uniq_DepTag = 65, + IPR_Base_DepTag = 66 + }; + + typedef uint64_t IntReg; + typedef IntReg IntRegFile[NumIntRegs]; + + // floating point register file entry type + typedef union { + uint64_t q; + double d; + } FloatReg; + + typedef union { + uint64_t q[NumFloatRegs]; // integer qword view + double d[NumFloatRegs]; // double-precision floating point view + } FloatRegFile; + + // control register file contents + typedef uint64_t MiscReg; + typedef struct { + uint64_t fpcr; // floating point condition codes + uint64_t uniq; // process-unique register + bool lock_flag; // lock flag for LL/SC + Addr lock_addr; // lock address for LL/SC + } MiscRegFile; + +#ifdef FULL_SYSTEM + + typedef uint64_t InternalProcReg; + +#include "isa_fullsys_traits.hh" + +#else + enum { + NumInternalProcRegs = 0 + }; +#endif + + enum { + TotalNumRegs = + NumIntRegs + NumFloatRegs + NumMiscRegs + NumInternalProcRegs + }; + + typedef union { + IntReg intreg; + FloatReg fpreg; + MiscReg ctrlreg; + } AnyReg; + + struct RegFile { + IntRegFile intRegFile; // (signed) integer register file + FloatRegFile floatRegFile; // floating point register file + MiscRegFile miscRegs; // control register file + Addr pc; // program counter + Addr npc; // next-cycle program counter +#ifdef FULL_SYSTEM + IntReg palregs[NumIntRegs]; // PAL shadow registers + InternalProcReg ipr[NumInternalProcRegs]; // internal processor regs + int intrlock; // interrupt register lock flag + int intrflag; // interrupt flag + bool pal_shadow; // using pal_shadow registers +#endif // FULL_SYSTEM + // Are these architectural, or just for convenience? + uint8_t opcode, ra; // current instruction details (for intr's) + }; + + static StaticInstPtr<AlphaISA> decodeInst(MachInst); + + enum annotes { + ANNOTE_NONE = 0, + // An impossible number for instruction annotations + ITOUCH_ANNOTE = 0xffffffff, + }; + +#if 0 + static inline Addr + extractInstructionPrefetchTarget(const MachInst &IR, Addr PC) { + return(0); + } +#endif + + static inline bool isCallerSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 1 && reg <= 8 || reg >= 22 && reg <= 25 || reg == 27); + } + + static inline bool isCalleeSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 9 && reg <= 15); + } + + static inline bool isCallerSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + static inline bool isCalleeSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + static inline Addr alignAddress(const Addr &addr, + unsigned int nbytes) { + return (addr & ~(nbytes - 1)); + } + + // Instruction address compression hooks + static inline Addr realPCToFetchPC(const Addr &addr) { + return addr; + } + + static inline Addr fetchPCToRealPC(const Addr &addr) { + return addr; + } + + // the size of "fetched" instructions (not necessarily the size + // of real instructions for PISA) + static inline size_t fetchInstSize() { + return sizeof(MachInst); + } + + static inline MachInst makeRegisterCopy(int dest, int src) { + panic("makeRegisterCopy not implemented"); + return 0; + } + + // Machine operations + + static void saveMachineReg(AnyReg &savereg, const RegFile ®_file, + int regnum); + + static void restoreMachineReg(RegFile ®s, const AnyReg ®, + int regnum); + +#if 0 + static void serializeSpecialRegs(const Serializeable::Proxy &proxy, + const RegFile ®s); + + static void unserializeSpecialRegs(IniFile &db, + const std::string &category, + ConfigNode *node, + RegFile ®s); +#endif +}; + + +typedef AlphaISA TheISA; + +typedef TheISA::MachInst MachInst; +typedef TheISA::Addr Addr; +typedef TheISA::RegIndex RegIndex; +typedef TheISA::IntReg IntReg; +typedef TheISA::IntRegFile IntRegFile; +typedef TheISA::FloatReg FloatReg; +typedef TheISA::FloatRegFile FloatRegFile; +typedef TheISA::MiscReg MiscReg; +typedef TheISA::MiscRegFile MiscRegFile; +typedef TheISA::AnyReg AnyReg; +typedef TheISA::RegFile RegFile; + +const int NumIntRegs = TheISA::NumIntRegs; +const int NumFloatRegs = TheISA::NumFloatRegs; +const int NumMiscRegs = TheISA::NumMiscRegs; +const int TotalNumRegs = TheISA::TotalNumRegs; +const int VMPageSize = TheISA::VMPageSize; +const int LogVMPageSize = TheISA::LogVMPageSize; +const int ZeroReg = TheISA::ZeroReg; +const int StackPointerReg = TheISA::StackPointerReg; +const int GlobalPointerReg = TheISA::GlobalPointerReg; +const int ReturnAddressReg = TheISA::ReturnAddressReg; +const int ReturnValueReg = TheISA::ReturnValueReg; +const int ArgumentReg0 = TheISA::ArgumentReg0; +const int ArgumentReg1 = TheISA::ArgumentReg1; +const int BranchPredAddrShiftAmt = TheISA::BranchPredAddrShiftAmt; + +#ifdef FULL_SYSTEM +typedef TheISA::InternalProcReg InternalProcReg; +const int NumInternalProcRegs = TheISA::NumInternalProcRegs; +const int NumInterruptLevels = TheISA::NumInterruptLevels; + +// more stuff that should be imported here, but I'm too tired to do it +// right now... +#include "ev5.hh" +#endif + +#endif // __ALPHA_ISA_H__ diff --git a/arch/alpha/osfpal.cc b/arch/alpha/osfpal.cc new file mode 100644 index 000000000..796651666 --- /dev/null +++ b/arch/alpha/osfpal.cc @@ -0,0 +1,304 @@ +/* + * 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 "osfpal.hh" + +namespace { + const char *strings[PAL::NumCodes] = { + // Priviledged PAL instructions + "halt", // 0x00 + "cflush", // 0x01 + "draina", // 0x02 + 0, // 0x03 + 0, // 0x04 + 0, // 0x05 + 0, // 0x06 + 0, // 0x07 + 0, // 0x08 + "cserve", // 0x09 + "swppal", // 0x0a + 0, // 0x0b + 0, // 0x0c + "wripir", // 0x0d + 0, // 0x0e + 0, // 0x0f + "rdmces", // 0x10 + "wrmces", // 0x11 + 0, // 0x12 + 0, // 0x13 + 0, // 0x14 + 0, // 0x15 + 0, // 0x16 + 0, // 0x17 + 0, // 0x18 + 0, // 0x19 + 0, // 0x1a + 0, // 0x1b + 0, // 0x1c + 0, // 0x1d + 0, // 0x1e + 0, // 0x1f + 0, // 0x20 + 0, // 0x21 + 0, // 0x22 + 0, // 0x23 + 0, // 0x24 + 0, // 0x25 + 0, // 0x26 + 0, // 0x27 + 0, // 0x28 + 0, // 0x29 + 0, // 0x2a + "wrfen", // 0x2b + 0, // 0x2c + "wrvptptr", // 0x2d + 0, // 0x2e + 0, // 0x2f + "swpctx", // 0x30 + "wrval", // 0x31 + "rdval", // 0x32 + "tbi", // 0x33 + "wrent", // 0x34 + "swpipl", // 0x35 + "rdps", // 0x36 + "wrkgp", // 0x37 + "wrusp", // 0x38 + "wrperfmon", // 0x39 + "rdusp", // 0x3a + 0, // 0x3b + "whami", // 0x3c + "retsys", // 0x3d + "wtint", // 0x3e + "rti", // 0x3f + 0, // 0x40 + 0, // 0x41 + 0, // 0x42 + 0, // 0x43 + 0, // 0x44 + 0, // 0x45 + 0, // 0x46 + 0, // 0x47 + 0, // 0x48 + 0, // 0x49 + 0, // 0x4a + 0, // 0x4b + 0, // 0x4c + 0, // 0x4d + 0, // 0x4e + 0, // 0x4f + 0, // 0x50 + 0, // 0x51 + 0, // 0x52 + 0, // 0x53 + 0, // 0x54 + 0, // 0x55 + 0, // 0x56 + 0, // 0x57 + 0, // 0x58 + 0, // 0x59 + 0, // 0x5a + 0, // 0x5b + 0, // 0x5c + 0, // 0x5d + 0, // 0x5e + 0, // 0x5f + 0, // 0x60 + 0, // 0x61 + 0, // 0x62 + 0, // 0x63 + 0, // 0x64 + 0, // 0x65 + 0, // 0x66 + 0, // 0x67 + 0, // 0x68 + 0, // 0x69 + 0, // 0x6a + 0, // 0x6b + 0, // 0x6c + 0, // 0x6d + 0, // 0x6e + 0, // 0x6f + 0, // 0x70 + 0, // 0x71 + 0, // 0x72 + 0, // 0x73 + 0, // 0x74 + 0, // 0x75 + 0, // 0x76 + 0, // 0x77 + 0, // 0x78 + 0, // 0x79 + 0, // 0x7a + 0, // 0x7b + 0, // 0x7c + 0, // 0x7d + 0, // 0x7e + 0, // 0x7f + + // Unpriviledged PAL instructions + "bpt", // 0x80 + "bugchk", // 0x81 + 0, // 0x82 + "callsys", // 0x83 + 0, // 0x84 + 0, // 0x85 + "imb", // 0x86 + 0, // 0x87 + 0, // 0x88 + 0, // 0x89 + 0, // 0x8a + 0, // 0x8b + 0, // 0x8c + 0, // 0x8d + 0, // 0x8e + 0, // 0x8f + 0, // 0x90 + 0, // 0x91 + "urti", // 0x92 + 0, // 0x93 + 0, // 0x94 + 0, // 0x95 + 0, // 0x96 + 0, // 0x97 + 0, // 0x98 + 0, // 0x99 + 0, // 0x9a + 0, // 0x9b + 0, // 0x9c + 0, // 0x9d + "rdunique", // 0x9e + "wrunique", // 0x9f + 0, // 0xa0 + 0, // 0xa1 + 0, // 0xa2 + 0, // 0xa3 + 0, // 0xa4 + 0, // 0xa5 + 0, // 0xa6 + 0, // 0xa7 + 0, // 0xa8 + 0, // 0xa9 + "gentrap", // 0xaa + 0, // 0xab + 0, // 0xac + 0, // 0xad + "clrfen", // 0xae + 0, // 0xaf + 0, // 0xb0 + 0, // 0xb1 + 0, // 0xb2 + 0, // 0xb3 + 0, // 0xb4 + 0, // 0xb5 + 0, // 0xb6 + 0, // 0xb7 + 0, // 0xb8 + 0, // 0xb9 + 0, // 0xba + 0, // 0xbb + 0, // 0xbc + 0, // 0xbd + "nphalt", // 0xbe +#if 0 + 0, // 0xbf + 0, // 0xc0 + 0, // 0xc1 + 0, // 0xc2 + 0, // 0xc3 + 0, // 0xc4 + 0, // 0xc5 + 0, // 0xc6 + 0, // 0xc7 + 0, // 0xc8 + 0, // 0xc9 + 0, // 0xca + 0, // 0xcb + 0, // 0xcc + 0, // 0xcd + 0, // 0xce + 0, // 0xcf + 0, // 0xd0 + 0, // 0xd1 + 0, // 0xd2 + 0, // 0xd3 + 0, // 0xd4 + 0, // 0xd5 + 0, // 0xd6 + 0, // 0xd7 + 0, // 0xd8 + 0, // 0xd9 + 0, // 0xda + 0, // 0xdb + 0, // 0xdc + 0, // 0xdd + 0, // 0xde + 0, // 0xdf + 0, // 0xe0 + 0, // 0xe1 + 0, // 0xe2 + 0, // 0xe3 + 0, // 0xe4 + 0, // 0xe5 + 0, // 0xe6 + 0, // 0xe7 + 0, // 0xe8 + 0, // 0xe9 + 0, // 0xea + 0, // 0xeb + 0, // 0xec + 0, // 0xed + 0, // 0xee + 0, // 0xef + 0, // 0xf0 + 0, // 0xf1 + 0, // 0xf2 + 0, // 0xf3 + 0, // 0xf4 + 0, // 0xf5 + 0, // 0xf6 + 0, // 0xf7 + 0, // 0xf8 + 0, // 0xf9 + 0, // 0xfa + 0, // 0xfb + 0, // 0xfc + 0, // 0xfd + 0, // 0xfe + 0 // 0xff +#endif + }; +} + +const char * +PAL::name(int index) +{ + if (index > NumCodes || index < 0) + return 0; + + return strings[index]; +} diff --git a/arch/alpha/osfpal.hh b/arch/alpha/osfpal.hh new file mode 100644 index 000000000..61e545306 --- /dev/null +++ b/arch/alpha/osfpal.hh @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#ifndef __OSFPAL_HH__ +#define __OSFPAL_HH__ + +struct PAL +{ + enum { + // Privileged PAL functions + halt = 0x00, + cflush = 0x01, + draina = 0x02, + cserve = 0x09, + swppal = 0x0a, + wripir = 0x0d, + rdmces = 0x10, + wrmces = 0x11, + wrfen = 0x2b, + wrvptptr = 0x2d, + swpctx = 0x30, + wrval = 0x31, + rdval = 0x32, + tbi = 0x33, + wrent = 0x34, + swpipl = 0x35, + rdps = 0x36, + wrkgp = 0x37, + wrusp = 0x38, + wrperfmon = 0x39, + rdusp = 0x3a, + whami = 0x3c, + retsys = 0x3d, + wtint = 0x3e, + rti = 0x3f, + + // unprivileged pal functions + bpt = 0x80, + bugchk = 0x81, + callsys = 0x83, + imb = 0x86, + urti = 0x92, + rdunique = 0x9e, + wrunique = 0x9f, + gentrap = 0xaa, + clrfen = 0xae, + nphalt = 0xbe, + NumCodes + }; + + static const char *name(int index); +}; + +#endif // __OSFPAL_HH__ diff --git a/arch/alpha/vtophys.cc b/arch/alpha/vtophys.cc new file mode 100644 index 000000000..33f8f02ad --- /dev/null +++ b/arch/alpha/vtophys.cc @@ -0,0 +1,123 @@ +/* + * 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 <string> + +#include "pmap.h" + +#include "exec_context.hh" +#include "physical_memory.hh" +#include "trace.hh" +#include "vtophys.hh" + +using namespace std; + +inline Addr +level3_index(Addr vaddr) +{ return (vaddr >> ALPHA_PGSHIFT) & PTEMASK; } + +inline Addr +level2_index(Addr vaddr) +{ return (vaddr >> (ALPHA_PGSHIFT + NPTEPG_SHIFT)) & PTEMASK; } + +inline Addr +level1_index(Addr vaddr) +{ return (vaddr >> (ALPHA_PGSHIFT + 2 * NPTEPG_SHIFT)) & PTEMASK; } + +Addr +kernel_pte_lookup(PhysicalMemory *pmem, Addr ptbr, Addr vaddr) +{ + uint64_t level1_map = ptbr; + Addr level1_pte = level1_map + (level1_index(vaddr) << PTESHIFT); + + uint64_t level1 = pmem->phys_read_qword(level1_pte); + if (!entry_valid(level1)) { + DPRINTF(VtoPhys, "level 1 PTE not valid, va = %#\n", vaddr); + return 0; + } + + uint64_t level2_map = PMAP_PTE_PA(level1); + Addr level2_pte = level2_map + (level2_index(vaddr) << PTESHIFT); + uint64_t level2 = pmem->phys_read_qword(level2_pte); + if (!entry_valid(level2)) { + DPRINTF(VtoPhys, "level 2 PTE not valid, va = %#x\n", vaddr); + return 0; + } + + uint64_t level3_map = PMAP_PTE_PA(level2); + Addr level3_pte = level3_map + (level3_index(vaddr) << PTESHIFT); + + return level3_pte; +} + +Addr +vtophys(PhysicalMemory *xc, Addr vaddr) +{ + Addr paddr = 0; + if (vaddr < ALPHA_K0SEG_BASE) + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + else if (vaddr < ALPHA_K1SEG_BASE) + paddr = ALPHA_K0SEG_TO_PHYS(vaddr); + else + panic("vtophys: ptbr is not set on virtual lookup"); + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +Addr +vtophys(ExecContext *xc, Addr vaddr) +{ + Addr ptbr = xc->regs.ipr[AlphaISA::IPR_PALtemp20]; + Addr paddr = 0; + if (vaddr < ALPHA_K0SEG_BASE) { + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + } else if (vaddr < ALPHA_K1SEG_BASE) { + paddr = ALPHA_K0SEG_TO_PHYS(vaddr); + } else { + if (!ptbr) + panic("vtophys: ptbr is not set on virtual lookup"); + + Addr pte = kernel_pte_lookup(xc->physmem, ptbr, vaddr); + uint64_t entry = xc->physmem->phys_read_qword(pte); + if (pte && entry_valid(entry)) + paddr = PMAP_PTE_PA(entry) | (vaddr & PGOFSET); + } + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +uint8_t * +vtomem(ExecContext *xc, Addr vaddr, size_t len) +{ + Addr paddr = vtophys(xc, vaddr); + return xc->physmem->dma_addr(paddr, len); +} diff --git a/arch/alpha/vtophys.hh b/arch/alpha/vtophys.hh new file mode 100644 index 000000000..0b65a506f --- /dev/null +++ b/arch/alpha/vtophys.hh @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __VTOPHYS_H__ +#define __VTOPHYS_H__ + +#include "pmap.h" + +inline bool entry_valid(uint64_t entry) +{ return (entry & ALPHA_PTE_VALID) != 0; } + +class ExecContext; +class PhysicalMemory; + +Addr kernel_pte_lookup(PhysicalMemory *pmem, Addr ptbr, Addr vaddr); +Addr vtophys(PhysicalMemory *xc, Addr vaddr); +Addr vtophys(ExecContext *xc, Addr vaddr); +uint8_t *vtomem(ExecContext *xc, Addr vaddr, size_t len); + +#endif // __VTOPHYS_H__ + diff --git a/arch/isa_parser.py b/arch/isa_parser.py new file mode 100644 index 000000000..a4b588197 --- /dev/null +++ b/arch/isa_parser.py @@ -0,0 +1,1446 @@ +#! /usr/bin/env python + +# $Id$ + +# 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. + +import os +import sys +import re +import string +# get type names +from types import * + +# Check arguments. Right now there are only two: the name of the ISA +# description (input) file and the name of the C++ decoder (output) file. +isa_desc_filename = sys.argv[1] +decoder_filename = sys.argv[2] + +# Might as well suck the file in while we're here. This way if it's a +# bad filename we don't waste a lot of time building the parser :-). +input = open(isa_desc_filename) +isa_desc = input.read() +input.close() + +# Prepend the directory where the PLY lex & yacc modules are found +# to the search path. Assumes we're compiling in a subdirectory +# of 'build' in the current tree. +sys.path[0:0] = [os.environ['M5_EXT'] + '/ply'] + +import lex +import yacc + +##################################################################### +# +# Lexer +# +# The PLY lexer module takes two things as input: +# - A list of token names (the string list 'tokens') +# - A regular expression describing a match for each token. The +# regexp for token FOO can be provided in two ways: +# - as a string variable named t_FOO +# - as the doc string for a function named t_FOO. In this case, +# the function is also executed, allowing an action to be +# associated with each token match. +# +##################################################################### + +# Reserved words. These are listed separately as they are matched +# using the same regexp as generic IDs, but distinguished in the +# t_ID() function. The PLY documentation suggests this approach. +reserved = ( + 'BITFIELD', 'DECLARE', 'DECODE', 'DEFAULT', 'DEF', 'FORMAT', + 'LET', 'NAMESPACE', 'SIGNED', 'TEMPLATE' + ) + +# List of tokens. The lex module requires this. +tokens = reserved + ( + # identifier + 'ID', + + # integer literal + 'INTLIT', + + # string literal + 'STRLIT', + + # code literal + 'CODELIT', + + # ( ) [ ] { } < > , ; : :: * + 'LPAREN', 'RPAREN', +# not used any more... commented out to suppress PLY warning +# 'LBRACKET', 'RBRACKET', + 'LBRACE', 'RBRACE', + 'LESS', 'GREATER', + 'COMMA', 'SEMI', 'COLON', 'DBLCOLON', + 'ASTERISK', + + # C preprocessor directives + 'CPPDIRECTIVE' +) + +# Regular expressions for token matching +t_LPAREN = r'\(' +t_RPAREN = r'\)' +# not used any more... commented out to suppress PLY warning +# t_LBRACKET = r'\[' +# t_RBRACKET = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_LESS = r'\<' +t_GREATER = r'\>' +t_COMMA = r',' +t_SEMI = r';' +t_COLON = r':' +t_DBLCOLON = r'::' +t_ASTERISK = r'\*' + +# Identifiers and reserved words +reserved_map = { } +for r in reserved: + reserved_map[r.lower()] = r + +def t_ID(t): + r'[A-Za-z_]\w*' + t.type = reserved_map.get(t.value,'ID') + return t + +# Integer literal +def t_INTLIT(t): + r'(0x[\da-fA-F]+)|\d+' + try: + t.value = int(t.value,0) + except ValueError: + error(t.lineno, 'Integer value "%s" too large' % t.value) + t.value = 0 + return t + +# String literal. Note that these use only single quotes, and +# can span multiple lines. +def t_STRLIT(t): + r"(?m)'([^'])+'" + # strip off quotes + t.value = t.value[1:-1] + t.lineno += t.value.count('\n') + return t + + +# "Code literal"... like a string literal, but delimiters are +# '{{' and '}}' so they get formatted nicely under emacs c-mode +def t_CODELIT(t): + r"(?m)\{\{([^\}]|}(?!\}))+\}\}" + # strip off {{ & }} + t.value = t.value[2:-2] + t.lineno += t.value.count('\n') + return t + +def t_CPPDIRECTIVE(t): + r'^\#.*\n' + t.lineno += t.value.count('\n') + return t + +# +# The functions t_NEWLINE, t_ignore, and t_error are +# special for the lex module. +# + +# Newlines +def t_NEWLINE(t): + r'\n+' + t.lineno += t.value.count('\n') + +# Comments +def t_comment(t): + r'//.*' + +# Completely ignored characters +t_ignore = ' \t\x0c' + +# Error handler +def t_error(t): + error(t.lineno, "illegal character '%s'" % t.value[0]) + t.skip(1) + +# Build the lexer +lex.lex() + +##################################################################### +# +# Parser +# +# Every function whose name starts with 'p_' defines a grammar rule. +# The rule is encoded in the function's doc string, while the +# function body provides the action taken when the rule is matched. +# The argument to each function is a list of the values of the +# rule's symbols: t[0] for the LHS, and t[1..n] for the symbols +# on the RHS. For tokens, the value is copied from the t.value +# attribute provided by the lexer. For non-terminals, the value +# is assigned by the producing rule; i.e., the job of the grammar +# rule function is to set the value for the non-terminal on the LHS +# (by assigning to t[0]). +##################################################################### + +# Not sure why, but we get a handful of shift/reduce conflicts on DECLARE. +# By default these get resolved as shifts, which is correct, but +# warnings are printed. Explicitly marking DECLARE as right-associative +# suppresses the warnings. +precedence = ( + ('right', 'DECLARE'), + ) + +# The LHS of the first grammar rule is used as the start symbol +# (in this case, 'specification'). Note that this rule enforces +# that there will be exactly one namespace declaration, with 0 or more +# global defs/decls before and after it. The defs & decls before +# the namespace decl will be outside the namespace; those after +# will be inside. The decoder function is always inside the namespace. +def p_specification(t): + 'specification : opt_defs_and_declares name_decl opt_defs_and_declares decode_block' + global_decls1 = t[1] + isa_name = t[2] + namespace = isa_name + "Inst" + global_decls2 = t[3] + (inst_decls, code) = t[4] + code = indent(code) + # grab the last three path components of isa_desc_filename + filename = '/'.join(isa_desc_filename.split('/')[-3:]) + # if the isa_desc file defines a 'rcs_id' string, + # echo that into the output too + try: + local_rcs_id = rcs_id + # strip $s out of ID so it doesn't get re-substituted + local_rcs_id = re.sub(r'\$', '', local_rcs_id) + except NameError: + local_rcs_id = 'Id: no RCS id found' + output = open(decoder_filename, 'w') + # split string to keep rcs from substituting this file's RCS id in + print >> output, '/* $Id' + '''$ */ + +/* + * Copyright (c) 2003 + * The Regents of The University of Michigan + * All Rights Reserved + * + * Permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any + * purpose, so long as the copyright notice above, this grant of + * permission, and the disclaimer below appear in all copies made; and + * so long as the name of The University of Michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND + * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE + * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, + * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM + * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGES. + */ + +/* + * DO NOT EDIT THIS FILE!!! + * + * It was automatically generated from this ISA description: + * Filename: %(filename)s + * RCS %(local_rcs_id)s + */ + +#include "bitfield.hh" // required for bitfield support + + +///////////////////////////////////// +// Global defs (outside namespace) // +///////////////////////////////////// + +%(global_decls1)s + +/** + * Namespace for %(isa_name)s static instruction objects. + */ +namespace %(namespace)s +{ + +///////////////////////////////////// +// Global defs (within namespace) // +///////////////////////////////////// + +%(global_decls2)s + +//////////////////////////////////// +// Declares from inst definitions // +//////////////////////////////////// + +%(inst_decls)s + +} // namespace %(namespace)s + +////////////////////// +// Decoder function // +////////////////////// + +StaticInstPtr<%(isa_name)s> +%(isa_name)s::decodeInst(%(isa_name)s::MachInst machInst) +{ + using namespace %(namespace)s; +%(code)s +} // decodeInst +''' % vars() + output.close() + +# ISA name declaration looks like "namespace <foo>;" +def p_name_decl(t): + 'name_decl : NAMESPACE ID SEMI' + t[0] = t[2] + +# 'opt_defs_and_declares' is a possibly empty sequence of +# defs and/or declares. +def p_opt_defs_and_declares_0(t): + 'opt_defs_and_declares : empty' + t[0] = '' + +def p_opt_defs_and_declares_1(t): + 'opt_defs_and_declares : defs_and_declares' + t[0] = t[1] + +def p_defs_and_declares_0(t): + 'defs_and_declares : def_or_declare' + t[0] = t[1] + +def p_defs_and_declares_1(t): + 'defs_and_declares : defs_and_declares def_or_declare' + t[0] = t[1] + t[2] + +# The list of possible definition/declaration statements. +def p_def_or_declare(t): + '''def_or_declare : def_format + | def_bitfield + | def_template + | global_declare + | global_let + | cpp_directive''' + t[0] = t[1] + +# preprocessor directives are copied directly to the output. +def p_cpp_directive(t): + '''cpp_directive : CPPDIRECTIVE''' + t[0] = t[1] + +# Global declares 'declare {{...}}' (C++ code blocks) are copied +# directly to the output. +def p_global_declare(t): + 'global_declare : DECLARE CODELIT SEMI' + t[0] = substBitOps(t[2]) + +# global let blocks 'let {{...}}' (Python code blocks) are executed +# directly when seen. These are typically used to initialize global +# Python variables used in later format definitions. +def p_global_let(t): + 'global_let : LET CODELIT SEMI' + try: + exec(fixPythonIndentation(t[2])) + except: + error_bt(t.lineno(1), 'error in global let block "%s".' % t[2]) + t[0] = '' # contributes nothing to the output C++ file + +# A bitfield definition looks like: +# 'def [signed] bitfield <ID> [<first>:<last>]' +# This generates a preprocessor macro in the output file. +def p_def_bitfield_0(t): + 'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT COLON INTLIT GREATER SEMI' + expr = 'bits(machInst, %2d, %2d)' % (t[6], t[8]) + if (t[2] == 'signed'): + expr = 'sext<%d>(%s)' % (t[6] - t[8] + 1, expr) + t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + +# alternate form for single bit: 'def [signed] bitfield <ID> [<bit>]' +def p_def_bitfield_1(t): + 'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT GREATER SEMI' + expr = 'bits(machInst, %2d, %2d)' % (t[6], t[6]) + if (t[2] == 'signed'): + expr = 'sext<%d>(%s)' % (1, expr) + t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + +def p_opt_signed_0(t): + 'opt_signed : SIGNED' + t[0] = t[1] + +def p_opt_signed_1(t): + 'opt_signed : empty' + t[0] = '' + +# Global map variable to hold templates +templateMap = {} + +def p_def_template(t): + 'def_template : DEF TEMPLATE ID CODELIT SEMI' + templateMap[t[3]] = t[4] + t[0] = '' + +# An instruction format definition looks like +# "def format <fmt>(<params>) {{...}};" +def p_def_format(t): + 'def_format : DEF FORMAT ID LPAREN param_list RPAREN CODELIT SEMI' + (id, params, code) = (t[3], t[5], t[7]) + defFormat(id, params, code, t.lineno(1)) + # insert a comment into the output to note that the def was processed + t[0] = ''' +// +// parser: format %s defined +// +''' % id + +# The formal parameter list for an instruction format is a possibly +# empty list of comma-separated parameters. +def p_param_list_0(t): + 'param_list : empty' + t[0] = [ ] + +def p_param_list_1(t): + 'param_list : param' + t[0] = [t[1]] + +def p_param_list_2(t): + 'param_list : param_list COMMA param' + t[0] = t[1] + t[0].append(t[3]) + +# Each formal parameter is either an identifier or an identifier +# preceded by an asterisk. As in Python, the latter (if present) gets +# a tuple containing all the excess positional arguments, allowing +# varargs functions. +def p_param_0(t): + 'param : ID' + t[0] = t[1] + +def p_param_1(t): + 'param : ASTERISK ID' + # just concatenate them: '*ID' + t[0] = t[1] + t[2] + +# End of format definition-related rules. +############## + +# +# A decode block looks like: +# decode <field1> [, <field2>]* [default <inst>] { ... } +# +def p_decode_block(t): + 'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE' + default_defaults = defaultStack.pop() + (decls, code, has_default) = t[5] + # use the "default defaults" only if there was no explicit + # default statement in decode_stmt_list + if not has_default: + (default_decls, default_code) = default_defaults + decls += default_decls + code += default_code + t[0] = (decls, ''' +switch (%s) { +%s +} +''' % (t[2], indent(code))) + +# The opt_default statement serves only to push the "default defaults" +# onto defaultStack. This value will be used by nested decode blocks, +# and used and popped off when the current decode_block is processed +# (in p_decode_block() above). +def p_opt_default_0(t): + 'opt_default : empty' + # no default specified: reuse the one currently at the top of the stack + defaultStack.push(defaultStack.top()) + # no meaningful value returned + t[0] = None + +def p_opt_default_1(t): + 'opt_default : DEFAULT inst' + # push the new default + (decls, code) = t[2] + defaultStack.push((decls, '\ndefault:\n%sbreak;' % code)) + # no meaningful value returned + t[0] = None + +def p_decode_stmt_list_0(t): + 'decode_stmt_list : decode_stmt' + t[0] = t[1] + +def p_decode_stmt_list_1(t): + 'decode_stmt_list : decode_stmt decode_stmt_list' + (decls1, code1, has_default1) = t[1] + (decls2, code2, has_default2) = t[2] + if (has_default1 and has_default2): + error(t.lineno(1), 'Two default cases in decode block') + t[0] = (decls1 + '\n' + decls2, code1 + '\n' + code2, + has_default1 or has_default2) + +# +# Decode statement rules +# +# There are four types of statements allowed in a decode block: +# 1. Format blocks 'format <foo> { ... }' +# 2. Nested decode blocks +# 3. Instruction definitions. +# 4. C preprocessor directives. + + +# Preprocessor directives found in a decode statement list are passed +# through to the output, replicated to both the declaration and decode +# streams. This works well for ifdefs, so we can ifdef out both the +# declarations and the decode cases generated by an instruction +# definition. Handling them as part of the grammar makes it easy to +# keep them in the right place with respect to the code generated by +# the other statements. +def p_decode_stmt_cpp(t): + 'decode_stmt : CPPDIRECTIVE' + t[0] = (t[1], t[1], 0) + +# A format block 'format <foo> { ... }' sets the default instruction +# format used to handle instruction definitions inside the block. +# This format can be overridden by using an explicit format on the +# instruction definition or with a nested format block. +def p_decode_stmt_format(t): + 'decode_stmt : FORMAT push_format_id LBRACE decode_stmt_list RBRACE' + # The format will be pushed on the stack when 'push_format_id' is + # processed (see below). Once the parser has recognized the full + # production (though the right brace), we're done with the format, + # so now we can pop it. + formatStack.pop() + t[0] = t[4] + +# This rule exists so we can set the current format (& push the stack) +# when we recognize the format name part of the format block. +def p_push_format_id(t): + 'push_format_id : ID' + try: + formatStack.push(formatMap[t[1]]) + t[0] = ('', '// format %s' % t[1]) + except KeyError: + error(t.lineno(1), 'instruction format "%s" not defined.' % t[1]) + +# Nested decode block: if the value of the current field matches the +# specified constant, do a nested decode on some other field. +def p_decode_stmt_decode(t): + 'decode_stmt : case_label COLON decode_block' + (label, is_default) = t[1] + (decls, code) = t[3] + # just wrap the decoding code from the block as a case in the + # outer switch statement. + t[0] = (decls, '\n%s:\n%s' % (label, indent(code)), is_default) + +# Instruction definition (finally!). +def p_decode_stmt_inst(t): + 'decode_stmt : case_label COLON inst SEMI' + (label, is_default) = t[1] + (decls, code) = t[3] + t[0] = (decls, '\n%s:%sbreak;' % (label, indent(code)), is_default) + +# The case label is either a list of one or more constants or 'default' +def p_case_label_0(t): + 'case_label : intlit_list' + t[0] = (': '.join(map(lambda a: 'case %#x' % a, t[1])), 0) + +def p_case_label_1(t): + 'case_label : DEFAULT' + t[0] = ('default', 1) + +# +# The constant list for a decode case label must be non-empty, but may have +# one or more comma-separated integer literals in it. +# +def p_intlit_list_0(t): + 'intlit_list : INTLIT' + t[0] = [t[1]] + +def p_intlit_list_1(t): + 'intlit_list : intlit_list COMMA INTLIT' + t[0] = t[1] + t[0].append(t[3]) + +# Define an instruction using the current instruction format (specified +# by an enclosing format block). +# "<mnemonic>(<args>)" +def p_inst_0(t): + 'inst : ID LPAREN arg_list RPAREN' + # Pass the ID and arg list to the current format class to deal with. + currentFormat = formatStack.top() + (decls, code) = currentFormat.defineInst(t[1], t[3], t.lineno(1)) + args = ','.join(map(str, t[3])) + args = re.sub('(?m)^', '//', args) + args = re.sub('^//', '', args) + comment = '// %s::%s(%s)\n' % (currentFormat.id, t[1], args) + t[0] = (comment + decls, comment + code) + +# Define an instruction using an explicitly specified format: +# "<fmt>::<mnemonic>(<args>)" +def p_inst_1(t): + 'inst : ID DBLCOLON ID LPAREN arg_list RPAREN' + try: + format = formatMap[t[1]] + except KeyError: + error(t.lineno(1), 'instruction format "%s" not defined.' % t[1]) + (decls, code) = format.defineInst(t[3], t[5], t.lineno(1)) + comment = '// %s::%s(%s)\n' % (t[1], t[3], t[5]) + t[0] = (comment + decls, comment + code) + +def p_arg_list_0(t): + 'arg_list : empty' + t[0] = [ ] + +def p_arg_list_1(t): + 'arg_list : arg' + t[0] = [t[1]] + +def p_arg_list_2(t): + 'arg_list : arg_list COMMA arg' + t[0] = t[1] + t[0].append(t[3]) + +def p_arg(t): + '''arg : ID + | INTLIT + | STRLIT + | CODELIT''' + t[0] = t[1] + +# +# Empty production... use in other rules for readability. +# +def p_empty(t): + 'empty :' + pass + +# Parse error handler. Note that the argument here is the offending +# *token*, not a grammar symbol (hence the need to use t.value) +def p_error(t): + if t: + error(t.lineno, "syntax error at '%s'" % t.value) + else: + error_bt(0, "unknown syntax error") + +# END OF GRAMMAR RULES +# +# Now build the parser. +yacc.yacc() + +################ +# Format object. +# +# A format object encapsulates an instruction format. It must provide +# a defineInst() method that generates the code for an instruction +# definition. + +class Format: + def __init__(self, id, params, code): + # constructor: just save away arguments + self.id = id + self.params = params + # strip blank lines from code (ones at the end are troublesome) + code = re.sub(r'(?m)^\s*$', '', code); + if code == '': + code = ' pass\n' + param_list = string.join(params, ", ") + f = 'def defInst(name, Name, ' + param_list + '):\n' + code + exec(f) + self.func = defInst + + def defineInst(self, name, args, lineno): + # automatically provide a capitalized version of mnemonic + Name = string.capitalize(name) + try: + retval = self.func(name, Name, *args) + except: + error_bt(lineno, 'error defining "%s".' % name) + return retval + +# Special null format to catch an implicit-format instruction +# definition outside of any format block. +class NoFormat: + def __init__(self): + self.defaultInst = '' + + def defineInst(self, name, args, lineno): + error(lineno, + 'instruction definition "%s" with no active format!' % name) + +# This dictionary maps format name strings to Format objects. +formatMap = {} + +# Define a new format +def defFormat(id, params, code, lineno): + # make sure we haven't already defined this one + if formatMap.get(id, None) != None: + error(lineno, 'format %s redefined.' % id) + # create new object and store in global map + formatMap[id] = Format(id, params, code) + + +############## +# Stack: a simple stack object. Used for both formats (formatStack) +# and default cases (defaultStack). + +class Stack: + def __init__(self, initItem): + self.stack = [ initItem ] + + def push(self, item): + self.stack.append(item); + + def pop(self): + return self.stack.pop() + + def top(self): + return self.stack[-1] + +# The global format stack. +formatStack = Stack(NoFormat()) + +# The global default case stack. +defaultStack = Stack( None ) + +################### +# Utility functions + +# +# Indent every line in string 's' by two spaces +# (except preprocessor directives). +# Used to make nested code blocks look pretty. +# +def indent(s): + return re.sub(r'(?m)^(?!\#)', ' ', s) + +# +# Munge a somewhat arbitrarily formatted piece of Python code +# (e.g. from a format 'let' block) into something whose indentation +# will get by the Python parser. +# +# The two keys here are that Python will give a syntax error if +# there's any whitespace at the beginning of the first line, and that +# all lines at the same lexical nesting level must have identical +# indentation. Unfortunately the way code literals work, an entire +# let block tends to have some initial indentation. Rather than +# trying to figure out what that is and strip it off, we prepend 'if +# 1:' to make the let code the nested block inside the if (and have +# the parser automatically deal with the indentation for us). +# +# We don't want to do this if (1) the code block is empty or (2) the +# first line of the block doesn't have any whitespace at the front. + +def fixPythonIndentation(s): + # get rid of blank lines first + s = re.sub(r'(?m)^\s*\n', '', s); + if (s != '' and re.match(r'[ \t]', s[0])): + s = 'if 1:\n' + s + return s + +# Error handler. Just call exit. Output formatted to work under +# Emacs compile-mode. +def error(lineno, string): + sys.exit("%s:%d: %s" % (isa_desc_filename, lineno, string)) + +# Like error(), but include a Python stack backtrace (for processing +# Python exceptions). +def error_bt(lineno, string): + print >> sys.stderr, "%s:%d: %s" % (isa_desc_filename, lineno, string) + raise + + +##################################################################### +# +# Bitfield Operator Support +# +##################################################################### + +bitOp1ArgRE = re.compile(r'<\s*(\w+)\s*:\s*>') + +bitOpWordRE = re.compile(r'(?<![\w\.])([\w\.]+)<\s*(\w+)\s*:\s*(\w+)\s*>') +bitOpExprRE = re.compile(r'\)<\s*(\w+)\s*:\s*(\w+)\s*>') + +def substBitOps(code): + # first convert single-bit selectors to two-index form + # i.e., <n> --> <n:n> + code = bitOp1ArgRE.sub(r'<\1:\1>', code) + # simple case: selector applied to ID (name) + # i.e., foo<a:b> --> bits(foo, a, b) + code = bitOpWordRE.sub(r'bits(\1, \2, \3)', code) + # if selector is applied to expression (ending in ')'), + # we need to search backward for matching '(' + match = bitOpExprRE.search(code) + while match: + exprEnd = match.start() + here = exprEnd - 1 + nestLevel = 1 + while nestLevel > 0: + if code[here] == '(': + nestLevel -= 1 + elif code[here] == ')': + nestLevel += 1 + here -= 1 + if here < 0: + sys.exit("Didn't find '('!") + exprStart = here+1 + newExpr = r'bits(%s, %s, %s)' % (code[exprStart:exprEnd+1], + match.group(1), match.group(2)) + code = code[:exprStart] + newExpr + code[match.end():] + match = bitOpExprRE.search(code) + return code + + +##################################################################### +# +# Code Parser +# +# The remaining code is the support for automatically extracting +# instruction characteristics from pseudocode. +# +##################################################################### + +# Force the argument to be a list +def makeList(list_or_item): + if not list_or_item: + return [] + elif type(list_or_item) == ListType: + return list_or_item + else: + return [ list_or_item ] + +# generate operandSizeMap based on provided operandTypeMap: +# basically generate equiv. C++ type and make is_signed flag +def buildOperandSizeMap(): + global operandSizeMap + operandSizeMap = {} + for ext in operandTypeMap.keys(): + (desc, size) = operandTypeMap[ext] + if desc == 'signed int': + type = 'int%d_t' % size + is_signed = 1 + elif desc == 'unsigned int': + type = 'uint%d_t' % size + is_signed = 0 + elif desc == 'float': + is_signed = 1 # shouldn't really matter + if size == 32: + type = 'float' + elif size == 64: + type = 'double' + if type == '': + error(0, 'Unrecognized type description "%s" in operandTypeMap') + operandSizeMap[ext] = (size, type, is_signed) + +# +# Base class for operand traits. An instance of this class (or actually +# a class derived from this one) encapsulates the traits of a particular +# operand type (e.g., "32-bit integer register"). +# +class OperandTraits: + def __init__(self, dflt_ext, reg_spec, flags, sort_pri): + # Force construction of operandSizeMap from operandTypeMap + # if it hasn't happened yet + if not globals().has_key('operandSizeMap'): + buildOperandSizeMap() + self.dflt_ext = dflt_ext + (self.dflt_size, self.dflt_type, self.dflt_is_signed) = \ + operandSizeMap[dflt_ext] + self.reg_spec = reg_spec + # Canonical flag structure is a triple of lists, where each list + # indicates the set of flags implied by this operand always, when + # used as a source, and when used as a dest, respectively. + # For simplicity this can be initialized using a variety of fairly + # obvious shortcuts; we convert these to canonical form here. + if not flags: + # no flags specified (e.g., 'None') + self.flags = ( [], [], [] ) + elif type(flags) == StringType: + # a single flag: assumed to be unconditional + self.flags = ( [ flags ], [], [] ) + elif type(flags) == ListType: + # a list of flags: also assumed to be unconditional + self.flags = ( flags, [], [] ) + elif type(flags) == TupleType: + # it's a tuple: it should be a triple, + # but each item could be a single string or a list + (uncond_flags, src_flags, dest_flags) = flags + self.flags = (makeList(uncond_flags), + makeList(src_flags), makeList(dest_flags)) + self.sort_pri = sort_pri + + def isMem(self): + return 0 + + def isReg(self): + return 0 + + def isFloatReg(self): + return 0 + + def isIntReg(self): + return 0 + + def isControlReg(self): + return 0 + + def getFlags(self, op_desc): + # note the empty slice '[:]' gives us a copy of self.flags[0] + # instead of a reference to it + my_flags = self.flags[0][:] + if op_desc.is_src: + my_flags += self.flags[1] + if op_desc.is_dest: + my_flags += self.flags[2] + return my_flags + + def makeDecl(self, op_desc): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + # Note that initializations in the declarations are solely + # to avoid 'uninitialized variable' errors from the compiler. + return type + ' ' + op_desc.munged_name + ' = 0;\n'; + +class IntRegOperandTraits(OperandTraits): + def isReg(self): + return 1 + + def isIntReg(self): + return 1 + + def makeConstructor(self, op_desc): + c = '' + if op_desc.is_src: + c += '\n\t_srcRegIdx[%d] = %s;' % \ + (op_desc.src_reg_idx, self.reg_spec) + if op_desc.is_dest: + c += '\n\t_destRegIdx[%d] = %s;' % \ + (op_desc.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + if (type == 'float' or type == 'double'): + error(0, 'Attempt to read integer register as FP') + if (size == self.dflt_size): + return '%s = xc->readIntReg(_srcRegIdx[%d]);\n' % \ + (op_desc.munged_name, op_desc.src_reg_idx) + else: + return '%s = bits(xc->readIntReg(_srcRegIdx[%d]), %d, 0);\n' % \ + (op_desc.munged_name, op_desc.src_reg_idx, size-1) + + def makeWrite(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + if (type == 'float' or type == 'double'): + error(0, 'Attempt to write integer register as FP') + if (size != self.dflt_size and is_signed): + final_val = 'sext<%d>(%s)' % (size, op_desc.munged_name) + else: + final_val = op_desc.munged_name + wb = ''' + { + %s final_val = %s; + xc->setIntReg(_destRegIdx[%d], final_val);\n + if (traceData) { traceData->setData(final_val); } + }''' % (self.dflt_type, final_val, op_desc.dest_reg_idx) + return wb + +class FloatRegOperandTraits(OperandTraits): + def isReg(self): + return 1 + + def isFloatReg(self): + return 1 + + def makeConstructor(self, op_desc): + c = '' + if op_desc.is_src: + c += '\n\t_srcRegIdx[%d] = %s + FP_Base_DepTag;' % \ + (op_desc.src_reg_idx, self.reg_spec) + if op_desc.is_dest: + c += '\n\t_destRegIdx[%d] = %s + FP_Base_DepTag;' % \ + (op_desc.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + bit_select = 0 + if (type == 'float'): + func = 'readFloatRegSingle' + elif (type == 'double'): + func = 'readFloatRegDouble' + else: + func = 'readFloatRegInt' + if (size != self.dflt_size): + bit_select = 1 + base = 'xc->%s(_srcRegIdx[%d] - FP_Base_DepTag)' % \ + (func, op_desc.src_reg_idx) + if bit_select: + return '%s = bits(%s, %d, 0);\n' % \ + (op_desc.munged_name, base, size-1) + else: + return '%s = %s;\n' % (op_desc.munged_name, base) + + def makeWrite(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + final_val = op_desc.munged_name + if (type == 'float'): + func = 'setFloatRegSingle' + elif (type == 'double'): + func = 'setFloatRegDouble' + else: + func = 'setFloatRegInt' + type = 'uint%d_t' % self.dflt_size + if (size != self.dflt_size and is_signed): + final_val = 'sext<%d>(%s)' % (size, op_desc.munged_name) + wb = ''' + { + %s final_val = %s; + xc->%s(_destRegIdx[%d] - FP_Base_DepTag, final_val);\n + if (traceData) { traceData->setData(final_val); } + }''' % (type, final_val, func, op_desc.dest_reg_idx) + return wb + +class ControlRegOperandTraits(OperandTraits): + def isReg(self): + return 1 + + def isControlReg(self): + return 1 + + def makeConstructor(self, op_desc): + c = '' + if op_desc.is_src: + c += '\n\t_srcRegIdx[%d] = %s_DepTag;' % \ + (op_desc.src_reg_idx, self.reg_spec) + if op_desc.is_dest: + c += '\n\t_destRegIdx[%d] = %s_DepTag;' % \ + (op_desc.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + bit_select = 0 + if (type == 'float' or type == 'double'): + error(0, 'Attempt to read control register as FP') + base = 'xc->read%s()' % self.reg_spec + if size == self.dflt_size: + return '%s = %s;\n' % (op_desc.munged_name, base) + else: + return '%s = bits(%s, %d, 0);\n' % \ + (op_desc.munged_name, base, size-1) + + def makeWrite(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + if (type == 'float' or type == 'double'): + error(0, 'Attempt to write control register as FP') + wb = 'xc->set%s(%s);\n' % (self.reg_spec, op_desc.munged_name) + wb += 'if (traceData) { traceData->setData(%s); }' % \ + op_desc.munged_name + return wb + +class MemOperandTraits(OperandTraits): + def isMem(self): + return 1 + + def makeConstructor(self, op_desc): + return '' + + def makeDecl(self, op_desc): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + # Note that initializations in the declarations are solely + # to avoid 'uninitialized variable' errors from the compiler. + # Declare memory data variable. + c = '%s %s = 0;\n' % (type, op_desc.munged_name) + # Declare var to hold memory access flags. + c += 'unsigned %s_flags = memAccessFlags;\n' % op_desc.base_name + # If this operand is a dest (i.e., it's a store operation), + # then we need to declare a variable for the write result code + # as well. + if op_desc.is_dest: + c += 'uint64_t %s_write_result = 0;\n' % op_desc.base_name + return c + + def makeRead(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + eff_type = 'uint%d_t' % size + return 'fault = memAccessObj->read(EA, (%s&)%s, %s_flags);\n' \ + % (eff_type, op_desc.munged_name, op_desc.base_name) + + def makeWrite(self, op_desc, cpu_model): + (size, type, is_signed) = operandSizeMap[op_desc.eff_ext] + eff_type = 'uint%d_t' % size + return 'fault = memAccessObj->write((%s&)%s, EA, %s_flags,' \ + ' &%s_write_result);\n' \ + % (eff_type, op_desc.munged_name, op_desc.base_name, + op_desc.base_name) + +class NPCOperandTraits(OperandTraits): + def makeConstructor(self, op_desc): + return '' + + def makeRead(self, op_desc, cpu_model): + return '%s = xc->readPC() + 4;\n' % op_desc.munged_name + + def makeWrite(self, op_desc, cpu_model): + return 'xc->setNextPC(%s);\n' % op_desc.munged_name + + +# +# Define operand variables that get derived from the basic declaration +# of ISA-specific operands in operandTraitsMap. This function must be +# called by the ISA description file explicitly after defining +# operandTraitsMap (in a 'let' block). +# +def defineDerivedOperandVars(): + global operands + operands = operandTraitsMap.keys() + + operandsREString = (r''' + (?<![\w\.]) # neg. lookbehind assertion: prevent partial matches + ((%s)(?:\.(\w+))?) # match: operand with optional '.' then suffix + (?![\w\.]) # neg. lookahead assertion: prevent partial matches + ''' + % string.join(operands, '|')) + + global operandsRE + operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE) + + # Same as operandsREString, but extension is mandatory, and only two + # groups are returned (base and ext, not full name as above). + # Used for subtituting '_' for '.' to make C++ identifiers. + operandsWithExtREString = (r'(?<![\w\.])(%s)\.(\w+)(?![\w\.])' + % string.join(operands, '|')) + + global operandsWithExtRE + operandsWithExtRE = re.compile(operandsWithExtREString, re.MULTILINE) + + +# +# Operand descriptor class. An instance of this class represents +# a specific operand for a code block. +# +class OperandDescriptor: + def __init__(self, full_name, base_name, ext, is_src, is_dest): + self.full_name = full_name + self.base_name = base_name + self.ext = ext + self.is_src = is_src + self.is_dest = is_dest + self.traits = operandTraitsMap[base_name] + # The 'effective extension' (eff_ext) is either the actual + # extension, if one was explicitly provided, or the default. + # The 'munged name' replaces the '.' between the base and + # extension (if any) with a '_' to make a legal C++ variable name. + if ext: + self.eff_ext = ext + self.munged_name = base_name + '_' + ext + else: + self.eff_ext = self.traits.dflt_ext + self.munged_name = base_name + + # Finalize additional fields (primarily code fields). This step + # is done separately since some of these fields may depend on the + # register index enumeration that hasn't been performed yet at the + # time of __init__(). + def finalize(self): + self.flags = self.traits.getFlags(self) + self.constructor = self.traits.makeConstructor(self) + self.exec_decl = self.traits.makeDecl(self) + + if self.is_src: + self.simple_rd = self.traits.makeRead(self, 'simple') + self.dtld_rd = self.traits.makeRead(self, 'dtld') + else: + self.simple_rd = '' + self.dtld_rd = '' + + if self.is_dest: + self.simple_wb = self.traits.makeWrite(self, 'simple') + self.dtld_wb = self.traits.makeWrite(self, 'dtld') + else: + self.simple_wb = '' + self.dtld_wb = '' + +class OperandDescriptorList: + def __init__(self): + self.items = [] + self.bases = {} + + def __len__(self): + return len(self.items) + + def __getitem__(self, index): + return self.items[index] + + def append(self, op_desc): + self.items.append(op_desc) + self.bases[op_desc.base_name] = op_desc + + def find_base(self, base_name): + # like self.bases[base_name], but returns None if not found + # (rather than raising exception) + return self.bases.get(base_name) + + # internal helper function for concat[Some]Attr{Strings|Lists} + def __internalConcatAttrs(self, attr_name, filter, result): + for op_desc in self.items: + if filter(op_desc): + result += getattr(op_desc, attr_name) + return result + + # return a single string that is the concatenation of the (string) + # values of the specified attribute for all operands + def concatAttrStrings(self, attr_name): + return self.__internalConcatAttrs(attr_name, lambda x: 1, '') + + # like concatAttrStrings, but only include the values for the operands + # for which the provided filter function returns true + def concatSomeAttrStrings(self, filter, attr_name): + return self.__internalConcatAttrs(attr_name, filter, '') + + # return a single list that is the concatenation of the (list) + # values of the specified attribute for all operands + def concatAttrLists(self, attr_name): + return self.__internalConcatAttrs(attr_name, lambda x: 1, []) + + # like concatAttrLists, but only include the values for the operands + # for which the provided filter function returns true + def concatSomeAttrLists(self, filter, attr_name): + return self.__internalConcatAttrs(attr_name, filter, []) + + def sort(self): + self.items.sort(lambda a, b: a.traits.sort_pri - b.traits.sort_pri) + +# Regular expression object to match C++ comments +# (used in findOperands()) +commentRE = re.compile(r'//.*\n') + +# Regular expression object to match assignment statements +# (used in findOperands()) +assignRE = re.compile(r'\s*=(?!=)', re.MULTILINE) + +# +# Find all the operands in the given code block. Returns an operand +# descriptor list (instance of class OperandDescriptorList). +# +def findOperands(code): + operands = OperandDescriptorList() + # delete comments so we don't accidentally match on reg specifiers inside + code = commentRE.sub('', code) + # search for operands + next_pos = 0 + while 1: + match = operandsRE.search(code, next_pos) + if not match: + # no more matches: we're done + break + op = match.groups() + # regexp groups are operand full name, base, and extension + (op_full, op_base, op_ext) = op + # if the token following the operand is an assignment, this is + # a destination (LHS), else it's a source (RHS) + is_dest = (assignRE.match(code, match.end()) != None) + is_src = not is_dest + # see if we've already seen this one + op_desc = operands.find_base(op_base) + if op_desc: + if op_desc.ext != op_ext: + error(0, 'Inconsistent extensions for operand %s' % op_base) + op_desc.is_src = op_desc.is_src or is_src + op_desc.is_dest = op_desc.is_dest or is_dest + else: + # new operand: create new descriptor + op_desc = OperandDescriptor(op_full, op_base, op_ext, + is_src, is_dest) + operands.append(op_desc) + # start next search after end of current match + next_pos = match.end() + operands.sort() + # enumerate source & dest register operands... used in building + # constructor later + srcRegs = 0 + destRegs = 0 + operands.numFPDestRegs = 0 + operands.numIntDestRegs = 0 + for op_desc in operands: + if op_desc.traits.isReg(): + if op_desc.is_src: + op_desc.src_reg_idx = srcRegs + srcRegs += 1 + if op_desc.is_dest: + op_desc.dest_reg_idx = destRegs + destRegs += 1 + if op_desc.traits.isFloatReg(): + operands.numFPDestRegs += 1 + elif op_desc.traits.isIntReg(): + operands.numIntDestRegs += 1 + operands.numSrcRegs = srcRegs + operands.numDestRegs = destRegs + # now make a final pass to finalize op_desc fields that may depend + # on the register enumeration + for op_desc in operands: + op_desc.finalize() + return operands + +# Munge operand names in code string to make legal C++ variable names. +# (Will match munged_name attribute of OperandDescriptor object.) +def substMungedOpNames(code): + return operandsWithExtRE.sub(r'\1_\2', code) + +def joinLists(t): + return map(string.join, t) + +def makeFlagConstructor(flag_list): + if len(flag_list) == 0: + return '' + # filter out repeated flags + flag_list.sort() + i = 1 + while i < len(flag_list): + if flag_list[i] == flag_list[i-1]: + del flag_list[i] + else: + i += 1 + pre = '\n\tflags[' + post = '] = true;' + code = pre + string.join(flag_list, post + pre) + post + return code + +class CodeBlock: + def __init__(self, code): + self.orig_code = code + self.operands = findOperands(code) + self.code = substMungedOpNames(substBitOps(code)) + self.constructor = self.operands.concatAttrStrings('constructor') + self.constructor += \ + '\n\t_numSrcRegs = %d;' % self.operands.numSrcRegs + self.constructor += \ + '\n\t_numDestRegs = %d;' % self.operands.numDestRegs + self.constructor += \ + '\n\t_numFPDestRegs = %d;' % self.operands.numFPDestRegs + self.constructor += \ + '\n\t_numIntDestRegs = %d;' % self.operands.numIntDestRegs + + self.exec_decl = self.operands.concatAttrStrings('exec_decl') + + is_mem = lambda op: op.traits.isMem() + not_mem = lambda op: not op.traits.isMem() + + self.simple_rd = self.operands.concatAttrStrings('simple_rd') + self.simple_wb = self.operands.concatAttrStrings('simple_wb') + self.simple_mem_rd = \ + self.operands.concatSomeAttrStrings(is_mem, 'simple_rd') + self.simple_mem_wb = \ + self.operands.concatSomeAttrStrings(is_mem, 'simple_wb') + self.simple_nonmem_rd = \ + self.operands.concatSomeAttrStrings(not_mem, 'simple_rd') + self.simple_nonmem_wb = \ + self.operands.concatSomeAttrStrings(not_mem, 'simple_wb') + + self.dtld_rd = self.operands.concatAttrStrings('dtld_rd') + self.dtld_wb = self.operands.concatAttrStrings('dtld_wb') + self.dtld_mem_rd = \ + self.operands.concatSomeAttrStrings(is_mem, 'dtld_rd') + self.dtld_mem_wb = \ + self.operands.concatSomeAttrStrings(is_mem, 'dtld_wb') + self.dtld_nonmem_rd = \ + self.operands.concatSomeAttrStrings(not_mem, 'dtld_rd') + self.dtld_nonmem_wb = \ + self.operands.concatSomeAttrStrings(not_mem, 'dtld_wb') + + self.flags = self.operands.concatAttrLists('flags') + + # Make a basic guess on the operand class (function unit type). + # These are good enough for most cases, and will be overridden + # later otherwise. + if 'IsStore' in self.flags: + self.op_class = 'WrPort' + elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags: + self.op_class = 'RdPort' + elif 'IsFloating' in self.flags: + self.op_class = 'FloatADD' + else: + self.op_class = 'IntALU' + +# Assume all instruction flags are of the form 'IsFoo' +instFlagRE = re.compile(r'Is.*') + +# OpClass constants are just a little more complicated +opClassRE = re.compile(r'Int.*|Float.*|.*Port|No_OpClass') + +class InstObjParams: + def __init__(self, mnem, class_name, base_class = '', + code_block = None, opt_args = []): + self.mnemonic = mnem + self.class_name = class_name + self.base_class = base_class + if code_block: + for code_attr in code_block.__dict__.keys(): + setattr(self, code_attr, getattr(code_block, code_attr)) + else: + self.constructor = '' + self.flags = [] + # Optional arguments are assumed to be either StaticInst flags + # or an OpClass value. To avoid having to import a complete + # list of these values to match against, we do it ad-hoc + # with regexps. + for oa in opt_args: + if instFlagRE.match(oa): + self.flags.append(oa) + elif opClassRE.match(oa): + self.op_class = oa + else: + error(0, 'InstObjParams: optional arg "%s" not recognized ' + 'as StaticInst::Flag or OpClass.' % oa) + + # add flag initialization to contructor here to include + # any flags added via opt_args + self.constructor += makeFlagConstructor(self.flags) + + # if 'IsFloating' is set, add call to the FP enable check + # function (which should be provided by isa_desc via a declare) + if 'IsFloating' in self.flags: + self.fp_enable_check = 'fault = checkFpEnableFault(xc);' + else: + self.fp_enable_check = '' + + def subst(self, *args): + result = [] + for t in args: + if not templateMap.has_key(t): + error(0, 'InstObjParams::subst: undefined template "%s"' % t) + try: + result.append(templateMap[t] % self.__dict__) + except KeyError, key: + error(0, 'InstObjParams::subst: no definition for "%s"' % key) + if len(args) == 1: + result = result[0] + return result + +# +# All set... read in and parse the ISA description. +# +yacc.parse(isa_desc) |