arm: Handle functional TLB walks properly
authorGeoffrey Blake <Geoffrey.Blake@arm.com>
Fri, 7 Mar 2014 20:56:23 +0000 (15:56 -0500)
committerGeoffrey Blake <Geoffrey.Blake@arm.com>
Fri, 7 Mar 2014 20:56:23 +0000 (15:56 -0500)
The table walker code currently accounts for two types of walks,
Atomic and Timing, and treats them differently. Atomic walks keep a
single instance of WalkerState around for all walks to use in
currState. Timing mode keeps a queue of in-flight WalkerStates and
maintains currState as NULL between walks.

If a functional walk is done during Timing mode, it is treated as an
atomic walk and either creates a persistent WalkerState if in between
Timing walks, or stomps an existing currState for an in-progress
Timing walk.

This patch distinguishes functional walks as being able to exist at
any time and sets up a temporary WalkerState for its exclusive use and
then cleans up when finished, leaving any in progress Atomic or Timing
walks undisturbed.

src/arch/arm/table_walker.cc

index 7eda13b3e53ae0497bdda848bd254c8136e82149..ea2865aebc6c9aef468e22e945d031a400fe8472 100644 (file)
@@ -160,13 +160,22 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
                   bool secure, TLB::ArmTranslationType tranType)
 {
     assert(!(_functional && _timing));
+    WalkerState *savedCurrState = NULL;
 
-    if (!currState) {
+    if (!currState && !_functional) {
         // For atomic mode, a new WalkerState instance should be only created
         // once per TLB. For timing mode, a new instance is generated for every
         // TLB miss.
         DPRINTF(TLBVerbose, "creating new instance of WalkerState\n");
 
+        currState = new WalkerState();
+        currState->tableWalker = this;
+    } else if (_functional) {
+        // If we are mixing functional mode with timing (or even
+        // atomic), we need to to be careful and clean up after
+        // ourselves to not risk getting into an inconsistent state.
+        DPRINTF(TLBVerbose, "creating functional instance of WalkerState\n");
+        savedCurrState = currState;
         currState = new WalkerState();
         currState->tableWalker = this;
     } else if (_timing) {
@@ -264,12 +273,21 @@ TableWalker::walk(RequestPtr _req, ThreadContext *_tc, uint16_t _asid,
     }
 
     if (!currState->timing) {
+        Fault fault = NoFault;
         if (currState->aarch64)
-            return processWalkAArch64();
+            fault = processWalkAArch64();
         else if (long_desc_format)
-            return processWalkLPAE();
+            fault = processWalkLPAE();
         else
-            return processWalk();
+            fault = processWalk();
+
+        // If this was a functional non-timing access restore state to
+        // how we found it.
+        if (currState->functional) {
+            delete currState;
+            currState = savedCurrState;
+        }
+        return fault;
     }
 
     if (pending || pendingQueue.size()) {