systemc: Teach the TLM bridges how to use gem5's new backdoor mechanism.

This change teaches the TLM bridges to translate between TLM's DMI
mechanism and gem5's backdoor mechanism.

Change-Id: I942a6cce4fb87f10e8173f4ee49b6c7b0ffa7e4a
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/17591
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
diff --git a/src/systemc/tlm_bridge/gem5_to_tlm.cc b/src/systemc/tlm_bridge/gem5_to_tlm.cc
index f78c679..03cce22 100644
--- a/src/systemc/tlm_bridge/gem5_to_tlm.cc
+++ b/src/systemc/tlm_bridge/gem5_to_tlm.cc
@@ -80,32 +80,45 @@
 
 /**
  * Convert a gem5 packet to a TLM payload by copying all the relevant
- * information to a previously allocated tlm payload
+ * information to new tlm payload.
  */
-void
-packet2payload(PacketPtr packet, tlm::tlm_generic_payload &trans)
+tlm::tlm_generic_payload *
+packet2payload(PacketPtr packet)
 {
-    trans.set_address(packet->getAddr());
+    tlm::tlm_generic_payload *trans = mm.allocate();
+    trans->acquire();
+
+    trans->set_address(packet->getAddr());
 
     /* Check if this transaction was allocated by mm */
-    sc_assert(trans.has_mm());
+    sc_assert(trans->has_mm());
 
     unsigned int size = packet->getSize();
     unsigned char *data = packet->getPtr<unsigned char>();
 
-    trans.set_data_length(size);
-    trans.set_streaming_width(size);
-    trans.set_data_ptr(data);
+    trans->set_data_length(size);
+    trans->set_streaming_width(size);
+    trans->set_data_ptr(data);
 
-    if (packet->isRead()) {
-        trans.set_command(tlm::TLM_READ_COMMAND);
+    if ((packet->req->getFlags() & Request::NO_ACCESS) != 0) {
+        /* Do nothing */
+        trans->set_command(tlm::TLM_IGNORE_COMMAND);
+    } else if (packet->isRead()) {
+        trans->set_command(tlm::TLM_READ_COMMAND);
     } else if (packet->isInvalidate()) {
         /* Do nothing */
+        trans->set_command(tlm::TLM_IGNORE_COMMAND);
     } else if (packet->isWrite()) {
-        trans.set_command(tlm::TLM_WRITE_COMMAND);
+        trans->set_command(tlm::TLM_WRITE_COMMAND);
     } else {
         SC_REPORT_FATAL("Gem5ToTlmBridge", "No R/W packet");
     }
+
+    // Attach the packet pointer to the TLM transaction to keep track.
+    auto *extension = new Gem5SystemC::Gem5Extension(packet);
+    trans->set_auto_extension(extension);
+
+    return trans;
 }
 
 template <unsigned int BITWIDTH>
@@ -166,6 +179,37 @@
     delete pe;
 }
 
+template <unsigned int BITWIDTH>
+MemBackdoorPtr
+Gem5ToTlmBridge<BITWIDTH>::getBackdoor(tlm::tlm_generic_payload &trans)
+{
+    sc_dt::uint64 start = trans.get_address();
+    sc_dt::uint64 end = start + trans.get_data_length();
+
+    // Check for a back door we already know about.
+    AddrRange r(start, end);
+    auto it = backdoorMap.contains(r);
+    if (it != backdoorMap.end())
+        return it->second;
+
+    // If not, ask the target for one.
+    tlm::tlm_dmi dmi_data;
+    if (!socket->get_direct_mem_ptr(trans, dmi_data))
+        return nullptr;
+
+    // If the target gave us one, translate it to a gem5 MemBackdoor and
+    // store it in our cache.
+    AddrRange dmi_r(dmi_data.get_start_address(), dmi_data.get_end_address());
+    auto backdoor = new MemBackdoor(
+            dmi_r, dmi_data.get_dmi_ptr(), MemBackdoor::NoAccess);
+    backdoor->readable(dmi_data.is_read_allowed());
+    backdoor->writeable(dmi_data.is_write_allowed());
+
+    backdoorMap.insert(dmi_r, backdoor);
+
+    return backdoor;
+}
+
 // Similar to TLM's blocking transport (LT)
 template <unsigned int BITWIDTH>
 Tick
