arch-riscv: Fixed CPU switching and PLIC issue with MinorCPU

Added takeover methods for PMA Checker and RiscvTLB to ensure
that checkpoint restoration works. Also added logic in PLIC
to prevent posting interrupts to a CPU that has yet to complete
the current interrupt. PLIC's behaviour when a CPU claims another
interrupt before completion is also changed. Now PLIC will return
the uncompleted interrupt ID instead of return 0. This behaviour
is not documented in the specs but is designed this way to avoid
issues from CPU side (especially MinorCPU).

Change-Id: I68eaaf56d2c4d76cc1e0a1e2160f5abe184c2cd5
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/41933
Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Ayaz Akram <yazakram@ucdavis.edu>
diff --git a/src/arch/generic/mmu.hh b/src/arch/generic/mmu.hh
index 9765273..79e53dc 100644
--- a/src/arch/generic/mmu.hh
+++ b/src/arch/generic/mmu.hh
@@ -103,7 +103,7 @@
         return getTlb(mode)->finalizePhysical(req, tc, mode);
     }
 
-    void takeOverFrom(BaseMMU *old_mmu);
+    virtual void takeOverFrom(BaseMMU *old_mmu);
 
   public:
     BaseTLB* dtb;
diff --git a/src/arch/riscv/RiscvMMU.py b/src/arch/riscv/RiscvMMU.py
index 4ff477e..38f1da9 100644
--- a/src/arch/riscv/RiscvMMU.py
+++ b/src/arch/riscv/RiscvMMU.py
@@ -35,6 +35,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+from m5.params import *
+
 from m5.objects.BaseMMU import BaseMMU
 from m5.objects.RiscvTLB import RiscvTLB
 from m5.objects.PMAChecker import PMAChecker
@@ -45,7 +47,7 @@
     cxx_header = 'arch/riscv/mmu.hh'
     itb = RiscvTLB()
     dtb = RiscvTLB()
-    pma_checker = PMAChecker()
+    pma_checker = Param.PMAChecker(PMAChecker(), "PMA Checker")
 
     @classmethod
     def walkerPorts(cls):
diff --git a/src/arch/riscv/RiscvTLB.py b/src/arch/riscv/RiscvTLB.py
index b419262..05ff521 100644
--- a/src/arch/riscv/RiscvTLB.py
+++ b/src/arch/riscv/RiscvTLB.py
@@ -43,7 +43,7 @@
     num_squash_per_cycle = Param.Unsigned(4,
             "Number of outstanding walks that can be squashed per cycle")
     # Grab the pma_checker from the MMU
-    pma_checker = Param.PMAChecker(Parent.any, "PMA Chekcer")
+    pma_checker = Param.PMAChecker(Parent.any, "PMA Checker")
 
 class RiscvTLB(BaseTLB):
     type = 'RiscvTLB'
@@ -53,4 +53,4 @@
     walker = Param.RiscvPagetableWalker(\
             RiscvPagetableWalker(), "page table walker")
     # Grab the pma_checker from the MMU
-    pma_checker = Param.PMAChecker(Parent.any, "PMA Chekcer")
+    pma_checker = Param.PMAChecker(Parent.any, "PMA Checker")
diff --git a/src/arch/riscv/mmu.hh b/src/arch/riscv/mmu.hh
index 322f0af..ce3ce30 100644
--- a/src/arch/riscv/mmu.hh
+++ b/src/arch/riscv/mmu.hh
@@ -40,6 +40,7 @@
 
 #include "arch/generic/mmu.hh"
 #include "arch/riscv/isa.hh"
+#include "arch/riscv/pma_checker.hh"
 #include "arch/riscv/tlb.hh"
 
 #include "params/RiscvMMU.hh"
@@ -49,8 +50,10 @@
 class MMU : public BaseMMU
 {
   public:
+    PMAChecker *pma;
+
     MMU(const RiscvMMUParams &p)
-      : BaseMMU(p)
+      : BaseMMU(p), pma(p.pma_checker)
     {}
 
     PrivilegeMode
@@ -64,6 +67,14 @@
     {
         return static_cast<TLB*>(dtb)->getWalker();
     }
+
+    void
+    takeOverFrom(BaseMMU *old_mmu) override
+    {
+      MMU *ommu = dynamic_cast<MMU*>(old_mmu);
+      BaseMMU::takeOverFrom(ommu);
+      pma->takeOverFrom(ommu->pma);
+    }
 };
 
 } // namespace RiscvISA
diff --git a/src/arch/riscv/pma_checker.cc b/src/arch/riscv/pma_checker.cc
index 32cb66d..d36dc1d 100644
--- a/src/arch/riscv/pma_checker.cc
+++ b/src/arch/riscv/pma_checker.cc
@@ -81,3 +81,9 @@
 {
     return isUncacheable(pkt->getAddrRange());
 }
+
+void
+PMAChecker::takeOverFrom(PMAChecker *old)
+{
+    uncacheable = old->uncacheable;
+}
diff --git a/src/arch/riscv/pma_checker.hh b/src/arch/riscv/pma_checker.hh
index 5833dbe..298d4a0 100644
--- a/src/arch/riscv/pma_checker.hh
+++ b/src/arch/riscv/pma_checker.hh
@@ -74,6 +74,8 @@
     bool isUncacheable(const AddrRange &range);
     bool isUncacheable(const Addr &addr, const unsigned size);
     bool isUncacheable(PacketPtr pkt);
