summaryrefslogtreecommitdiff
path: root/src/mem/mem_checker_monitor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/mem_checker_monitor.cc')
-rw-r--r--src/mem/mem_checker_monitor.cc374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/mem/mem_checker_monitor.cc b/src/mem/mem_checker_monitor.cc
new file mode 100644
index 000000000..e0f863d99
--- /dev/null
+++ b/src/mem/mem_checker_monitor.cc
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ *
+ * Authors: Thomas Grass
+ * Andreas Hansson
+ * Marco Elver
+ */
+
+#include <memory>
+
+#include "base/output.hh"
+#include "base/trace.hh"
+#include "debug/MemCheckerMonitor.hh"
+#include "mem/mem_checker_monitor.hh"
+
+using namespace std;
+
+MemCheckerMonitor::MemCheckerMonitor(Params* params)
+ : MemObject(params),
+ masterPort(name() + "-master", *this),
+ slavePort(name() + "-slave", *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 (!slavePort.isConnected() || !masterPort.isConnected())
+ fatal("Communication monitor is not connected on both sides.\n");
+}
+
+BaseMasterPort&
+MemCheckerMonitor::getMasterPort(const std::string& if_name, PortID idx)
+{
+ if (if_name == "master" || if_name == "mem_side") {
+ return masterPort;
+ } else {
+ return MemObject::getMasterPort(if_name, idx);
+ }
+}
+
+BaseSlavePort&
+MemCheckerMonitor::getSlavePort(const std::string& if_name, PortID idx)
+{
+ if (if_name == "slave" || if_name == "cpu_side") {
+ return slavePort;
+ } else {
+ return MemObject::getSlavePort(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);
+
+ masterPort.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);
+
+ slavePort.sendFunctionalSnoop(pkt);
+
+ DPRINTF(MemCheckerMonitor,
+ "Received functional snoop: addr = %#llx, size = %d\n",
+ addr, size);
+}
+
+Tick
+MemCheckerMonitor::recvAtomic(PacketPtr pkt)
+{
+ assert(false && "Atomic not supported");
+ return masterPort.sendAtomic(pkt);
+}
+
+Tick
+MemCheckerMonitor::recvAtomicSnoop(PacketPtr pkt)
+{
+ assert(false && "Atomic not supported");
+ return slavePort.sendAtomicSnoop(pkt);
+}
+
+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->memInhibitAsserted();
+ 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]);
+ memcpy(pkt_data.get(), pkt->getConstPtr<uint8_t*>(), size);
+ }
+
+ // 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 be inhibited and 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 (always succeeds for inhibited
+ // packets)
+ bool successful = masterPort.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 masterPort 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 inhibited request: 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]);
+ memcpy(pkt_data.get(), pkt->getConstPtr<uint8_t*>(), size);
+ }
+
+ 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 = slavePort.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)
+{
+ slavePort.sendTimingSnoopReq(pkt);
+}
+
+bool
+MemCheckerMonitor::recvTimingSnoopResp(PacketPtr pkt)
+{
+ return masterPort.sendTimingSnoopResp(pkt);
+}
+
+bool
+MemCheckerMonitor::isSnooping() const
+{
+ // check if the connected master port is snooping
+ return slavePort.isSnooping();
+}
+
+AddrRangeList
+MemCheckerMonitor::getAddrRanges() const
+{
+ // get the address ranges of the connected slave port
+ return masterPort.getAddrRanges();
+}
+
+void
+MemCheckerMonitor::recvRetryMaster()
+{
+ slavePort.sendRetry();
+}
+
+void
+MemCheckerMonitor::recvRetrySlave()
+{
+ masterPort.sendRetry();
+}
+
+void
+MemCheckerMonitor::recvRangeChange()
+{
+ slavePort.sendRangeChange();
+}