1/*
2 * Copyright (c) 2012-2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Thomas Grass
38 *          Andreas Hansson
39 *          Marco Elver
40 */
41
42#include "mem/mem_checker_monitor.hh"
43
44#include <memory>
45
46#include "base/logging.hh"
47#include "base/output.hh"
48#include "base/trace.hh"
49#include "debug/MemCheckerMonitor.hh"
50
51MemCheckerMonitor::MemCheckerMonitor(Params* params)
52    : SimObject(params),
53      masterPort(name() + "-master", *this),
54      slavePort(name() + "-slave", *this),
55      warnOnly(params->warn_only),
56      memchecker(params->memchecker)
57{}
58
59MemCheckerMonitor::~MemCheckerMonitor()
60{}
61
62MemCheckerMonitor*
63MemCheckerMonitorParams::create()
64{
65    return new MemCheckerMonitor(this);
66}
67
68void
69MemCheckerMonitor::init()
70{
71    // make sure both sides of the monitor are connected
72    if (!slavePort.isConnected() || !masterPort.isConnected())
73        fatal("Communication monitor is not connected on both sides.\n");
74}
75
76Port &
77MemCheckerMonitor::getPort(const std::string &if_name, PortID idx)
78{
79    if (if_name == "master" || if_name == "mem_side") {
80        return masterPort;
81    } else if (if_name == "slave" || if_name == "cpu_side") {
82        return slavePort;
83    } else {
84        return SimObject::getPort(if_name, idx);
85    }
86}
87
88void
89MemCheckerMonitor::recvFunctional(PacketPtr pkt)
90{
91    Addr addr = pkt->getAddr();
92    unsigned size = pkt->getSize();
93
94    // Conservatively reset this address-range. Alternatively we could try to
95    // update the values seen by the memchecker, however, there may be other
96    // reads/writes to these location from other devices we do not see.
97    memchecker->reset(addr, size);
98
99    masterPort.sendFunctional(pkt);
100
101    DPRINTF(MemCheckerMonitor,
102            "Forwarded functional access: addr = %#llx, size = %d\n",
103            addr, size);
104}
105
106void
107MemCheckerMonitor::recvFunctionalSnoop(PacketPtr pkt)
108{
109    Addr addr = pkt->getAddr();
110    unsigned size = pkt->getSize();
111
112    // See above.
113    memchecker->reset(addr, size);
114
115    slavePort.sendFunctionalSnoop(pkt);
116
117    DPRINTF(MemCheckerMonitor,
118            "Received functional snoop: addr = %#llx, size = %d\n",
119            addr, size);
120}
121
122Tick
123MemCheckerMonitor::recvAtomic(PacketPtr pkt)
124{
125    panic("Atomic not supported");
126}
127
128Tick
129MemCheckerMonitor::recvAtomicSnoop(PacketPtr pkt)
130{
131    panic("Atomic not supported");
132}
133
134bool
135MemCheckerMonitor::recvTimingReq(PacketPtr pkt)
136{
137    // should always see a request
138    assert(pkt->isRequest());
139
140    // Store relevant fields of packet, because packet may be modified
141    // or even deleted when sendTiming() is called.
142    //
143    // For reads we are only interested in real reads, and not prefetches, as
144    // it is not guaranteed that the prefetch returns any useful data.
145    bool is_read = pkt->isRead() && !pkt->req->isPrefetch();
146    bool is_write = pkt->isWrite();
147    unsigned size = pkt->getSize();
148    Addr addr = pkt->getAddr();
149    bool expects_response = pkt->needsResponse() && !pkt->cacheResponding();
150    std::unique_ptr<uint8_t[]> pkt_data;
151    MemCheckerMonitorSenderState* state = NULL;
152
153    if (expects_response && is_write) {
154        // On receipt of a request, only need to allocate pkt_data if this is a
155        // write. For reads, we have no data yet, so it doesn't make sense to
156        // allocate.
157        pkt_data.reset(new uint8_t[size]);
158        pkt->writeData(pkt_data.get());
159    }
160
161    // If a cache miss is served by a cache, a monitor near the memory
162    // would see a request which needs a response, but this response
163    // would not come back from the memory. Therefore
164    // we additionally have to check the inhibit flag.
165    if (expects_response && (is_read || is_write)) {
166        state = new MemCheckerMonitorSenderState(0);
167        pkt->pushSenderState(state);
168    }
169
170    // Attempt to send the packet
171    bool successful = masterPort.sendTimingReq(pkt);
172
173    // If not successful, restore the sender state
174    if (!successful && expects_response && (is_read || is_write)) {
175        delete pkt->popSenderState();
176    }
177
178    if (successful && expects_response) {
179        if (is_read) {
180            MemChecker::Serial serial = memchecker->startRead(curTick(),
181                                                              addr,
182                                                              size);
183
184            // At the time where we push the sender-state, we do not yet know
185            // the serial the MemChecker class will assign to this request. We
186            // cannot call startRead at the time we push the sender-state, as
187            // the masterPort may not be successful in executing sendTimingReq,
188            // and in case of a failure, we must not modify the state of the
189            // MemChecker.
190            //
191            // Once we know that sendTimingReq was successful, we can set the
192            // serial of the newly constructed sender-state. This is legal, as
193            // we know that nobody else will touch nor is responsible for
194            // deletion of our sender-state.
195            state->serial = serial;
196
197            DPRINTF(MemCheckerMonitor,
198                    "Forwarded read request: serial = %d, addr = %#llx, "
199                    "size = %d\n",
200                    serial, addr, size);
201        } else if (is_write) {
202            MemChecker::Serial serial = memchecker->startWrite(curTick(),
203                                                               addr,
204                                                               size,
205                                                               pkt_data.get());
206
207            state->serial = serial;
208
209            DPRINTF(MemCheckerMonitor,
210                    "Forwarded write request: serial = %d, addr = %#llx, "
211                    "size = %d\n",
212                    serial, addr, size);
213        } else {
214            DPRINTF(MemCheckerMonitor,
215                    "Forwarded non read/write request: addr = %#llx\n", addr);
216        }
217    } else if (successful) {
218        DPRINTF(MemCheckerMonitor,
219                "Forwarded request marked for cache response: addr = %#llx\n",
220                addr);
221    }
222
223    return successful;
224}
225
226bool
227MemCheckerMonitor::recvTimingResp(PacketPtr pkt)
228{
229    // should always see responses
230    assert(pkt->isResponse());
231
232    // Store relevant fields of packet, because packet may be modified
233    // or even deleted when sendTiming() is called.
234    bool is_read = pkt->isRead() && !pkt->req->isPrefetch();
235    bool is_write = pkt->isWrite();
236    bool is_failed_LLSC = pkt->isLLSC() && pkt->req->getExtraData() == 0;
237    unsigned size = pkt->getSize();
238    Addr addr = pkt->getAddr();
239    std::unique_ptr<uint8_t[]> pkt_data;
240    MemCheckerMonitorSenderState* received_state = NULL;
241
242    if (is_read) {
243        // On receipt of a response, only need to allocate pkt_data if this is
244        // a read. For writes, we have already given the MemChecker the data on
245        // the request, so it doesn't make sense to allocate on write.
246        pkt_data.reset(new uint8_t[size]);
247        pkt->writeData(pkt_data.get());
248    }
249
250    if (is_read || is_write) {
251        received_state =
252            dynamic_cast<MemCheckerMonitorSenderState*>(pkt->senderState);
253
254        // Restore initial sender state
255        panic_if(received_state == NULL,
256                 "Monitor got a response without monitor sender state\n");
257
258        // Restore the state
259        pkt->senderState = received_state->predecessor;
260    }
261
262    // Attempt to send the packet
263    bool successful = slavePort.sendTimingResp(pkt);
264
265    // If packet successfully send, complete transaction in MemChecker
266    // instance, and delete sender state, otherwise restore state.
267    if (successful) {
268        if (is_read) {
269            DPRINTF(MemCheckerMonitor,
270                    "Received read response: serial = %d, addr = %#llx, "
271                    "size = %d\n",
272                    received_state->serial, addr, size);
273
274            bool result = memchecker->completeRead(received_state->serial,
275                                                   curTick(),
276                                                   addr,
277                                                   size,
278                                                   pkt_data.get());
279
280            if (!result) {
281                warn("%s: read of %#llx @ cycle %d failed:\n%s\n",
282                     name(),
283                     addr, curTick(),
284                     memchecker->getErrorMessage().c_str());
285
286                panic_if(!warnOnly, "MemChecker violation!");
287            }
288
289            delete received_state;
290        } else if (is_write) {
291            DPRINTF(MemCheckerMonitor,
292                    "Received write response: serial = %d, addr = %#llx, "
293                    "size = %d\n",
294                    received_state->serial, addr, size);
295
296            if (is_failed_LLSC) {
297                // The write was not successful, let MemChecker know.
298                memchecker->abortWrite(received_state->serial,
299                                       addr,
300                                       size);
301            } else {
302                memchecker->completeWrite(received_state->serial,
303                                          curTick(),
304                                          addr,
305                                          size);
306            }
307
308            delete received_state;
309        } else {
310            DPRINTF(MemCheckerMonitor,
311                    "Received non read/write response: addr = %#llx\n", addr);
312        }
313    } else if (is_read || is_write) {
314        // Don't delete anything and let the packet look like we
315        // did not touch it
316        pkt->senderState = received_state;
317    }
318
319    return successful;
320}
321
322void
323MemCheckerMonitor::recvTimingSnoopReq(PacketPtr pkt)
324{
325    slavePort.sendTimingSnoopReq(pkt);
326}
327
328bool
329MemCheckerMonitor::recvTimingSnoopResp(PacketPtr pkt)
330{
331    return masterPort.sendTimingSnoopResp(pkt);
332}
333
334bool
335MemCheckerMonitor::isSnooping() const
336{
337    // check if the connected master port is snooping
338    return slavePort.isSnooping();
339}
340
341AddrRangeList
342MemCheckerMonitor::getAddrRanges() const
343{
344    // get the address ranges of the connected slave port
345    return masterPort.getAddrRanges();
346}
347
348void
349MemCheckerMonitor::recvReqRetry()
350{
351    slavePort.sendRetryReq();
352}
353
354void
355MemCheckerMonitor::recvRespRetry()
356{
357    masterPort.sendRetryResp();
358}
359
360void
361MemCheckerMonitor::recvRangeChange()
362{
363    slavePort.sendRangeChange();
364}
365