1/* 2 * Copyright (c) 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: Rune Holm 38 * Marco Elver 39 */ 40 41#include "mem/mem_checker.hh" 42 43#include <cassert> 44 45void 46MemChecker::WriteCluster::startWrite(MemChecker::Serial serial, Tick _start, 47 uint8_t data) 48{ 49 assert(!isComplete()); 50 51 if (start == TICK_FUTURE) { 52 // Initialize a fresh write cluster 53 start = _start; 54 } 55 chatty_assert(start <= _start, "WriteClusters must filled in order!"); 56 57 ++numIncomplete; 58 59 if (complete != TICK_FUTURE) { 60 // Reopen a closed write cluster 61 assert(_start < complete); // should open a new write cluster, instead; 62 // also somewhat fishy wrt causality / ordering of calls vs time 63 // progression TODO: Check me! 64 complete = TICK_FUTURE; 65 } 66 67 // Create new transaction, and denote completion time to be in the future. 68 writes.insert(std::make_pair(serial, 69 MemChecker::Transaction(serial, _start, TICK_FUTURE, data))); 70} 71 72void 73MemChecker::WriteCluster::completeWrite(MemChecker::Serial serial, Tick _complete) 74{ 75 auto it = writes.find(serial); 76 77 if (it == writes.end()) { 78 warn("Could not locate write transaction: serial = %d, complete = %d\n", 79 serial, _complete); 80 return; 81 } 82 83 // Record completion time of the write 84 assert(it->second.complete == TICK_FUTURE); 85 it->second.complete = _complete; 86 87 // Update max completion time for the cluster 88 if (completeMax < _complete) { 89 completeMax = _complete; 90 } 91 92 if (--numIncomplete == 0) { 93 // All writes have completed, this cluster is now complete and will be 94 // assigned the max of completion tick values among all writes. 95 // 96 // Note that we cannot simply keep updating complete, because that would 97 // count the cluster as closed already. Instead, we keep TICK_FUTURE 98 // until all writes have completed. 99 complete = completeMax; 100 } 101} 102 103void 104MemChecker::WriteCluster::abortWrite(MemChecker::Serial serial) 105{ 106 if (!writes.erase(serial)) { 107 warn("Could not locate write transaction: serial = %d\n", serial); 108 return; 109 } 110 111 if (--numIncomplete == 0 && !writes.empty()) { 112 // This write cluster is now complete, and we can assign the current 113 // completeMax value. 114 complete = completeMax; 115 } 116 117 // Note: this WriteCluster is in pristine state if this was the only 118 // write present; the cluster will get reused through 119 // getIncompleteWriteCluster(). 120} 121 122void 123MemChecker::ByteTracker::startRead(MemChecker::Serial serial, Tick start) 124{ 125 outstandingReads.insert(std::make_pair(serial, 126 MemChecker::Transaction(serial, start, TICK_FUTURE))); 127} 128 129bool 130MemChecker::ByteTracker::inExpectedData(Tick start, Tick complete, uint8_t data) 131{ 132 _lastExpectedData.clear(); 133 134 bool wc_overlap = true; 135 136 // Find the last value read from the location 137 const Transaction& last_obs = 138 *lastCompletedTransaction(&readObservations, start); 139 bool last_obs_valid = (last_obs.complete != TICK_INITIAL); 140 141 // Scan backwards through the write clusters to find the closest younger 142 // preceding & overlapping writes. 143 for (auto cluster = writeClusters.rbegin(); 144 cluster != writeClusters.rend() && wc_overlap; ++cluster) { 145 for (const auto& addr_write : cluster->writes) { 146 const Transaction& write = addr_write.second; 147 148 if (write.complete < last_obs.start) { 149 // If this write transaction completed before the last 150 // observation, we ignore it as the last_observation has the 151 // correct value 152 continue; 153 } 154 155 if (write.data == data) { 156 // Found a match, end search. 157 return true; 158 } 159 160 // Record possible, but non-matching data for debugging 161 _lastExpectedData.push_back(write.data); 162 163 if (write.complete > start) { 164 // This write overlapped with the transaction we want to check 165 // -> continue checking the overlapping write cluster 166 continue; 167 } 168 169 // This write cluster has writes that have completed before the 170 // checked transaction. There is no need to check an earlier 171 // write-cluster -> set the exit condition for the outer loop 172 wc_overlap = false; 173 174 if (last_obs.complete < write.start) { 175 // We found a write which started after the last observed read, 176 // therefore we can not longer consider the value seen by the 177 // last observation as a valid expected value. 178 // 179 // Once all writes have been iterated through, we can check if 180 // the last observation is still valid to compare against. 181 last_obs_valid = false; 182 } 183 } 184 } 185 186 // We have not found any matching write, so far; check other sources of 187 // confirmation 188 if (last_obs_valid) { 189 // The last observation is not outdated according to the writes we have 190 // seen so far. 191 assert(last_obs.complete <= start); 192 if (last_obs.data == data) { 193 // Matched data from last observation -> all good 194 return true; 195 } 196 // Record non-matching, but possible value 197 _lastExpectedData.push_back(last_obs.data); 198 } else { 199 // We have not seen any valid observation, and the only writes 200 // observed are overlapping, so anything (in particular the 201 // initialisation value) goes 202 // NOTE: We can overlap with multiple write clusters, here 203 if (!writeClusters.empty() && wc_overlap) { 204 // ensure that all write clusters really overlap this read 205 assert(writeClusters.begin()->start < complete && 206 writeClusters.rbegin()->complete > start); 207 return true; 208 } 209 } 210 211 if (_lastExpectedData.empty()) { 212 assert(last_obs.complete == TICK_INITIAL); 213 // We have not found any possible (non-matching data). Can happen in 214 // initial system state 215 DPRINTF(MemChecker, "no last observation nor write! start = %d, "\ 216 "complete = %d, data = %#x\n", start, complete, data); 217 return true; 218 } 219 return false; 220} 221 222bool 223MemChecker::ByteTracker::completeRead(MemChecker::Serial serial, 224 Tick complete, uint8_t data) 225{ 226 auto it = outstandingReads.find(serial); 227 228 if (it == outstandingReads.end()) { 229 // Can happen if concurrent with reset_address_range 230 warn("Could not locate read transaction: serial = %d, complete = %d\n", 231 serial, complete); 232 return true; 233 } 234 235 Tick start = it->second.start; 236 outstandingReads.erase(it); 237 238 // Verify data 239 const bool result = inExpectedData(start, complete, data); 240 241 readObservations.emplace_back(serial, start, complete, data); 242 pruneTransactions(); 243 244 return result; 245} 246 247MemChecker::WriteCluster* 248MemChecker::ByteTracker::getIncompleteWriteCluster() 249{ 250 if (writeClusters.empty() || writeClusters.back().isComplete()) { 251 writeClusters.emplace_back(); 252 } 253 254 return &writeClusters.back(); 255} 256 257void 258MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start, 259 uint8_t data) 260{ 261 getIncompleteWriteCluster()->startWrite(serial, start, data); 262} 263 264void 265MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete) 266{ 267 getIncompleteWriteCluster()->completeWrite(serial, complete); 268 pruneTransactions(); 269} 270 271void 272MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial) 273{ 274 getIncompleteWriteCluster()->abortWrite(serial); 275} 276 277void 278MemChecker::ByteTracker::pruneTransactions() 279{ 280 // Obtain tick of first outstanding read. If there are no outstanding 281 // reads, we use curTick(), i.e. we will remove all readObservation except 282 // the most recent one. 283 const Tick before = outstandingReads.empty() ? curTick() : 284 outstandingReads.begin()->second.start; 285 286 // Pruning of readObservations 287 readObservations.erase(readObservations.begin(), 288 lastCompletedTransaction(&readObservations, before)); 289 290 // Pruning of writeClusters 291 if (!writeClusters.empty()) { 292 writeClusters.erase(writeClusters.begin(), 293 lastCompletedTransaction(&writeClusters, before)); 294 } 295} 296 297bool 298MemChecker::completeRead(MemChecker::Serial serial, Tick complete, 299 Addr addr, size_t size, uint8_t *data) 300{ 301 bool result = true; 302 303 DPRINTF(MemChecker, 304 "completing read: serial = %d, complete = %d, " 305 "addr = %#llx, size = %d\n", serial, complete, addr, size); 306 307 for (size_t i = 0; i < size; ++i) { 308 ByteTracker *tracker = getByteTracker(addr + i); 309 310 if (!tracker->completeRead(serial, complete, data[i])) { 311 // Generate error message, and aggregate all failures for the bytes 312 // considered in this transaction in one message. 313 if (result) { 314 result = false; 315 errorMessage = ""; 316 } else { 317 errorMessage += "\n"; 318 } 319 320 errorMessage += csprintf(" Read transaction for address %#llx " 321 "failed: received %#x, expected ", 322 (unsigned long long)(addr + i), data[i]); 323 324 for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) { 325 errorMessage += 326 csprintf("%#x%s", 327 tracker->lastExpectedData()[j], 328 (j == tracker->lastExpectedData().size() - 1) 329 ? "" : "|"); 330 } 331 } 332 } 333 334 if (!result) { 335 DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr, 336 complete, errorMessage); 337 } 338 339 return result; 340} 341 342void 343MemChecker::reset(Addr addr, size_t size) 344{ 345 for (size_t i = 0; i < size; ++i) { 346 byte_trackers.erase(addr + i); 347 } 348} 349 350MemChecker* 351MemCheckerParams::create() 352{ 353 return new MemChecker(this); 354} 355