arch, arm: Preserve TLB bootUncacheability when switching CPUs
authorGeoffrey Blake <Geoffrey.Blake@arm.com>
Fri, 9 May 2014 22:58:47 +0000 (18:58 -0400)
committerGeoffrey Blake <Geoffrey.Blake@arm.com>
Fri, 9 May 2014 22:58:47 +0000 (18:58 -0400)
The ARM TLBs have a bootUncacheability flag used to make some loads
and stores become uncacheable when booting in FS mode. Later the
flag is cleared to let those loads and stores operate as normal.  When
doing a takeOverFrom(), this flag's state is not preserved and is
momentarily reset until the CPSR is touched. On single core runs this
is a non-issue. On multi-core runs this can lead to crashes on the O3
CPU model from the following series of events:
 1) takeOverFrom executed to switch from Atomic -> O3
 2) All bootUncacheability flags are reset to true
 3) Core2 tries to execute a load covered by bootUncacheability, it
    is flagged as uncacheable
 4) Core2's load needs to replay due to a pipeline flush
 3) Core1 core does an action on CPSR
 4) The handling code for CPSR then checks all other cores
    to determine if bootUncacheability can be set to false
 5) Asynchronously set bootUncacheability on all cores to false
 6) Core2 replays load previously set as uncacheable and notices
    it is now flagged as cacheable, leads to a panic.
This patch implements takeOverFrom() functionality for the ARM TLBs
to preserve flag values when switching from atomic -> detailed.

src/arch/alpha/tlb.hh
src/arch/arm/tlb.cc
src/arch/arm/tlb.hh
src/arch/mips/tlb.hh
src/arch/power/tlb.hh
src/arch/sparc/tlb.hh
src/arch/x86/tlb.hh
src/cpu/base.cc
src/sim/tlb.hh

index 3300e57610b247f8083191f089ffb8eff83edb26..91394e9728176d8c06e65c5627f60a95c83eb45b 100644 (file)
@@ -87,6 +87,8 @@ class TLB : public BaseTLB
     TLB(const Params *p);
     virtual ~TLB();
 
+    void takeOverFrom(BaseTLB *otlb) {}
+
     virtual void regStats();
 
     int getsize() const { return size; }
index 037f7490e63592f8bc092f650d677b2e94c26cd5..37cf9b14944c8dd3ac5059da75d3c2a2c7f13dcb 100644 (file)
@@ -353,6 +353,30 @@ TLB::drainResume()
     miscRegValid = false;
 }
 
+void
+TLB::takeOverFrom(BaseTLB *_otlb)
+{
+    TLB *otlb = dynamic_cast<TLB*>(_otlb);
+    /* Make sure we actually have a valid type */
+    if (otlb) {
+        _attr = otlb->_attr;
+        haveLPAE = otlb->haveLPAE;
+        directToStage2 = otlb->directToStage2;
+        stage2Req = otlb->stage2Req;
+        bootUncacheability = otlb->bootUncacheability;
+
+        /* Sync the stage2 MMU if they exist in both
+         * the old CPU and the new
+         */
+        if (!isStage2 &&
+            stage2Tlb && otlb->stage2Tlb) {
+            stage2Tlb->takeOverFrom(otlb->stage2Tlb);
+        }
+    } else {
+        panic("Incompatible TLB type!");
+    }
+}
+
 void
 TLB::serialize(ostream &os)
 {
index ac8c672bffc2e56ebe24bfb231265bd9bd394133..b9025fa5f42ff8ff3620fa0297b23921a8656822 100644 (file)
@@ -155,6 +155,8 @@ class TLB : public BaseTLB
 
     virtual ~TLB();
 
+    void takeOverFrom(BaseTLB *otlb);
+
     /// setup all the back pointers
     virtual void init();
 
index fdd590e85064cf76852336c377604f8b3c40f8c9..706a96ff0eafa3f6ca7d3c34ddf65429eaede0e3 100644 (file)
@@ -87,6 +87,9 @@ class TLB : public BaseTLB
     int probeEntry(Addr vpn,uint8_t) const;
     MipsISA::PTE *getEntry(unsigned) const;
     virtual ~TLB();
+
+    void takeOverFrom(BaseTLB *otlb) {}
+
     int smallPages;
     int getsize() const { return size; }
 
index 753231a89dbed4b25b599bee4f897b25b7e39a3f..b18956b076015a97539e95e4edeba141b595dbc6 100644 (file)
@@ -130,6 +130,8 @@ class TLB : public BaseTLB
     TLB(const Params *p);
     virtual ~TLB();
 
+    void takeOverFrom(BaseTLB *otlb) {}
+
     int probeEntry(Addr vpn,uint8_t) const;
     PowerISA::PTE *getEntry(unsigned) const;
 
index e084f665cabd6794703f9c780282c0879a1ea288..e29c5171a47f7007010bb10a5ea76fff90f37c6e 100644 (file)
@@ -154,6 +154,8 @@ class TLB : public BaseTLB
     typedef SparcTLBParams Params;
     TLB(const Params *p);
 
+    void takeOverFrom(BaseTLB *otlb) {}
+
     void
     demapPage(Addr vaddr, uint64_t asn)
     {
index ea2d50ec2bc90886ffd454a9f2f2013335c24364..6b65b6f37b3ed2fc3f682e2c3c20cabd9b1d93d2 100644 (file)
@@ -75,6 +75,8 @@ namespace X86ISA
         typedef X86TLBParams Params;
         TLB(const Params *p);
 
+        void takeOverFrom(BaseTLB *otlb) {}
+
         TlbEntry *lookup(Addr va, bool update_lru = true);
 
         void setConfigAddress(uint32_t addr);
index 9057856316860cd62ac0a900b9570255585e8f35..c8c8ac57171c3e77ab9169cc41073912121cf864 100644 (file)
@@ -432,6 +432,8 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU)
             old_dtb_port->unbind();
             new_dtb_port->bind(slavePort);
         }
+        newTC->getITBPtr()->takeOverFrom(oldTC->getITBPtr());
+        newTC->getDTBPtr()->takeOverFrom(oldTC->getDTBPtr());
 
         // Checker whether or not we have to transfer CheckerCPU
         // objects over in the switch
@@ -447,6 +449,9 @@ BaseCPU::takeOverFrom(BaseCPU *oldCPU)
             BaseMasterPort *new_checker_dtb_port =
                 newChecker->getDTBPtr()->getMasterPort();
 
+            newChecker->getITBPtr()->takeOverFrom(oldChecker->getITBPtr());
+            newChecker->getDTBPtr()->takeOverFrom(oldChecker->getDTBPtr());
+
             // Move over any table walker ports if they exist for checker
             if (new_checker_itb_port) {
                 assert(!new_checker_itb_port->isConnected());
index f46c2d856ee911fd92f648030084580775803d11..397d6e0f2ddec47aefd38868bd446dd181822827 100644 (file)
@@ -69,6 +69,11 @@ class BaseTLB : public SimObject
      */
     virtual void flushAll() = 0;
 
+    /**
+     * Take over from an old tlb context
+     */
+    virtual void takeOverFrom(BaseTLB *otlb) = 0;
+
     /**
      * Get the table walker master port if present. This is used for
      * migrating port connections during a CPU takeOverFrom()