mem: Fix guest corruption when caches handle uncacheable accesses
authorAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:47 +0000 (13:05 -0500)
committerAndreas Sandberg <Andreas.Sandberg@ARM.com>
Mon, 7 Jan 2013 18:05:47 +0000 (13:05 -0500)
When the classic gem5 cache sees an uncacheable memory access, it used
to ignore it or silently drop the cache line in case of a
write. Normally, there shouldn't be any data in the cache belonging to
an uncacheable address range. However, since some architecture models
don't implement cache maintenance instructions, there might be some
dirty data in the cache that is discarded when this happens. The
reason it has mostly worked before is because such cache lines were
most likely evicted by normal memory activity before a TLB flush was
requested by the OS.

Previously, the cache model would invalidate cache lines when they
were accessed by an uncacheable write. This changeset alters this
behavior so all uncacheable memory accesses cause a cache flush with
an associated writeback if necessary. This is implemented by reusing
the cache flushing machinery used when draining the cache, which
implies that writebacks are performed using functional accesses.

src/mem/cache/cache.hh
src/mem/cache/cache_impl.hh

index 5df725683e0ad1fe1235cb7a9d07baf9bf68c7bd..6b062ef40f6d737d739f8627445175a3e997eca4 100644 (file)
@@ -278,6 +278,18 @@ class Cache : public BaseCache
      */
     bool invalidateVisitor(BlkType &blk);
 
+    /**
+     * Flush a cache line due to an uncacheable memory access to the
+     * line.
+     *
+     * @note This shouldn't normally happen, but we need to handle it
+     * since some architecture models don't implement cache
+     * maintenance operations. We won't even try to get a decent
+     * timing here since the line should have been flushed earlier by
+     * a cache maintenance operation.
+     */
+    void uncacheableFlush(PacketPtr pkt);
+
   public:
     /** Instantiates a basic cache object. */
     Cache(const Params *p, TagStore *tags);
index fc01e2c061ccb6e2e75d54216981bbd85fa28ad9..a1d945103d70d72dbfe72839b66511ea0aa538ef 100644 (file)
@@ -279,16 +279,7 @@ Cache<TagStore>::access(PacketPtr pkt, BlkType *&blk,
                         Cycles &lat, PacketList &writebacks)
 {
     if (pkt->req->isUncacheable()) {
-        if (pkt->req->isClearLL()) {
-            tags->clearLocks();
-        } else if (pkt->isWrite()) {
-           blk = tags->findBlock(pkt->getAddr());
-           if (blk != NULL) {
-               tags->invalidate(blk);
-               blk->invalidate();
-           }
-        }
-
+        uncacheableFlush(pkt);
         blk = NULL;
         lat = hitLatency;
         return false;
@@ -444,15 +435,7 @@ Cache<TagStore>::timingAccess(PacketPtr pkt)
     }
 
     if (pkt->req->isUncacheable()) {
-        if (pkt->req->isClearLL()) {
-            tags->clearLocks();
-        } else if (pkt->isWrite()) {
-            BlkType *blk = tags->findBlock(pkt->getAddr());
-            if (blk != NULL) {
-                tags->invalidate(blk);
-                blk->invalidate();
-            }
-        }
+        uncacheableFlush(pkt);
 
         // writes go in write buffer, reads use MSHR
         if (pkt->isWrite() && !pkt->isRead()) {
@@ -1103,6 +1086,25 @@ Cache<TagStore>::invalidateVisitor(BlkType &blk)
     return true;
 }
 
+template<class TagStore>
+void
+Cache<TagStore>::uncacheableFlush(PacketPtr pkt)
+{
+    DPRINTF(Cache, "%s%s %x uncacheable\n", pkt->cmdString(),
+            pkt->req->isInstFetch() ? " (ifetch)" : "",
+            pkt->getAddr());
+
+    if (pkt->req->isClearLL())
+        tags->clearLocks();
+
+    BlkType *blk(tags->findBlock(pkt->getAddr()));
+    if (blk) {
+        writebackVisitor(*blk);
+        invalidateVisitor(*blk);
+    }
+}
+
+
 template<class TagStore>
 typename Cache<TagStore>::BlkType*
 Cache<TagStore>::allocateBlock(Addr addr, PacketList &writebacks)