summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/r2300_misc.S
blob: 1a557d456e1c069d71e8b76cd4becfcb8cf8434b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
/* $Id: r2300_misc.S,v 1.2 1996/06/29 12:41:08 dm Exp $
 * r2300_misc.S: Misc. exception handling code for R3000/R2000.
 *
 * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse
 *
 * Multi-cpu abstraction reworking:
 * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
 */
#include <linux/config.h>

#include <asm/asm.h>
#include <asm/bootinfo.h>
#include <asm/cachectl.h>
#include <asm/fpregdef.h>
#include <asm/mipsconfig.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>

	.text
	.set	mips1
	.set	noreorder

	.align	5
	NESTED(r2300_handle_tlbl, PT_SIZE, sp)
	.set	noat
	/* Check whether this is a refill or an invalid exception */
	mfc0	k0,CP0_BADVADDR
	mfc0	k1,CP0_ENTRYHI
	ori	k0,0xfff			# clear ASID...
	xori	k0,0xfff			# in BadVAddr
	andi	k1,0xfc0			# get current ASID
	or	k0,k1				# make new entryhi
	mfc0	k1,CP0_ENTRYHI
	mtc0	k0,CP0_ENTRYHI
	nop					# for pipeline
	nop
	nop
	tlbp
	nop					# for pipeline
	nop
	mfc0	k0,CP0_INDEX

	bgez	k0,invalid_tlbl			# bad addr in c0_badvaddr
	 mtc0	k1,CP0_ENTRYHI

	/* Damn... The next nop is required on the R4400PC V5.0, but
	 * I don't know why - at least there is no documented
	 * reason as for the others :-(
	 * And I haven't tested it as being necessary on R3000 - PMA.
	 * (The R3000 pipeline has only 5 stages, so it's probably not
	 * required -- Ralf)
	 */
	nop

#ifdef CONF_DEBUG_TLB
	/* OK, this is a double fault. Let's see whether this is
	 * due to an invalid entry in the page_table.
	 */
	/* used to be dmfc0 */
	mfc0	k0,CP0_BADVADDR
	/* FIXME: This srl/sll sequence is as it is for the R4xx0,
	 *        and I suspect that it should be different for
	 *	  the R[23]000.  PMA
	 *        (No, it's the assembler way to do
	 *            k0 = k0 / PAGE_SIZE;
	 *            k0 = k0 * sizeof(pte_t)
	 *        Acutally the R4xx0 code will have to change when
	 *        switching to 64 bit ... -- Ralf)
	 */
	srl	k0,12				# get PFN?
	sll	k0,2
	lui	k1,%HI(TLBMAP)
	addu	k0,k1
	lw	k1,(k0)
	andi	k1,(_PAGE_PRESENT|_PAGE_ACCESSED)
	bnez	k1,reload_pgd_entries
	 nop

	.set	noat
	SAVE_ALL
	.set	at
	PRINT("Double fault caused by invalid entries in pgd:\n")
	mfc0	a1,CP0_BADVADDR
	PRINT("Double fault address     : %08lx\n")
	mfc0	a1,CP0_EPC
	PRINT("c0_epc                   : %08lx\n")

	jal	show_regs
	 move	a0,sp

	jal	dump_tlb_nonwired
	 nop

	mfc0	a0,CP0_BADVADDR

	jal	dump_list_current
	 nop

	.set	noat
	STI
	.set	at
	PANIC("Corrupted pagedir")
	.set	noat

reload_pgd_entries:
#endif /* CONF_DEBUG_TLB */

	/* Load missing pair of entries from the pgd and return. */
	mfc0	k1,CP0_CONTEXT
	lw	k0,(k1)			# Never causes nested exception
	mfc0	k1,CP0_EPC		# get the return PC
	srl	k0,12			# Convert to EntryLo format
	mtc0	k0,CP0_ENTRYLO0
	nop				# for pipeline
	tlbwr
	nop				# for pipeline
	nop
	nop
	/* We don't know whether the original access was read or
	 * write, so return and see what happens...
	 */
	jr	k1
	 rfe

	/* Handle invalid exception
	 *
	 * There are two possible causes for an invalid (tlbl)
	 * exception:
	 * 1) pages with present bit set but the valid bit clear
	 * 2) nonexistant pages
	 * Case one needs fast handling, therefore don't save
	 * registers yet.
	 *
	 * k0 contains c0_index.
	 */
