arch-riscv: Add xret instructions

This patch adds the uret, sret, and mret instructions for use with
returning from user-, supervisor-, and machine-level code, respectively.
These instructions read the STATUS register to determine the previous
privilege level and modify it to re-enable interrupts at the old
privilege level. These instructions can only be executed at the
corresponding privilege level or higher.

Change-Id: I6125c31cb2fdcc3f83eca86910519e81ffbbbfc9
Reviewed-on: https://gem5-review.googlesource.com/11136
Maintainer: Alec Roelke <alec.roelke@gmail.com>
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Robert Scheffel <robert.scheffel1@tu-dresden.de>
diff --git a/src/arch/riscv/faults.hh b/src/arch/riscv/faults.hh
index 6d3fdeb..d9cb44c 100644
--- a/src/arch/riscv/faults.hh
+++ b/src/arch/riscv/faults.hh
@@ -34,8 +34,10 @@
 #ifndef __ARCH_RISCV_FAULTS_HH__
 #define __ARCH_RISCV_FAULTS_HH__
 
+#include <map>
 #include <string>
 
+#include "arch/riscv/isa.hh"
 #include "arch/riscv/registers.hh"
 #include "cpu/thread_context.hh"
 #include "sim/faults.hh"
@@ -71,59 +73,12 @@
     AMO_PAGE = 15
 };
 
-/**
- * These fields are specified in the RISC-V Instruction Set Manual, Volume II,
- * v1.10, accessible at www.riscv.org. in Figure 3.7. The main register that
- * uses these fields is the MSTATUS register, which is shadowed by two others
- * accessible at lower privilege levels (SSTATUS and USTATUS) that can't see
- * the fields for higher privileges.
- */
-BitUnion64(STATUS)
-    Bitfield<63> sd;
-    Bitfield<35, 34> sxl;
-    Bitfield<33, 32> uxl;
-    Bitfield<22> tsr;
-    Bitfield<21> tw;
-    Bitfield<20> tvm;
-    Bitfield<19> mxr;
-    Bitfield<18> sum;
-    Bitfield<17> mprv;
-    Bitfield<16, 15> xs;
-    Bitfield<14, 13> fs;
-    Bitfield<12, 11> mpp;
-    Bitfield<8> spp;
-    Bitfield<7> mpie;
-    Bitfield<5> spie;
-    Bitfield<4> upie;
-    Bitfield<3> mie;
-    Bitfield<1> sie;
-    Bitfield<0> uie;
-EndBitUnion(STATUS)
-
-/**
- * These fields are specified in the RISC-V Instruction Set Manual, Volume II,
- * v1.10 in Figures 3.11 and 3.12, accessible at www.riscv.org. Both the MIP
- * and MIE registers have the same fields, so accesses to either should use
- * this bit union.
- */
-BitUnion64(INTERRUPT)
-    Bitfield<11> mei;
-    Bitfield<9> sei;
-    Bitfield<8> uei;
-    Bitfield<7> mti;
-    Bitfield<5> sti;
-    Bitfield<4> uti;
-    Bitfield<3> msi;
-    Bitfield<1> ssi;
-    Bitfield<0> usi;
-EndBitUnion(INTERRUPT)
-
 class RiscvFault : public FaultBase
 {
   protected:
     const FaultName _name;
-    bool _interrupt;
-    const ExceptionCode _code;
+    const bool _interrupt;
+    ExceptionCode _code;
 
     RiscvFault(FaultName n, bool i, ExceptionCode c)
         : _name(n), _interrupt(i), _code(c)
@@ -254,12 +209,28 @@
 class SyscallFault : public RiscvFault
 {
   public:
-    // TODO: replace ECALL_USER with the appropriate privilege level of the
-    // caller
-    SyscallFault() : RiscvFault("System call", false, ECALL_USER) {}
+    SyscallFault(PrivilegeMode prv)
+        : RiscvFault("System call", false, ECALL_USER)
+    {
+        switch (prv) {
+          case PRV_U:
+            _code = ECALL_USER;
+            break;
+          case PRV_S:
+            _code = ECALL_SUPER;
+            break;
+          case PRV_M:
+            _code = ECALL_MACHINE;
+            break;
+          default:
+            panic("Unknown privilege mode %d.", prv);
+            break;
+        }
+    }
+
     void invokeSE(ThreadContext *tc, const StaticInstPtr &inst) override;
 };
 
 } // namespace RiscvISA
 
