arm: Add support for the mcr dc{ic,i,c}mvac, dccmvau instructions

This patch adds support for the ARMv7 cache maintenance
intructions:
* mcr dccmvac cleans a VA to the PoC
* mcr dcimvac invalidates a VA to the PoC
* mcr dccimvac cleans and invalidates a VA to the PoC
* mcr dccmvau cleans a VA to the PoU

Change-Id: I6511f203039ca145cc9128ddf61d09d6d7e40c10
Reviewed-by: Stephan Diestelhorst <stephan.diestelhorst@arm.com>
Reviewed-by: Anouk Van Laer <anouk.vanlaer@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/5059
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
diff --git a/src/arch/arm/insts/misc.cc b/src/arch/arm/insts/misc.cc
index ec1b935..ba97eff 100644
--- a/src/arch/arm/insts/misc.cc
+++ b/src/arch/arm/insts/misc.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2017 ARM Limited
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved
  *
@@ -322,6 +322,16 @@
 }
 
 std::string
+MiscRegRegImmMemOp::generateDisassembly(Addr pc,
+                                        const SymbolTable *symtab) const
+{
+    std::stringstream ss;
+    printMnemonic(ss);
+    printIntReg(ss, op1);
+    return ss.str();
+}
+
+std::string
 UnknownOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const
 {
     return csprintf("%-10s (inst %#08x)", "unknown", machInst);
diff --git a/src/arch/arm/insts/misc.hh b/src/arch/arm/insts/misc.hh
index c2dcaa8..72d1694 100644
--- a/src/arch/arm/insts/misc.hh
+++ b/src/arch/arm/insts/misc.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2012-2013 ARM Limited
+ * Copyright (c) 2010, 2012-2013, 2017 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -344,6 +344,23 @@
     std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const;
 };
 
+class MiscRegRegImmMemOp : public PredOp
+{
+  protected:
+    MiscRegIndex dest;
+    IntRegIndex op1;
+    uint64_t imm;
+
+    MiscRegRegImmMemOp(const char *mnem, ExtMachInst _machInst,
+                       OpClass __opClass, MiscRegIndex _dest, IntRegIndex _op1,
+                       uint64_t _imm) :
+        PredOp(mnem, _machInst, __opClass),
+        dest(_dest), op1(_op1), imm(_imm)
+    {}
+
+    std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const;
+};
+
 class UnknownOp : public PredOp
 {
   protected:
diff --git a/src/arch/arm/isa/formats/misc.isa b/src/arch/arm/isa/formats/misc.isa
index 26681e4..4f834b8 100644
--- a/src/arch/arm/isa/formats/misc.isa
+++ b/src/arch/arm/isa/formats/misc.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010-2013,2016 ARM Limited
+// Copyright (c) 2010-2013,2016-2017 ARM Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -219,15 +219,20 @@
                     machInst,
                     csprintf("miscreg crn:%d opc1:%d crm:%d opc2:%d %s unknown",
                     crn, opc1, crm, opc2, isRead ? "read" : "write"));
-          case MISCREG_DCCMVAC:
-            return new McrMrcMiscInst(isRead ? "mrc dccmvac" : "mcr dccmvac",
-                                      machInst, iss, MISCREG_DCCMVAC);
           case MISCREG_CP15ISB:
             return new Isb(machInst, iss);
           case MISCREG_CP15DSB:
             return new Dsb(machInst, iss);
           case MISCREG_CP15DMB:
             return new Dmb(machInst, iss);