invalid_tlbl:	
#ifdef CONFIG_TLB_SHUTDOWN
	/* Remove entry so we don't need to care later
	 * For sake of the pipeline the tlbwi insn has been moved down.
	 * Moving it around is juggling with explosives...
	 */
	/* FIXME: Why is Ralf setting bit 3 of k1?  This may need to
	 *	  be changed for R[236]000! PMA
	 *        (The new ENTRYHI value will then point represent a
	 *        inique virtual address outside the 32 bit address
	 *        limit.  This is just paranoia to avoid a tlb
	 *        shutdown.  This whole part of the routine is probably
	 *        no longer required and can be removed -- Ralf)
	 */
	lui	k1,0x0008
	or	k0,k1
	sll	k0,12				# make it EntryHi format
	mtc0	k0,CP0_ENTRYHI
	mtc0	zero,CP0_ENTRYLO0
#endif
	/* Test present bit in entry */
	mfc0	k0,CP0_BADVADDR
	/* FIXME: This srl/sll sequence is as it is for the R4xx0,
	 *        and I suspect that it should be different for
	 *	  the R[23]000.  PMA
	 *        (No, it's the assembler way to do
	 *            k0 = k0 / PAGE_SIZE;
	 *            k0 = k0 * sizeof(pte_t)
	 *        Acutally the R4xx0 code will have to change when
	 *        switching to 64 bit ... -- Ralf)
	 */
	srl	k0,12
	sll	k0,2
#ifdef CONFIG_TLB_SHUTDOWN
	tlbwi						# do not move!
#endif
	lui	k1,%HI(TLBMAP)
	addu	k0,k1
	lw	k1,(k0)
	andi	k1,(_PAGE_PRESENT|_PAGE_READ)
	xori	k1,(_PAGE_PRESENT|_PAGE_READ)

	bnez	k1,nopage_tlbl
	 lw	k1,(k0)

	/* Present and read bits are set -> set valid and accessed bits */
	ori	k1,(_PAGE_VALID|_PAGE_ACCESSED)
	sw	k1,(k0)
	mfc0	k1,CP0_EPC
	nop

	jr	k1
	 rfe

	/* Page doesn't exist. Lots of work which is less important
	 * for speed needs to be done, so hand it all over to the
	 * kernel memory management routines.
	 */
nopage_tlbl:
	SAVE_ALL
	mfc0	a2,CP0_BADVADDR
	STI
	.set	at
	/* a0 (struct pt_regs *) regs
	 * a1 (unsigned long)    0 for read access
	 * a2 (unsigned long)    faulting virtual address
	 */
	move	a0,sp
	jal	do_page_fault
	 li	a1,0

	j	ret_from_sys_call
	 nop
	END(r2300_handle_tlbl)


	.text
	.align	5
	NESTED(r2300_handle_tlbs, PT_SIZE, sp)
	.set	noat
	/* It is impossible that is a nested reload exception.
	 * Therefore this must be a invalid exception.
	 * Two possible cases:
	 * 1) Page exists but not dirty.
	 * 2) Page doesn't exist yet. Hand over to the kernel.
	 *
	 * Test whether present bit in entry is set
	 */
	/* used to be dmfc0 */
	mfc0	k0,CP0_BADVADDR
	/* FIXME: This srl/sll sequence is as it is for the R4xx0,
	 *        and I suspect that it should be different for
	 *	  the R[23]000.  PMA
	 */
	srl	k0,12
	sll	k0,2
	lui	k1,%HI(TLBMAP)
	addu	k0,k1
	lw	k1,(k0)
	tlbp					# find faulting entry
	andi	k1,(_PAGE_PRESENT|_PAGE_WRITE)
	xori	k1,(_PAGE_PRESENT|_PAGE_WRITE)

	bnez	k1,nopage_tlbs
	 lw	k1,(k0)

	/* Present and writable bits set: set accessed and dirty bits. */
	ori	k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \
		       _PAGE_VALID|_PAGE_DIRTY)
	sw	k1,(k0)
	/* Now reload the entry into the TLB */
	/* FIXME: Why has Ralf set bit 2?  Should it be different for
	 *	  R[23]000?  PMA
	 *        (The ori/xori combination actually _clears_ bit 2.
	 *        This is required for the R4xx0 these CPUs always
	 *        map page pairs; a page pair of 4k pages therfore
	 *        has always an address with bit 2 set to zero. -- Ralf)
	 */
	ori	k0,0x0004
	xori	k0,0x0004
	lw	k0,(k0)
	srl	k0,12
	mtc0	k0,CP0_ENTRYLO0
	mfc0	k1,CP0_EPC
	nop				# for pipeline
	tlbwi
	nop				# for pipeline
	nop
	nop

	jr	k1
	 rfe

	/* Page doesn't exist. Lots of work which is less important
	 * for speed needs to be done, so hand it all over to the
	 * kernel memory management routines.
	 */
