summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/r2300_misc.S
blob: 30031e295b8d8b84e6778ffe34a4e30854a59099 (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
/* $Id: r2300_misc.S,v 1.8 1999/12/08 22:05:10 harald Exp $
 * 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)
 *
 * Further modifications to make this work:
 * Copyright (c) 1998 Harald Koerfgen
 * Copyright (c) 1998, 1999 Gleb Raiko & Vladimir Roganov
 */
#include <asm/asm.h>
#include <asm/current.h>
#include <asm/bootinfo.h>
#include <asm/cachectl.h>
#include <asm/fpregdef.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/regdef.h>
#include <asm/segment.h>
#include <asm/stackframe.h>

	.text
	.set	mips1
	.set	noreorder

#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */

	/* ABUSE of CPP macros 101. */

	/* After this macro runs, the pte faulted on is
	 * in register PTE, a ptr into the table in which
	 * the pte belongs is in PTR.
	 */
#define LOAD_PTE(pte, ptr) \
	mfc0	pte, CP0_BADVADDR; \
	lw	ptr, current_pgd; \
	srl	pte, pte, 22; \
	sll	pte, pte, 2; \
	addu	ptr, ptr, pte; \
	mfc0	pte, CP0_CONTEXT; \
	lw	ptr, (ptr); \
	andi	pte, pte, 0xffc; \
	addu	ptr, ptr, pte; \
	lw	pte, (ptr); \
	nop;

	/* This places the even/odd pte pair in the page
	 * table at PTR into ENTRYLO0 and ENTRYLO1 using
	 * TMP as a scratch register.
	 */
#define PTE_RELOAD(ptr) \
	lw	ptr, (ptr)	; \
	nop			; \
	mtc0	ptr, CP0_ENTRYLO0; \
	nop;

#define DO_FAULT(write) \
	.set	noat; \
	.set	macro; \
	SAVE_ALL; \
	mfc0	a2, CP0_BADVADDR; \
	STI; \
	.set	at; \
	move	a0, sp; \
	jal	do_page_fault; \
	 li	a1, write; \
	j	ret_from_sys_call; \
	 nop; \
	.set	noat; \
	.set	nomacro;

	/* Check is PTE is present, if not then jump to LABEL.
	 * PTR points to the page table where this PTE is located,
	 * when the macro is done executing PTE will be restored
	 * with it's original value.
	 */
#define PTE_PRESENT(pte, ptr, label) \
	andi	pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
	xori	pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
	bnez	pte, label; \
	.set	push;       \
	.set	reorder;    \
	 lw	pte, (ptr); \
	.set	pop; 

	/* Make PTE valid, store result in PTR. */
#define PTE_MAKEVALID(pte, ptr) \
	ori	pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \
	sw	pte, (ptr);

	/* Check if PTE can be written to, if not branch to LABEL.
	 * Regardless restore PTE with value from PTR when done.
	 */
#define PTE_WRITABLE(pte, ptr, label) \
	andi	pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
	xori	pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
	bnez	pte, label; \
	.set    push;       \
	.set    reorder;    \
	lw      pte, (ptr); \
	.set    pop;
 

	/* Make PTE writable, update software status bits as well,
	 * then store at PTR.
	 */
#define PTE_MAKEWRITE(pte, ptr) \
	ori	pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \
			   _PAGE_VALID | _PAGE_DIRTY); \
	sw	pte, (ptr);

/*
 * The index register may have the probe fail bit set,
 * because we would trap on access kseg2, i.e. without refill.
 */
#define TLB_WRITE(reg) \
	mfc0	reg, CP0_INDEX; \
	nop; \
	bltz    reg, 1f; \
	 nop; \
	tlbwi; \
	j	2f; \
	 nop; \
1:	tlbwr; \
2:

#define RET(reg) \
	mfc0	reg, CP0_EPC; \
	nop; \
	jr	reg; \
	 rfe
			
	.set	noreorder

	.align	5
NESTED(handle_tlbl, PT_SIZE, sp)
	.set	noat

#ifndef NOTLB_OPTIMIZE
	/* Test present bit in entry. */
	LOAD_PTE(k0, k1)
        tlbp
        PTE_PRESENT(k0, k1, nopage_tlbl)
        PTE_MAKEVALID(k0, k1)
        PTE_RELOAD(k1)
	TLB_WRITE(k0)
	RET(k0)
nopage_tlbl:
#endif

	DO_FAULT(0)
END(handle_tlbl)

NESTED(handle_tlbs, PT_SIZE, sp)
	.set	noat

#ifndef NOTLB_OPTIMIZE
	LOAD_PTE(k0, k1)
	tlbp                            # find faulting entry
	PTE_WRITABLE(k0, k1, nopage_tlbs)
	PTE_MAKEWRITE(k0, k1)
	PTE_RELOAD(k1)
	TLB_WRITE(k0)
	RET(k0)
nopage_tlbs:
#endif

	DO_FAULT(1)
END(handle_tlbs)

	.align	5
NESTED(handle_mod, PT_SIZE, sp)
	.set	noat
#ifndef NOTLB_OPTIMIZE
	LOAD_PTE(k0, k1)
	tlbp					# find faulting entry
	andi	k0, k0, _PAGE_WRITE
	beqz	k0, nowrite_mod
	.set	push
	.set    reorder
	lw	k0, (k1)
	.set    pop

	/* Present and writable bits set, set accessed and dirty bits. */
	PTE_MAKEWRITE(k0, k1)

	/* Now reload the entry into the tlb. */
	PTE_RELOAD(k1)
	tlbwi
	RET(k0)
#endif

nowrite_mod:
	DO_FAULT(1)
END(handle_mod)