tlb.cc revision 3806:65ae5388c059
1/*
2 * Copyright (c) 2001-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Ali Saidi
29 */
30
31#include "arch/sparc/asi.hh"
32#include "arch/sparc/tlb.hh"
33#include "sim/builder.hh"
34#include "arch/sparc/miscregfile.hh"
35#include "cpu/thread_context.hh"
36
37/* @todo remove some of the magic constants.  -- ali
38 * */
39namespace SparcISA
40{
41
42TLB::TLB(const std::string &name, int s)
43    : SimObject(name), size(s)
44{
45    // To make this work you'll have to change the hypervisor and OS
46    if (size > 64)
47        fatal("SPARC T1 TLB registers don't support more than 64 TLB entries.");
48
49    tlb = new TlbEntry[size];
50    memset(tlb, 0, sizeof(TlbEntry) * size);
51}
52
53void
54TLB::clearUsedBits()
55{
56    MapIter i;
57    for (i = lookupTable.begin(); i != lookupTable.end();) {
58        TlbEntry *t = i->second;
59        if (!t->pte.locked()) {
60            t->used = false;
61            usedEntries--;
62        }
63    }
64}
65
66
67void
68TLB::insert(Addr va, int partition_id, int context_id, bool real,
69        const PageTableEntry& PTE)
70{
71
72
73    MapIter i;
74    TlbEntry *new_entry;
75    int x = -1;
76    for (x = 0; x < size; x++) {
77        if (!tlb[x].valid || !tlb[x].used)  {
78            new_entry = &tlb[x];
79            break;
80        }
81    }
82
83    // Update the last ently if their all locked
84    if (x == -1)
85       x = size - 1;
86
87    assert(PTE.valid());
88    new_entry->range.va = va;
89    new_entry->range.size = PTE.size();
90    new_entry->range.partitionId = partition_id;
91    new_entry->range.contextId = context_id;
92    new_entry->range.real = real;
93    new_entry->pte = PTE;
94    new_entry->used = true;;
95    new_entry->valid = true;
96    usedEntries++;
97
98
99    // Demap any entry that conflicts
100    i = lookupTable.find(new_entry->range);
101    if (i != lookupTable.end()) {
102        i->second->valid = false;
103        if (i->second->used) {
104            i->second->used = false;
105            usedEntries--;
106        }
107        lookupTable.erase(i);
108    }
109
110    lookupTable.insert(new_entry->range, new_entry);;
111
112    // If all entries have there used bit set, clear it on them all, but the
113    // one we just inserted
114    if (usedEntries == size) {
115        clearUsedBits();
116        new_entry->used = true;
117        usedEntries++;
118    }
119
120}
121
122
123TlbEntry*
124TLB::lookup(Addr va, int partition_id, bool real, int context_id)
125{
126    MapIter i;
127    TlbRange tr;
128    TlbEntry *t;
129
130    // Assemble full address structure
131    tr.va = va;
132    tr.size = va + MachineBytes;
133    tr.contextId = context_id;
134    tr.partitionId = partition_id;
135    tr.real = real;
136
137    // Try to find the entry
138    i = lookupTable.find(tr);
139    if (i == lookupTable.end()) {
140        return NULL;
141    }
142
143    // Mark the entries used bit and clear other used bits in needed
144    t = i->second;
145    if (!t->used) {
146        t->used = true;
147        usedEntries++;
148        if (usedEntries == size) {
149            clearUsedBits();
150            t->used = true;
151            usedEntries++;
152        }
153    }
154
155    return t;
156}
157
158
159void
160TLB::demapPage(Addr va, int partition_id, bool real, int context_id)
161{
162    TlbRange tr;
163    MapIter i;
164
165    // Assemble full address structure
166    tr.va = va;
167    tr.size = va + MachineBytes;
168    tr.contextId = context_id;
169    tr.partitionId = partition_id;
170    tr.real = real;
171
172    // Demap any entry that conflicts
173    i = lookupTable.find(tr);
174    if (i != lookupTable.end()) {
175        i->second->valid = false;
176        if (i->second->used) {
177            i->second->used = false;
178            usedEntries--;
179        }
180        lookupTable.erase(i);
181    }
182}
183
184void
185TLB::demapContext(int partition_id, int context_id)
186{
187    int x;
188    for (x = 0; x < size; x++) {
189        if (tlb[x].range.contextId == context_id &&
190            tlb[x].range.partitionId == partition_id) {
191            tlb[x].valid = false;
192            if (tlb[x].used) {
193                tlb[x].used = false;
194                usedEntries--;
195            }
196            lookupTable.erase(tlb[x].range);
197        }
198    }
199}
200
201void
202TLB::demapAll(int partition_id)
203{
204    int x;
205    for (x = 0; x < size; x++) {
206        if (!tlb[x].pte.locked() && tlb[x].range.partitionId == partition_id) {
207            tlb[x].valid = false;
208            if (tlb[x].used) {
209                tlb[x].used = false;
210                usedEntries--;
211            }
212            lookupTable.erase(tlb[x].range);
213        }
214    }
215}
216
217void
218TLB::invalidateAll()
219{
220    int x;
221    for (x = 0; x < size; x++) {
222        tlb[x].valid = false;
223    }
224    usedEntries = 0;
225}
226
227uint64_t
228TLB::TteRead(int entry) {
229    assert(entry < size);
230    return tlb[entry].pte();
231}
232
233uint64_t
234TLB::TagRead(int entry) {
235    assert(entry < size);
236    uint64_t tag;
237
238    tag = tlb[entry].range.contextId | tlb[entry].range.va |
239          (uint64_t)tlb[entry].range.partitionId << 61;
240    tag |= tlb[entry].range.real ? ULL(1) << 60 : 0;
241    tag |= (uint64_t)~tlb[entry].pte._size() << 56;
242    return tag;
243}
244
245bool
246TLB::validVirtualAddress(Addr va, bool am)
247{
248    if (am)
249        return true;
250    if (va >= StartVAddrHole && va <= EndVAddrHole)
251        return false;
252    return true;
253}
254
255void
256TLB::writeSfsr(ThreadContext *tc, int reg,  bool write, ContextType ct,
257        bool se, FaultTypes ft, int asi)
258{
259    uint64_t sfsr;
260    sfsr = tc->readMiscReg(reg);
261
262    if (sfsr & 0x1)
263        sfsr = 0x3;
264    else
265        sfsr = 1;
266
267    if (write)
268        sfsr |= 1 << 2;
269    sfsr |= ct << 4;
270    if (se)
271        sfsr |= 1 << 6;
272    sfsr |= ft << 7;
273    sfsr |= asi << 16;
274    tc->setMiscReg(reg, sfsr);
275}
276
277
278void
279ITB::writeSfsr(ThreadContext *tc, bool write, ContextType ct,
280        bool se, FaultTypes ft, int asi)
281{
282    TLB::writeSfsr(tc, MISCREG_MMU_ITLB_SFSR, write, ct, se, ft, asi);
283}
284
285void
286DTB::writeSfr(ThreadContext *tc, Addr a, bool write, ContextType ct,
287        bool se, FaultTypes ft, int asi)
288{
289    TLB::writeSfsr(tc, MISCREG_MMU_DTLB_SFSR, write, ct, se, ft, asi);
290    tc->setMiscReg(MISCREG_MMU_DTLB_SFAR, a);
291}
292
293
294Fault
295ITB::translate(RequestPtr &req, ThreadContext *tc)
296{
297    uint64_t hpstate = tc->readMiscReg(MISCREG_HPSTATE);
298    uint64_t pstate = tc->readMiscReg(MISCREG_PSTATE);
299    bool lsuIm = tc->readMiscReg(MISCREG_MMU_LSU_CTRL) >> 2 & 0x1;
300    uint64_t tl = tc->readMiscReg(MISCREG_TL);
301    uint64_t part_id = tc->readMiscReg(MISCREG_MMU_PART_ID);
302    bool addr_mask = pstate >> 3 & 0x1;
303    bool priv = pstate >> 2 & 0x1;
304    Addr vaddr = req->getVaddr();
305    int context;
306    ContextType ct;
307    int asi;
308    bool real = false;
309    TlbEntry *e;
310
311    assert(req->getAsi() == ASI_IMPLICIT);
312
313    if (tl > 0) {
314        asi = ASI_N;
315        ct = Nucleus;
316        context = 0;
317    } else {
318        asi = ASI_P;
319        ct = Primary;
320        context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
321    }
322
323    if ( hpstate >> 2 & 0x1 || hpstate >> 5 & 0x1 ) {
324        req->setPaddr(req->getVaddr() & PAddrImplMask);
325        return NoFault;
326    }
327
328    // If the asi is unaligned trap
329    if (vaddr & 0x7) {
330        writeSfsr(tc, false, ct, false, OtherFault, asi);
331        return new MemAddressNotAligned;
332    }
333
334    if (addr_mask)
335        vaddr = vaddr & VAddrAMask;
336
337    if (!validVirtualAddress(vaddr, addr_mask)) {
338        writeSfsr(tc, false, ct, false, VaOutOfRange, asi);
339        return new InstructionAccessException;
340    }
341
342    if (lsuIm) {
343        e = lookup(req->getVaddr(), part_id, true);
344        real = true;
345        context = 0;
346    } else {
347        e = lookup(vaddr, part_id, false, context);
348    }
349
350    if (e == NULL || !e->valid) {
351        tc->setMiscReg(MISCREG_MMU_ITLB_TAG_ACCESS,
352                vaddr & ~BytesInPageMask | context);
353        if (real)
354            return new InstructionRealTranslationMiss;
355        else
356            return new FastInstructionAccessMMUMiss;
357    }
358
359    // were not priviledged accesing priv page
360    if (!priv && e->pte.priv()) {
361        writeSfsr(tc, false, ct, false, PrivViolation, asi);
362        return new InstructionAccessException;
363    }
364
365    req->setPaddr(e->pte.paddr() & ~e->pte.size() |
366                  req->getVaddr() & e->pte.size());
367    return NoFault;
368}
369
370
371
372Fault
373DTB::translate(RequestPtr &req, ThreadContext *tc, bool write)
374{
375    /* @todo this could really use some profiling and fixing to make it faster! */
376    uint64_t hpstate = tc->readMiscReg(MISCREG_HPSTATE);
377    uint64_t pstate = tc->readMiscReg(MISCREG_PSTATE);
378    bool lsuDm = tc->readMiscReg(MISCREG_MMU_LSU_CTRL) >> 3 & 0x1;
379    uint64_t tl = tc->readMiscReg(MISCREG_TL);
380    uint64_t part_id = tc->readMiscReg(MISCREG_MMU_PART_ID);
381    bool hpriv = hpstate >> 2 & 0x1;
382    bool red = hpstate >> 5 >> 0x1;
383    bool addr_mask = pstate >> 3 & 0x1;
384    bool priv = pstate >> 2 & 0x1;
385    bool implicit = false;
386    bool real = false;
387    Addr vaddr = req->getVaddr();
388    ContextType ct;
389    int context;
390    ASI asi;
391
392    TlbEntry *e;
393
394
395    asi = (ASI)req->getAsi();
396    if (asi == ASI_IMPLICIT)
397        implicit = true;
398
399    if (implicit) {
400        if (tl > 0) {
401            asi = ASI_N;
402            ct = Nucleus;
403            context = 0;
404        } else {
405            asi = ASI_P;
406            ct = Primary;
407            context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
408        }
409    } else if (!hpriv && !red) {
410        if (tl > 0) {
411            ct = Nucleus;
412            context = 0;
413        } else if (AsiIsSecondary(asi)) {
414            ct = Secondary;
415            context = tc->readMiscReg(MISCREG_MMU_S_CONTEXT);
416        } else {
417            context = tc->readMiscReg(MISCREG_MMU_P_CONTEXT);
418            ct = Primary; //???
419        }
420
421        // We need to check for priv level/asi priv
422        if (!priv && !AsiIsUnPriv(asi)) {
423            // It appears that context should be Nucleus in these cases?
424            writeSfr(tc, vaddr, write, Nucleus, false, IllegalAsi, asi);
425            return new PrivilegedAction;
426        }
427        if (priv && AsiIsHPriv(asi)) {
428            writeSfr(tc, vaddr, write, Nucleus, false, IllegalAsi, asi);
429            return new DataAccessException;
430        }
431
432    }
433
434    // If the asi is unaligned trap
435    if (AsiIsBlock(asi) && vaddr & 0x3f || vaddr & 0x7) {
436        writeSfr(tc, vaddr, false, ct, false, OtherFault, asi);
437        return new MemAddressNotAligned;
438    }
439
440    if (addr_mask)
441        vaddr = vaddr & VAddrAMask;
442
443    if (!validVirtualAddress(vaddr, addr_mask)) {
444        writeSfr(tc, vaddr, false, ct, true, VaOutOfRange, asi);
445        return new DataAccessException;
446    }
447
448    if (!implicit) {
449        if (AsiIsLittle(asi))
450            panic("Little Endian ASIs not supported\n");
451        if (AsiIsBlock(asi))
452            panic("Block ASIs not supported\n");
453        if (AsiIsNoFault(asi))
454            panic("No Fault ASIs not supported\n");
455        if (AsiIsTwin(asi))
456            panic("Twin ASIs not supported\n");
457        if (AsiIsPartialStore(asi))
458            panic("Partial Store ASIs not supported\n");
459        if (AsiIsMmu(asi))
460            goto handleMmuRegAccess;
461
462        if (AsiIsScratchPad(asi))
463            goto handleScratchRegAccess;
464    }
465
466    if ((!lsuDm && !hpriv) || AsiIsReal(asi)) {
467        real = true;
468        context = 0;
469    };
470
471    if (hpriv && (implicit || (!AsiIsAsIfUser(asi) && !AsiIsReal(asi)))) {
472        req->setPaddr(req->getVaddr() & PAddrImplMask);
473        return NoFault;
474    }
475
476    e = lookup(req->getVaddr(), part_id, real, context);
477
478    if (e == NULL || !e->valid) {
479        tc->setMiscReg(MISCREG_MMU_DTLB_TAG_ACCESS,
480                vaddr & ~BytesInPageMask | context);
481        if (real)
482            return new DataRealTranslationMiss;
483        else
484            return new FastDataAccessMMUMiss;
485
486    }
487
488
489    if (write && !e->pte.writable()) {
490        writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), OtherFault, asi);
491        return new FastDataAccessProtection;
492    }
493
494    if (e->pte.nofault() && !AsiIsNoFault(asi)) {
495        writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), LoadFromNfo, asi);
496        return new DataAccessException;
497    }
498
499    if (e->pte.sideffect())
500        req->setFlags(req->getFlags() | UNCACHEABLE);
501
502
503    if (!priv && e->pte.priv()) {
504        writeSfr(tc, vaddr, write, ct, e->pte.sideffect(), PrivViolation, asi);
505        return new DataAccessException;
506    }
507
508    req->setPaddr(e->pte.paddr() & ~e->pte.size() |
509                  req->getVaddr() & e->pte.size());
510    return NoFault;
511    /** Normal flow ends here. */
512
513handleScratchRegAccess:
514    if (vaddr > 0x38 || (vaddr >= 0x20 && vaddr < 0x30 && !hpriv)) {
515        writeSfr(tc, vaddr, write, Primary, true, IllegalAsi, asi);
516        return new DataAccessException;
517    }
518handleMmuRegAccess:
519    req->setMmapedIpr(true);
520    req->setPaddr(req->getVaddr());
521    return NoFault;
522};
523
524Tick
525DTB::doMmuRegRead(ThreadContext *tc, Packet *pkt)
526{
527    panic("need to implement DTB::doMmuRegRead()\n");
528}
529
530Tick
531DTB::doMmuRegWrite(ThreadContext *tc, Packet *pkt)
532{
533    panic("need to implement DTB::doMmuRegWrite()\n");
534}
535
536void
537TLB::serialize(std::ostream &os)
538{
539    panic("Need to implement serialize tlb for SPARC\n");
540}
541
542void
543TLB::unserialize(Checkpoint *cp, const std::string &section)
544{
545    panic("Need to implement unserialize tlb for SPARC\n");
546}
547
548
549DEFINE_SIM_OBJECT_CLASS_NAME("SparcTLB", TLB)
550
551BEGIN_DECLARE_SIM_OBJECT_PARAMS(ITB)
552
553    Param<int> size;
554
555END_DECLARE_SIM_OBJECT_PARAMS(ITB)
556
557BEGIN_INIT_SIM_OBJECT_PARAMS(ITB)
558
559    INIT_PARAM_DFLT(size, "TLB size", 48)
560
561END_INIT_SIM_OBJECT_PARAMS(ITB)
562
563
564CREATE_SIM_OBJECT(ITB)
565{
566    return new ITB(getInstanceName(), size);
567}
568
569REGISTER_SIM_OBJECT("SparcITB", ITB)
570
571BEGIN_DECLARE_SIM_OBJECT_PARAMS(DTB)
572
573    Param<int> size;
574
575END_DECLARE_SIM_OBJECT_PARAMS(DTB)
576
577BEGIN_INIT_SIM_OBJECT_PARAMS(DTB)
578
579    INIT_PARAM_DFLT(size, "TLB size", 64)
580
581END_INIT_SIM_OBJECT_PARAMS(DTB)
582
583
584CREATE_SIM_OBJECT(DTB)
585{
586    return new DTB(getInstanceName(), size);
587}
588
589REGISTER_SIM_OBJECT("SparcDTB", DTB)
590}
591