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 * Authors: Nathan Binkert
30 *          Steve Reinhardt
31 *          Jaidev Patwardhan
32 *          Zhengxing Li
33 *          Deyuan Guo
34 */
35
36#include "arch/riscv/tlb.hh"
37
38#include <string>
39#include <vector>
40
41#include "arch/riscv/faults.hh"
42#include "arch/riscv/pagetable.hh"
43#include "arch/riscv/pra_constants.hh"
44#include "arch/riscv/system.hh"
45#include "arch/riscv/utility.hh"
46#include "base/inifile.hh"
47#include "base/str.hh"
48#include "base/trace.hh"
49#include "cpu/thread_context.hh"
50#include "debug/RiscvTLB.hh"
51#include "debug/TLB.hh"
52#include "mem/page_table.hh"
53#include "params/RiscvTLB.hh"
54#include "sim/full_system.hh"
55#include "sim/process.hh"
56
57using namespace std;
58using namespace RiscvISA;
59
60///////////////////////////////////////////////////////////////////////
61//
62//  RISC-V TLB
63//
64
65TLB::TLB(const Params *p)
66    : BaseTLB(p), size(p->size), nlu(0)
67{
68    table = new PTE[size];
69    memset(table, 0, sizeof(PTE[size]));
70    smallPages = 0;
71}
72
73TLB::~TLB()
74{
75    if (table)
76        delete [] table;
77}
78
79// look up an entry in the TLB
80RiscvISA::PTE *
81TLB::lookup(Addr vpn, uint8_t asn) const
82{
83    // assume not found...
84    PTE *retval = nullptr;
85    PageTable::const_iterator i = lookupTable.find(vpn);
86    if (i != lookupTable.end()) {
87        while (i->first == vpn) {
88            int index = i->second;
89            PTE *pte = &table[index];
90
91            /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
92            Addr Mask = pte->Mask;
93            Addr InvMask = ~Mask;
94            Addr VPN  = pte->VPN;
95            if (((vpn & InvMask) == (VPN & InvMask)) &&
96                    (pte->G  || (asn == pte->asid))) {
97                // We have a VPN + ASID Match
98                retval = pte;
99                break;
100            }
101            ++i;
102        }
103    }
104
105    DPRINTF(TLB, "lookup %#x, asn %#x -> %s ppn %#x\n", vpn, (int)asn,
106            retval ? "hit" : "miss", retval ? retval->PFN1 : 0);
107    return retval;
108}
109
110RiscvISA::PTE*
111TLB::getEntry(unsigned Index) const
112{
113    // Make sure that Index is valid
114    assert(Index<size);
115    return &table[Index];
116}
117
118int
119TLB::probeEntry(Addr vpn, uint8_t asn) const
120{
121    // assume not found...
122    int Ind = -1;
123    PageTable::const_iterator i = lookupTable.find(vpn);
124    if (i != lookupTable.end()) {
125        while (i->first == vpn) {
126            int index = i->second;
127            PTE *pte = &table[index];
128
129            /* 1KB TLB Lookup code - from MIPS ARM Volume III - Rev. 2.50 */
130            Addr Mask = pte->Mask;
131            Addr InvMask = ~Mask;
132            Addr VPN = pte->VPN;
133            if (((vpn & InvMask) == (VPN & InvMask)) &&
134                    (pte->G  || (asn == pte->asid))) {
135                // We have a VPN + ASID Match
136                Ind = index;
137                break;
138            }
139            ++i;
140        }
141    }
142    DPRINTF(RiscvTLB,"VPN: %x, asid: %d, Result of TLBP: %d\n",vpn,asn,Ind);
143    return Ind;
144}
145
146inline Fault
147TLB::checkCacheability(const RequestPtr &req)
148{
149    Addr VAddrUncacheable = 0xA0000000;
150    // In MIPS, cacheability is controlled by certain bits of the virtual
151    // address or by the TLB entry
152    if ((req->getVaddr() & VAddrUncacheable) == VAddrUncacheable) {
153        // mark request as uncacheable
154        req->setFlags(Request::UNCACHEABLE | Request::STRICT_ORDER);
155    }
156    return NoFault;
157}
158
159void
160TLB::insertAt(PTE &pte, unsigned Index, int _smallPages)
161{
162    smallPages = _smallPages;
163    if (Index > size) {
164        warn("Attempted to write at index (%d) beyond TLB size (%d)",
165                Index, size);
166    } else {
167        // Update TLB
168        DPRINTF(TLB, "TLB[%d]: %x %x %x %x\n",
169                Index, pte.Mask << 11,
170                ((pte.VPN << 11) | pte.asid),
171                ((pte.PFN0 << 6) | (pte.C0 << 3) |
172                 (pte.D0 << 2) | (pte.V0 <<1) | pte.G),
173                ((pte.PFN1 <<6) | (pte.C1 << 3) |
174                 (pte.D1 << 2) | (pte.V1 <<1) | pte.G));
175        if (table[Index].V0 || table[Index].V1) {
176            // Previous entry is valid
177            PageTable::iterator i = lookupTable.find(table[Index].VPN);
178            lookupTable.erase(i);
179        }
180        table[Index]=pte;
181        // Update fast lookup table
182        lookupTable.insert(make_pair(table[Index].VPN, Index));
183    }
184}
185
186// insert a new TLB entry
187void
188TLB::insert(Addr addr, PTE &pte)
189{
190    fatal("TLB Insert not yet implemented\n");
191}
192
193void
194TLB::flushAll()
195{
196    DPRINTF(TLB, "flushAll\n");
197    memset(table, 0, sizeof(PTE[size]));
198    lookupTable.clear();
199    nlu = 0;
200}
201
202void
203TLB::serialize(CheckpointOut &cp) const
204{
205    SERIALIZE_SCALAR(size);
206    SERIALIZE_SCALAR(nlu);
207
208    for (int i = 0; i < size; i++) {
209        ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
210        table[i].serialize(cp);
211    }
212}
213
214void
215TLB::unserialize(CheckpointIn &cp)
216{
217    UNSERIALIZE_SCALAR(size);
218    UNSERIALIZE_SCALAR(nlu);
219
220    for (int i = 0; i < size; i++) {
221        ScopedCheckpointSection sec(cp, csprintf("PTE%d", i));
222        table[i].unserialize(cp);
223        if (table[i].V0 || table[i].V1) {
224            lookupTable.insert(make_pair(table[i].VPN, i));
225        }
226    }
227}
228
229void
230TLB::regStats()
231{
232    BaseTLB::regStats();
233
234    read_hits
235        .name(name() + ".read_hits")
236        .desc("DTB read hits")
237        ;
238
239    read_misses
240        .name(name() + ".read_misses")
241        .desc("DTB read misses")
242        ;
243
244
245    read_accesses
246        .name(name() + ".read_accesses")
247        .desc("DTB read accesses")
248        ;
249
250    write_hits
251        .name(name() + ".write_hits")
252        .desc("DTB write hits")
253        ;
254
255    write_misses
256        .name(name() + ".write_misses")
257        .desc("DTB write misses")
258        ;
259
260
261    write_accesses
262        .name(name() + ".write_accesses")
263        .desc("DTB write accesses")
264        ;
265
266    hits
267        .name(name() + ".hits")
268        .desc("DTB hits")
269        ;
270
271    misses
272        .name(name() + ".misses")
273        .desc("DTB misses")
274        ;
275
276    accesses
277        .name(name() + ".accesses")
278        .desc("DTB accesses")
279        ;
280
281    hits = read_hits + write_hits;
282    misses = read_misses + write_misses;
283    accesses = read_accesses + write_accesses;
284}
285
286Fault
287TLB::translateInst(const RequestPtr &req, ThreadContext *tc)
288{
289    if (FullSystem) {
290        /**
291         * check if we simulate a bare metal system
292         * if so, we have no tlb, phys addr == virt addr
293         */
294        if (static_cast<RiscvSystem *>(tc->getSystemPtr())->isBareMetal())
295            req->setFlags(Request::PHYSICAL);
296
297        if (req->getFlags() & Request::PHYSICAL) {
298            /**
299             * we simply set the virtual address to physical address
300             */
301            req->setPaddr(req->getVaddr());
302            return checkCacheability(req);
303        } else {
304            /**
305             * as we currently support bare metal only, we throw a panic,
306             * if it is not a bare metal system
307             */
308            panic("translateInst not implemented in RISC-V.\n");
309        }
310    } else {
311        Process * p = tc->getProcessPtr();
312
313        Fault fault = p->pTable->translate(req);
314        if (fault != NoFault)
315            return fault;
316
317        return NoFault;
318    }
319}
320
321Fault
322TLB::translateData(const RequestPtr &req, ThreadContext *tc, bool write)
323{
324    if (FullSystem) {
325        /**
326         * check if we simulate a bare metal system
327         * if so, we have no tlb, phys addr == virt addr
328         */
329        if (static_cast<RiscvSystem *>(tc->getSystemPtr())->isBareMetal())
330            req->setFlags(Request::PHYSICAL);
331
332        if (req->getFlags() & Request::PHYSICAL) {
333            /**
334             * we simply set the virtual address to physical address
335             */
336            req->setPaddr(req->getVaddr());
337            return checkCacheability(req);
338        } else {
339            /**
340             * as we currently support bare metal only, we throw a panic,
341             * if it is not a bare metal system
342             */
343            panic("translateData not implemented in RISC-V.\n");
344        }
345    } else {
346        // In the O3 CPU model, sometimes a memory access will be speculatively
347        // executed along a branch that will end up not being taken where the
348        // address is invalid.  In that case, return a fault rather than trying
349        // to translate it (which will cause a panic).  Since RISC-V allows
350        // unaligned memory accesses, this should only happen if the request's
351        // length is long enough to wrap around from the end of the memory to
352        // the start.
353        assert(req->getSize() > 0);
354        if (req->getVaddr() + req->getSize() - 1 < req->getVaddr())
355            return make_shared<GenericPageTableFault>(req->getVaddr());
356
357        Process * p = tc->getProcessPtr();
358
359        Fault fault = p->pTable->translate(req);
360        if (fault != NoFault)
361            return fault;
362
363        return NoFault;
364    }
365}
366
367Fault
368TLB::translateAtomic(const RequestPtr &req, ThreadContext *tc, Mode mode)
369{
370    if (mode == Execute)
371        return translateInst(req, tc);
372    else
373        return translateData(req, tc, mode == Write);
374}
375
376void
377TLB::translateTiming(const RequestPtr &req, ThreadContext *tc,
378        Translation *translation, Mode mode)
379{
380    assert(translation);
381    translation->finish(translateAtomic(req, tc, mode), req, tc, mode);
382}
383
384Fault
385TLB::finalizePhysical(const RequestPtr &req,
386                      ThreadContext *tc, Mode mode) const
387{
388    return NoFault;
389}
390
391
392RiscvISA::PTE &
393TLB::index(bool advance)
394{
395    PTE *pte = &table[nlu];
396
397    if (advance)
398        nextnlu();
399
400    return *pte;
401}
402
403RiscvISA::TLB *
404RiscvTLBParams::create()
405{
406    return new TLB(this);
407}
408