configs,arch,sim: Move fixFuncEventAddr into the Workload class.
[gem5.git] / src / arch / riscv / tlb.cc
1 /*
2 * Copyright (c) 2001-2005 The Regents of The University of Michigan
3 * Copyright (c) 2007 MIPS Technologies, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met: redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer;
10 * redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution;
13 * neither the name of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "arch/riscv/tlb.hh"
31
32 #include <string>
33 #include <vector>
34
35 #include "arch/riscv/faults.hh"
36 #include "arch/riscv/fs_workload.hh"
37 #include "arch/riscv/pagetable.hh"
38 #include "arch/riscv/pra_constants.hh"
39 #include "arch/riscv/utility.hh"
40 #include "base/inifile.hh"
41 #include "base/str.hh"
42 #include "base/trace.hh"
43 #include "cpu/thread_context.hh"
44 #include "debug/RiscvTLB.hh"
45 #include "debug/TLB.hh"
46 #include "mem/page_table.hh"
47 #include "params/RiscvTLB.hh"
48 #include "sim/full_system.hh"
49 #include "sim/process.hh"
50 #include "sim/system.hh"
51
52 using namespace std;
53 using namespace RiscvISA;
54
55 ///////////////////////////////////////////////////////////////////////
56 //
57 // RISC-V TLB
58 //
59
60 TLB::TLB(const Params *p)
61 : BaseTLB(p), size(p->size), nlu(0)
62 {
63 table = new PTE[size];
64 memset(table, 0, sizeof(PTE[size]));
65 smallPages = 0;
66 }
67
68 TLB::~TLB()
69 {
70 if (table)
71 delete [] table;
72 }
73
74 // look up an entry in the TLB
75 RiscvISA::PTE *
76 TLB::lookup(Addr vpn, uint8_t asn) const
77 {
78 // assume not found...
79 PTE *retval = nullptr;
80 PageTable::const_iterator i = lookupTable.find(vpn);
81 if (i != lookupTable.end()) {
82 while (i->first == vpn) {
83 int index = i->second;
84 PTE *pte = &table[index];
85
86 /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
87 Addr Mask = pte->Mask;
88 Addr InvMask = ~Mask;
89 Addr VPN = pte->VPN;
90 if (((vpn & InvMask) == (VPN & InvMask)) &&
91 (pte->G || (asn == pte->asid))) {
92 // We have a VPN + ASID Match
93 retval = pte;
94 break;
95 }
96 ++i;
97 }
98 }
99
100 DPRINTF(TLB, "lookup %#x, asn %#x -> %s ppn %#x\n", vpn, (int)asn,
101 retval ? "hit" : "miss", retval ? retval->PFN1 : 0);
102 return retval;
103 }
104
105 RiscvISA::PTE*
106 TLB::getEntry(unsigned Index) const
107 {
108 // Make sure that Index is valid
109 assert(Index<size);
110 return &table[Index];
111 }
112
113 int
114 TLB::probeEntry(Addr vpn, uint8_t asn) const
115 {
116 // assume not found...
117 int Ind = -1;
118 PageTable::const_iterator i = lookupTable.find(vpn);
119 if (i != lookupTable.end()) {
120 while (i->first == vpn) {
121 int index = i->second;
122 PTE *pte = &table[index];
123
124 /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
125 Addr Mask = pte->Mask;
126 Addr InvMask = ~Mask;
127 Addr VPN = pte->VPN;
128 if (((vpn & InvMask) == (VPN & InvMask)) &&
129 (pte->G || (asn == pte->asid))) {
130 // We have a VPN + ASID Match
131 Ind = index;
132 break;
133 }
134 ++i;
135 }
136 }
137 DPRINTF(RiscvTLB,"VPN: %x, asid: %d, Result of TLBP: %d\n",vpn,asn,Ind);
138 return Ind;
139 }
140
141 inline Fault
142 TLB::checkCacheability(const RequestPtr &req)
143 {
144 Addr VAddrUncacheable = 0xA0000000;
145 // In MIPS, cacheability is controlled by certain bits of the virtual
146 // address or by the TLB entry
147 if ((req->getVaddr() & VAddrUncacheable) == VAddrUncacheable) {
148 // mark request as uncacheable
149 req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER);
150 }
151 return NoFault;
152 }
153
154 void
155 TLB::insertAt(PTE &pte, unsigned Index, int _smallPages)
156 {
157 smallPages = _smallPages;
158 if (Index > size) {
159 warn("Attempted to write at index (%d) beyond TLB size (%d)",
160 Index, size);
161 } else {
162 // Update TLB
163 DPRINTF(TLB, "TLB[%d]: %x %x %x %x\n",
164 Index, pte.Mask << 11,
165 ((pte.VPN << 11) | pte.asid),
166 ((pte.PFN0 << 6) | (pte.C0 << 3) |
167 (pte.D0 << 2) | (pte.V0 <<1) | pte.G),
168 ((pte.PFN1 <<6) | (pte.C1 << 3) |
169 (pte.D1 << 2) | (pte.V1 <<1) | pte.G));
170 if (table[Index].V0 || table[Index].V1) {
171 // Previous entry is valid
172 PageTable::iterator i = lookupTable.find(table[Index].VPN);
173 lookupTable.erase(i);
174 }
175 table[Index]=pte;
176 // Update fast lookup table
177 lookupTable.insert(make_pair(table[Index].VPN, Index));
178 }
179 }
180
181 // insert a new TLB entry
182 void
183 TLB::insert(Addr addr, PTE &pte)
184 {
185 fatal("TLB Insert not yet implemented\n");
186 }
187
188 void
189 TLB::flushAll()
190 {
191 DPRINTF(TLB, "flushAll\n");
192 memset(table, 0, sizeof(PTE[size]));
193 lookupTable.clear();
194 nlu = 0;
195 }
196
197 void
198 TLB::serialize(CheckpointOut &cp) const
199 {
200 SERIALIZE_SCALAR(size);
201 SERIALIZE_SCALAR(nlu);
202
203 for (int i = 0; i < size; i++) {
204 ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
205 table[i].serialize(cp);
206 }
207 }
208
209 void
210 TLB::unserialize(CheckpointIn &cp)
211 {
212 UNSERIALIZE_SCALAR(size);
213 UNSERIALIZE_SCALAR(nlu);
214
215 for (int i = 0; i < size; i++) {
216 ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
217 table[i].unserialize(cp);
218 if (table[i].V0 || table[i].V1) {
219 lookupTable.insert(make_pair(table[i].VPN, i));
220 }
221 }
222 }
223
224 void
225 TLB::regStats()
226 {
227 BaseTLB::regStats();
228
229 read_hits
230 .name(name() + ".read_hits")
231 .desc("DTB read hits")
232 ;
233
234 read_misses
235 .name(name() + ".read_misses")
236 .desc("DTB read misses")
237 ;
238
239
240 read_accesses
241 .name(name() + ".read_accesses")
242 .desc("DTB read accesses")
243 ;
244
245 write_hits
246 .name(name() + ".write_hits")
247 .desc("DTB write hits")
248 ;
249
250 write_misses
251 .name(name() + ".write_misses")
252 .desc("DTB write misses")
253 ;
254
255
256 write_accesses
257 .name(name() + ".write_accesses")
258 .desc("DTB write accesses")
259 ;
260
261 hits
262 .name(name() + ".hits")
263 .desc("DTB hits")
264 ;
265
266 misses
267 .name(name() + ".misses")
268 .desc("DTB misses")
269 ;
270
271 accesses
272 .name(name() + ".accesses")
273 .desc("DTB accesses")
274 ;
275
276 hits = read_hits + write_hits;
277 misses = read_misses + write_misses;
278 accesses = read_accesses + write_accesses;
279 }
280
281 Fault
282 TLB::translateInst(const RequestPtr &req, ThreadContext *tc)
283 {
284 if (FullSystem) {
285 /**
286 * check if we simulate a bare metal system
287 * if so, we have no tlb, phys addr == virt addr
288 */
289 auto *workload = dynamic_cast<FsWorkload *>(
290 tc->getSystemPtr()->workload);
291 if (workload->isBareMetal())
292 req->setFlags(Request::PHYSICAL);
293
294 if (req->getFlags() & Request::PHYSICAL) {
295 /**
296 * we simply set the virtual address to physical address
297 */
298 req->setPaddr(req->getVaddr());
299 return checkCacheability(req);
300 } else {
301 /**
302 * as we currently support bare metal only, we throw a panic,
303 * if it is not a bare metal system
304 */
305 panic("translateInst not implemented in RISC-V.\n");
306 }
307 } else {
308 Process * p = tc->getProcessPtr();
309
310 Fault fault = p->pTable->translate(req);
311 if (fault != NoFault)
312 return fault;
313
314 return NoFault;
315 }
316 }
317
318 Fault
319 TLB::translateData(const RequestPtr &req, ThreadContext *tc, bool write)
320 {
321 if (FullSystem) {
322 /**
323 * check if we simulate a bare metal system
324 * if so, we have no tlb, phys addr == virt addr
325 */
326 auto *workload = dynamic_cast<FsWorkload *>(
327 tc->getSystemPtr()->workload);
328 if (workload->isBareMetal())
329 req->setFlags(Request::PHYSICAL);
330
331 if (req->getFlags() & Request::PHYSICAL) {
332 /**
333 * we simply set the virtual address to physical address
334 */
335 req->setPaddr(req->getVaddr());
336 return checkCacheability(req);
337 } else {
338 /**
339 * as we currently support bare metal only, we throw a panic,
340 * if it is not a bare metal system
341 */
342 panic("translateData not implemented in RISC-V.\n");
343 }
344 } else {
345 // In the O3 CPU model, sometimes a memory access will be speculatively
346 // executed along a branch that will end up not being taken where the
347 // address is invalid. In that case, return a fault rather than trying
348 // to translate it (which will cause a panic). Since RISC-V allows
349 // unaligned memory accesses, this should only happen if the request's
350 // length is long enough to wrap around from the end of the memory to
351 // the start.
352 assert(req->getSize() > 0);
353 if (req->getVaddr() + req->getSize() - 1 < req->getVaddr())
354 return make_shared<GenericPageTableFault>(req->getVaddr());
355
356 Process * p = tc->getProcessPtr();
357
358 Fault fault = p->pTable->translate(req);
359 if (fault != NoFault)
360 return fault;
361
362 return NoFault;
363 }
364 }
365
366 Fault
367 TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode)
368 {
369 if (mode == Execute)
370 return translateInst(req, tc);
371 else
372 return translateData(req, tc, mode == Write);
373 }
374
375 void
376 TLB::translateTiming(const RequestPtr &req, ThreadContext *tc,
377 Translation *translation, Mode mode)
378 {
379 assert(translation);
380 translation->finish(translateAtomic(req, tc, mode), req, tc, mode);
381 }
382
383 Fault
384 TLB::translateFunctional(const RequestPtr &req, ThreadContext *tc, Mode mode)
385 {
386 panic_if(FullSystem,
387 "translateFunctional not implemented for full system.");
388
389 const Addr vaddr = req->getVaddr();
390 Process *process = tc->getProcessPtr();
391 const auto *pte = process->pTable->lookup(vaddr);
392
393 if (!pte && mode != Execute) {
394 // Check if we just need to grow the stack.
395 if (process->fixupFault(vaddr)) {
396 // If we did, lookup the entry for the new page.
397 pte = process->pTable->lookup(vaddr);
398 }
399 }
400
401 if (!pte)
402 return std::make_shared<GenericPageTableFault>(req->getVaddr());
403
404 Addr paddr = pte->paddr | process->pTable->pageOffset(vaddr);
405
406 DPRINTF(TLB, "Translated (functional) %#x -> %#x.\n", vaddr, paddr);
407 req->setPaddr(paddr);
408 return NoFault;
409 }
410
411 Fault
412 TLB::finalizePhysical(const RequestPtr &req,
413 ThreadContext *tc, Mode mode) const
414 {
415 return NoFault;
416 }
417
418
419 RiscvISA::PTE &
420 TLB::index(bool advance)
421 {
422 PTE *pte = &table[nlu];
423
424 if (advance)
425 nextnlu();
426
427 return *pte;
428 }
429
430 RiscvISA::TLB *
431 RiscvTLBParams::create()
432 {
433 return new TLB(this);
434 }