nopage_tlbs:
nowrite_mod:
#ifdef CONFIG_TLB_SHUTDOWN
	/* Remove entry so we don't need to care later */
	mfc0	k0,CP0_INDEX
#ifdef CONF_DEBUG_TLB
	bgez	k0,2f
	 nop
	/* We got a tlbs exception but found no matching entry in
	 * the tlb.  This should never happen.  Paranoia makes us
	 * check it, though.
	 */
	SAVE_ALL
	jal	show_regs
	 move	a0,sp
	.set	at
	mfc0	a1,CP0_BADVADDR
	PRINT("c0_badvaddr == %08lx\n")
	mfc0	a1,CP0_INDEX
	PRINT("c0_index    == %08x\n")
	mfc0	a1,CP0_ENTRYHI
	PRINT("c0_entryhi  == %08x\n")
	.set	noat
	STI
	.set	at
	PANIC("Tlbs or tlbm exception with no matching entry in tlb")
1:
	j	1b
	 nop
2:
#endif /* CONF_DEBUG_TLB */
	/* FIXME: Why is Ralf setting bit 3 of k1?  This may need to
	 *	  be changed for R[236]000! PMA
	 *        (The new ENTRYHI value will then point represent a
	 *        inique virtual address outside the 32 bit address
	 *        limit.  This is just paranoia to avoid a tlb
	 *        shutdown.  This whole part of the routine is probably
	 *        no longer required and can be removed -- Ralf)
	 */
	lui	k1,0x0008
	or	k0,k1
	sll	k0,12
	mtc0	k0,CP0_ENTRYHI
	mtc0	zero,CP0_ENTRYLO0
	nop				# for pipeline
	nop				# R4000 V2.2 requires 4 NOPs
	nop
	nop
	tlbwi
#endif /* CONFIG_TLB_SHUTDOWN */
	.set	noat
	SAVE_ALL
	mfc0	a2,CP0_BADVADDR
	STI
	.set	at
	/* a0 (struct pt_regs *) regs
	 * a1 (unsigned long)    1 for write access
	 * a2 (unsigned long)    faulting virtual address
	 */
	move	a0,sp
	jal	do_page_fault
	 li	a1,1

	j	ret_from_sys_call
	 nop
	END(r2300_handle_tlbs)


	.align	5
	NESTED(r2300_handle_mod, PT_SIZE, sp)
	.set	noat
	/* Two possible cases:
	 * 1) Page is writable but not dirty -> set dirty and return
	 * 2) Page is not writable -> call C handler
	 */
	/* used to be dmfc0 */
	mfc0	k0,CP0_BADVADDR
	/* FIXME: This srl/sll sequence is as it is for the R4xx0,
	 *        and I suspect that it should be different for
	 *	  the R[23]000.  PMA
	 */
	srl	k0,12
	sll	k0,2
	lui	k1,%HI(TLBMAP)
	addu	k0,k1
	lw	k1,(k0)
	tlbp					# find faulting entry
	andi	k1,_PAGE_WRITE

	beqz	k1,nowrite_mod
	 lw	k1,(k0)

	/* Present and writable bits set: set accessed and dirty bits. */
	ori	k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
	sw	k1,(k0)
	/* Now reload the entry into the tlb */
	/* FIXME: Why has Ralf set bit 2?  Should it be different for
	 *	  R[23]000?  PMA
	 *        (The ori/xori combination actually _clears_ bit 2.
	 *        This is required for the R4xx0 these CPUs always
	 *        map page pairs; a page pair of 4k pages therfore
	 *        has always an address with bit 2 set to zero. -- Ralf)
	 */
	ori	k0,0x0004
	xori	k0,0x0004
	lw	k0,(k0)
	srl	k0,12
	mtc0	k0,CP0_ENTRYLO0
	mfc0	k1,CP0_EPC
	nop				# for pipeline
	nop
	nop
	tlbwi
	nop				# for pipeline
	nop
	nop

	jr	k1
	 rfe
	END(r2300_handle_mod)
	.set	at