@@ -174,36 +218,51 @@
     panic_if(packet->cacheResponding(),
              "Should not see packets where cache is responding");
 
-    panic_if(!(packet->isRead() || packet->isWrite()),
-             "Should only see read and writes at TLM memory\n");
+    // Prepare the transaction.
+    auto *trans = packet2payload(packet);
+
+    sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
+
+    if (trans->get_command() != tlm::TLM_IGNORE_COMMAND) {
+        // Execute b_transport:
+        socket->b_transport(*trans, delay);
+    }
+
+    if (packet->needsResponse())
+        packet->makeResponse();
+
+    trans->release();
+
+    return delay.value();
+}
+
+template <unsigned int BITWIDTH>
+Tick
+Gem5ToTlmBridge<BITWIDTH>::recvAtomicBackdoor(
+        PacketPtr packet, MemBackdoorPtr &backdoor)
+{
+    panic_if(packet->cacheResponding(),
+             "Should not see packets where cache is responding");
 
     sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
 
     // Prepare the transaction.
-    tlm::tlm_generic_payload *trans = mm.allocate();
-    trans->acquire();
-    packet2payload(packet, *trans);
+    auto *trans = packet2payload(packet);
 
-    // Attach the packet pointer to the TLM transaction to keep track.
-    auto *extension = new Gem5SystemC::Gem5Extension(packet);
-    trans->set_auto_extension(extension);
-
-    // Execute b_transport:
-    if (packet->cmd == MemCmd::SwapReq) {
-        SC_REPORT_FATAL("Gem5ToTlmBridge", "SwapReq not supported");
-    } else if (packet->isRead()) {
+    if (trans->get_command() != tlm::TLM_IGNORE_COMMAND) {
+        // Execute b_transport:
         socket->b_transport(*trans, delay);
-    } else if (packet->isInvalidate()) {
-        // do nothing
-    } else if (packet->isWrite()) {
-        socket->b_transport(*trans, delay);
+        // If the hint said we could use DMI, set that up.
+        if (trans->is_dmi_allowed())
+            backdoor = getBackdoor(*trans);
     } else {
-        SC_REPORT_FATAL("Gem5ToTlmBridge", "Typo of request not supported");
+        // There's no transaction to piggy back on, so just request the
+        // backdoor normally.
+        backdoor = getBackdoor(*trans);
     }
 
-    if (packet->needsResponse()) {
+    if (packet->needsResponse())
         packet->makeResponse();
-    }
 
     trans->release();
 
@@ -254,13 +313,7 @@
      */
 
     // Prepare the transaction.
-    tlm::tlm_generic_payload *trans = mm.allocate();
-    trans->acquire();
-    packet2payload(packet, *trans);
-
-    // Attach the packet pointer to the TLM transaction to keep track.
-    auto *extension = new Gem5SystemC::Gem5Extension(packet);
-    trans->set_auto_extension(extension);
+    auto *trans = packet2payload(packet);
 
     /*
      * Pay for annotated transport delays.
@@ -362,13 +415,7 @@
 Gem5ToTlmBridge<BITWIDTH>::recvFunctional(PacketPtr packet)
 {
     // Prepare the transaction.
-    tlm::tlm_generic_payload *trans = mm.allocate();
-    trans->acquire();
-    packet2payload(packet, *trans);
-
-    // Attach the packet pointer to the TLM transaction to keep track.
-    auto *extension = new Gem5SystemC::Gem5Extension(packet);
-    trans->set_auto_extension(extension);
+    auto *trans = packet2payload(packet);
 
     /* Execute Debug Transport: */
     unsigned int bytes = socket->transport_dbg(*trans);
@@ -394,6 +441,24 @@
 }
 
 template <unsigned int BITWIDTH>
+void
+Gem5ToTlmBridge<BITWIDTH>::invalidate_direct_mem_ptr(
+        sc_dt::uint64 start_range, sc_dt::uint64 end_range)
+{
+    AddrRange r(start_range, end_range);
+
+    for (;;) {
+        auto it = backdoorMap.intersects(r);
+        if (it == backdoorMap.end())
+            break;
+
+        it->second->invalidate();
+        delete it->second;
+        backdoorMap.erase(it);
+    };
+}
+
+template <unsigned int BITWIDTH>
 Gem5ToTlmBridge<BITWIDTH>::Gem5ToTlmBridge(
         Params *params, const sc_core::sc_module_name &mn) :
     Gem5ToTlmBridgeBase(mn), bsp(std::string(name()) + ".gem5", *this),
@@ -424,6 +489,8 @@
     bsp.sendRangeChange();
 
     socket.register_nb_transport_bw(this, &Gem5ToTlmBridge::nb_transport_bw);
+    socket.register_invalidate_direct_mem_ptr(
+            this, &Gem5ToTlmBridge::invalidate_direct_mem_ptr);
     sc_core::sc_module::before_end_of_elaboration();
 }
 
