diff --git a/src/arch/riscvcapstone/insts/standard.cc b/src/arch/riscvcapstone/insts/standard.cc index 86aae1053b..b6e38d7fc0 100644 --- a/src/arch/riscvcapstone/insts/standard.cc +++ b/src/arch/riscvcapstone/insts/standard.cc @@ -50,6 +50,26 @@ namespace gem5 namespace RiscvcapstoneISA { +std::string +CapExitClass::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(0)) << ", " << + registerName(srcRegIdx(1)); + return ss.str(); +} + +std::string +CapEnterClass::generateDisassembly(Addr pc, + const loader::SymbolTable *symtab) const +{ + std::stringstream ss; + ss << mnemonic << ' ' << registerName(srcRegIdx(0)) << ", " << + registerName(srcRegIdx(1)); + return ss.str(); +} + std::string CallsClass::generateDisassembly(Addr pc, const loader::SymbolTable *symtab) const diff --git a/src/arch/riscvcapstone/insts/standard.hh b/src/arch/riscvcapstone/insts/standard.hh index 23c8439091..20032ca485 100644 --- a/src/arch/riscvcapstone/insts/standard.hh +++ b/src/arch/riscvcapstone/insts/standard.hh @@ -85,6 +85,27 @@ class CallsClass : public RiscvStaticInst Addr pc, const loader::SymbolTable *symtab) const override; }; +/** + * Base class for world switching insts + */ +class CapEnterClass : public RiscvStaticInst +{ + protected: + using RiscvStaticInst::RiscvStaticInst; + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + +class CapExitClass : public RiscvStaticInst +{ + protected: + using RiscvStaticInst::RiscvStaticInst; + + std::string generateDisassembly( + Addr pc, const loader::SymbolTable *symtab) const override; +}; + /** * Base class for operations that work only on registers */ diff --git a/src/arch/riscvcapstone/isa/decoder.isa b/src/arch/riscvcapstone/isa/decoder.isa index d3428a33e3..87b45245f4 100644 --- a/src/arch/riscvcapstone/isa/decoder.isa +++ b/src/arch/riscvcapstone/isa/decoder.isa @@ -415,7 +415,7 @@ decode QUADRANT default Unknown::unknown() { 0x16: decode FUNCT3 { 0x1: decode FUNCT7 { format ROp { - //format CIOp { + //format CIOp 0x4: lcc ({{ using namespace gem5::RiscvcapstoneISA::o3; if(!Rs1_trv.getTag()) { @@ -468,6 +468,8 @@ decode QUADRANT default Unknown::unknown() { }}); 0xc: cincoffset ({{ using namespace gem5::RiscvcapstoneISA::o3; + DynInst *dyn_inst = dynamic_cast(xc); + assert(dyn_inst); //todo: set Rs1 to cnull if(!(Rs1_trv.getTag() && !Rs2_trv.getTag())) { @@ -487,11 +489,20 @@ decode QUADRANT default Unknown::unknown() { Rd_trv = Rs1_trv; - Rs1_trv.setTag(false); + //movc semantics + //rs1 still retains the original cap + if(rs1_cap.type() != CapType::NONLIN) + Rs1_trv.setTag(false); + else { + uint64_t node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeRcUpdate(node_id, 1)); + } Rs1_trv = Rs1_trv; }}); 0x5: scc ({{ using namespace gem5::RiscvcapstoneISA::o3; + DynInst *dyn_inst = dynamic_cast(xc); + assert(dyn_inst); //todo: set Rs1 to cnull if(!(Rs1_trv.getTag() && !Rs2_trv.getTag())) { @@ -505,11 +516,21 @@ decode QUADRANT default Unknown::unknown() { } rs1_cap.setCursor(Rs2_trv.getRegVal().intVal()); - Rs1_trv.getRegVal().rawCapVal() = (uint128_t)rs1_cap; - Rd_trv = Rs1_trv; + ConstTaggedRegVal temp; + temp.getRegVal().rawCapVal() = (uint128_t)rs1_cap; + temp.setTag(true); - Rs1_trv.setTag(false); + Rd_trv = temp; + + //movc semantics + //rs1 still retains the original cap + if(rs1_cap.type() != CapType::NONLIN) + Rs1_trv.setTag(false); + else { + uint64_t node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeRcUpdate(node_id, 1)); + } Rs1_trv = Rs1_trv; }}); 0x1: shrink ({{ @@ -787,10 +808,13 @@ decode QUADRANT default Unknown::unknown() { Rs1_trv = Rs1_trv; }}); } - //format CIOp { + //format CIOp format ROp { 0x2: tighten ({{ using namespace gem5::RiscvcapstoneISA::o3; + DynInst *dyn_inst = dynamic_cast(xc); + assert(dyn_inst); + if(!Rs1_trv.getTag()) { return std::make_shared("Unexpected operand type (24)", machInst); } @@ -817,7 +841,14 @@ decode QUADRANT default Unknown::unknown() { Rd_trv = Rs1_trv; Rd_trv.getRegVal().rawCapVal() = (uint128_t)rs1_cap; - Rs1_trv.setTag(false); + //movc semantics + //rs1 still retains the original cap + if(rs1_cap.type() != CapType::NONLIN) + Rs1_trv.setTag(false); + else { + uint64_t node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeRcUpdate(node_id, 1)); + } Rs1_trv = Rs1_trv; }}); } @@ -852,6 +883,7 @@ decode QUADRANT default Unknown::unknown() { Cap pc_cap = dyn_inst->cpu->getIEWObject().getPCCap(dyn_inst->threadNumber); pc_cap.setCursor(npc); // prepare the PC cap to save + //todo: clear Rs1 ra_trv = Rs1_trv; Cap rs1_cap = Rs1_trv.getRegVal().capVal(); @@ -1145,29 +1177,229 @@ decode QUADRANT default Unknown::unknown() { Rs1_trv = Rs1_trv; }}); } + format CapEnter { + 0x42: capenter ({{ + if(dyn_inst->isSecureWorld()) { + return std::make_shared("Illegal instruction (0)", machInst); + } + + if(!(Rs1_trv.getTag())) { + return std::make_shared("Unexpected operand type (24)", machInst); + } + + Cap rs1_cap = Rs1_trv.getRegVal().capVal(); + + if(rs1_cap.type() != CapType::SEALED) { + return std::make_shared("Unexpected capability type (26)", machInst); + } + + NodeID node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeQuery(node_id)); + + if(rs1_cap.async() == CapAsync::SYNC) { + Addr cur_addr = rs1_cap.start(); + + RegVal dummy; + for(int i = 0; i < 3; i++, cur_addr += sizeof(RegVal)) { + initiateMemRead(xc, traceData, cur_addr, dummy, Request::Flags()); + dyn_inst->initiateGetTag(cur_addr); + } + } else { //upon exception or interrupt + Addr cur_addr = rs1_cap.start(); + + RegVal dummy; + for(int i = 0; i < 33; i++, cur_addr += sizeof(RegVal)) { + initiateMemRead(xc, traceData, cur_addr, dummy, Request::Flags()); + dyn_inst->initiateGetTag(cur_addr); + } + } + }}, {{ + Cap rs1_cap = Rs1_trv.getRegVal().capVal(); + CPU *cpu = dynamic_cast(dyn_inst->cpu); + + if(rs1_cap.async() == CapAsync::SYNC) { + Cap pc_cap = dyn_inst->cpu->getIEWObject().getPCCap(dyn_inst->threadNumber); + uint64_t pc = pc_cap.cursor(); + + cpu->normal_pc[dyn_inst->threadNumber] = pc; + cpu->normal_sp[dyn_inst->threadNumber] = sp_trv; + + uint128_t pc_mem_load, ceh_mem_load, csp_mem_load; + //need to fix rescheduled call? like a rescheduled load + //it's serializing, why would it be rescheduled + + memcpy(&pc_mem_load, dyn_inst->getMemReadRes(0), sizeof(pc_mem_load)); + memcpy(&ceh_mem_load, dyn_inst->getMemReadRes(1), sizeof(ceh_mem_load)); + memcpy(&csp_mem_load, dyn_inst->getMemReadRes(2), sizeof(csp_mem_load)); + //also assert tag results maybe? + + Cap pc_cap_new(pc_mem_load); + dyn_inst->cpu->getIEWObject().setPCCap(pc_cap_new, dyn_inst->threadNumber); + + ConstTaggedRegVal ceh; + ceh.setTag(true); + ceh.getRegVal().rawCapVal() = ceh_mem_load; + dyn_inst->setTaggedMiscReg(CAPMISCREG_CEH, ceh); + + sp_trv.getRegVal().rawCapVal() = csp_mem_load; + sp_trv.setTag(true); + sp_trv = sp_trv; + + ConstTaggedRegVal temp_cra; + temp_cra.setTag(true); + rs1_cap.setType(CapType::EXIT); + rs1_cap.setCursor(rs1_cap.start()); + temp_cra.getRegVal().rawCapVal() = (uint128_t)rs1_cap; + ra_trv = temp_cra; + + cpu->switch_reg[dyn_inst->threadNumber] = RS1; + cpu->exit_reg[dyn_inst->threadNumber] = RD; + } else { + Cap pc_cap = dyn_inst->cpu->getIEWObject().getPCCap(dyn_inst->threadNumber); + uint64_t pc = pc_cap.cursor(); + + cpu->normal_pc[dyn_inst->threadNumber] = pc; + cpu->normal_sp[dyn_inst->threadNumber] = sp_trv; + + uint128_t pc_mem_load, ceh_mem_load; + //need to fix rescheduled call? like a rescheduled load + //it's serializing, why would it be rescheduled + + memcpy(&pc_mem_load, dyn_inst->getMemReadRes(0), sizeof(pc_mem_load)); + memcpy(&ceh_mem_load, dyn_inst->getMemReadRes(1), sizeof(ceh_mem_load)); + //also assert tag results maybe? + + Cap pc_cap_new(pc_mem_load); + dyn_inst->cpu->getIEWObject().setPCCap(pc_cap_new, dyn_inst->threadNumber); + + ConstTaggedRegVal ceh; + ceh.setTag(true); + ceh.getRegVal().rawCapVal() = ceh_mem_load; + dyn_inst->setTaggedMiscReg(CAPMISCREG_CEH, ceh); + + for(int reg_idx = 0; reg_idx < NumIntArchRegs; reg_idx ++) { + RegVal& v = *(RegVal*)(dyn_inst->getMemReadRes(reg_idx + 2)); + ConstTagRef tag = dyn_inst->getTagQueryRes(reg_idx + 2); + //DPRINTFN("Load context reg %d = 0x%llx (tag = %d)\n", + // reg_idx, v.intVal(), tag); + + // write back to registers + ConstTaggedRegVal tagged_val(v, tag); + dyn_inst->setTaggedRegOperand(this, reg_idx, tagged_val); + } + + rs1_cap.setType(CapType::UNINIT); + rs1_cap.setCursor(rs1_cap.start()); + + ConstTaggedRegVal temp_switch_cap; + temp_switch_cap.setTag(true); + temp_switch_cap.getRegVal().rawCapVal() = (uint128_t)rs1_cap; + dyn_inst->setTaggedMiscReg(CAPMISCREG_SWITCH_CAP, temp_switch_cap); + + cpu->switch_reg[dyn_inst->threadNumber] = RS1; + cpu->exit_reg[dyn_inst->threadNumber] = RD; + } + + cpu->cwrld[dyn_inst->threadNumber] = 1; + }}); + } + format CapExit { + 0x43: capexit ({{ + //i don't need comp code do i? + CPU *cpu = dynamic_cast(dyn_inst->cpu); + + if(!dyn_inst->isSecureWorld()) { + return std::make_shared("Illegal instruction (2)", machInst); + } + + if(!Rs1_trv.getTag() || Rs2_trv.getTag()) { + return std::make_shared("Unexpected operand type (24)", machInst); + } + + Cap rs1_cap = Rs1_trv.getRegVal().capVal(); + + if(rs1_cap.type() != CapType::EXIT) { + return std::make_shared("Unexpected capability type (26)", machInst); + } + + NodeID node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeQuery(node_id)); + + Cap pc_cap = dyn_inst->cpu->getIEWObject().getPCCap(dyn_inst->threadNumber); + pc_cap.setCursor(Rs2_trv.getRegVal().intVal()); // prepare the PC cap to save + + ConstTaggedRegVal ceh = dyn_inst->readTaggedMiscReg(CAPMISCREG_CEH); + Cap ceh_cap = ceh.getRegVal().capVal(); + + Addr EA = rs1_cap.start(); + + //todo: load the cap from the mem location and update refcount + //also, update refcount for pc, ceh and csp + Fault fault = writeMemTimingLE(xc, traceData, (uint128_t)pc_cap, EA, Request::Flags(), nullptr); + if (fault != NoFault) + return fault; + fault = writeMemTimingLE(xc, traceData, (uint128_t)ceh_cap, EA + sizeof(RegVal), Request::Flags(), nullptr); + if (fault != NoFault) + return fault; + fault = writeMemTimingLE(xc, traceData, sp_trv.getRegVal().rawCapVal(), EA + 2*sizeof(RegVal), Request::Flags(), nullptr); + if (fault != NoFault) + return fault; + + //todo: update PC + sp_trv = cpu->normal_sp[dyn_inst->threadNumber]; + + rs1_cap.setType(CapType::SEALED); + rs1_cap.setAsync(CapAsync::SYNC); + unsigned reg_idx = cpu->switch_reg[dyn_inst->threadNumber]; + ConstTaggedRegVal tagged_val; + tagged_val.setTag(true); + tagged_val.getRegVal().rawCapVal() = (uint128_t)rs1_cap; + dyn_inst->setTaggedRegOperand(this, reg_idx, tagged_val); + + //use a different variable here or this is okay? + tagged_val.setTag(false); + tagged_val.getRegVal().intVal() = 0; + reg_idx = cpu->exit_reg[dyn_inst->threadNumber]; + dyn_inst->setTaggedRegOperand(this, reg_idx, tagged_val); + + cpu->cwrld[dyn_inst->threadNumber] = 0; + }}); + } } format IOp { 0x2: cincoffsetimm ({{ using namespace gem5::RiscvcapstoneISA::o3; + DynInst *dyn_inst = dynamic_cast(xc); + assert(dyn_inst); //todo: set Rs1 to cnull if(!(Rs1_trv.getTag())) { return std::make_shared("Unexpected operand type (24)", machInst); } - if(!(Rs1_trv.getRegVal().capVal().type() != CapType::UNINIT && - Rs1_trv.getRegVal().capVal().type() != CapType::SEALED)) { + Cap rs1_cap = Rs1_trv.getRegVal().capVal(); + + if(rs1_cap.type() == CapType::UNINIT || + rs1_cap.type() == CapType::SEALED) { return std::make_shared("Unexpected capability type (26)", machInst); } - RegVal& reg_val = Rs1_trv.getRegVal(); - uint64_t cursor = reg_val.capVal().cursor(); + rs1_cap.setCursor(rs1_cap.cursor() + IMM12); - reg_val.capVal().setCursor(cursor + IMM12); + ConstTaggedRegVal temp; + temp.getRegVal().rawCapVal() = (uint128_t)rs1_cap; + temp.setTag(true); - Rd_trv = Rs1_trv; + Rd_trv = temp; - Rs1_trv.setTag(false); + //movc semantics + //rs1 still retains the original cap + if(rs1_cap.type() != CapType::NONLIN) + Rs1_trv.setTag(false); + else { + uint64_t node_id = rs1_cap.nodeId(); + dyn_inst->initiateNodeCommand(new NodeRcUpdate(node_id, 1)); + } Rs1_trv = Rs1_trv; }}); } diff --git a/src/arch/riscvcapstone/isa/formats/formats.isa b/src/arch/riscvcapstone/isa/formats/formats.isa index 5a03bc8db5..4911d04474 100644 --- a/src/arch/riscvcapstone/isa/formats/formats.isa +++ b/src/arch/riscvcapstone/isa/formats/formats.isa @@ -62,5 +62,8 @@ // Tag Access ##include "tags.isa" +// World Switching +##include "worlds.isa" + // Include the unknown ##include "unknown.isa" diff --git a/src/arch/riscvcapstone/isa/formats/worlds.isa b/src/arch/riscvcapstone/isa/formats/worlds.isa new file mode 100644 index 0000000000..e733b9c0ee --- /dev/null +++ b/src/arch/riscvcapstone/isa/formats/worlds.isa @@ -0,0 +1,174 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2015 RISC-V Foundation +// Copyright (c) 2016 The University of Virginia +// 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. + +//insn format for world switching +def format CapEnter(code, comp_code, *opt_flags) {{ + regs = [] # FIXME: this is not actually used + iop = InstObjParams(name, Name, 'CapEnterClass', + {'code': code, 'comp_code': comp_code, + 'regs': ','.join(regs)}, + opt_flags) + header_output = CapEnterDeclare.subst(iop) + decoder_output = CapEnterConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = CapEnterExecute.subst(iop) + \ + CapEnterCompleteAcc.subst(iop) +}}; + +def template CapEnterDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + private: + RegId srcRegIdxArr[NumIntArchRegs]; RegId destRegIdxArr[NumIntArchRegs]; + + public: + /// Constructor. + %(class_name)s(ExtMachInst machInst); + + Fault execute(ExecContext *, Trace::InstRecord *) const override; + Fault completeAcc(PacketPtr, ExecContext*, Trace::InstRecord*) const override; + }; +}}; + +def template CapEnterConstructor {{ + %(class_name)s::%(class_name)s(ExtMachInst machInst): + %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + setRegIdxArrays( + reinterpret_cast( + &std::remove_pointer_t::srcRegIdxArr), + reinterpret_cast( + &std::remove_pointer_t::destRegIdxArr)); + + assert(_numSrcRegs == 0 && _numDestRegs == 0); + + for(int i = 1; i < NumIntArchRegs; i ++) { + setSrcRegIdx(_numSrcRegs++, RegId(IntRegClass, i)); + setDestRegIdx(_numDestRegs++, RegId(IntRegClass, i)); + } + + flags[IsInteger] = true; + flags[IsLoad] = true; + flags[IsStore] = true; + flags[IsCall] = true; + flags[IsIndirectControl] = true; + flags[IsUncondControl] = true; + flags[IsNonSpeculative] = true; + flags[IsSerializeAfter] = true; + hasNodeOp = true; + } +}}; + +def template CapEnterExecute {{ + Fault + %(class_name)s::execute( + ExecContext *xc, Trace::InstRecord *traceData) const + { + %(op_decl)s; + %(op_rd)s; + + using namespace gem5::RiscvcapstoneISA::o3; + + DynInst* dyn_inst = dynamic_cast(xc); + assert(dyn_inst); + + %(code)s; + + %(op_wb)s; + } +}}; + +def template CapEnterCompleteAcc {{ + Fault + %(class_name)s::completeAcc(PacketPtr pkt, ExecContext* xc, Trace::InstRecord* traceData) const { + %(op_decl)s; + %(op_rd)s; + + using namespace gem5::RiscvcapstoneISA::o3; + + DynInst* dyn_inst = dynamic_cast(xc); + assert(dyn_inst); + + %(comp_code)s; + + %(op_wb)s; + } + +}}; + +def format CapExit(code, *opt_flags) {{ + regs = [] # FIXME: this is not actually used + iop = InstObjParams(name, Name, 'CapExitClass', + {'code': code, + 'regs': ','.join(regs)}, + opt_flags) + header_output = CapExitDeclare.subst(iop) + decoder_output = CapEnterConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = CapExitExecute.subst(iop) +}}; + +def template CapExitDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + private: + RegId srcRegIdxArr[NumIntArchRegs]; RegId destRegIdxArr[NumIntArchRegs]; + + public: + /// Constructor. + %(class_name)s(ExtMachInst machInst); + + Fault execute(ExecContext *, Trace::InstRecord *) const override; + }; +}}; + +def template CapExitExecute {{ + Fault + %(class_name)s::execute( + ExecContext *xc, Trace::InstRecord *traceData) const + { + %(op_decl)s; + %(op_rd)s; + + using namespace gem5::RiscvcapstoneISA::o3; + + DynInst* dyn_inst = dynamic_cast(xc); + assert(dyn_inst); + + %(code)s; + + %(op_wb)s; + } +}}; \ No newline at end of file diff --git a/src/arch/riscvcapstone/o3/node_commands.cc b/src/arch/riscvcapstone/o3/node_commands.cc index 1f964944b6..363c897dcc 100644 --- a/src/arch/riscvcapstone/o3/node_commands.cc +++ b/src/arch/riscvcapstone/o3/node_commands.cc @@ -544,7 +544,7 @@ NodeDrop::handleResp(PacketPtr pkt) { inst->getNodeController().setRoot(NODE_ID_INVALID); status = COMPLETED; } else if(prevNodeId == NODE_ID_INVALID) { - inst->getNodeController().setRoot(nodeId); + inst->getNodeController().setRoot(nodeId); //shouldn't this be nextNodeId? state = NCDrop_LOAD_RIGHT; status = TO_RESUME; } else {