text: /*
   * Copyright (c) 2012-2014 ARM Limited
   * All rights reserved
   *
   * The license below extends only to copyright in the software and shall
   * not be construed as granting a license to any other intellectual
   * property including but not limited to intellectual property relating
   * to a hardware implementation of the functionality of the software
   * licensed hereunder.  You may use the software subject to the license
   * terms below provided that you ensure that this notice is replicated
   * unmodified and in its entirety in all distributions of the software,
   * modified or unmodified, in source code or in binary form.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met: redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer;
   * redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution;
   * neither the name of the copyright holders nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include "mem/mem_checker_monitor.hh"
  
  #include <memory>
  
  #include "base/logging.hh"
  #include "base/output.hh"
  #include "base/trace.hh"
  #include "debug/MemCheckerMonitor.hh"
  
  MemCheckerMonitor::MemCheckerMonitor(Params* params)
      : SimObject(params),
        memSidePort(name() + "-memSidePort", *this),
        cpuSidePort(name() + "-cpuSidePort", *this),
        warnOnly(params->warn_only),
        memchecker(params->memchecker)
  {}
  
  MemCheckerMonitor::~MemCheckerMonitor()
  {}
  
  MemCheckerMonitor*
  MemCheckerMonitorParams::create()
  {
      return new MemCheckerMonitor(this);
  }
  
  void
  MemCheckerMonitor::init()
  {
      // make sure both sides of the monitor are connected
      if (!cpuSidePort.isConnected() || !memSidePort.isConnected())
          fatal("Communication monitor is not connected on both sides.\n");
  }
  
  Port &
  MemCheckerMonitor::getPort(const std::string &if_name, PortID idx)
  {
      if (if_name == "request" || if_name == "mem_side_port") {
          return memSidePort;
      } else if (if_name == "response" || if_name == "cpu_side_port") {
          return cpuSidePort;
      } else {
          return SimObject::getPort(if_name, idx);
      }
  }
  
  void
  MemCheckerMonitor::recvFunctional(PacketPtr pkt)
  {
      Addr addr = pkt->getAddr();
      unsigned size = pkt->getSize();
  
      // Conservatively reset this address-range. Alternatively we could try to
      // update the values seen by the memchecker, however, there may be other
      // reads/writes to these location from other devices we do not see.
      memchecker->reset(addr, size);
  
      memSidePort.sendFunctional(pkt);
  
      DPRINTF(MemCheckerMonitor,
              "Forwarded functional access: addr = %#llx, size = %d\n",
              addr, size);
  }
  
  void
  MemCheckerMonitor::recvFunctionalSnoop(PacketPtr pkt)
  {
      Addr addr = pkt->getAddr();
      unsigned size = pkt->getSize();
  
      // See above.
      memchecker->reset(addr, size);
  
      cpuSidePort.sendFunctionalSnoop(pkt);
  
      DPRINTF(MemCheckerMonitor,
              "Received functional snoop: addr = %#llx, size = %d\n",
              addr, size);
  }
  
  Tick
  MemCheckerMonitor::recvAtomic(PacketPtr pkt)
  {
      panic("Atomic not supported");
  }
  
  Tick
  MemCheckerMonitor::recvAtomicSnoop(PacketPtr pkt)
  {
      panic("Atomic not supported");
  }
  
  bool
  MemCheckerMonitor::recvTimingReq(PacketPtr pkt)
  {
      // should always see a request
      assert(pkt->isRequest());
  
      // Store relevant fields of packet, because packet may be modified
      // or even deleted when sendTiming() is called.
      //
      // For reads we are only interested in real reads, and not prefetches, as
      // it is not guaranteed that the prefetch returns any useful data.
      bool is_read = pkt->isRead() && !pkt->req->isPrefetch();
      bool is_write = pkt->isWrite();
      unsigned size = pkt->getSize();
      Addr addr = pkt->getAddr();
      bool expects_response = pkt->needsResponse() && !pkt->cacheResponding();
      std::unique_ptr<uint8_t[]> pkt_data;
      MemCheckerMonitorSenderState* state = NULL;
  
      if (expects_response && is_write) {
          // On receipt of a request, only need to allocate pkt_data if this is a
          // write. For reads, we have no data yet, so it doesn't make sense to
          // allocate.
          pkt_data.reset(new uint8_t[size]);
          pkt->writeData(pkt_data.get());
      }
  
      // If a cache miss is served by a cache, a monitor near the memory
      // would see a request which needs a response, but this response
      // would not come back from the memory. Therefore
      // we additionally have to check the inhibit flag.
      if (expects_response && (is_read || is_write)) {
          state = new MemCheckerMonitorSenderState(0);
          pkt->pushSenderState(state);
      }
  
      // Attempt to send the packet
      bool successful = memSidePort.sendTimingReq(pkt);
  
      // If not successful, restore the sender state
      if (!successful && expects_response && (is_read || is_write)) {
          delete pkt->popSenderState();
      }
  
      if (successful && expects_response) {
          if (is_read) {
              MemChecker::Serial serial = memchecker->startRead(curTick(),
                                                                addr,
                                                                size);
  
              // At the time where we push the sender-state, we do not yet know
              // the serial the MemChecker class will assign to this request. We
              // cannot call startRead at the time we push the sender-state, as
              // the memSidePort may not be successful in executing
              // sendTimingReq, and in case of a failure, we must not
              // modify the state of the MemChecker.
              //
              // Once we know that sendTimingReq was successful, we can set the
              // serial of the newly constructed sender-state. This is legal, as
              // we know that nobody else will touch nor is responsible for
              // deletion of our sender-state.
              state->serial = serial;
  
              DPRINTF(MemCheckerMonitor,
                      "Forwarded read request: serial = %d, addr = %#llx, "
                      "size = %d\n",
                      serial, addr, size);
          } else if (is_write) {
              MemChecker::Serial serial = memchecker->startWrite(curTick(),
                                                                 addr,
                                                                 size,
                                                                 pkt_data.get());
  
              state->serial = serial;
  
              DPRINTF(MemCheckerMonitor,
                      "Forwarded write request: serial = %d, addr = %#llx, "
                      "size = %d\n",
                      serial, addr, size);
          } else {
              DPRINTF(MemCheckerMonitor,
                      "Forwarded non read/write request: addr = %#llx\n", addr);
          }
      } else if (successful) {
          DPRINTF(MemCheckerMonitor,
                  "Forwarded request marked for cache response: addr = %#llx\n",
                  addr);
      }
  
      return successful;
  }
  
  bool
  MemCheckerMonitor::recvTimingResp(PacketPtr pkt)
  {
      // should always see responses
      assert(pkt->isResponse());
  
      // Store relevant fields of packet, because packet may be modified
      // or even deleted when sendTiming() is called.
      bool is_read = pkt->isRead() && !pkt->req->isPrefetch();
      bool is_write = pkt->isWrite();
      bool is_failed_LLSC = pkt->isLLSC() && pkt->req->getExtraData() == 0;
      unsigned size = pkt->getSize();
      Addr addr = pkt->getAddr();
      std::unique_ptr<uint8_t[]> pkt_data;
      MemCheckerMonitorSenderState* received_state = NULL;
  
      if (is_read) {
          // On receipt of a response, only need to allocate pkt_data if this is
          // a read. For writes, we have already given the MemChecker the data on
          // the request, so it doesn't make sense to allocate on write.
          pkt_data.reset(new uint8_t[size]);
          pkt->writeData(pkt_data.get());
      }
  
      if (is_read || is_write) {
          received_state =
              dynamic_cast<MemCheckerMonitorSenderState*>(pkt->senderState);
  
          // Restore initial sender state
          panic_if(received_state == NULL,
                   "Monitor got a response without monitor sender state\n");
  
          // Restore the state
          pkt->senderState = received_state->predecessor;
      }
  
      // Attempt to send the packet
      bool successful = cpuSidePort.sendTimingResp(pkt);
  
      // If packet successfully send, complete transaction in MemChecker
      // instance, and delete sender state, otherwise restore state.
      if (successful) {
          if (is_read) {
              DPRINTF(MemCheckerMonitor,
                      "Received read response: serial = %d, addr = %#llx, "
                      "size = %d\n",
                      received_state->serial, addr, size);
  
              bool result = memchecker->completeRead(received_state->serial,
                                                     curTick(),
                                                     addr,
                                                     size,
                                                     pkt_data.get());
  
              if (!result) {
                  warn("%s: read of %#llx @ cycle %d failed:\n%s\n",
                       name(),
                       addr, curTick(),
                       memchecker->getErrorMessage().c_str());
  
                  panic_if(!warnOnly, "MemChecker violation!");
              }
  
              delete received_state;
          } else if (is_write) {
              DPRINTF(MemCheckerMonitor,
                      "Received write response: serial = %d, addr = %#llx, "
                      "size = %d\n",
                      received_state->serial, addr, size);
  
              if (is_failed_LLSC) {
                  // The write was not successful, let MemChecker know.
                  memchecker->abortWrite(received_state->serial,
                                         addr,
                                         size);
              } else {
                  memchecker->completeWrite(received_state->serial,
                                            curTick(),
                                            addr,
                                            size);
              }
  
              delete received_state;
          } else {
              DPRINTF(MemCheckerMonitor,
                      "Received non read/write response: addr = %#llx\n", addr);
          }
      } else if (is_read || is_write) {
          // Don't delete anything and let the packet look like we
          // did not touch it
          pkt->senderState = received_state;
      }
  
      return successful;
  }
  
  void
  MemCheckerMonitor::recvTimingSnoopReq(PacketPtr pkt)
  {
      cpuSidePort.sendTimingSnoopReq(pkt);
  }
  
  bool
  MemCheckerMonitor::recvTimingSnoopResp(PacketPtr pkt)
  {
      return memSidePort.sendTimingSnoopResp(pkt);
  }
  
  bool
  MemCheckerMonitor::isSnooping() const
  {
      // check if the connected memSidePort is snooping
      return cpuSidePort.isSnooping();
  }
  
  AddrRangeList
  MemCheckerMonitor::getAddrRanges() const
  {
      // get the address ranges of the connected cpuSidePort
      return memSidePort.getAddrRanges();
  }
  
  void
  MemCheckerMonitor::recvReqRetry()
  {
      cpuSidePort.sendRetryReq();
  }
  
  void
  MemCheckerMonitor::recvRespRetry()
  {
      memSidePort.sendRetryResp();
  }
  
  void
  MemCheckerMonitor::recvRangeChange()
  {
      cpuSidePort.sendRangeChange();
  }
  