]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/mips/mips/tlb.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / mips / mips / tlb.c
1 /*-
2  * Copyright (c) 2004-2010 Juli Mallett <jmallett@FreeBSD.org>
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
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. 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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include "opt_ddb.h"
30
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/pcpu.h>
35 #include <sys/smp.h>
36
37 #include <vm/vm.h>
38 #include <vm/vm_page.h>
39
40 #include <machine/pte.h>
41 #include <machine/tlb.h>
42
43 struct tlb_state {
44         unsigned wired;
45         struct tlb_entry {
46                 register_t entryhi;
47                 register_t entrylo0;
48                 register_t entrylo1;
49         } entry[MIPS_MAX_TLB_ENTRIES];
50 };
51
52 static struct tlb_state tlb_state[MAXCPU];
53
54 #if 0
55 /*
56  * PageMask must increment in steps of 2 bits.
57  */
58 COMPILE_TIME_ASSERT(POPCNT(TLBMASK_MASK) % 2 == 0);
59 #endif
60
61 static inline void
62 tlb_probe(void)
63 {
64         __asm __volatile ("tlbp" : : : "memory");
65         mips_cp0_sync();
66 }
67
68 static inline void
69 tlb_read(void)
70 {
71         __asm __volatile ("tlbr" : : : "memory");
72         mips_cp0_sync();
73 }
74
75 static inline void
76 tlb_write_indexed(void)
77 {
78         __asm __volatile ("tlbwi" : : : "memory");
79         mips_cp0_sync();
80 }
81
82 static inline void
83 tlb_write_random(void)
84 {
85         __asm __volatile ("tlbwr" : : : "memory");
86         mips_cp0_sync();
87 }
88
89 static void tlb_invalidate_one(unsigned);
90
91 void
92 tlb_insert_wired(unsigned i, vm_offset_t va, pt_entry_t pte0, pt_entry_t pte1)
93 {
94         register_t asid;
95         register_t s;
96
97         va &= ~PAGE_MASK;
98
99         s = intr_disable();
100         asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
101
102         mips_wr_index(i);
103         mips_wr_pagemask(0);
104         mips_wr_entryhi(TLBHI_ENTRY(va, 0));
105         mips_wr_entrylo0(pte0);
106         mips_wr_entrylo1(pte1);
107         tlb_write_indexed();
108
109         mips_wr_entryhi(asid);
110         intr_restore(s);
111 }
112
113 void
114 tlb_invalidate_address(struct pmap *pmap, vm_offset_t va)
115 {
116         register_t asid;
117         register_t s;
118         int i;
119
120         va &= ~PAGE_MASK;
121
122         s = intr_disable();
123         asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
124
125         mips_wr_pagemask(0);
126         mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap)));
127         tlb_probe();
128         i = mips_rd_index();
129         if (i >= 0)
130                 tlb_invalidate_one(i);
131
132         mips_wr_entryhi(asid);
133         intr_restore(s);
134 }
135
136 void
137 tlb_invalidate_all(void)
138 {
139         register_t asid;
140         register_t s;
141         unsigned i;
142
143         s = intr_disable();
144         asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
145
146         for (i = mips_rd_wired(); i < num_tlbentries; i++)
147                 tlb_invalidate_one(i);
148
149         mips_wr_entryhi(asid);
150         intr_restore(s);
151 }
152
153 void
154 tlb_invalidate_all_user(struct pmap *pmap)
155 {
156         register_t asid;
157         register_t s;
158         unsigned i;
159
160         s = intr_disable();
161         asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
162
163         for (i = mips_rd_wired(); i < num_tlbentries; i++) {
164                 register_t uasid;
165
166                 mips_wr_index(i);
167                 tlb_read();
168
169                 uasid = mips_rd_entryhi() & TLBHI_ASID_MASK;
170                 if (pmap == NULL) {
171                         /*
172                          * Invalidate all non-kernel entries.
173                          */
174                         if (uasid == 0)
175                                 continue;
176                 } else {
177                         /*
178                          * Invalidate this pmap's entries.
179                          */
180                         if (uasid != pmap_asid(pmap))
181                                 continue;
182                 }
183                 tlb_invalidate_one(i);
184         }
185
186         mips_wr_entryhi(asid);
187         intr_restore(s);
188 }
189
190 /* XXX Only if DDB?  */
191 void
192 tlb_save(void)
193 {
194         unsigned i, cpu;
195
196         cpu = PCPU_GET(cpuid);
197
198         tlb_state[cpu].wired = mips_rd_wired();
199         for (i = 0; i < num_tlbentries; i++) {
200                 mips_wr_index(i);
201                 tlb_read();
202
203                 tlb_state[cpu].entry[i].entryhi = mips_rd_entryhi();
204                 tlb_state[cpu].entry[i].entrylo0 = mips_rd_entrylo0();
205                 tlb_state[cpu].entry[i].entrylo1 = mips_rd_entrylo1();
206         }
207 }
208
209 void
210 tlb_update(struct pmap *pmap, vm_offset_t va, pt_entry_t pte)
211 {
212         register_t asid;
213         register_t s;
214         int i;
215
216         va &= ~PAGE_MASK;
217         pte &= ~TLBLO_SWBITS_MASK;
218
219         s = intr_disable();
220         asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
221
222         mips_wr_pagemask(0);
223         mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap)));
224         tlb_probe();
225         i = mips_rd_index();
226         if (i >= 0) {
227                 tlb_read();
228
229                 if ((va & PAGE_SIZE) == 0) {
230                         mips_wr_entrylo0(pte);
231                 } else {
232                         mips_wr_entrylo1(pte);
233                 }
234                 tlb_write_indexed();
235         }
236
237         mips_wr_entryhi(asid);
238         intr_restore(s);
239 }
240
241 static void
242 tlb_invalidate_one(unsigned i)
243 {
244         /* XXX an invalid ASID? */
245         mips_wr_entryhi(TLBHI_ENTRY(MIPS_KSEG0_START + (2 * i * PAGE_SIZE), 0));
246         mips_wr_entrylo0(0);
247         mips_wr_entrylo1(0);
248         mips_wr_pagemask(0);
249         mips_wr_index(i);
250         tlb_write_indexed();
251 }
252
253 #ifdef DDB
254 #include <ddb/ddb.h>
255
256 DB_SHOW_COMMAND(tlb, ddb_dump_tlb)
257 {
258         register_t ehi, elo0, elo1;
259         unsigned i, cpu;
260
261         /*
262          * XXX
263          * The worst conversion from hex to decimal ever.
264          */
265         if (have_addr)
266                 cpu = ((addr >> 4) % 16) * 10 + (addr % 16);
267         else
268                 cpu = PCPU_GET(cpuid);
269
270         if (cpu < 0 || cpu >= mp_ncpus) {
271                 db_printf("Invalid CPU %u\n", cpu);
272                 return;
273         }
274
275         if (cpu == PCPU_GET(cpuid))
276                 tlb_save();
277
278         db_printf("Beginning TLB dump for CPU %u...\n", cpu);
279         for (i = 0; i < num_tlbentries; i++) {
280                 if (i == tlb_state[cpu].wired) {
281                         if (i != 0)
282                                 db_printf("^^^ WIRED ENTRIES ^^^\n");
283                         else
284                                 db_printf("(No wired entries.)\n");
285                 }
286
287                 /* XXX PageMask.  */
288                 ehi = tlb_state[cpu].entry[i].entryhi;
289                 elo0 = tlb_state[cpu].entry[i].entrylo0;
290                 elo1 = tlb_state[cpu].entry[i].entrylo1;
291
292                 if (elo0 == 0 && elo1 == 0)
293                         continue;
294
295                 db_printf("#%u\t=> %jx\n", i, (intmax_t)ehi);
296                 db_printf(" Lo0\t%jx\t(%#jx)\n", (intmax_t)elo0, (intmax_t)TLBLO_PTE_TO_PA(elo0));
297                 db_printf(" Lo1\t%jx\t(%#jx)\n", (intmax_t)elo1, (intmax_t)TLBLO_PTE_TO_PA(elo1));
298         }
299         db_printf("Finished.\n");
300 }
301 #endif