cpu: Apply the ARM TLB rework to the O3 checker CPU.
[gem5.git] / src / mem / abstract_mem.cc
1 /*
2 * Copyright (c) 2010-2012,2017-2019 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 * Copyright (c) 2001-2005 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Ron Dreslinski
41 * Ali Saidi
42 * Andreas Hansson
43 */
44
45 #include "mem/abstract_mem.hh"
46
47 #include <vector>
48
49 #include "arch/locked_mem.hh"
50 #include "cpu/base.hh"
51 #include "cpu/thread_context.hh"
52 #include "debug/LLSC.hh"
53 #include "debug/MemoryAccess.hh"
54 #include "mem/packet_access.hh"
55 #include "sim/system.hh"
56
57 using namespace std;
58
59 AbstractMemory::AbstractMemory(const Params *p) :
60 ClockedObject(p), range(params()->range), pmemAddr(NULL),
61 backdoor(params()->range, nullptr,
62 (MemBackdoor::Flags)(MemBackdoor::Readable |
63 MemBackdoor::Writeable)),
64 confTableReported(p->conf_table_reported), inAddrMap(p->in_addr_map),
65 kvmMap(p->kvm_map), _system(NULL),
66 stats(*this)
67 {
68 }
69
70 void
71 AbstractMemory::init()
72 {
73 assert(system());
74
75 if (size() % _system->getPageBytes() != 0)
76 panic("Memory Size not divisible by page size\n");
77 }
78
79 void
80 AbstractMemory::setBackingStore(uint8_t* pmem_addr)
81 {
82 // If there was an existing backdoor, let everybody know it's going away.
83 if (backdoor.ptr())
84 backdoor.invalidate();
85
86 // The back door can't handle interleaved memory.
87 backdoor.ptr(range.interleaved() ? nullptr : pmem_addr);
88
89 pmemAddr = pmem_addr;
90 }
91
92 AbstractMemory::MemStats::MemStats(AbstractMemory &_mem)
93 : Stats::Group(&_mem), mem(_mem),
94 bytesRead(this, "bytes_read",
95 "Number of bytes read from this memory"),
96 bytesInstRead(this, "bytes_inst_read",
97 "Number of instructions bytes read from this memory"),
98 bytesWritten(this, "bytes_written",
99 "Number of bytes written to this memory"),
100 numReads(this, "num_reads",
101 "Number of read requests responded to by this memory"),
102 numWrites(this, "num_writes",
103 "Number of write requests responded to by this memory"),
104 numOther(this, "num_other",
105 "Number of other requests responded to by this memory"),
106 bwRead(this, "bw_read",
107 "Total read bandwidth from this memory (bytes/s)"),
108 bwInstRead(this, "bw_inst_read",
109 "Instruction read bandwidth from this memory (bytes/s)"),
110 bwWrite(this, "bw_write",
111 "Write bandwidth from this memory (bytes/s)"),
112 bwTotal(this, "bw_total",
113 "Total bandwidth to/from this memory (bytes/s)")
114 {
115 }
116
117 void
118 AbstractMemory::MemStats::regStats()
119 {
120 using namespace Stats;
121
122 Stats::Group::regStats();
123
124 System *sys = mem.system();
125 assert(sys);
126 const auto max_masters = sys->maxMasters();
127
128 bytesRead
129 .init(max_masters)
130 .flags(total | nozero | nonan)
131 ;
132 for (int i = 0; i < max_masters; i++) {
133 bytesRead.subname(i, sys->getMasterName(i));
134 }
135
136 bytesInstRead
137 .init(max_masters)
138 .flags(total | nozero | nonan)
139 ;
140 for (int i = 0; i < max_masters; i++) {
141 bytesInstRead.subname(i, sys->getMasterName(i));
142 }
143
144 bytesWritten
145 .init(max_masters)
146 .flags(total | nozero | nonan)
147 ;
148 for (int i = 0; i < max_masters; i++) {
149 bytesWritten.subname(i, sys->getMasterName(i));
150 }
151
152 numReads
153 .init(max_masters)
154 .flags(total | nozero | nonan)
155 ;
156 for (int i = 0; i < max_masters; i++) {
157 numReads.subname(i, sys->getMasterName(i));
158 }
159
160 numWrites
161 .init(max_masters)
162 .flags(total | nozero | nonan)
163 ;
164 for (int i = 0; i < max_masters; i++) {
165 numWrites.subname(i, sys->getMasterName(i));
166 }
167
168 numOther
169 .init(max_masters)
170 .flags(total | nozero | nonan)
171 ;
172 for (int i = 0; i < max_masters; i++) {
173 numOther.subname(i, sys->getMasterName(i));
174 }
175
176 bwRead
177 .precision(0)
178 .prereq(bytesRead)
179 .flags(total | nozero | nonan)
180 ;
181 for (int i = 0; i < max_masters; i++) {
182 bwRead.subname(i, sys->getMasterName(i));
183 }
184
185 bwInstRead
186 .precision(0)
187 .prereq(bytesInstRead)
188 .flags(total | nozero | nonan)
189 ;
190 for (int i = 0; i < max_masters; i++) {
191 bwInstRead.subname(i, sys->getMasterName(i));
192 }
193
194 bwWrite
195 .precision(0)
196 .prereq(bytesWritten)
197 .flags(total | nozero | nonan)
198 ;
199 for (int i = 0; i < max_masters; i++) {
200 bwWrite.subname(i, sys->getMasterName(i));
201 }
202
203 bwTotal
204 .precision(0)
205 .prereq(bwTotal)
206 .flags(total | nozero | nonan)
207 ;
208 for (int i = 0; i < max_masters; i++) {
209 bwTotal.subname(i, sys->getMasterName(i));
210 }
211
212 bwRead = bytesRead / simSeconds;
213 bwInstRead = bytesInstRead / simSeconds;
214 bwWrite = bytesWritten / simSeconds;
215 bwTotal = (bytesRead + bytesWritten) / simSeconds;
216 }
217
218 AddrRange
219 AbstractMemory::getAddrRange() const
220 {
221 return range;
222 }
223
224 // Add load-locked to tracking list. Should only be called if the
225 // operation is a load and the LLSC flag is set.
226 void
227 AbstractMemory::trackLoadLocked(PacketPtr pkt)
228 {
229 const RequestPtr &req = pkt->req;
230 Addr paddr = LockedAddr::mask(req->getPaddr());
231
232 // first we check if we already have a locked addr for this
233 // xc. Since each xc only gets one, we just update the
234 // existing record with the new address.
235 list<LockedAddr>::iterator i;
236
237 for (i = lockedAddrList.begin(); i != lockedAddrList.end(); ++i) {
238 if (i->matchesContext(req)) {
239 DPRINTF(LLSC, "Modifying lock record: context %d addr %#x\n",
240 req->contextId(), paddr);
241 i->addr = paddr;
242 return;
243 }
244 }
245
246 // no record for this xc: need to allocate a new one
247 DPRINTF(LLSC, "Adding lock record: context %d addr %#x\n",
248 req->contextId(), paddr);
249 lockedAddrList.push_front(LockedAddr(req));
250 }
251
252
253 // Called on *writes* only... both regular stores and
254 // store-conditional operations. Check for conventional stores which
255 // conflict with locked addresses, and for success/failure of store
256 // conditionals.
257 bool
258 AbstractMemory::checkLockedAddrList(PacketPtr pkt)
259 {
260 const RequestPtr &req = pkt->req;
261 Addr paddr = LockedAddr::mask(req->getPaddr());
262 bool isLLSC = pkt->isLLSC();
263
264 // Initialize return value. Non-conditional stores always
265 // succeed. Assume conditional stores will fail until proven
266 // otherwise.
267 bool allowStore = !isLLSC;
268
269 // Iterate over list. Note that there could be multiple matching records,
270 // as more than one context could have done a load locked to this location.
271 // Only remove records when we succeed in finding a record for (xc, addr);
272 // then, remove all records with this address. Failed store-conditionals do
273 // not blow unrelated reservations.
274 list<LockedAddr>::iterator i = lockedAddrList.begin();
275
276 if (isLLSC) {
277 while (i != lockedAddrList.end()) {
278 if (i->addr == paddr && i->matchesContext(req)) {
279 // it's a store conditional, and as far as the memory system can
280 // tell, the requesting context's lock is still valid.
281 DPRINTF(LLSC, "StCond success: context %d addr %#x\n",
282 req->contextId(), paddr);
283 allowStore = true;
284 break;
285 }
286 // If we didn't find a match, keep searching! Someone else may well
287 // have a reservation on this line here but we may find ours in just
288 // a little while.
289 i++;
290 }
291 req->setExtraData(allowStore ? 1 : 0);
292 }
293 // LLSCs that succeeded AND non-LLSC stores both fall into here:
294 if (allowStore) {
295 // We write address paddr. However, there may be several entries with a
296 // reservation on this address (for other contextIds) and they must all
297 // be removed.
298 i = lockedAddrList.begin();
299 while (i != lockedAddrList.end()) {
300 if (i->addr == paddr) {
301 DPRINTF(LLSC, "Erasing lock record: context %d addr %#x\n",
302 i->contextId, paddr);
303 ContextID owner_cid = i->contextId;
304 assert(owner_cid != InvalidContextID);
305 ContextID requester_cid = req->hasContextId() ?
306 req->contextId() :
307 InvalidContextID;
308 if (owner_cid != requester_cid) {
309 ThreadContext* ctx = system()->getThreadContext(owner_cid);
310 TheISA::globalClearExclusive(ctx);
311 }
312 i = lockedAddrList.erase(i);
313 } else {
314 i++;
315 }
316 }
317 }
318
319 return allowStore;
320 }
321
322 #if TRACING_ON
323 static inline void
324 tracePacket(System *sys, const char *label, PacketPtr pkt)
325 {
326 int size = pkt->getSize();
327 #if THE_ISA != NULL_ISA
328 if (size == 1 || size == 2 || size == 4 || size == 8) {
329 DPRINTF(MemoryAccess,"%s from %s of size %i on address %#x data "
330 "%#x %c\n", label, sys->getMasterName(pkt->req->masterId()),
331 size, pkt->getAddr(), pkt->getUintX(TheISA::GuestByteOrder),
332 pkt->req->isUncacheable() ? 'U' : 'C');
333 return;
334 }
335 #endif
336 DPRINTF(MemoryAccess, "%s from %s of size %i on address %#x %c\n",
337 label, sys->getMasterName(pkt->req->masterId()),
338 size, pkt->getAddr(), pkt->req->isUncacheable() ? 'U' : 'C');
339 DDUMP(MemoryAccess, pkt->getConstPtr<uint8_t>(), pkt->getSize());
340 }
341
342 # define TRACE_PACKET(A) tracePacket(system(), A, pkt)
343 #else
344 # define TRACE_PACKET(A)
345 #endif
346
347 void
348 AbstractMemory::access(PacketPtr pkt)
349 {
350 if (pkt->cacheResponding()) {
351 DPRINTF(MemoryAccess, "Cache responding to %#llx: not responding\n",
352 pkt->getAddr());
353 return;
354 }
355
356 if (pkt->cmd == MemCmd::CleanEvict || pkt->cmd == MemCmd::WritebackClean) {
357 DPRINTF(MemoryAccess, "CleanEvict on 0x%x: not responding\n",
358 pkt->getAddr());
359 return;
360 }
361
362 assert(pkt->getAddrRange().isSubset(range));
363
364 uint8_t *hostAddr = pmemAddr + pkt->getAddr() - range.start();
365
366 if (pkt->cmd == MemCmd::SwapReq) {
367 if (pkt->isAtomicOp()) {
368 if (pmemAddr) {
369 pkt->setData(hostAddr);
370 (*(pkt->getAtomicOp()))(hostAddr);
371 }
372 } else {
373 std::vector<uint8_t> overwrite_val(pkt->getSize());
374 uint64_t condition_val64;
375 uint32_t condition_val32;
376
377 panic_if(!pmemAddr, "Swap only works if there is real memory " \
378 "(i.e. null=False)");
379
380 bool overwrite_mem = true;
381 // keep a copy of our possible write value, and copy what is at the
382 // memory address into the packet
383 pkt->writeData(&overwrite_val[0]);
384 pkt->setData(hostAddr);
385
386 if (pkt->req->isCondSwap()) {
387 if (pkt->getSize() == sizeof(uint64_t)) {
388 condition_val64 = pkt->req->getExtraData();
389 overwrite_mem = !std::memcmp(&condition_val64, hostAddr,
390 sizeof(uint64_t));
391 } else if (pkt->getSize() == sizeof(uint32_t)) {
392 condition_val32 = (uint32_t)pkt->req->getExtraData();
393 overwrite_mem = !std::memcmp(&condition_val32, hostAddr,
394 sizeof(uint32_t));
395 } else
396 panic("Invalid size for conditional read/write\n");
397 }
398
399 if (overwrite_mem)
400 std::memcpy(hostAddr, &overwrite_val[0], pkt->getSize());
401
402 assert(!pkt->req->isInstFetch());
403 TRACE_PACKET("Read/Write");
404 stats.numOther[pkt->req->masterId()]++;
405 }
406 } else if (pkt->isRead()) {
407 assert(!pkt->isWrite());
408 if (pkt->isLLSC()) {
409 assert(!pkt->fromCache());
410 // if the packet is not coming from a cache then we have
411 // to do the LL/SC tracking here
412 trackLoadLocked(pkt);
413 }
414 if (pmemAddr) {
415 pkt->setData(hostAddr);
416 }
417 TRACE_PACKET(pkt->req->isInstFetch() ? "IFetch" : "Read");
418 stats.numReads[pkt->req->masterId()]++;
419 stats.bytesRead[pkt->req->masterId()] += pkt->getSize();
420 if (pkt->req->isInstFetch())
421 stats.bytesInstRead[pkt->req->masterId()] += pkt->getSize();
422 } else if (pkt->isInvalidate() || pkt->isClean()) {
423 assert(!pkt->isWrite());
424 // in a fastmem system invalidating and/or cleaning packets
425 // can be seen due to cache maintenance requests
426
427 // no need to do anything
428 } else if (pkt->isWrite()) {
429 if (writeOK(pkt)) {
430 if (pmemAddr) {
431 pkt->writeData(hostAddr);
432 DPRINTF(MemoryAccess, "%s wrote %i bytes to address %x\n",
433 __func__, pkt->getSize(), pkt->getAddr());
434 }
435 assert(!pkt->req->isInstFetch());
436 TRACE_PACKET("Write");
437 stats.numWrites[pkt->req->masterId()]++;
438 stats.bytesWritten[pkt->req->masterId()] += pkt->getSize();
439 }
440 } else {
441 panic("Unexpected packet %s", pkt->print());
442 }
443
444 if (pkt->needsResponse()) {
445 pkt->makeResponse();
446 }
447 }
448
449 void
450 AbstractMemory::functionalAccess(PacketPtr pkt)
451 {
452 assert(pkt->getAddrRange().isSubset(range));
453
454 uint8_t *hostAddr = pmemAddr + pkt->getAddr() - range.start();
455
456 if (pkt->isRead()) {
457 if (pmemAddr) {
458 pkt->setData(hostAddr);
459 }
460 TRACE_PACKET("Read");
461 pkt->makeResponse();
462 } else if (pkt->isWrite()) {
463 if (pmemAddr) {
464 pkt->writeData(hostAddr);
465 }
466 TRACE_PACKET("Write");
467 pkt->makeResponse();
468 } else if (pkt->isPrint()) {
469 Packet::PrintReqState *prs =
470 dynamic_cast<Packet::PrintReqState*>(pkt->senderState);
471 assert(prs);
472 // Need to call printLabels() explicitly since we're not going
473 // through printObj().
474 prs->printLabels();
475 // Right now we just print the single byte at the specified address.
476 ccprintf(prs->os, "%s%#x\n", prs->curPrefix(), *hostAddr);
477 } else {
478 panic("AbstractMemory: unimplemented functional command %s",
479 pkt->cmdString());
480 }
481 }