diff --git a/src/systemc/tlm_bridge/gem5_to_tlm.hh b/src/systemc/tlm_bridge/gem5_to_tlm.hh
index e36058a..182512a 100644
--- a/src/systemc/tlm_bridge/gem5_to_tlm.hh
+++ b/src/systemc/tlm_bridge/gem5_to_tlm.hh
@@ -103,6 +103,11 @@
         {
             return bridge.recvAtomic(pkt);
         }
+        Tick
+        recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor) override
+        {
+            return bridge.recvAtomicBackdoor(pkt, backdoor);
+        }
         void
         recvFunctional(PacketPtr pkt) override
         {
@@ -163,8 +168,12 @@
     void pec(Gem5SystemC::PayloadEvent<Gem5ToTlmBridge<BITWIDTH>> *pe,
              tlm::tlm_generic_payload &trans, const tlm::tlm_phase &phase);
 
+    MemBackdoorPtr getBackdoor(tlm::tlm_generic_payload &trans);
+    AddrRangeMap<MemBackdoorPtr> backdoorMap;
+
     // The gem5 port interface.
     Tick recvAtomic(PacketPtr packet);
+    Tick recvAtomicBackdoor(PacketPtr pkt, MemBackdoorPtr &backdoor);
     void recvFunctional(PacketPtr packet);
     bool recvTimingReq(PacketPtr packet);
     bool tryTiming(PacketPtr packet);
@@ -177,6 +186,8 @@
     tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload &trans,
                                        tlm::tlm_phase &phase,
                                        sc_core::sc_time &t);
+    void invalidate_direct_mem_ptr(
+            sc_dt::uint64 start_range, sc_dt::uint64 end_range);
 
   public:
     ::Port &gem5_getPort(const std::string &if_name, int idx=-1) override;
diff --git a/src/systemc/tlm_bridge/tlm_to_gem5.cc b/src/systemc/tlm_bridge/tlm_to_gem5.cc
index 4b69286..d978aa6 100644
--- a/src/systemc/tlm_bridge/tlm_to_gem5.cc
+++ b/src/systemc/tlm_bridge/tlm_to_gem5.cc
@@ -64,6 +64,7 @@
 #include "params/TlmToGem5Bridge64.hh"
 #include "sim/system.hh"
 #include "systemc/ext/core/sc_module_name.hh"
