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