+
+    void takeOverFrom(PMAChecker *old);
 };
 
 #endif // __ARCH_RISCV_PMA_CHECKER_HH__
diff --git a/src/arch/riscv/tlb.cc b/src/arch/riscv/tlb.cc
index 5109d2a..b7b0984 100644
--- a/src/arch/riscv/tlb.cc
+++ b/src/arch/riscv/tlb.cc
@@ -519,3 +519,9 @@
              readAccesses + writeAccesses)
 {
 }
+
+Port *
+TLB::getTableWalkerPort()
+{
+    return &walker->getPort("port");
+}
\ No newline at end of file
diff --git a/src/arch/riscv/tlb.hh b/src/arch/riscv/tlb.hh
index 8dcf0fc..9c7172a 100644
--- a/src/arch/riscv/tlb.hh
+++ b/src/arch/riscv/tlb.hh
@@ -92,7 +92,7 @@
 
     Walker *getWalker();
 
-    void takeOverFrom(BaseTLB *otlb) override {}
+    void takeOverFrom(BaseTLB *old) override {}
 
     TlbEntry *insert(Addr vpn, const TlbEntry &entry);
     void flushAll() override;
@@ -108,6 +108,18 @@
     void serialize(CheckpointOut &cp) const override;
     void unserialize(CheckpointIn &cp) override;
 
+    /**
+     * Get the table walker port. This is used for
+     * migrating port connections during a CPU takeOverFrom()
+     * call. For architectures that do not have a table walker,
+     * NULL is returned, hence the use of a pointer rather than a
+     * reference. For RISC-V this method will always return a valid
+     * port pointer.
+     *
+     * @return A pointer to the walker port
+     */
+    Port *getTableWalkerPort() override;
+
     Addr translateWithTLB(Addr vaddr, uint16_t asid, Mode mode);
 
     Fault translateAtomic(const RequestPtr &req,
diff --git a/src/dev/riscv/plic.cc b/src/dev/riscv/plic.cc
index 60ac192..2183183 100644
--- a/src/dev/riscv/plic.cc
+++ b/src/dev/riscv/plic.cc
@@ -354,17 +354,18 @@
                 context_id, max_int_id);
             clear(max_int_id);
             reg.update(max_int_id);
+            return reg.get();
         } else {
             DPRINTF(Plic,
                 "Claim already cleared - context: %d, interrupt ID: %d\n",
                 context_id, max_int_id);
-            reg.update(0);
+            return 0;
         }
     } else {
-        warn("PLIC claim failed (not completed) - context: %d", context_id);
-        reg.update(0);
+        warn("PLIC claim repeated (not completed) - context: %d, last: %d",
+            context_id, lastID[context_id]);
+        return lastID[context_id];
     }
-    return reg.get();
 }
 
 void
@@ -381,6 +382,7 @@
     DPRINTF(Plic,
         "Complete - context: %d, interrupt ID: %d\n",
         context_id, reg.get());
+    updateInt();
 }
 
 void
@@ -445,11 +447,11 @@
         uint32_t max_id = output.maxID[i];
         uint32_t priority = output.maxPriority[i];
         uint32_t threshold = registers.threshold[i].get();
-        if (priority > threshold && max_id > 0) {
+        if (priority > threshold && max_id > 0 && lastID[i] == 0) {
             DPRINTF(Plic,
                 "Int posted - thread: %d, int id: %d, ",
                 thread_id, int_id);
-            DPRINTF(Plic,
+            DPRINTFR(Plic,
                 "pri: %d, thres: %d\n", priority, threshold);
             intrctrl->post(thread_id, int_id, 0);
         } else {
@@ -457,7 +459,7 @@
                 DPRINTF(Plic,
                     "Int filtered - thread: %d, int id: %d, ",
                     thread_id, int_id);
-                DPRINTF(Plic,
+                DPRINTFR(Plic,
                     "pri: %d, thres: %d\n", priority, threshold);
             }
             intrctrl->clear(thread_id, int_id, 0);
@@ -499,6 +501,12 @@
     SERIALIZE_SCALAR(n_outputs);
     SERIALIZE_CONTAINER(output.maxID);
     SERIALIZE_CONTAINER(output.maxPriority);
+    SERIALIZE_CONTAINER(pendingPriority);
+    for (int i=0; i < effPriority.size(); i++) {
+        arrayParamOut(cp, std::string("effPriority") +
+            std::to_string(i), effPriority[i]);
+    }
+    SERIALIZE_CONTAINER(lastID);
 }
 
 void
@@ -541,4 +549,11 @@
     }
     UNSERIALIZE_CONTAINER(output.maxID);
     UNSERIALIZE_CONTAINER(output.maxPriority);
+    UNSERIALIZE_CONTAINER(pendingPriority);
+    for (int i=0; i < effPriority.size(); i++) {
+        arrayParamIn(cp, std::string("effPriority") +
+            std::to_string(i), effPriority[i]);
+    }
+    UNSERIALIZE_CONTAINER(lastID);
+    updateInt();
 }