+          case MISCREG_DCIMVAC:
+            return new McrDcimvac(machInst, miscReg, rt, iss);
+          case MISCREG_DCCMVAC:
+            return new McrDccmvac(machInst, miscReg, rt, iss);
+          case MISCREG_DCCMVAU:
+            return new McrDccmvau(machInst, miscReg, rt, iss);
+          case MISCREG_DCCIMVAC:
+            return new McrDccimvac(machInst, miscReg, rt, iss);
           default:
             if (miscRegInfo[miscReg][MISCREG_WARN_NOT_FAIL]) {
                 std::string full_mnem = csprintf("%s %s",
diff --git a/src/arch/arm/isa/insts/misc.isa b/src/arch/arm/isa/insts/misc.isa
index 80ad6cd..ef0a670 100644
--- a/src/arch/arm/isa/insts/misc.isa
+++ b/src/arch/arm/isa/insts/misc.isa
@@ -1063,6 +1063,106 @@
     decoder_output += BasicConstructor.subst(clrexIop)
     exec_output += PredOpExecute.subst(clrexIop)
 
+    McrDcCheckCode = '''
+        int preFlatDest = flattenMiscRegNsBanked(dest, xc->tcBase());
+        MiscRegIndex miscReg = (MiscRegIndex) xc->tcBase()->flattenRegId(
+            RegId(MiscRegClass, preFlatDest)).index();
+        bool hypTrap  = mcrMrc15TrapToHyp(miscReg, Hcr, Cpsr, Scr, Hdcr, Hstr,
+                                          Hcptr, imm);
+        bool can_write, undefined;
+        std::tie(can_write, undefined) = canWriteCoprocReg(miscReg, Scr, Cpsr);
+
+        // if we're in non secure PL1 mode then we can trap regardless
+        // of whether the register is accessible, in other modes we
+        // trap if only if the register IS accessible.
+        if (undefined || (!can_write & !(hypTrap & !inUserMode(Cpsr) &
+                                         !inSecureState(Scr, Cpsr)))) {
+            return std::make_shared<UndefinedInstruction>(machInst, false,
+                                                          mnemonic);
+        }
+        if (hypTrap) {
+            return std::make_shared<HypervisorTrap>(machInst, imm,
+                                                    EC_TRAPPED_CP15_MCR_MRC);
+        }
+    '''
+
+    McrDcimvacCode = '''
+        const Request::Flags memAccessFlags(ArmISA::TLB::MustBeOne |
+                                            Request::INVALIDATE |
+                                            Request::DST_POC);
+        EA = Op1;
+    '''
+    McrDcimvacIop = InstObjParams("mcr dcimvac", "McrDcimvac",
+                                  "MiscRegRegImmMemOp",
+                                  {"memacc_code": McrDcCheckCode,
+                                   "postacc_code": "",
+                                   "ea_code": McrDcimvacCode,
+                                   "predicate_test": predicateTest},
+                                ['IsMemRef', 'IsStore'])
+    header_output += MiscRegRegImmMemOpDeclare.subst(McrDcimvacIop)
+    decoder_output += MiscRegRegImmOpConstructor.subst(McrDcimvacIop)
+    exec_output += Mcr15Execute.subst(McrDcimvacIop) + \
+                   Mcr15InitiateAcc.subst(McrDcimvacIop) + \
+                   Mcr15CompleteAcc.subst(McrDcimvacIop)
+
+    McrDccmvacCode = '''
+        const Request::Flags memAccessFlags(ArmISA::TLB::MustBeOne |
+                                            Request::CLEAN |
+                                            Request::DST_POC);
+        EA = Op1;
+    '''
+    McrDccmvacIop = InstObjParams("mcr dccmvac", "McrDccmvac",
+                                  "MiscRegRegImmMemOp",
+                                  {"memacc_code": McrDcCheckCode,
+                                   "postacc_code": "",
+                                   "ea_code": McrDccmvacCode,
+                                   "predicate_test": predicateTest},
+                                ['IsMemRef', 'IsStore'])
+    header_output += MiscRegRegImmMemOpDeclare.subst(McrDccmvacIop)
+    decoder_output += MiscRegRegImmOpConstructor.subst(McrDccmvacIop)
+    exec_output += Mcr15Execute.subst(McrDccmvacIop) + \
+                   Mcr15InitiateAcc.subst(McrDccmvacIop) + \
+                   Mcr15CompleteAcc.subst(McrDccmvacIop)
+
+    McrDccmvauCode = '''
+        const Request::Flags memAccessFlags(ArmISA::TLB::MustBeOne |
+                                            Request::CLEAN |
+                                            Request::DST_POU);
+        EA = Op1;
+    '''
+    McrDccmvauIop = InstObjParams("mcr dccmvau", "McrDccmvau",
+                                  "MiscRegRegImmMemOp",
+                                  {"memacc_code": McrDcCheckCode,
+                                   "postacc_code": "",
+                                   "ea_code": McrDccmvauCode,
+                                   "predicate_test": predicateTest},
+                                ['IsMemRef', 'IsStore'])
+    header_output += MiscRegRegImmMemOpDeclare.subst(McrDccmvauIop)
+    decoder_output += MiscRegRegImmOpConstructor.subst(McrDccmvauIop)
+    exec_output += Mcr15Execute.subst(McrDccmvauIop) + \
+                   Mcr15InitiateAcc.subst(McrDccmvauIop) + \
+                   Mcr15CompleteAcc.subst(McrDccmvauIop)
+
+    McrDccimvacCode = '''
+        const Request::Flags memAccessFlags(ArmISA::TLB::MustBeOne |
+                                            Request::CLEAN |
+                                            Request::INVALIDATE |
+                                            Request::DST_POC);
+        EA = Op1;
+    '''
+    McrDccimvacIop = InstObjParams("mcr dccimvac", "McrDccimvac",
+                                  "MiscRegRegImmMemOp",
+                                  {"memacc_code": McrDcCheckCode,
+                                   "postacc_code": "",
+                                   "ea_code": McrDccimvacCode,
+                                   "predicate_test": predicateTest},
+                                ['IsMemRef', 'IsStore'])
+    header_output += MiscRegRegImmMemOpDeclare.subst(McrDccimvacIop)
+    decoder_output += MiscRegRegImmOpConstructor.subst(McrDccimvacIop)
+    exec_output += Mcr15Execute.subst(McrDccimvacIop) + \
+                   Mcr15InitiateAcc.subst(McrDccimvacIop) + \
+                   Mcr15CompleteAcc.subst(McrDccimvacIop)
+
     isbCode = '''
         // If the barrier is due to a CP15 access check for hyp traps
         if ((imm != 0) && mcrMrc15TrapToHyp(MISCREG_CP15ISB, Hcr, Cpsr, Scr,
diff --git a/src/arch/arm/isa/templates/misc.isa b/src/arch/arm/isa/templates/misc.isa
index 0a23ba5..639ff3a 100644
--- a/src/arch/arm/isa/templates/misc.isa
+++ b/src/arch/arm/isa/templates/misc.isa
@@ -1,6 +1,6 @@
 // -*- mode:c++ -*-
 
-// Copyright (c) 2010-2013 ARM Limited
+// Copyright (c) 2010-2013,2017 ARM Limited
 // All rights reserved
 //
 // The license below extends only to copyright in the software and shall
@@ -614,3 +614,84 @@
     }
 }};
 
+def template MiscRegRegImmMemOpDeclare {{
+    class %(class_name)s : public %(base_class)s
+    {
+    protected:
+    public:
+      // Constructor
+      %(class_name)s(ExtMachInst machInst,
+                     MiscRegIndex _dest, IntRegIndex _op1,
+                     uint64_t _imm);
+      Fault execute(ExecContext *, Trace::InstRecord *) const;
+      Fault initiateAcc(ExecContext *, Trace::InstRecord *) const;
+      Fault completeAcc(PacketPtr, ExecContext *, Trace::InstRecord *) const;
+    };
+}};
+
+def template Mcr15Execute {{
+    Fault %(class_name)s::execute(ExecContext *xc,
+                                  Trace::InstRecord *traceData) const
+    {
+        Addr EA;
+        Fault fault = NoFault;
+
+        %(op_decl)s;
+        %(op_rd)s;
+        %(ea_code)s;
+
+        if (%(predicate_test)s) {
+            if (fault == NoFault) {
+                %(memacc_code)s;
+            }
+
+            if (fault == NoFault) {
+                Addr size = 64;
+                EA &= ~(size - 1);
+                fault = xc->writeMem(NULL, size, EA, memAccessFlags, NULL);
+            }
+        } else {
+            xc->setPredicate(false);
+        }
+
+        return fault;
+    }
+}};
+
+def template Mcr15InitiateAcc {{
+    Fault %(class_name)s::initiateAcc(ExecContext *xc,
+                                      Trace::InstRecord *traceData) const
+    {
+        Addr EA;
+        Fault fault = NoFault;
+
+        %(op_decl)s;
+        %(op_rd)s;
+        %(ea_code)s;
+
+        if (%(predicate_test)s) {
+            if (fault == NoFault) {
+                %(memacc_code)s;
+            }
+
+            if (fault == NoFault) {
+                Addr size = 64;
+                EA &= ~(size - 1);
+                fault = xc->writeMem(NULL, size, EA, memAccessFlags, NULL);
+            }
+        } else {
+            xc->setPredicate(false);
+        }
+
+        return fault;
+    }
+}};
+
+def template Mcr15CompleteAcc {{
+    Fault %(class_name)s::completeAcc(PacketPtr pkt,
+                                      ExecContext *xc,
+                                      Trace::InstRecord *traceData) const
+    {
+        return NoFault;
+    }
+}};