+#include "systemc/ext/core/sc_time.hh"
 
 namespace sc_gem5
 {
@@ -212,6 +213,14 @@
 
 template <unsigned int BITWIDTH>
 void
+TlmToGem5Bridge<BITWIDTH>::invalidateDmi(const ::MemBackdoor &backdoor)
+{
+    socket->invalidate_direct_mem_ptr(
+            backdoor.range().start(), backdoor.range().end());
+}
+
+template <unsigned int BITWIDTH>
+void
 TlmToGem5Bridge<BITWIDTH>::peq_cb(tlm::tlm_generic_payload &trans,
                                   const tlm::tlm_phase &phase)
 {
@@ -272,7 +281,10 @@
         pkt = generatePacket(trans);
     }
 
-    Tick ticks = bmp.sendAtomic(pkt);
+    MemBackdoorPtr backdoor = nullptr;
+    Tick ticks = bmp.sendAtomicBackdoor(pkt, backdoor);
+    if (backdoor)
+        trans.set_dmi_allowed(true);
 
     // send an atomic request to gem5
     panic_if(pkt->needsResponse() && !pkt->isResponse(),
@@ -318,7 +330,51 @@
 TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr(tlm::tlm_generic_payload &trans,
                                               tlm::tlm_dmi &dmi_data)
 {
-    return false;
+    Gem5SystemC::Gem5Extension *extension = nullptr;
+    trans.get_extension(extension);
+
+    PacketPtr pkt = nullptr;
+
+    // If there is an extension, this transaction was initiated by the gem5
+    // world and we can pipe through the original packet.
+    if (extension != nullptr) {
+        extension->setPipeThrough();
+        pkt = extension->getPacket();
+    } else {
+        pkt = generatePacket(trans);
+        pkt->req->setFlags(Request::NO_ACCESS);
+    }
+
+    MemBackdoorPtr backdoor = nullptr;
+    bmp.sendAtomicBackdoor(pkt, backdoor);
+    if (backdoor) {
+        trans.set_dmi_allowed(true);
+        dmi_data.set_dmi_ptr(backdoor->ptr());
+        dmi_data.set_start_address(backdoor->range().start());
+        dmi_data.set_end_address(backdoor->range().end());
+
+        typedef tlm::tlm_dmi::dmi_access_e access_t;
+        access_t access = tlm::tlm_dmi::DMI_ACCESS_NONE;
+        if (backdoor->readable())
+            access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_READ);
+        if (backdoor->writeable())
+            access = (access_t)(access | tlm::tlm_dmi::DMI_ACCESS_WRITE);
+        dmi_data.set_granted_access(access);
+
+        backdoor->addInvalidationCallback(
+            [this](const MemBackdoor &backdoor)
+            {
+                invalidateDmi(backdoor);
+            }
+        );
+    }
+
+    if (extension == nullptr)
+        destroyPacket(pkt);
+
+    trans.set_response_status(tlm::TLM_OK_RESPONSE);
+
+    return backdoor != nullptr;
 }
 
 template <unsigned int BITWIDTH>
@@ -443,6 +499,8 @@
         SC_REPORT_INFO("TlmToGem5Bridge", "register blocking interface");
         socket.register_b_transport(
                 this, &TlmToGem5Bridge<BITWIDTH>::b_transport);
+        socket.register_get_direct_mem_ptr(
+                this, &TlmToGem5Bridge<BITWIDTH>::get_direct_mem_ptr);
     } else {
         panic("gem5 operates neither in Timing nor in Atomic mode");
     }
diff --git a/src/systemc/tlm_bridge/tlm_to_gem5.hh b/src/systemc/tlm_bridge/tlm_to_gem5.hh
index d2b15af..82d3d10 100644
--- a/src/systemc/tlm_bridge/tlm_to_gem5.hh
+++ b/src/systemc/tlm_bridge/tlm_to_gem5.hh
@@ -139,6 +139,8 @@
 
     void checkTransaction(tlm::tlm_generic_payload &trans);
 
+    void invalidateDmi(const ::MemBackdoor &backdoor);
+
   protected:
     // payload event call back
     void peq_cb(tlm::tlm_generic_payload &trans, const tlm::tlm_phase &phase);