/* * 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 "mem/mem_checker_monitor.hh" #include #include "base/logging.hh" #include "base/output.hh" #include "base/trace.hh" #include "debug/MemCheckerMonitor.hh" 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) { 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 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 = 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 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 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(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::recvReqRetry() { slavePort.sendRetryReq(); } void MemCheckerMonitor::recvRespRetry() { masterPort.sendRetryResp(); } void MemCheckerMonitor::recvRangeChange() { slavePort.sendRangeChange(); }