-#endif // __ARCH_RISCV_FAULTS_HH__
\ No newline at end of file
+#endif // __ARCH_RISCV_FAULTS_HH__
diff --git a/src/arch/riscv/isa/decoder.isa b/src/arch/riscv/isa/decoder.isa
index a0e3ad1..0c1d772 100644
--- a/src/arch/riscv/isa/decoder.isa
+++ b/src/arch/riscv/isa/decoder.isa
@@ -1708,16 +1708,50 @@
             format SystemOp {
                 0x0: decode FUNCT12 {
                     0x0: ecall({{
-                        fault = make_shared<SyscallFault>();
+                        fault = make_shared<SyscallFault>(
+                                (PrivilegeMode)xc->readMiscReg(MISCREG_PRV));
                     }}, IsSerializeAfter, IsNonSpeculative, IsSyscall,
                         No_OpClass);
                     0x1: ebreak({{
                         fault = make_shared<BreakpointFault>(xc->pcState());
                     }}, IsSerializeAfter, IsNonSpeculative, No_OpClass);
-                    0x100: eret({{
-                        fault = make_shared<UnimplementedFault>("eret",
-                                                                machInst);
-                    }}, No_OpClass);
+                    0x2: uret({{
+                        STATUS status = xc->readMiscReg(MISCREG_STATUS);
+                        status.uie = status.upie;
+                        status.upie = 1;
+                        xc->setMiscReg(MISCREG_STATUS, status);
+                        NPC = xc->readMiscReg(MISCREG_UEPC);
+                    }}, IsReturn);
+                    0x102: sret({{
+                        if (xc->readMiscReg(MISCREG_PRV) == PRV_U) {
+                            fault = make_shared<IllegalInstFault>(
+                                        "sret in user mode", machInst);
+                            NPC = NPC;
+                        } else {
+                            STATUS status = xc->readMiscReg(MISCREG_STATUS);
+                            xc->setMiscReg(MISCREG_PRV, status.spp);
+                            status.sie = status.spie;
+                            status.spie = 1;
+                            status.spp = PRV_U;
+                            xc->setMiscReg(MISCREG_STATUS, status);
+                            NPC = xc->readMiscReg(MISCREG_SEPC);
+                        }
+                    }}, IsReturn);
+                    0x302: mret({{
+                        if (xc->readMiscReg(MISCREG_PRV) != PRV_M) {
+                            fault = make_shared<IllegalInstFault>(
+                                        "mret at lower privilege", machInst);
+                            NPC = NPC;
+                        } else {
+                            STATUS status = xc->readMiscReg(MISCREG_STATUS);
+                            xc->setMiscReg(MISCREG_PRV, status.mpp);
+                            status.mie = status.mpie;
+                            status.mpie = 1;
+                            status.mpp = PRV_U;
+                            xc->setMiscReg(MISCREG_STATUS, status);
+                            NPC = xc->readMiscReg(MISCREG_MEPC);
+                        }
+                    }}, IsReturn);
                 }
             }
             format CSROp {
diff --git a/src/arch/riscv/registers.hh b/src/arch/riscv/registers.hh
index e1d57ee..bd95cf8 100644
--- a/src/arch/riscv/registers.hh
+++ b/src/arch/riscv/registers.hh
@@ -583,6 +583,53 @@
     {CSR_DSCRATCH, {"dscratch", MISCREG_DSCRATCH}}
 };
 
+/**
+ * These fields are specified in the RISC-V Instruction Set Manual, Volume II,
+ * v1.10, accessible at www.riscv.org. in Figure 3.7. The main register that
+ * uses these fields is the MSTATUS register, which is shadowed by two others
+ * accessible at lower privilege levels (SSTATUS and USTATUS) that can't see
+ * the fields for higher privileges.
+ */
+BitUnion64(STATUS)
+    Bitfield<63> sd;
+    Bitfield<35, 34> sxl;
+    Bitfield<33, 32> uxl;
+    Bitfield<22> tsr;
+    Bitfield<21> tw;
+    Bitfield<20> tvm;
+    Bitfield<19> mxr;
+    Bitfield<18> sum;
+    Bitfield<17> mprv;
+    Bitfield<16, 15> xs;
+    Bitfield<14, 13> fs;
+    Bitfield<12, 11> mpp;
+    Bitfield<8> spp;
+    Bitfield<7> mpie;
+    Bitfield<5> spie;
+    Bitfield<4> upie;
+    Bitfield<3> mie;
+    Bitfield<1> sie;
+    Bitfield<0> uie;
+EndBitUnion(STATUS)
+
+/**
+ * These fields are specified in the RISC-V Instruction Set Manual, Volume II,
+ * v1.10 in Figures 3.11 and 3.12, accessible at www.riscv.org. Both the MIP
+ * and MIE registers have the same fields, so accesses to either should use
+ * this bit union.
+ */
+BitUnion64(INTERRUPT)
+    Bitfield<11> mei;
+    Bitfield<9> sei;
+    Bitfield<8> uei;
+    Bitfield<7> mti;
+    Bitfield<5> sti;
+    Bitfield<4> uti;
+    Bitfield<3> msi;
+    Bitfield<1> ssi;
+    Bitfield<0> usi;
+EndBitUnion(INTERRUPT)
+
 const off_t MXL_OFFSET = (sizeof(MiscReg) * 8 - 2);
 const off_t SXL_OFFSET = 34;
 const off_t UXL_OFFSET = 32;