1209243Sjchandra/*-
2209243Sjchandra * Copyright (c) 2004-2010 Juli Mallett <jmallett@FreeBSD.org>
3209243Sjchandra * All rights reserved.
4209243Sjchandra *
5209243Sjchandra * Redistribution and use in source and binary forms, with or without
6209243Sjchandra * modification, are permitted provided that the following conditions
7209243Sjchandra * are met:
8209243Sjchandra * 1. Redistributions of source code must retain the above copyright
9209243Sjchandra *    notice, this list of conditions and the following disclaimer.
10209243Sjchandra * 2. Redistributions in binary form must reproduce the above copyright
11209243Sjchandra *    notice, this list of conditions and the following disclaimer in the
12209243Sjchandra *    documentation and/or other materials provided with the distribution.
13209243Sjchandra *
14209243Sjchandra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15209243Sjchandra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16209243Sjchandra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17209243Sjchandra * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18209243Sjchandra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19209243Sjchandra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20209243Sjchandra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21209243Sjchandra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22209243Sjchandra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23209243Sjchandra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24209243Sjchandra * SUCH DAMAGE.
25209243Sjchandra *
26209243Sjchandra * $FreeBSD$
27209243Sjchandra */
28209243Sjchandra
29209243Sjchandra#include "opt_ddb.h"
30209243Sjchandra
31209243Sjchandra#include <sys/param.h>
32209243Sjchandra#include <sys/kernel.h>
33209243Sjchandra#include <sys/systm.h>
34209243Sjchandra#include <sys/pcpu.h>
35209243Sjchandra#include <sys/smp.h>
36209243Sjchandra
37209243Sjchandra#include <vm/vm.h>
38209243Sjchandra#include <vm/vm_page.h>
39209243Sjchandra
40209243Sjchandra#include <machine/pte.h>
41209243Sjchandra#include <machine/tlb.h>
42209243Sjchandra
43209243Sjchandrastruct tlb_state {
44209243Sjchandra	unsigned wired;
45209243Sjchandra	struct tlb_entry {
46209243Sjchandra		register_t entryhi;
47209243Sjchandra		register_t entrylo0;
48209243Sjchandra		register_t entrylo1;
49209243Sjchandra	} entry[MIPS_MAX_TLB_ENTRIES];
50209243Sjchandra};
51209243Sjchandra
52209243Sjchandrastatic struct tlb_state tlb_state[MAXCPU];
53209243Sjchandra
54209243Sjchandra#if 0
55209243Sjchandra/*
56209243Sjchandra * PageMask must increment in steps of 2 bits.
57209243Sjchandra */
58209243SjchandraCOMPILE_TIME_ASSERT(POPCNT(TLBMASK_MASK) % 2 == 0);
59209243Sjchandra#endif
60209243Sjchandra
61209243Sjchandrastatic inline void
62209243Sjchandratlb_probe(void)
63209243Sjchandra{
64209243Sjchandra	__asm __volatile ("tlbp" : : : "memory");
65209243Sjchandra	mips_cp0_sync();
66209243Sjchandra}
67209243Sjchandra
68209243Sjchandrastatic inline void
69209243Sjchandratlb_read(void)
70209243Sjchandra{
71209243Sjchandra	__asm __volatile ("tlbr" : : : "memory");
72209243Sjchandra	mips_cp0_sync();
73209243Sjchandra}
74209243Sjchandra
75209243Sjchandrastatic inline void
76209243Sjchandratlb_write_indexed(void)
77209243Sjchandra{
78209243Sjchandra	__asm __volatile ("tlbwi" : : : "memory");
79209243Sjchandra	mips_cp0_sync();
80209243Sjchandra}
81209243Sjchandra
82209243Sjchandrastatic inline void
83209243Sjchandratlb_write_random(void)
84209243Sjchandra{
85209243Sjchandra	__asm __volatile ("tlbwr" : : : "memory");
86209243Sjchandra	mips_cp0_sync();
87209243Sjchandra}
88209243Sjchandra
89209243Sjchandrastatic void tlb_invalidate_one(unsigned);
90209243Sjchandra
91209243Sjchandravoid
92209243Sjchandratlb_insert_wired(unsigned i, vm_offset_t va, pt_entry_t pte0, pt_entry_t pte1)
93209243Sjchandra{
94209645Sjchandra	register_t asid;
95209243Sjchandra	register_t s;
96209243Sjchandra
97209243Sjchandra	va &= ~PAGE_MASK;
98209243Sjchandra
99209243Sjchandra	s = intr_disable();
100209243Sjchandra	asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
101209243Sjchandra
102209243Sjchandra	mips_wr_index(i);
103209243Sjchandra	mips_wr_pagemask(0);
104209243Sjchandra	mips_wr_entryhi(TLBHI_ENTRY(va, 0));
105209243Sjchandra	mips_wr_entrylo0(pte0);
106209243Sjchandra	mips_wr_entrylo1(pte1);
107209243Sjchandra	tlb_write_indexed();
108209243Sjchandra
109209243Sjchandra	mips_wr_entryhi(asid);
110209243Sjchandra	intr_restore(s);
111209243Sjchandra}
112209243Sjchandra
113209243Sjchandravoid
114209243Sjchandratlb_invalidate_address(struct pmap *pmap, vm_offset_t va)
115209243Sjchandra{
116209645Sjchandra	register_t asid;
117209243Sjchandra	register_t s;
118209243Sjchandra	int i;
119209243Sjchandra
120209243Sjchandra	va &= ~PAGE_MASK;
121209243Sjchandra
122209243Sjchandra	s = intr_disable();
123209243Sjchandra	asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
124209243Sjchandra
125209243Sjchandra	mips_wr_pagemask(0);
126209243Sjchandra	mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap)));
127209243Sjchandra	tlb_probe();
128209243Sjchandra	i = mips_rd_index();
129209243Sjchandra	if (i >= 0)
130209243Sjchandra		tlb_invalidate_one(i);
131209243Sjchandra
132209243Sjchandra	mips_wr_entryhi(asid);
133209243Sjchandra	intr_restore(s);
134209243Sjchandra}
135209243Sjchandra
136209243Sjchandravoid
137209243Sjchandratlb_invalidate_all(void)
138209243Sjchandra{
139209645Sjchandra	register_t asid;
140209243Sjchandra	register_t s;
141209243Sjchandra	unsigned i;
142209243Sjchandra
143209243Sjchandra	s = intr_disable();
144209243Sjchandra	asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
145209243Sjchandra
146209243Sjchandra	for (i = mips_rd_wired(); i < num_tlbentries; i++)
147209243Sjchandra		tlb_invalidate_one(i);
148209243Sjchandra
149209243Sjchandra	mips_wr_entryhi(asid);
150209243Sjchandra	intr_restore(s);
151209243Sjchandra}
152209243Sjchandra
153209243Sjchandravoid
154209243Sjchandratlb_invalidate_all_user(struct pmap *pmap)
155209243Sjchandra{
156209645Sjchandra	register_t asid;
157209243Sjchandra	register_t s;
158209243Sjchandra	unsigned i;
159209243Sjchandra
160209243Sjchandra	s = intr_disable();
161209243Sjchandra	asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
162209243Sjchandra
163209243Sjchandra	for (i = mips_rd_wired(); i < num_tlbentries; i++) {
164209243Sjchandra		register_t uasid;
165209243Sjchandra
166209243Sjchandra		mips_wr_index(i);
167209243Sjchandra		tlb_read();
168209243Sjchandra
169209243Sjchandra		uasid = mips_rd_entryhi() & TLBHI_ASID_MASK;
170209243Sjchandra		if (pmap == NULL) {
171209243Sjchandra			/*
172209243Sjchandra			 * Invalidate all non-kernel entries.
173209243Sjchandra			 */
174209243Sjchandra			if (uasid == 0)
175209243Sjchandra				continue;
176209243Sjchandra		} else {
177209243Sjchandra			/*
178209243Sjchandra			 * Invalidate this pmap's entries.
179209243Sjchandra			 */
180209243Sjchandra			if (uasid != pmap_asid(pmap))
181209243Sjchandra				continue;
182209243Sjchandra		}
183209243Sjchandra		tlb_invalidate_one(i);
184209243Sjchandra	}
185209243Sjchandra
186209243Sjchandra	mips_wr_entryhi(asid);
187209243Sjchandra	intr_restore(s);
188209243Sjchandra}
189209243Sjchandra
190209243Sjchandra/* XXX Only if DDB?  */
191209243Sjchandravoid
192209243Sjchandratlb_save(void)
193209243Sjchandra{
194209243Sjchandra	unsigned i, cpu;
195209243Sjchandra
196209243Sjchandra	cpu = PCPU_GET(cpuid);
197209243Sjchandra
198209243Sjchandra	tlb_state[cpu].wired = mips_rd_wired();
199209243Sjchandra	for (i = 0; i < num_tlbentries; i++) {
200209243Sjchandra		mips_wr_index(i);
201209243Sjchandra		tlb_read();
202209243Sjchandra
203209243Sjchandra		tlb_state[cpu].entry[i].entryhi = mips_rd_entryhi();
204209243Sjchandra		tlb_state[cpu].entry[i].entrylo0 = mips_rd_entrylo0();
205209243Sjchandra		tlb_state[cpu].entry[i].entrylo1 = mips_rd_entrylo1();
206209243Sjchandra	}
207209243Sjchandra}
208209243Sjchandra
209209243Sjchandravoid
210209243Sjchandratlb_update(struct pmap *pmap, vm_offset_t va, pt_entry_t pte)
211209243Sjchandra{
212209645Sjchandra	register_t asid;
213209243Sjchandra	register_t s;
214209243Sjchandra	int i;
215209243Sjchandra
216209243Sjchandra	va &= ~PAGE_MASK;
217209243Sjchandra	pte &= ~TLBLO_SWBITS_MASK;
218209243Sjchandra
219209243Sjchandra	s = intr_disable();
220209243Sjchandra	asid = mips_rd_entryhi() & TLBHI_ASID_MASK;
221209243Sjchandra
222209243Sjchandra	mips_wr_pagemask(0);
223209243Sjchandra	mips_wr_entryhi(TLBHI_ENTRY(va, pmap_asid(pmap)));
224209243Sjchandra	tlb_probe();
225209243Sjchandra	i = mips_rd_index();
226209243Sjchandra	if (i >= 0) {
227209243Sjchandra		tlb_read();
228209243Sjchandra
229209243Sjchandra		if ((va & PAGE_SIZE) == 0) {
230209243Sjchandra			mips_wr_entrylo0(pte);
231209243Sjchandra		} else {
232209243Sjchandra			mips_wr_entrylo1(pte);
233209243Sjchandra		}
234209243Sjchandra		tlb_write_indexed();
235209243Sjchandra	}
236209243Sjchandra
237209243Sjchandra	mips_wr_entryhi(asid);
238209243Sjchandra	intr_restore(s);
239209243Sjchandra}
240209243Sjchandra
241209243Sjchandrastatic void
242209243Sjchandratlb_invalidate_one(unsigned i)
243209243Sjchandra{
244209243Sjchandra	/* XXX an invalid ASID? */
245209243Sjchandra	mips_wr_entryhi(TLBHI_ENTRY(MIPS_KSEG0_START + (2 * i * PAGE_SIZE), 0));
246209243Sjchandra	mips_wr_entrylo0(0);
247209243Sjchandra	mips_wr_entrylo1(0);
248209243Sjchandra	mips_wr_pagemask(0);
249209243Sjchandra	mips_wr_index(i);
250209243Sjchandra	tlb_write_indexed();
251209243Sjchandra}
252209243Sjchandra
253209243Sjchandra#ifdef DDB
254209243Sjchandra#include <ddb/ddb.h>
255209243Sjchandra
256209243SjchandraDB_SHOW_COMMAND(tlb, ddb_dump_tlb)
257209243Sjchandra{
258209243Sjchandra	register_t ehi, elo0, elo1;
259209243Sjchandra	unsigned i, cpu;
260209243Sjchandra
261209243Sjchandra	/*
262209243Sjchandra	 * XXX
263209243Sjchandra	 * The worst conversion from hex to decimal ever.
264209243Sjchandra	 */
265209243Sjchandra	if (have_addr)
266209243Sjchandra		cpu = ((addr >> 4) % 16) * 10 + (addr % 16);
267209243Sjchandra	else
268209243Sjchandra		cpu = PCPU_GET(cpuid);
269209243Sjchandra
270209243Sjchandra	if (cpu < 0 || cpu >= mp_ncpus) {
271209243Sjchandra		db_printf("Invalid CPU %u\n", cpu);
272209243Sjchandra		return;
273209243Sjchandra	}
274209243Sjchandra
275209243Sjchandra	if (cpu == PCPU_GET(cpuid))
276209243Sjchandra		tlb_save();
277209243Sjchandra
278209243Sjchandra	db_printf("Beginning TLB dump for CPU %u...\n", cpu);
279209243Sjchandra	for (i = 0; i < num_tlbentries; i++) {
280209243Sjchandra		if (i == tlb_state[cpu].wired) {
281209243Sjchandra			if (i != 0)
282209243Sjchandra				db_printf("^^^ WIRED ENTRIES ^^^\n");
283209243Sjchandra			else
284209243Sjchandra				db_printf("(No wired entries.)\n");
285209243Sjchandra		}
286209243Sjchandra
287209243Sjchandra		/* XXX PageMask.  */
288209243Sjchandra		ehi = tlb_state[cpu].entry[i].entryhi;
289209243Sjchandra		elo0 = tlb_state[cpu].entry[i].entrylo0;
290209243Sjchandra		elo1 = tlb_state[cpu].entry[i].entrylo1;
291209243Sjchandra
292209243Sjchandra		if (elo0 == 0 && elo1 == 0)
293209243Sjchandra			continue;
294209243Sjchandra
295209243Sjchandra		db_printf("#%u\t=> %jx\n", i, (intmax_t)ehi);
296209243Sjchandra		db_printf(" Lo0\t%jx\t(%#jx)\n", (intmax_t)elo0, (intmax_t)TLBLO_PTE_TO_PA(elo0));
297209243Sjchandra		db_printf(" Lo1\t%jx\t(%#jx)\n", (intmax_t)elo1, (intmax_t)TLBLO_PTE_TO_PA(elo1));
298209243Sjchandra	}
299209243Sjchandra	db_printf("Finished.\n");
300209243Sjchandra}
301209243Sjchandra#endif
302