diff options
Diffstat (limited to 'arch/mn10300/mm')
-rw-r--r-- | arch/mn10300/mm/Makefile | 14 | ||||
-rw-r--r-- | arch/mn10300/mm/cache-flush-mn10300.S | 192 | ||||
-rw-r--r-- | arch/mn10300/mm/cache-mn10300.S | 289 | ||||
-rw-r--r-- | arch/mn10300/mm/cache.c | 121 | ||||
-rw-r--r-- | arch/mn10300/mm/dma-alloc.c | 56 | ||||
-rw-r--r-- | arch/mn10300/mm/extable.c | 26 | ||||
-rw-r--r-- | arch/mn10300/mm/fault.c | 405 | ||||
-rw-r--r-- | arch/mn10300/mm/init.c | 160 | ||||
-rw-r--r-- | arch/mn10300/mm/misalignment.c | 661 | ||||
-rw-r--r-- | arch/mn10300/mm/mmu-context.c | 80 | ||||
-rw-r--r-- | arch/mn10300/mm/pgtable.c | 197 | ||||
-rw-r--r-- | arch/mn10300/mm/tlb-mn10300.S | 207 |
12 files changed, 2408 insertions, 0 deletions
diff --git a/arch/mn10300/mm/Makefile b/arch/mn10300/mm/Makefile new file mode 100644 index 000000000000..28b9d983db0c --- /dev/null +++ b/arch/mn10300/mm/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # Makefile for the MN10300-specific memory management code | ||
3 | # | ||
4 | |||
5 | obj-y := \ | ||
6 | init.o fault.o pgtable.o extable.o tlb-mn10300.o mmu-context.o \ | ||
7 | misalignment.o dma-alloc.o | ||
8 | |||
9 | ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y) | ||
10 | obj-y += cache.o cache-mn10300.o | ||
11 | ifeq ($(CONFIG_MN10300_CACHE_WBACK),y) | ||
12 | obj-y += cache-flush-mn10300.o | ||
13 | endif | ||
14 | endif | ||
diff --git a/arch/mn10300/mm/cache-flush-mn10300.S b/arch/mn10300/mm/cache-flush-mn10300.S new file mode 100644 index 000000000000..c8ed1cbac107 --- /dev/null +++ b/arch/mn10300/mm/cache-flush-mn10300.S | |||
@@ -0,0 +1,192 @@ | |||
1 | /* MN10300 CPU core caching routines | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/sys.h> | ||
13 | #include <linux/linkage.h> | ||
14 | #include <asm/smp.h> | ||
15 | #include <asm/page.h> | ||
16 | #include <asm/cache.h> | ||
17 | |||
18 | .am33_2 | ||
19 | .globl mn10300_dcache_flush | ||
20 | .globl mn10300_dcache_flush_page | ||
21 | .globl mn10300_dcache_flush_range | ||
22 | .globl mn10300_dcache_flush_range2 | ||
23 | .globl mn10300_dcache_flush_inv | ||
24 | .globl mn10300_dcache_flush_inv_page | ||
25 | .globl mn10300_dcache_flush_inv_range | ||
26 | .globl mn10300_dcache_flush_inv_range2 | ||
27 | |||
28 | ############################################################################### | ||
29 | # | ||
30 | # void mn10300_dcache_flush(void) | ||
31 | # Flush the entire data cache back to RAM | ||
32 | # | ||
33 | ############################################################################### | ||
34 | ALIGN | ||
35 | mn10300_dcache_flush: | ||
36 | movhu (CHCTR),d0 | ||
37 | btst CHCTR_DCEN,d0 | ||
38 | beq mn10300_dcache_flush_end | ||
39 | |||
40 | # read the addresses tagged in the cache's tag RAM and attempt to flush | ||
41 | # those addresses specifically | ||
42 | # - we rely on the hardware to filter out invalid tag entry addresses | ||
43 | mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address | ||
44 | mov DCACHE_PURGE(0,0),a1 # dcache purge request address | ||
45 | mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries | ||
46 | |||
47 | mn10300_dcache_flush_loop: | ||
48 | mov (a0),d0 | ||
49 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 | ||
50 | or L1_CACHE_TAG_VALID,d0 # retain valid entries in the | ||
51 | # cache | ||
52 | mov d0,(a1) # conditional purge | ||
53 | |||
54 | mn10300_dcache_flush_skip: | ||
55 | add L1_CACHE_BYTES,a0 | ||
56 | add L1_CACHE_BYTES,a1 | ||
57 | add -1,d1 | ||
58 | bne mn10300_dcache_flush_loop | ||
59 | |||
60 | mn10300_dcache_flush_end: | ||
61 | ret [],0 | ||
62 | |||
63 | ############################################################################### | ||
64 | # | ||
65 | # void mn10300_dcache_flush_page(unsigned start) | ||
66 | # void mn10300_dcache_flush_range(unsigned start, unsigned end) | ||
67 | # void mn10300_dcache_flush_range2(unsigned start, unsigned size) | ||
68 | # Flush a range of addresses on a page in the dcache | ||
69 | # | ||
70 | ############################################################################### | ||
71 | ALIGN | ||
72 | mn10300_dcache_flush_page: | ||
73 | mov PAGE_SIZE,d1 | ||
74 | mn10300_dcache_flush_range2: | ||
75 | add d0,d1 | ||
76 | mn10300_dcache_flush_range: | ||
77 | movm [d2,d3],(sp) | ||
78 | |||
79 | movhu (CHCTR),d2 | ||
80 | btst CHCTR_DCEN,d2 | ||
81 | beq mn10300_dcache_flush_range_end | ||
82 | |||
83 | # round start addr down | ||
84 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 | ||
85 | mov d0,a1 | ||
86 | |||
87 | add L1_CACHE_BYTES,d1 # round end addr up | ||
88 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 | ||
89 | |||
90 | # write a request to flush all instances of an address from the cache | ||
91 | mov DCACHE_PURGE(0,0),a0 | ||
92 | mov a1,d0 | ||
93 | and L1_CACHE_TAG_ENTRY,d0 | ||
94 | add d0,a0 # starting dcache purge control | ||
95 | # reg address | ||
96 | |||
97 | sub a1,d1 | ||
98 | lsr L1_CACHE_SHIFT,d1 # total number of entries to | ||
99 | # examine | ||
100 | |||
101 | or L1_CACHE_TAG_VALID,a1 # retain valid entries in the | ||
102 | # cache | ||
103 | |||
104 | mn10300_dcache_flush_range_loop: | ||
105 | mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line | ||
106 | # all ways | ||
107 | |||
108 | add L1_CACHE_BYTES,a0 | ||
109 | add L1_CACHE_BYTES,a1 | ||
110 | and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 | ||
111 | add -1,d1 | ||
112 | bne mn10300_dcache_flush_range_loop | ||
113 | |||
114 | mn10300_dcache_flush_range_end: | ||
115 | ret [d2,d3],8 | ||
116 | |||
117 | ############################################################################### | ||
118 | # | ||
119 | # void mn10300_dcache_flush_inv(void) | ||
120 | # Flush the entire data cache and invalidate all entries | ||
121 | # | ||
122 | ############################################################################### | ||
123 | ALIGN | ||
124 | mn10300_dcache_flush_inv: | ||
125 | movhu (CHCTR),d0 | ||
126 | btst CHCTR_DCEN,d0 | ||
127 | beq mn10300_dcache_flush_inv_end | ||
128 | |||
129 | # hit each line in the dcache with an unconditional purge | ||
130 | mov DCACHE_PURGE(0,0),a1 # dcache purge request address | ||
131 | mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries | ||
132 | |||
133 | mn10300_dcache_flush_inv_loop: | ||
134 | mov (a1),d0 # unconditional purge | ||
135 | |||
136 | add L1_CACHE_BYTES,a1 | ||
137 | add -1,d1 | ||
138 | bne mn10300_dcache_flush_inv_loop | ||
139 | |||
140 | mn10300_dcache_flush_inv_end: | ||
141 | ret [],0 | ||
142 | |||
143 | ############################################################################### | ||
144 | # | ||
145 | # void mn10300_dcache_flush_inv_page(unsigned start) | ||
146 | # void mn10300_dcache_flush_inv_range(unsigned start, unsigned end) | ||
147 | # void mn10300_dcache_flush_inv_range2(unsigned start, unsigned size) | ||
148 | # Flush and invalidate a range of addresses on a page in the dcache | ||
149 | # | ||
150 | ############################################################################### | ||
151 | ALIGN | ||
152 | mn10300_dcache_flush_inv_page: | ||
153 | mov PAGE_SIZE,d1 | ||
154 | mn10300_dcache_flush_inv_range2: | ||
155 | add d0,d1 | ||
156 | mn10300_dcache_flush_inv_range: | ||
157 | movm [d2,d3],(sp) | ||
158 | movhu (CHCTR),d2 | ||
159 | btst CHCTR_DCEN,d2 | ||
160 | beq mn10300_dcache_flush_inv_range_end | ||
161 | |||
162 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start | ||
163 | # addr down | ||
164 | mov d0,a1 | ||
165 | |||
166 | add L1_CACHE_BYTES,d1 # round end addr up | ||
167 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 | ||
168 | |||
169 | # write a request to flush and invalidate all instances of an address | ||
170 | # from the cache | ||
171 | mov DCACHE_PURGE(0,0),a0 | ||
172 | mov a1,d0 | ||
173 | and L1_CACHE_TAG_ENTRY,d0 | ||
174 | add d0,a0 # starting dcache purge control | ||
175 | # reg address | ||
176 | |||
177 | sub a1,d1 | ||
178 | lsr L1_CACHE_SHIFT,d1 # total number of entries to | ||
179 | # examine | ||
180 | |||
181 | mn10300_dcache_flush_inv_range_loop: | ||
182 | mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line | ||
183 | # in all ways | ||
184 | |||
185 | add L1_CACHE_BYTES,a0 | ||
186 | add L1_CACHE_BYTES,a1 | ||
187 | and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 | ||
188 | add -1,d1 | ||
189 | bne mn10300_dcache_flush_inv_range_loop | ||
190 | |||
191 | mn10300_dcache_flush_inv_range_end: | ||
192 | ret [d2,d3],8 | ||
diff --git a/arch/mn10300/mm/cache-mn10300.S b/arch/mn10300/mm/cache-mn10300.S new file mode 100644 index 000000000000..e839d0aedd69 --- /dev/null +++ b/arch/mn10300/mm/cache-mn10300.S | |||
@@ -0,0 +1,289 @@ | |||
1 | /* MN10300 CPU core caching routines | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/sys.h> | ||
12 | #include <linux/linkage.h> | ||
13 | #include <asm/smp.h> | ||
14 | #include <asm/page.h> | ||
15 | #include <asm/cache.h> | ||
16 | |||
17 | #define mn10300_dcache_inv_range_intr_interval \ | ||
18 | +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) | ||
19 | |||
20 | #if mn10300_dcache_inv_range_intr_interval > 0xff | ||
21 | #error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less | ||
22 | #endif | ||
23 | |||
24 | .am33_2 | ||
25 | |||
26 | .globl mn10300_icache_inv | ||
27 | .globl mn10300_dcache_inv | ||
28 | .globl mn10300_dcache_inv_range | ||
29 | .globl mn10300_dcache_inv_range2 | ||
30 | .globl mn10300_dcache_inv_page | ||
31 | |||
32 | ############################################################################### | ||
33 | # | ||
34 | # void mn10300_icache_inv(void) | ||
35 | # Invalidate the entire icache | ||
36 | # | ||
37 | ############################################################################### | ||
38 | ALIGN | ||
39 | mn10300_icache_inv: | ||
40 | mov CHCTR,a0 | ||
41 | |||
42 | movhu (a0),d0 | ||
43 | btst CHCTR_ICEN,d0 | ||
44 | beq mn10300_icache_inv_end | ||
45 | |||
46 | mov epsw,d1 | ||
47 | and ~EPSW_IE,epsw | ||
48 | nop | ||
49 | nop | ||
50 | |||
51 | # disable the icache | ||
52 | and ~CHCTR_ICEN,d0 | ||
53 | movhu d0,(a0) | ||
54 | |||
55 | # and wait for it to calm down | ||
56 | setlb | ||
57 | movhu (a0),d0 | ||
58 | btst CHCTR_ICBUSY,d0 | ||
59 | lne | ||
60 | |||
61 | # invalidate | ||
62 | or CHCTR_ICINV,d0 | ||
63 | movhu d0,(a0) | ||
64 | |||
65 | # wait for the cache to finish | ||
66 | mov CHCTR,a0 | ||
67 | setlb | ||
68 | movhu (a0),d0 | ||
69 | btst CHCTR_ICBUSY,d0 | ||
70 | lne | ||
71 | |||
72 | # and reenable it | ||
73 | and ~CHCTR_ICINV,d0 | ||
74 | or CHCTR_ICEN,d0 | ||
75 | movhu d0,(a0) | ||
76 | movhu (a0),d0 | ||
77 | |||
78 | mov d1,epsw | ||
79 | |||
80 | mn10300_icache_inv_end: | ||
81 | ret [],0 | ||
82 | |||
83 | ############################################################################### | ||
84 | # | ||
85 | # void mn10300_dcache_inv(void) | ||
86 | # Invalidate the entire dcache | ||
87 | # | ||
88 | ############################################################################### | ||
89 | ALIGN | ||
90 | mn10300_dcache_inv: | ||
91 | mov CHCTR,a0 | ||
92 | |||
93 | movhu (a0),d0 | ||
94 | btst CHCTR_DCEN,d0 | ||
95 | beq mn10300_dcache_inv_end | ||
96 | |||
97 | mov epsw,d1 | ||
98 | and ~EPSW_IE,epsw | ||
99 | nop | ||
100 | nop | ||
101 | |||
102 | # disable the dcache | ||
103 | and ~CHCTR_DCEN,d0 | ||
104 | movhu d0,(a0) | ||
105 | |||
106 | # and wait for it to calm down | ||
107 | setlb | ||
108 | movhu (a0),d0 | ||
109 | btst CHCTR_DCBUSY,d0 | ||
110 | lne | ||
111 | |||
112 | # invalidate | ||
113 | or CHCTR_DCINV,d0 | ||
114 | movhu d0,(a0) | ||
115 | |||
116 | # wait for the cache to finish | ||
117 | mov CHCTR,a0 | ||
118 | setlb | ||
119 | movhu (a0),d0 | ||
120 | btst CHCTR_DCBUSY,d0 | ||
121 | lne | ||
122 | |||
123 | # and reenable it | ||
124 | and ~CHCTR_DCINV,d0 | ||
125 | or CHCTR_DCEN,d0 | ||
126 | movhu d0,(a0) | ||
127 | movhu (a0),d0 | ||
128 | |||
129 | mov d1,epsw | ||
130 | |||
131 | mn10300_dcache_inv_end: | ||
132 | ret [],0 | ||
133 | |||
134 | ############################################################################### | ||
135 | # | ||
136 | # void mn10300_dcache_inv_range(unsigned start, unsigned end) | ||
137 | # void mn10300_dcache_inv_range2(unsigned start, unsigned size) | ||
138 | # void mn10300_dcache_inv_page(unsigned start) | ||
139 | # Invalidate a range of addresses on a page in the dcache | ||
140 | # | ||
141 | ############################################################################### | ||
142 | ALIGN | ||
143 | mn10300_dcache_inv_page: | ||
144 | mov PAGE_SIZE,d1 | ||
145 | mn10300_dcache_inv_range2: | ||
146 | add d0,d1 | ||
147 | mn10300_dcache_inv_range: | ||
148 | movm [d2,d3,a2],(sp) | ||
149 | mov CHCTR,a2 | ||
150 | |||
151 | movhu (a2),d2 | ||
152 | btst CHCTR_DCEN,d2 | ||
153 | beq mn10300_dcache_inv_range_end | ||
154 | |||
155 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start | ||
156 | # addr down | ||
157 | mov d0,a1 | ||
158 | |||
159 | add L1_CACHE_BYTES,d1 # round end addr up | ||
160 | and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 | ||
161 | |||
162 | clr d2 # we're going to clear tag ram | ||
163 | # entries | ||
164 | |||
165 | # read the tags from the tag RAM, and if they indicate a valid dirty | ||
166 | # cache line then invalidate that line | ||
167 | mov DCACHE_TAG(0,0),a0 | ||
168 | mov a1,d0 | ||
169 | and L1_CACHE_TAG_ENTRY,d0 | ||
170 | add d0,a0 # starting dcache tag RAM | ||
171 | # access address | ||
172 | |||
173 | sub a1,d1 | ||
174 | lsr L1_CACHE_SHIFT,d1 # total number of entries to | ||
175 | # examine | ||
176 | |||
177 | and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base | ||
178 | |||
179 | mn10300_dcache_inv_range_outer_loop: | ||
180 | # disable interrupts | ||
181 | mov epsw,d3 | ||
182 | and ~EPSW_IE,epsw | ||
183 | nop # note that reading CHCTR and | ||
184 | # AND'ing D0 occupy two delay | ||
185 | # slots after disabling | ||
186 | # interrupts | ||
187 | |||
188 | # disable the dcache | ||
189 | movhu (a2),d0 | ||
190 | and ~CHCTR_DCEN,d0 | ||
191 | movhu d0,(a2) | ||
192 | |||
193 | # and wait for it to calm down | ||
194 | setlb | ||
195 | movhu (a2),d0 | ||
196 | btst CHCTR_DCBUSY,d0 | ||
197 | lne | ||
198 | |||
199 | mn10300_dcache_inv_range_loop: | ||
200 | |||
201 | # process the way 0 slot | ||
202 | mov (L1_CACHE_WAYDISP*0,a0),d0 # read the tag in the way 0 slot | ||
203 | btst L1_CACHE_TAG_VALID,d0 | ||
204 | beq mn10300_dcache_inv_range_skip_0 # jump if this cacheline is not | ||
205 | # valid | ||
206 | |||
207 | xor a1,d0 | ||
208 | lsr 12,d0 | ||
209 | bne mn10300_dcache_inv_range_skip_0 # jump if not this cacheline | ||
210 | |||
211 | mov d2,(a0) # kill the tag | ||
212 | |||
213 | mn10300_dcache_inv_range_skip_0: | ||
214 | |||
215 | # process the way 1 slot | ||
216 | mov (L1_CACHE_WAYDISP*1,a0),d0 # read the tag in the way 1 slot | ||
217 | btst L1_CACHE_TAG_VALID,d0 | ||
218 | beq mn10300_dcache_inv_range_skip_1 # jump if this cacheline is not | ||
219 | # valid | ||
220 | |||
221 | xor a1,d0 | ||
222 | lsr 12,d0 | ||
223 | bne mn10300_dcache_inv_range_skip_1 # jump if not this cacheline | ||
224 | |||
225 | mov d2,(a0) # kill the tag | ||
226 | |||
227 | mn10300_dcache_inv_range_skip_1: | ||
228 | |||
229 | # process the way 2 slot | ||
230 | mov (L1_CACHE_WAYDISP*2,a0),d0 # read the tag in the way 2 slot | ||
231 | btst L1_CACHE_TAG_VALID,d0 | ||
232 | beq mn10300_dcache_inv_range_skip_2 # jump if this cacheline is not | ||
233 | # valid | ||
234 | |||
235 | xor a1,d0 | ||
236 | lsr 12,d0 | ||
237 | bne mn10300_dcache_inv_range_skip_2 # jump if not this cacheline | ||
238 | |||
239 | mov d2,(a0) # kill the tag | ||
240 | |||
241 | mn10300_dcache_inv_range_skip_2: | ||
242 | |||
243 | # process the way 3 slot | ||
244 | mov (L1_CACHE_WAYDISP*3,a0),d0 # read the tag in the way 3 slot | ||
245 | btst L1_CACHE_TAG_VALID,d0 | ||
246 | beq mn10300_dcache_inv_range_skip_3 # jump if this cacheline is not | ||
247 | # valid | ||
248 | |||
249 | xor a1,d0 | ||
250 | lsr 12,d0 | ||
251 | bne mn10300_dcache_inv_range_skip_3 # jump if not this cacheline | ||
252 | |||
253 | mov d2,(a0) # kill the tag | ||
254 | |||
255 | mn10300_dcache_inv_range_skip_3: | ||
256 | |||
257 | # approx every N steps we re-enable the cache and see if there are any | ||
258 | # interrupts to be processed | ||
259 | # we also break out if we've reached the end of the loop | ||
260 | # (the bottom nibble of the count is zero in both cases) | ||
261 | add L1_CACHE_BYTES,a0 | ||
262 | add L1_CACHE_BYTES,a1 | ||
263 | add -1,d1 | ||
264 | btst mn10300_dcache_inv_range_intr_interval,d1 | ||
265 | bne mn10300_dcache_inv_range_loop | ||
266 | |||
267 | # wait for the cache to finish what it's doing | ||
268 | setlb | ||
269 | movhu (a2),d0 | ||
270 | btst CHCTR_DCBUSY,d0 | ||
271 | lne | ||
272 | |||
273 | # and reenable it | ||
274 | or CHCTR_DCEN,d0 | ||
275 | movhu d0,(a2) | ||
276 | movhu (a2),d0 | ||
277 | |||
278 | # re-enable interrupts | ||
279 | # - we don't bother with delay NOPs as we'll have enough instructions | ||
280 | # before we disable interrupts again to give the interrupts a chance | ||
281 | # to happen | ||
282 | mov d3,epsw | ||
283 | |||
284 | # go around again if the counter hasn't yet reached zero | ||
285 | add 0,d1 | ||
286 | bne mn10300_dcache_inv_range_outer_loop | ||
287 | |||
288 | mn10300_dcache_inv_range_end: | ||
289 | ret [d2,d3,a2],12 | ||
diff --git a/arch/mn10300/mm/cache.c b/arch/mn10300/mm/cache.c new file mode 100644 index 000000000000..1b76719ec1c3 --- /dev/null +++ b/arch/mn10300/mm/cache.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* MN10300 Cache flushing routines | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/mman.h> | ||
14 | #include <linux/threads.h> | ||
15 | #include <asm/page.h> | ||
16 | #include <asm/pgtable.h> | ||
17 | #include <asm/processor.h> | ||
18 | #include <asm/cacheflush.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | |||
22 | EXPORT_SYMBOL(mn10300_icache_inv); | ||
23 | EXPORT_SYMBOL(mn10300_dcache_inv); | ||
24 | EXPORT_SYMBOL(mn10300_dcache_inv_range); | ||
25 | EXPORT_SYMBOL(mn10300_dcache_inv_range2); | ||
26 | EXPORT_SYMBOL(mn10300_dcache_inv_page); | ||
27 | |||
28 | #ifdef CONFIG_MN10300_CACHE_WBACK | ||
29 | EXPORT_SYMBOL(mn10300_dcache_flush); | ||
30 | EXPORT_SYMBOL(mn10300_dcache_flush_inv); | ||
31 | EXPORT_SYMBOL(mn10300_dcache_flush_inv_range); | ||
32 | EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2); | ||
33 | EXPORT_SYMBOL(mn10300_dcache_flush_inv_page); | ||
34 | EXPORT_SYMBOL(mn10300_dcache_flush_range); | ||
35 | EXPORT_SYMBOL(mn10300_dcache_flush_range2); | ||
36 | EXPORT_SYMBOL(mn10300_dcache_flush_page); | ||
37 | #endif | ||
38 | |||
39 | /* | ||
40 | * write a page back from the dcache and invalidate the icache so that we can | ||
41 | * run code from it that we've just written into it | ||
42 | */ | ||
43 | void flush_icache_page(struct vm_area_struct *vma, struct page *page) | ||
44 | { | ||
45 | mn10300_dcache_flush_page(page_to_phys(page)); | ||
46 | mn10300_icache_inv(); | ||
47 | } | ||
48 | EXPORT_SYMBOL(flush_icache_page); | ||
49 | |||
50 | /* | ||
51 | * write some code we've just written back from the dcache and invalidate the | ||
52 | * icache so that we can run that code | ||
53 | */ | ||
54 | void flush_icache_range(unsigned long start, unsigned long end) | ||
55 | { | ||
56 | #ifdef CONFIG_MN10300_CACHE_WBACK | ||
57 | unsigned long addr, size, off; | ||
58 | struct page *page; | ||
59 | pgd_t *pgd; | ||
60 | pud_t *pud; | ||
61 | pmd_t *pmd; | ||
62 | pte_t *ppte, pte; | ||
63 | |||
64 | for (; start < end; start += size) { | ||
65 | /* work out how much of the page to flush */ | ||
66 | off = start & (PAGE_SIZE - 1); | ||
67 | |||
68 | size = end - start; | ||
69 | if (size > PAGE_SIZE - off) | ||
70 | size = PAGE_SIZE - off; | ||
71 | |||
72 | /* get the physical address the page is mapped to from the page | ||
73 | * tables */ | ||
74 | pgd = pgd_offset(current->mm, start); | ||
75 | if (!pgd || !pgd_val(*pgd)) | ||
76 | continue; | ||
77 | |||
78 | pud = pud_offset(pgd, start); | ||
79 | if (!pud || !pud_val(*pud)) | ||
80 | continue; | ||
81 | |||
82 | pmd = pmd_offset(pud, start); | ||
83 | if (!pmd || !pmd_val(*pmd)) | ||
84 | continue; | ||
85 | |||
86 | ppte = pte_offset_map(pmd, start); | ||
87 | if (!ppte) | ||
88 | continue; | ||
89 | pte = *ppte; | ||
90 | pte_unmap(ppte); | ||
91 | |||
92 | if (pte_none(pte)) | ||
93 | continue; | ||
94 | |||
95 | page = pte_page(pte); | ||
96 | if (!page) | ||
97 | continue; | ||
98 | |||
99 | addr = page_to_phys(page); | ||
100 | |||
101 | /* flush the dcache and invalidate the icache coverage on that | ||
102 | * region */ | ||
103 | mn10300_dcache_flush_range2(addr + off, size); | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | mn10300_icache_inv(); | ||
108 | } | ||
109 | EXPORT_SYMBOL(flush_icache_range); | ||
110 | |||
111 | /* | ||
112 | * allow userspace to flush the instruction cache | ||
113 | */ | ||
114 | asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) | ||
115 | { | ||
116 | if (end < start) | ||
117 | return -EINVAL; | ||
118 | |||
119 | flush_icache_range(start, end); | ||
120 | return 0; | ||
121 | } | ||
diff --git a/arch/mn10300/mm/dma-alloc.c b/arch/mn10300/mm/dma-alloc.c new file mode 100644 index 000000000000..f3649d8f50e3 --- /dev/null +++ b/arch/mn10300/mm/dma-alloc.c | |||
@@ -0,0 +1,56 @@ | |||
1 | /* MN10300 Dynamic DMA mapping support | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * Derived from: arch/i386/kernel/pci-dma.c | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <asm/io.h> | ||
18 | |||
19 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
20 | dma_addr_t *dma_handle, int gfp) | ||
21 | { | ||
22 | unsigned long addr; | ||
23 | void *ret; | ||
24 | |||
25 | /* ignore region specifiers */ | ||
26 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
27 | |||
28 | if (dev == NULL || dev->coherent_dma_mask < 0xffffffff) | ||
29 | gfp |= GFP_DMA; | ||
30 | |||
31 | addr = __get_free_pages(gfp, get_order(size)); | ||
32 | if (!addr) | ||
33 | return NULL; | ||
34 | |||
35 | /* map the coherent memory through the uncached memory window */ | ||
36 | ret = (void *) (addr | 0x20000000); | ||
37 | |||
38 | /* fill the memory with obvious rubbish */ | ||
39 | memset((void *) addr, 0xfb, size); | ||
40 | |||
41 | /* write back and evict all cache lines covering this region */ | ||
42 | mn10300_dcache_flush_inv_range2(virt_to_phys((void *) addr), PAGE_SIZE); | ||
43 | |||
44 | *dma_handle = virt_to_bus((void *) addr); | ||
45 | return ret; | ||
46 | } | ||
47 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
48 | |||
49 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
50 | dma_addr_t dma_handle) | ||
51 | { | ||
52 | unsigned long addr = (unsigned long) vaddr & ~0x20000000; | ||
53 | |||
54 | free_pages(addr, get_order(size)); | ||
55 | } | ||
56 | EXPORT_SYMBOL(dma_free_coherent); | ||
diff --git a/arch/mn10300/mm/extable.c b/arch/mn10300/mm/extable.c new file mode 100644 index 000000000000..25e5485ab87d --- /dev/null +++ b/arch/mn10300/mm/extable.c | |||
@@ -0,0 +1,26 @@ | |||
1 | /* MN10300 In-kernel exception handling | ||
2 | * | ||
3 | * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | ||
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/spinlock.h> | ||
13 | #include <asm/uaccess.h> | ||
14 | |||
15 | int fixup_exception(struct pt_regs *regs) | ||
16 | { | ||
17 | const struct exception_table_entry *fixup; | ||
18 | |||
19 | fixup = search_exception_tables(regs->pc); | ||
20 | if (fixup) { | ||
21 | regs->pc = fixup->fixup; | ||
22 | return 1; | ||
23 | } | ||
24 | |||
25 | return 0; | ||
26 | } | ||
diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c new file mode 100644 index 000000000000..78f092ca0316 --- /dev/null +++ b/arch/mn10300/mm/fault.c | |||
@@ -0,0 +1,405 @@ | |||
1 | /* MN10300 MMU Fault handler | ||
2 | * | ||
3 | * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | ||
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
5 | * Modified by David Howells (dhowells@redhat.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/signal.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/smp.h> | ||
23 | #include <linux/smp_lock.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/vt_kern.h> /* For unblank_screen() */ | ||
27 | |||
28 | #include <asm/system.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/pgalloc.h> | ||
31 | #include <asm/hardirq.h> | ||
32 | #include <asm/gdb-stub.h> | ||
33 | #include <asm/cpu-regs.h> | ||
34 | |||
35 | /* | ||
36 | * Unlock any spinlocks which will prevent us from getting the | ||
37 | * message out | ||
38 | */ | ||
39 | void bust_spinlocks(int yes) | ||
40 | { | ||
41 | if (yes) { | ||
42 | oops_in_progress = 1; | ||
43 | #ifdef CONFIG_SMP | ||
44 | /* Many serial drivers do __global_cli() */ | ||
45 | global_irq_lock = 0; | ||
46 | #endif | ||
47 | } else { | ||
48 | int loglevel_save = console_loglevel; | ||
49 | #ifdef CONFIG_VT | ||
50 | unblank_screen(); | ||
51 | #endif | ||
52 | oops_in_progress = 0; | ||
53 | /* | ||
54 | * OK, the message is on the console. Now we call printk() | ||
55 | * without oops_in_progress set so that printk will give klogd | ||
56 | * a poke. Hold onto your hats... | ||
57 | */ | ||
58 | console_loglevel = 15; /* NMI oopser may have shut the console | ||
59 | * up */ | ||
60 | printk(" "); | ||
61 | console_loglevel = loglevel_save; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | void do_BUG(const char *file, int line) | ||
66 | { | ||
67 | bust_spinlocks(1); | ||
68 | printk(KERN_EMERG "------------[ cut here ]------------\n"); | ||
69 | printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); | ||
70 | } | ||
71 | |||
72 | #if 0 | ||
73 | static void print_pagetable_entries(pgd_t *pgdir, unsigned long address) | ||
74 | { | ||
75 | pgd_t *pgd; | ||
76 | pmd_t *pmd; | ||
77 | pte_t *pte; | ||
78 | |||
79 | pgd = pgdir + __pgd_offset(address); | ||
80 | printk(KERN_DEBUG "pgd entry %p: %016Lx\n", | ||
81 | pgd, (long long) pgd_val(*pgd)); | ||
82 | |||
83 | if (!pgd_present(*pgd)) { | ||
84 | printk(KERN_DEBUG "... pgd not present!\n"); | ||
85 | return; | ||
86 | } | ||
87 | pmd = pmd_offset(pgd, address); | ||
88 | printk(KERN_DEBUG "pmd entry %p: %016Lx\n", | ||
89 | pmd, (long long)pmd_val(*pmd)); | ||
90 | |||
91 | if (!pmd_present(*pmd)) { | ||
92 | printk(KERN_DEBUG "... pmd not present!\n"); | ||
93 | return; | ||
94 | } | ||
95 | pte = pte_offset(pmd, address); | ||
96 | printk(KERN_DEBUG "pte entry %p: %016Lx\n", | ||
97 | pte, (long long) pte_val(*pte)); | ||
98 | |||
99 | if (!pte_present(*pte)) | ||
100 | printk(KERN_DEBUG "... pte not present!\n"); | ||
101 | } | ||
102 | #endif | ||
103 | |||
104 | asmlinkage void monitor_signal(struct pt_regs *); | ||
105 | |||
106 | /* | ||
107 | * This routine handles page faults. It determines the address, | ||
108 | * and the problem, and then passes it off to one of the appropriate | ||
109 | * routines. | ||
110 | * | ||
111 | * fault_code: | ||
112 | * - LSW: either MMUFCR_IFC or MMUFCR_DFC as appropriate | ||
113 | * - MSW: 0 if data access, 1 if instruction access | ||
114 | * - bit 0: TLB miss flag | ||
115 | * - bit 1: initial write | ||
116 | * - bit 2: page invalid | ||
117 | * - bit 3: protection violation | ||
118 | * - bit 4: accessor (0=user 1=kernel) | ||
119 | * - bit 5: 0=read 1=write | ||
120 | * - bit 6-8: page protection spec | ||
121 | * - bit 9: illegal address | ||
122 | * - bit 16: 0=data 1=ins | ||
123 | * | ||
124 | */ | ||
125 | asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code, | ||
126 | unsigned long address) | ||
127 | { | ||
128 | struct vm_area_struct *vma; | ||
129 | struct task_struct *tsk; | ||
130 | struct mm_struct *mm; | ||
131 | unsigned long page; | ||
132 | siginfo_t info; | ||
133 | int write, fault; | ||
134 | |||
135 | #ifdef CONFIG_GDBSTUB | ||
136 | /* handle GDB stub causing a fault */ | ||
137 | if (gdbstub_busy) { | ||
138 | gdbstub_exception(regs, TBR & TBR_INT_CODE); | ||
139 | return; | ||
140 | } | ||
141 | #endif | ||
142 | |||
143 | #if 0 | ||
144 | printk(KERN_DEBUG "--- do_page_fault(%p,%s:%04lx,%08lx)\n", | ||
145 | regs, | ||
146 | fault_code & 0x10000 ? "ins" : "data", | ||
147 | fault_code & 0xffff, address); | ||
148 | #endif | ||
149 | |||
150 | tsk = current; | ||
151 | |||
152 | /* | ||
153 | * We fault-in kernel-space virtual memory on-demand. The | ||
154 | * 'reference' page table is init_mm.pgd. | ||
155 | * | ||
156 | * NOTE! We MUST NOT take any locks for this case. We may | ||
157 | * be in an interrupt or a critical region, and should | ||
158 | * only copy the information from the master page table, | ||
159 | * nothing more. | ||
160 | * | ||
161 | * This verifies that the fault happens in kernel space | ||
162 | * and that the fault was a page not present (invalid) error | ||
163 | */ | ||
164 | if (address >= VMALLOC_START && address < VMALLOC_END && | ||
165 | (fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR && | ||
166 | (fault_code & MMUFCR_xFC_PGINVAL) == MMUFCR_xFC_PGINVAL | ||
167 | ) | ||
168 | goto vmalloc_fault; | ||
169 | |||
170 | mm = tsk->mm; | ||
171 | info.si_code = SEGV_MAPERR; | ||
172 | |||
173 | /* | ||
174 | * If we're in an interrupt or have no user | ||
175 | * context, we must not take the fault.. | ||
176 | */ | ||
177 | if (in_interrupt() || !mm) | ||
178 | goto no_context; | ||
179 | |||
180 | down_read(&mm->mmap_sem); | ||
181 | |||
182 | vma = find_vma(mm, address); | ||
183 | if (!vma) | ||
184 | goto bad_area; | ||
185 | if (vma->vm_start <= address) | ||
186 | goto good_area; | ||
187 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
188 | goto bad_area; | ||
189 | |||
190 | if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { | ||
191 | /* accessing the stack below the stack pointer is always a | ||
192 | * bug */ | ||
193 | if ((address & PAGE_MASK) + 2 * PAGE_SIZE < regs->sp) { | ||
194 | #if 0 | ||
195 | printk(KERN_WARNING | ||
196 | "[%d] ### Access below stack @%lx (sp=%lx)\n", | ||
197 | current->pid, address, regs->sp); | ||
198 | printk(KERN_WARNING | ||
199 | "vma [%08x - %08x]\n", | ||
200 | vma->vm_start, vma->vm_end); | ||
201 | show_registers(regs); | ||
202 | printk(KERN_WARNING | ||
203 | "[%d] ### Code: [%08lx]" | ||
204 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
205 | current->pid, | ||
206 | regs->pc, | ||
207 | ((u8 *) regs->pc)[0], | ||
208 | ((u8 *) regs->pc)[1], | ||
209 | ((u8 *) regs->pc)[2], | ||
210 | ((u8 *) regs->pc)[3], | ||
211 | ((u8 *) regs->pc)[4], | ||
212 | ((u8 *) regs->pc)[5], | ||
213 | ((u8 *) regs->pc)[6], | ||
214 | ((u8 *) regs->pc)[7] | ||
215 | ); | ||
216 | #endif | ||
217 | goto bad_area; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | if (expand_stack(vma, address)) | ||
222 | goto bad_area; | ||
223 | |||
224 | /* | ||
225 | * Ok, we have a good vm_area for this memory access, so | ||
226 | * we can handle it.. | ||
227 | */ | ||
228 | good_area: | ||
229 | info.si_code = SEGV_ACCERR; | ||
230 | write = 0; | ||
231 | switch (fault_code & (MMUFCR_xFC_PGINVAL|MMUFCR_xFC_TYPE)) { | ||
232 | default: /* 3: write, present */ | ||
233 | case MMUFCR_xFC_TYPE_WRITE: | ||
234 | #ifdef TEST_VERIFY_AREA | ||
235 | if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) | ||
236 | printk(KERN_DEBUG "WP fault at %08lx\n", regs->pc); | ||
237 | #endif | ||
238 | /* write to absent page */ | ||
239 | case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_WRITE: | ||
240 | if (!(vma->vm_flags & VM_WRITE)) | ||
241 | goto bad_area; | ||
242 | write++; | ||
243 | break; | ||
244 | |||
245 | /* read from protected page */ | ||
246 | case MMUFCR_xFC_TYPE_READ: | ||
247 | goto bad_area; | ||
248 | |||
249 | /* read from absent page present */ | ||
250 | case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_READ: | ||
251 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) | ||
252 | goto bad_area; | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * If for any reason at all we couldn't handle the fault, | ||
258 | * make sure we exit gracefully rather than endlessly redo | ||
259 | * the fault. | ||
260 | */ | ||
261 | fault = handle_mm_fault(mm, vma, address, write); | ||
262 | if (unlikely(fault & VM_FAULT_ERROR)) { | ||
263 | if (fault & VM_FAULT_OOM) | ||
264 | goto out_of_memory; | ||
265 | else if (fault & VM_FAULT_SIGBUS) | ||
266 | goto do_sigbus; | ||
267 | BUG(); | ||
268 | } | ||
269 | if (fault & VM_FAULT_MAJOR) | ||
270 | current->maj_flt++; | ||
271 | else | ||
272 | current->min_flt++; | ||
273 | |||
274 | up_read(&mm->mmap_sem); | ||
275 | return; | ||
276 | |||
277 | /* | ||
278 | * Something tried to access memory that isn't in our memory map.. | ||
279 | * Fix it, but check if it's kernel or user first.. | ||
280 | */ | ||
281 | bad_area: | ||
282 | up_read(&mm->mmap_sem); | ||
283 | monitor_signal(regs); | ||
284 | |||
285 | /* User mode accesses just cause a SIGSEGV */ | ||
286 | if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { | ||
287 | info.si_signo = SIGSEGV; | ||
288 | info.si_errno = 0; | ||
289 | /* info.si_code has been set above */ | ||
290 | info.si_addr = (void *)address; | ||
291 | force_sig_info(SIGSEGV, &info, tsk); | ||
292 | return; | ||
293 | } | ||
294 | |||
295 | no_context: | ||
296 | monitor_signal(regs); | ||
297 | /* Are we prepared to handle this kernel fault? */ | ||
298 | if (fixup_exception(regs)) | ||
299 | return; | ||
300 | |||
301 | /* | ||
302 | * Oops. The kernel tried to access some bad page. We'll have to | ||
303 | * terminate things with extreme prejudice. | ||
304 | */ | ||
305 | |||
306 | bust_spinlocks(1); | ||
307 | |||
308 | if (address < PAGE_SIZE) | ||
309 | printk(KERN_ALERT | ||
310 | "Unable to handle kernel NULL pointer dereference"); | ||
311 | else | ||
312 | printk(KERN_ALERT | ||
313 | "Unable to handle kernel paging request"); | ||
314 | printk(" at virtual address %08lx\n", address); | ||
315 | printk(" printing pc:\n"); | ||
316 | printk(KERN_ALERT "%08lx\n", regs->pc); | ||
317 | |||
318 | #ifdef CONFIG_GDBSTUB | ||
319 | gdbstub_intercept( | ||
320 | regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR); | ||
321 | #endif | ||
322 | |||
323 | page = PTBR; | ||
324 | page = ((unsigned long *) __va(page))[address >> 22]; | ||
325 | printk(KERN_ALERT "*pde = %08lx\n", page); | ||
326 | if (page & 1) { | ||
327 | page &= PAGE_MASK; | ||
328 | address &= 0x003ff000; | ||
329 | page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; | ||
330 | printk(KERN_ALERT "*pte = %08lx\n", page); | ||
331 | } | ||
332 | |||
333 | die("Oops", regs, fault_code); | ||
334 | do_exit(SIGKILL); | ||
335 | |||
336 | /* | ||
337 | * We ran out of memory, or some other thing happened to us that made | ||
338 | * us unable to handle the page fault gracefully. | ||
339 | */ | ||
340 | out_of_memory: | ||
341 | up_read(&mm->mmap_sem); | ||
342 | monitor_signal(regs); | ||
343 | printk(KERN_ALERT "VM: killing process %s\n", tsk->comm); | ||
344 | if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) | ||
345 | do_exit(SIGKILL); | ||
346 | goto no_context; | ||
347 | |||
348 | do_sigbus: | ||
349 | up_read(&mm->mmap_sem); | ||
350 | monitor_signal(regs); | ||
351 | |||
352 | /* | ||
353 | * Send a sigbus, regardless of whether we were in kernel | ||
354 | * or user mode. | ||
355 | */ | ||
356 | info.si_signo = SIGBUS; | ||
357 | info.si_errno = 0; | ||
358 | info.si_code = BUS_ADRERR; | ||
359 | info.si_addr = (void *)address; | ||
360 | force_sig_info(SIGBUS, &info, tsk); | ||
361 | |||
362 | /* Kernel mode? Handle exceptions or die */ | ||
363 | if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) | ||
364 | goto no_context; | ||
365 | return; | ||
366 | |||
367 | vmalloc_fault: | ||
368 | { | ||
369 | /* | ||
370 | * Synchronize this task's top level page-table | ||
371 | * with the 'reference' page table. | ||
372 | * | ||
373 | * Do _not_ use "tsk" here. We might be inside | ||
374 | * an interrupt in the middle of a task switch.. | ||
375 | */ | ||
376 | int index = pgd_index(address); | ||
377 | pgd_t *pgd, *pgd_k; | ||
378 | pud_t *pud, *pud_k; | ||
379 | pmd_t *pmd, *pmd_k; | ||
380 | pte_t *pte_k; | ||
381 | |||
382 | pgd_k = init_mm.pgd + index; | ||
383 | |||
384 | if (!pgd_present(*pgd_k)) | ||
385 | goto no_context; | ||
386 | |||
387 | pud_k = pud_offset(pgd_k, address); | ||
388 | if (!pud_present(*pud_k)) | ||
389 | goto no_context; | ||
390 | |||
391 | pmd_k = pmd_offset(pud_k, address); | ||
392 | if (!pmd_present(*pmd_k)) | ||
393 | goto no_context; | ||
394 | |||
395 | pgd = (pgd_t *) PTBR + index; | ||
396 | pud = pud_offset(pgd, address); | ||
397 | pmd = pmd_offset(pud, address); | ||
398 | set_pmd(pmd, *pmd_k); | ||
399 | |||
400 | pte_k = pte_offset_kernel(pmd_k, address); | ||
401 | if (!pte_present(*pte_k)) | ||
402 | goto no_context; | ||
403 | return; | ||
404 | } | ||
405 | } | ||
diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c new file mode 100644 index 000000000000..8c5d88c7b90a --- /dev/null +++ b/arch/mn10300/mm/init.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* MN10300 Memory management initialisation | ||
2 | * | ||
3 | * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | ||
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
5 | * Modified by David Howells (dhowells@redhat.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | #include <linux/signal.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/mman.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/swap.h> | ||
24 | #include <linux/smp.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/initrd.h> | ||
27 | #include <linux/highmem.h> | ||
28 | #include <linux/pagemap.h> | ||
29 | #include <linux/bootmem.h> | ||
30 | |||
31 | #include <asm/processor.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/uaccess.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/pgalloc.h> | ||
36 | #include <asm/dma.h> | ||
37 | #include <asm/tlb.h> | ||
38 | #include <asm/sections.h> | ||
39 | |||
40 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
41 | |||
42 | unsigned long highstart_pfn, highend_pfn; | ||
43 | |||
44 | /* | ||
45 | * set up paging | ||
46 | */ | ||
47 | void __init paging_init(void) | ||
48 | { | ||
49 | unsigned long zones_size[MAX_NR_ZONES] = {0,}; | ||
50 | pte_t *ppte; | ||
51 | int loop; | ||
52 | |||
53 | /* main kernel space -> RAM mapping is handled as 1:1 transparent by | ||
54 | * the MMU */ | ||
55 | memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); | ||
56 | memset(kernel_vmalloc_ptes, 0, sizeof(kernel_vmalloc_ptes)); | ||
57 | |||
58 | /* load the VMALLOC area PTE table addresses into the kernel PGD */ | ||
59 | ppte = kernel_vmalloc_ptes; | ||
60 | for (loop = VMALLOC_START / (PAGE_SIZE * PTRS_PER_PTE); | ||
61 | loop < VMALLOC_END / (PAGE_SIZE * PTRS_PER_PTE); | ||
62 | loop++ | ||
63 | ) { | ||
64 | set_pgd(swapper_pg_dir + loop, __pgd(__pa(ppte) | _PAGE_TABLE)); | ||
65 | ppte += PAGE_SIZE / sizeof(pte_t); | ||
66 | } | ||
67 | |||
68 | /* declare the sizes of the RAM zones (only use the normal zone) */ | ||
69 | zones_size[ZONE_NORMAL] = | ||
70 | (contig_page_data.bdata->node_low_pfn) - | ||
71 | (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT); | ||
72 | |||
73 | /* pass the memory from the bootmem allocator to the main allocator */ | ||
74 | free_area_init(zones_size); | ||
75 | |||
76 | __flush_tlb_all(); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * transfer all the memory from the bootmem allocator to the runtime allocator | ||
81 | */ | ||
82 | void __init mem_init(void) | ||
83 | { | ||
84 | int codesize, reservedpages, datasize, initsize; | ||
85 | int tmp; | ||
86 | |||
87 | if (!mem_map) | ||
88 | BUG(); | ||
89 | |||
90 | #define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT) | ||
91 | #define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) | ||
92 | |||
93 | max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; | ||
94 | high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE); | ||
95 | |||
96 | /* clear the zero-page */ | ||
97 | memset(empty_zero_page, 0, PAGE_SIZE); | ||
98 | |||
99 | /* this will put all low memory onto the freelists */ | ||
100 | totalram_pages += free_all_bootmem(); | ||
101 | |||
102 | reservedpages = 0; | ||
103 | for (tmp = 0; tmp < num_physpages; tmp++) | ||
104 | if (PageReserved(&mem_map[tmp])) | ||
105 | reservedpages++; | ||
106 | |||
107 | codesize = (unsigned long) &_etext - (unsigned long) &_stext; | ||
108 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | ||
109 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | ||
110 | |||
111 | printk(KERN_INFO | ||
112 | "Memory: %luk/%luk available" | ||
113 | " (%dk kernel code, %dk reserved, %dk data, %dk init," | ||
114 | " %ldk highmem)\n", | ||
115 | (unsigned long) nr_free_pages() << (PAGE_SHIFT - 10), | ||
116 | max_mapnr << (PAGE_SHIFT - 10), | ||
117 | codesize >> 10, | ||
118 | reservedpages << (PAGE_SHIFT - 10), | ||
119 | datasize >> 10, | ||
120 | initsize >> 10, | ||
121 | (unsigned long) (totalhigh_pages << (PAGE_SHIFT - 10)) | ||
122 | ); | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * | ||
127 | */ | ||
128 | void free_init_pages(char *what, unsigned long begin, unsigned long end) | ||
129 | { | ||
130 | unsigned long addr; | ||
131 | |||
132 | for (addr = begin; addr < end; addr += PAGE_SIZE) { | ||
133 | ClearPageReserved(virt_to_page(addr)); | ||
134 | init_page_count(virt_to_page(addr)); | ||
135 | memset((void *) addr, 0xcc, PAGE_SIZE); | ||
136 | free_page(addr); | ||
137 | totalram_pages++; | ||
138 | } | ||
139 | printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * recycle memory containing stuff only required for initialisation | ||
144 | */ | ||
145 | void free_initmem(void) | ||
146 | { | ||
147 | free_init_pages("unused kernel memory", | ||
148 | (unsigned long) &__init_begin, | ||
149 | (unsigned long) &__init_end); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * dispose of the memory on which the initial ramdisk resided | ||
154 | */ | ||
155 | #ifdef CONFIG_BLK_DEV_INITRD | ||
156 | void free_initrd_mem(unsigned long start, unsigned long end) | ||
157 | { | ||
158 | free_init_pages("initrd memory", start, end); | ||
159 | } | ||
160 | #endif | ||
diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c new file mode 100644 index 000000000000..32aa89dc3848 --- /dev/null +++ b/arch/mn10300/mm/misalignment.c | |||
@@ -0,0 +1,661 @@ | |||
1 | /* MN10300 Misalignment fixup handler | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | #include <linux/timer.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/pci.h> | ||
26 | #include <asm/processor.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/atomic.h> | ||
31 | #include <asm/smp.h> | ||
32 | #include <asm/pgalloc.h> | ||
33 | #include <asm/cpu-regs.h> | ||
34 | #include <asm/busctl-regs.h> | ||
35 | #include <asm/fpu.h> | ||
36 | #include <asm/gdb-stub.h> | ||
37 | #include <asm/asm-offsets.h> | ||
38 | |||
39 | #if 0 | ||
40 | #define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__) | ||
41 | #else | ||
42 | #define kdebug(FMT, ...) do {} while (0) | ||
43 | #endif | ||
44 | |||
45 | static int misalignment_addr(unsigned long *registers, unsigned params, | ||
46 | unsigned opcode, unsigned disp, | ||
47 | void **_address, unsigned long **_postinc); | ||
48 | |||
49 | static int misalignment_reg(unsigned long *registers, unsigned params, | ||
50 | unsigned opcode, unsigned disp, | ||
51 | unsigned long **_register); | ||
52 | |||
53 | static inline unsigned int_log2(unsigned x) | ||
54 | { | ||
55 | unsigned y; | ||
56 | asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0)); | ||
57 | return y; | ||
58 | } | ||
59 | #define log2(x) int_log2(x) | ||
60 | |||
61 | static const unsigned Dreg_index[] = { | ||
62 | REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 | ||
63 | }; | ||
64 | |||
65 | static const unsigned Areg_index[] = { | ||
66 | REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2 | ||
67 | }; | ||
68 | |||
69 | static const unsigned Rreg_index[] = { | ||
70 | REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2, | ||
71 | REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2, | ||
72 | REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2, | ||
73 | REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 | ||
74 | }; | ||
75 | |||
76 | enum format_id { | ||
77 | FMT_S0, | ||
78 | FMT_S1, | ||
79 | FMT_S2, | ||
80 | FMT_S4, | ||
81 | FMT_D0, | ||
82 | FMT_D1, | ||
83 | FMT_D2, | ||
84 | FMT_D4, | ||
85 | FMT_D6, | ||
86 | FMT_D7, | ||
87 | FMT_D8, | ||
88 | FMT_D9, | ||
89 | }; | ||
90 | |||
91 | struct { | ||
92 | u_int8_t opsz, dispsz; | ||
93 | } format_tbl[16] = { | ||
94 | [FMT_S0] = { 8, 0 }, | ||
95 | [FMT_S1] = { 8, 8 }, | ||
96 | [FMT_S2] = { 8, 16 }, | ||
97 | [FMT_S4] = { 8, 32 }, | ||
98 | [FMT_D0] = { 16, 0 }, | ||
99 | [FMT_D1] = { 16, 8 }, | ||
100 | [FMT_D2] = { 16, 16 }, | ||
101 | [FMT_D4] = { 16, 32 }, | ||
102 | [FMT_D6] = { 24, 0 }, | ||
103 | [FMT_D7] = { 24, 8 }, | ||
104 | [FMT_D8] = { 24, 24 }, | ||
105 | [FMT_D9] = { 24, 32 }, | ||
106 | }; | ||
107 | |||
108 | enum value_id { | ||
109 | DM0, /* data reg in opcode in bits 0-1 */ | ||
110 | DM1, /* data reg in opcode in bits 2-3 */ | ||
111 | DM2, /* data reg in opcode in bits 4-5 */ | ||
112 | AM0, /* addr reg in opcode in bits 0-1 */ | ||
113 | AM1, /* addr reg in opcode in bits 2-3 */ | ||
114 | AM2, /* addr reg in opcode in bits 4-5 */ | ||
115 | RM0, /* reg in opcode in bits 0-3 */ | ||
116 | RM1, /* reg in opcode in bits 2-5 */ | ||
117 | RM2, /* reg in opcode in bits 4-7 */ | ||
118 | RM4, /* reg in opcode in bits 8-11 */ | ||
119 | RM6, /* reg in opcode in bits 12-15 */ | ||
120 | |||
121 | RD0, /* reg in displacement in bits 0-3 */ | ||
122 | RD2, /* reg in displacement in bits 4-7 */ | ||
123 | |||
124 | SP, /* stack pointer */ | ||
125 | |||
126 | SD8, /* 8-bit signed displacement */ | ||
127 | SD16, /* 16-bit signed displacement */ | ||
128 | SD24, /* 24-bit signed displacement */ | ||
129 | SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */ | ||
130 | SIMM8, /* 8-bit signed immediate */ | ||
131 | IMM24, /* 24-bit unsigned immediate */ | ||
132 | IMM32, /* 32-bit unsigned immediate */ | ||
133 | IMM32_HIGH8, /* 32-bit unsigned immediate, high 8-bits in opcode */ | ||
134 | |||
135 | DN0 = DM0, | ||
136 | DN1 = DM1, | ||
137 | DN2 = DM2, | ||
138 | AN0 = AM0, | ||
139 | AN1 = AM1, | ||
140 | AN2 = AM2, | ||
141 | RN0 = RM0, | ||
142 | RN1 = RM1, | ||
143 | RN2 = RM2, | ||
144 | RN4 = RM4, | ||
145 | RN6 = RM6, | ||
146 | DI = DM1, | ||
147 | RI = RM2, | ||
148 | |||
149 | }; | ||
150 | |||
151 | struct mn10300_opcode { | ||
152 | const char *name; | ||
153 | u_int32_t opcode; | ||
154 | u_int32_t opmask; | ||
155 | unsigned exclusion; | ||
156 | |||
157 | enum format_id format; | ||
158 | |||
159 | unsigned cpu_mask; | ||
160 | #define AM33 330 | ||
161 | |||
162 | unsigned params[2]; | ||
163 | #define MEM(ADDR) (0x80000000 | (ADDR)) | ||
164 | #define MEM2(ADDR1, ADDR2) (0x80000000 | (ADDR1) << 8 | (ADDR2)) | ||
165 | #define MEMINC(ADDR) (0x81000000 | (ADDR)) | ||
166 | #define MEMINC2(ADDR, INC) (0x81000000 | (ADDR) << 8 | (INC)) | ||
167 | }; | ||
168 | |||
169 | /* LIBOPCODES EXCERPT | ||
170 | Assemble Matsushita MN10300 instructions. | ||
171 | Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. | ||
172 | |||
173 | This program is free software; you can redistribute it and/or modify | ||
174 | it under the terms of the GNU General Public Licence as published by | ||
175 | the Free Software Foundation; either version 2 of the Licence, or | ||
176 | (at your option) any later version. | ||
177 | |||
178 | This program is distributed in the hope that it will be useful, | ||
179 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
180 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
181 | GNU General Public Licence for more details. | ||
182 | |||
183 | You should have received a copy of the GNU General Public Licence | ||
184 | along with this program; if not, write to the Free Software | ||
185 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
186 | */ | ||
187 | static const struct mn10300_opcode mn10300_opcodes[] = { | ||
188 | { "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}}, | ||
189 | { "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}}, | ||
190 | { "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}}, | ||
191 | { "mov", 0xf010, 0xfff0, 0, FMT_D0, 0, {AM1, MEM(AN0)}}, | ||
192 | { "mov", 0xf300, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, | ||
193 | { "mov", 0xf340, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, | ||
194 | { "mov", 0xf380, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), AN2}}, | ||
195 | { "mov", 0xf3c0, 0xffc0, 0, FMT_D0, 0, {AM2, MEM2(DI, AN0)}}, | ||
196 | { "mov", 0xf80000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, | ||
197 | { "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, | ||
198 | { "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}}, | ||
199 | { "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}}, | ||
200 | { "mov", 0xf8f000, 0xfffc00, 0, FMT_D1, AM33, {MEM2(SD8, AM0), SP}}, | ||
201 | { "mov", 0xf8f400, 0xfffc00, 0, FMT_D1, AM33, {SP, MEM2(SD8, AN0)}}, | ||
202 | { "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, | ||
203 | { "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, | ||
204 | { "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, | ||
205 | { "mov", 0xf97a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, | ||
206 | { "mov", 0xfa000000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, | ||
207 | { "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, | ||
208 | { "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}}, | ||
209 | { "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}}, | ||
210 | { "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, | ||
211 | { "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, | ||
212 | { "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, | ||
213 | { "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, | ||
214 | { "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, | ||
215 | { "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, | ||
216 | { "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, | ||
217 | { "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, | ||
218 | { "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}}, | ||
219 | { "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}}, | ||
220 | { "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, | ||
221 | { "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, | ||
222 | { "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, | ||
223 | { "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, | ||
224 | { "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, | ||
225 | { "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, | ||
226 | { "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, | ||
227 | { "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, | ||
228 | |||
229 | { "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}}, | ||
230 | { "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}}, | ||
231 | { "movhu", 0xf480, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, | ||
232 | { "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, | ||
233 | { "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, | ||
234 | { "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, | ||
235 | { "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, | ||
236 | { "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, | ||
237 | { "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, | ||
238 | { "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, | ||
239 | { "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, | ||
240 | { "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, | ||
241 | { "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, | ||
242 | { "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, | ||
243 | { "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, | ||
244 | { "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, | ||
245 | { "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, | ||
246 | { "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, | ||
247 | { "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, | ||
248 | { "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, | ||
249 | { "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, | ||
250 | { "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, | ||
251 | { "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, | ||
252 | { "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, | ||
253 | { "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, | ||
254 | { "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, | ||
255 | { "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, | ||
256 | { "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, | ||
257 | { 0, 0, 0, 0, 0, 0, {0}}, | ||
258 | }; | ||
259 | |||
260 | /* | ||
261 | * fix up misalignment problems where possible | ||
262 | */ | ||
263 | asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) | ||
264 | { | ||
265 | const struct exception_table_entry *fixup; | ||
266 | const struct mn10300_opcode *pop; | ||
267 | unsigned long *registers = (unsigned long *) regs; | ||
268 | unsigned long data, *store, *postinc; | ||
269 | mm_segment_t seg; | ||
270 | siginfo_t info; | ||
271 | uint32_t opcode, disp, noc, xo, xm; | ||
272 | uint8_t *pc, byte; | ||
273 | void *address; | ||
274 | unsigned tmp, npop; | ||
275 | |||
276 | kdebug("MISALIGN at %lx\n", regs->pc); | ||
277 | |||
278 | if (in_interrupt()) | ||
279 | die("Misalignment trap in interrupt context", regs, code); | ||
280 | |||
281 | if (regs->epsw & EPSW_IE) | ||
282 | asm volatile("or %0,epsw" : : "i"(EPSW_IE)); | ||
283 | |||
284 | seg = get_fs(); | ||
285 | set_fs(KERNEL_DS); | ||
286 | |||
287 | fixup = search_exception_tables(regs->pc); | ||
288 | |||
289 | /* first thing to do is to match the opcode */ | ||
290 | pc = (u_int8_t *) regs->pc; | ||
291 | |||
292 | if (__get_user(byte, pc) != 0) | ||
293 | goto fetch_error; | ||
294 | opcode = byte; | ||
295 | noc = 8; | ||
296 | |||
297 | for (pop = mn10300_opcodes; pop->name; pop++) { | ||
298 | npop = log2(pop->opcode | pop->opmask); | ||
299 | if (npop <= 0 || npop > 31) | ||
300 | continue; | ||
301 | npop = (npop + 8) & ~7; | ||
302 | |||
303 | got_more_bits: | ||
304 | if (npop == noc) { | ||
305 | if ((opcode & pop->opmask) == pop->opcode) | ||
306 | goto found_opcode; | ||
307 | } else if (npop > noc) { | ||
308 | xo = pop->opcode >> (npop - noc); | ||
309 | xm = pop->opmask >> (npop - noc); | ||
310 | |||
311 | if ((opcode & xm) != xo) | ||
312 | continue; | ||
313 | |||
314 | /* we've got a partial match (an exact match on the | ||
315 | * first N bytes), so we need to get some more data */ | ||
316 | pc++; | ||
317 | if (__get_user(byte, pc) != 0) | ||
318 | goto fetch_error; | ||
319 | opcode = opcode << 8 | byte; | ||
320 | noc += 8; | ||
321 | goto got_more_bits; | ||
322 | } else { | ||
323 | /* there's already been a partial match as long as the | ||
324 | * complete match we're now considering, so this one | ||
325 | * should't match */ | ||
326 | continue; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | /* didn't manage to find a fixup */ | ||
331 | if (!user_mode(regs)) | ||
332 | printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", | ||
333 | regs->pc, opcode); | ||
334 | |||
335 | failed: | ||
336 | set_fs(seg); | ||
337 | if (die_if_no_fixup("misalignment error", regs, code)) | ||
338 | return; | ||
339 | |||
340 | info.si_signo = SIGBUS; | ||
341 | info.si_errno = 0; | ||
342 | info.si_code = BUS_ADRALN; | ||
343 | info.si_addr = (void *) regs->pc; | ||
344 | force_sig_info(SIGBUS, &info, current); | ||
345 | return; | ||
346 | |||
347 | /* error reading opcodes */ | ||
348 | fetch_error: | ||
349 | if (!user_mode(regs)) | ||
350 | printk(KERN_CRIT | ||
351 | "MISALIGN: %p: fault whilst reading instruction data\n", | ||
352 | pc); | ||
353 | goto failed; | ||
354 | |||
355 | bad_addr_mode: | ||
356 | if (!user_mode(regs)) | ||
357 | printk(KERN_CRIT | ||
358 | "MISALIGN: %lx: unsupported addressing mode %x\n", | ||
359 | regs->pc, opcode); | ||
360 | goto failed; | ||
361 | |||
362 | bad_reg_mode: | ||
363 | if (!user_mode(regs)) | ||
364 | printk(KERN_CRIT | ||
365 | "MISALIGN: %lx: unsupported register mode %x\n", | ||
366 | regs->pc, opcode); | ||
367 | goto failed; | ||
368 | |||
369 | unsupported_instruction: | ||
370 | if (!user_mode(regs)) | ||
371 | printk(KERN_CRIT | ||
372 | "MISALIGN: %lx: unsupported instruction %x (%s)\n", | ||
373 | regs->pc, opcode, pop->name); | ||
374 | goto failed; | ||
375 | |||
376 | transfer_failed: | ||
377 | set_fs(seg); | ||
378 | if (fixup) { | ||
379 | regs->pc = fixup->fixup; | ||
380 | return; | ||
381 | } | ||
382 | if (die_if_no_fixup("misalignment fixup", regs, code)) | ||
383 | return; | ||
384 | |||
385 | info.si_signo = SIGSEGV; | ||
386 | info.si_errno = 0; | ||
387 | info.si_code = 0; | ||
388 | info.si_addr = (void *) regs->pc; | ||
389 | force_sig_info(SIGSEGV, &info, current); | ||
390 | return; | ||
391 | |||
392 | /* we matched the opcode */ | ||
393 | found_opcode: | ||
394 | kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n", | ||
395 | regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]); | ||
396 | |||
397 | tmp = format_tbl[pop->format].opsz; | ||
398 | if (tmp > noc) | ||
399 | BUG(); /* match was less complete than it ought to have been */ | ||
400 | |||
401 | if (tmp < noc) { | ||
402 | tmp = noc - tmp; | ||
403 | opcode >>= tmp; | ||
404 | pc -= tmp >> 3; | ||
405 | } | ||
406 | |||
407 | /* grab the extra displacement (note it's LSB first) */ | ||
408 | disp = 0; | ||
409 | tmp = format_tbl[pop->format].dispsz >> 3; | ||
410 | while (tmp > 0) { | ||
411 | tmp--; | ||
412 | disp <<= 8; | ||
413 | |||
414 | pc++; | ||
415 | if (__get_user(byte, pc) != 0) | ||
416 | goto fetch_error; | ||
417 | disp |= byte; | ||
418 | } | ||
419 | |||
420 | set_fs(KERNEL_XDS); | ||
421 | if (fixup || regs->epsw & EPSW_nSL) | ||
422 | set_fs(seg); | ||
423 | |||
424 | tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000; | ||
425 | if (!tmp) { | ||
426 | if (!user_mode(regs)) | ||
427 | printk(KERN_CRIT | ||
428 | "MISALIGN: %lx:" | ||
429 | " insn not move to/from memory %x\n", | ||
430 | regs->pc, opcode); | ||
431 | goto failed; | ||
432 | } | ||
433 | |||
434 | if (pop->params[0] & 0x80000000) { | ||
435 | /* move memory to register */ | ||
436 | if (!misalignment_addr(registers, pop->params[0], opcode, disp, | ||
437 | &address, &postinc)) | ||
438 | goto bad_addr_mode; | ||
439 | |||
440 | if (!misalignment_reg(registers, pop->params[1], opcode, disp, | ||
441 | &store)) | ||
442 | goto bad_reg_mode; | ||
443 | |||
444 | if (strcmp(pop->name, "mov") == 0) { | ||
445 | kdebug("FIXUP: mov (%p),DARn\n", address); | ||
446 | if (copy_from_user(&data, (void *) address, 4) != 0) | ||
447 | goto transfer_failed; | ||
448 | if (pop->params[0] & 0x1000000) | ||
449 | *postinc += 4; | ||
450 | } else if (strcmp(pop->name, "movhu") == 0) { | ||
451 | kdebug("FIXUP: movhu (%p),DARn\n", address); | ||
452 | data = 0; | ||
453 | if (copy_from_user(&data, (void *) address, 2) != 0) | ||
454 | goto transfer_failed; | ||
455 | if (pop->params[0] & 0x1000000) | ||
456 | *postinc += 2; | ||
457 | } else { | ||
458 | goto unsupported_instruction; | ||
459 | } | ||
460 | |||
461 | *store = data; | ||
462 | } else { | ||
463 | /* move register to memory */ | ||
464 | if (!misalignment_reg(registers, pop->params[0], opcode, disp, | ||
465 | &store)) | ||
466 | goto bad_reg_mode; | ||
467 | |||
468 | if (!misalignment_addr(registers, pop->params[1], opcode, disp, | ||
469 | &address, &postinc)) | ||
470 | goto bad_addr_mode; | ||
471 | |||
472 | data = *store; | ||
473 | |||
474 | if (strcmp(pop->name, "mov") == 0) { | ||
475 | kdebug("FIXUP: mov %lx,(%p)\n", data, address); | ||
476 | if (copy_to_user((void *) address, &data, 4) != 0) | ||
477 | goto transfer_failed; | ||
478 | if (pop->params[1] & 0x1000000) | ||
479 | *postinc += 4; | ||
480 | } else if (strcmp(pop->name, "movhu") == 0) { | ||
481 | kdebug("FIXUP: movhu %hx,(%p)\n", | ||
482 | (uint16_t) data, address); | ||
483 | if (copy_to_user((void *) address, &data, 2) != 0) | ||
484 | goto transfer_failed; | ||
485 | if (pop->params[1] & 0x1000000) | ||
486 | *postinc += 2; | ||
487 | } else { | ||
488 | goto unsupported_instruction; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; | ||
493 | regs->pc += tmp >> 3; | ||
494 | |||
495 | set_fs(seg); | ||
496 | return; | ||
497 | } | ||
498 | |||
499 | /* | ||
500 | * determine the address that was being accessed | ||
501 | */ | ||
502 | static int misalignment_addr(unsigned long *registers, unsigned params, | ||
503 | unsigned opcode, unsigned disp, | ||
504 | void **_address, unsigned long **_postinc) | ||
505 | { | ||
506 | unsigned long *postinc = NULL, address = 0, tmp; | ||
507 | |||
508 | params &= 0x7fffffff; | ||
509 | |||
510 | do { | ||
511 | switch (params & 0xff) { | ||
512 | case DM0: | ||
513 | postinc = ®isters[Dreg_index[opcode & 0x03]]; | ||
514 | address += *postinc; | ||
515 | break; | ||
516 | case DM1: | ||
517 | postinc = ®isters[Dreg_index[opcode >> 2 & 0x0c]]; | ||
518 | address += *postinc; | ||
519 | break; | ||
520 | case DM2: | ||
521 | postinc = ®isters[Dreg_index[opcode >> 4 & 0x30]]; | ||
522 | address += *postinc; | ||
523 | break; | ||
524 | case AM0: | ||
525 | postinc = ®isters[Areg_index[opcode & 0x03]]; | ||
526 | address += *postinc; | ||
527 | break; | ||
528 | case AM1: | ||
529 | postinc = ®isters[Areg_index[opcode >> 2 & 0x0c]]; | ||
530 | address += *postinc; | ||
531 | break; | ||
532 | case AM2: | ||
533 | postinc = ®isters[Areg_index[opcode >> 4 & 0x30]]; | ||
534 | address += *postinc; | ||
535 | break; | ||
536 | case RM0: | ||
537 | postinc = ®isters[Rreg_index[opcode & 0x0f]]; | ||
538 | address += *postinc; | ||
539 | break; | ||
540 | case RM1: | ||
541 | postinc = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; | ||
542 | address += *postinc; | ||
543 | break; | ||
544 | case RM2: | ||
545 | postinc = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; | ||
546 | address += *postinc; | ||
547 | break; | ||
548 | case RM4: | ||
549 | postinc = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; | ||
550 | address += *postinc; | ||
551 | break; | ||
552 | case RM6: | ||
553 | postinc = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; | ||
554 | address += *postinc; | ||
555 | break; | ||
556 | case RD0: | ||
557 | postinc = ®isters[Rreg_index[disp & 0x0f]]; | ||
558 | address += *postinc; | ||
559 | break; | ||
560 | case RD2: | ||
561 | postinc = ®isters[Rreg_index[disp >> 4 & 0x0f]]; | ||
562 | address += *postinc; | ||
563 | break; | ||
564 | |||
565 | case SD8: | ||
566 | case SIMM8: | ||
567 | address += (int32_t) (int8_t) (disp & 0xff); | ||
568 | break; | ||
569 | case SD16: | ||
570 | address += (int32_t) (int16_t) (disp & 0xffff); | ||
571 | break; | ||
572 | case SD24: | ||
573 | tmp = disp << 8; | ||
574 | asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); | ||
575 | address += tmp; | ||
576 | break; | ||
577 | case SIMM4_2: | ||
578 | tmp = opcode >> 4 & 0x0f; | ||
579 | tmp <<= 28; | ||
580 | asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); | ||
581 | address += tmp; | ||
582 | break; | ||
583 | case IMM24: | ||
584 | address += disp & 0x00ffffff; | ||
585 | break; | ||
586 | case IMM32: | ||
587 | case IMM32_HIGH8: | ||
588 | address += disp; | ||
589 | break; | ||
590 | default: | ||
591 | return 0; | ||
592 | } | ||
593 | } while ((params >>= 8)); | ||
594 | |||
595 | *_address = (void *) address; | ||
596 | *_postinc = postinc; | ||
597 | return 1; | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * determine the register that is acting as source/dest | ||
602 | */ | ||
603 | static int misalignment_reg(unsigned long *registers, unsigned params, | ||
604 | unsigned opcode, unsigned disp, | ||
605 | unsigned long **_register) | ||
606 | { | ||
607 | params &= 0x7fffffff; | ||
608 | |||
609 | if (params & 0xffffff00) | ||
610 | return 0; | ||
611 | |||
612 | switch (params & 0xff) { | ||
613 | case DM0: | ||
614 | *_register = ®isters[Dreg_index[opcode & 0x03]]; | ||
615 | break; | ||
616 | case DM1: | ||
617 | *_register = ®isters[Dreg_index[opcode >> 2 & 0x03]]; | ||
618 | break; | ||
619 | case DM2: | ||
620 | *_register = ®isters[Dreg_index[opcode >> 4 & 0x03]]; | ||
621 | break; | ||
622 | case AM0: | ||
623 | *_register = ®isters[Areg_index[opcode & 0x03]]; | ||
624 | break; | ||
625 | case AM1: | ||
626 | *_register = ®isters[Areg_index[opcode >> 2 & 0x03]]; | ||
627 | break; | ||
628 | case AM2: | ||
629 | *_register = ®isters[Areg_index[opcode >> 4 & 0x03]]; | ||
630 | break; | ||
631 | case RM0: | ||
632 | *_register = ®isters[Rreg_index[opcode & 0x0f]]; | ||
633 | break; | ||
634 | case RM1: | ||
635 | *_register = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; | ||
636 | break; | ||
637 | case RM2: | ||
638 | *_register = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; | ||
639 | break; | ||
640 | case RM4: | ||
641 | *_register = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; | ||
642 | break; | ||
643 | case RM6: | ||
644 | *_register = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; | ||
645 | break; | ||
646 | case RD0: | ||
647 | *_register = ®isters[Rreg_index[disp & 0x0f]]; | ||
648 | break; | ||
649 | case RD2: | ||
650 | *_register = ®isters[Rreg_index[disp >> 4 & 0x0f]]; | ||
651 | break; | ||
652 | case SP: | ||
653 | *_register = ®isters[REG_SP >> 2]; | ||
654 | break; | ||
655 | |||
656 | default: | ||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | return 1; | ||
661 | } | ||
diff --git a/arch/mn10300/mm/mmu-context.c b/arch/mn10300/mm/mmu-context.c new file mode 100644 index 000000000000..31c9d27a75ae --- /dev/null +++ b/arch/mn10300/mm/mmu-context.c | |||
@@ -0,0 +1,80 @@ | |||
1 | /* MN10300 MMU context allocation and management | ||
2 | * | ||
3 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <asm/mmu_context.h> | ||
14 | #include <asm/tlbflush.h> | ||
15 | |||
16 | /* | ||
17 | * list of the MMU contexts last allocated on each CPU | ||
18 | */ | ||
19 | unsigned long mmu_context_cache[NR_CPUS] = { | ||
20 | [0 ... NR_CPUS - 1] = MMU_CONTEXT_FIRST_VERSION * 2 - 1, | ||
21 | }; | ||
22 | |||
23 | /* | ||
24 | * flush the specified TLB entry | ||
25 | */ | ||
26 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) | ||
27 | { | ||
28 | unsigned long pteu, cnx, flags; | ||
29 | |||
30 | addr &= PAGE_MASK; | ||
31 | |||
32 | /* make sure the context doesn't migrate and defend against | ||
33 | * interference from vmalloc'd regions */ | ||
34 | local_irq_save(flags); | ||
35 | |||
36 | cnx = mm_context(vma->vm_mm); | ||
37 | |||
38 | if (cnx != MMU_NO_CONTEXT) { | ||
39 | pteu = addr | (cnx & 0x000000ffUL); | ||
40 | IPTEU = pteu; | ||
41 | DPTEU = pteu; | ||
42 | if (IPTEL & xPTEL_V) | ||
43 | IPTEL = 0; | ||
44 | if (DPTEL & xPTEL_V) | ||
45 | DPTEL = 0; | ||
46 | } | ||
47 | |||
48 | local_irq_restore(flags); | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * preemptively set a TLB entry | ||
53 | */ | ||
54 | void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) | ||
55 | { | ||
56 | unsigned long pteu, ptel, cnx, flags; | ||
57 | |||
58 | addr &= PAGE_MASK; | ||
59 | ptel = pte_val(pte) & ~(xPTEL_UNUSED1 | xPTEL_UNUSED2); | ||
60 | |||
61 | /* make sure the context doesn't migrate and defend against | ||
62 | * interference from vmalloc'd regions */ | ||
63 | local_irq_save(flags); | ||
64 | |||
65 | cnx = mm_context(vma->vm_mm); | ||
66 | |||
67 | if (cnx != MMU_NO_CONTEXT) { | ||
68 | pteu = addr | (cnx & 0x000000ffUL); | ||
69 | if (!(pte_val(pte) & _PAGE_NX)) { | ||
70 | IPTEU = pteu; | ||
71 | if (IPTEL & xPTEL_V) | ||
72 | IPTEL = ptel; | ||
73 | } | ||
74 | DPTEU = pteu; | ||
75 | if (DPTEL & xPTEL_V) | ||
76 | DPTEL = ptel; | ||
77 | } | ||
78 | |||
79 | local_irq_restore(flags); | ||
80 | } | ||
diff --git a/arch/mn10300/mm/pgtable.c b/arch/mn10300/mm/pgtable.c new file mode 100644 index 000000000000..a477038752ba --- /dev/null +++ b/arch/mn10300/mm/pgtable.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* MN10300 Page table management | ||
2 | * | ||
3 | * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | ||
4 | * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
5 | * Modified by David Howells (dhowells@redhat.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public Licence | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the Licence, or (at your option) any later version. | ||
11 | */ | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/swap.h> | ||
17 | #include <linux/smp.h> | ||
18 | #include <linux/highmem.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/pagemap.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/quicklist.h> | ||
23 | |||
24 | #include <asm/system.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/pgalloc.h> | ||
27 | #include <asm/tlb.h> | ||
28 | #include <asm/tlbflush.h> | ||
29 | |||
30 | void show_mem(void) | ||
31 | { | ||
32 | unsigned long i; | ||
33 | int free = 0, total = 0, reserved = 0, shared = 0; | ||
34 | |||
35 | int cached = 0; | ||
36 | printk(KERN_INFO "Mem-info:\n"); | ||
37 | show_free_areas(); | ||
38 | i = max_mapnr; | ||
39 | while (i-- > 0) { | ||
40 | total++; | ||
41 | if (PageReserved(mem_map + i)) | ||
42 | reserved++; | ||
43 | else if (PageSwapCache(mem_map + i)) | ||
44 | cached++; | ||
45 | else if (!page_count(mem_map + i)) | ||
46 | free++; | ||
47 | else | ||
48 | shared += page_count(mem_map + i) - 1; | ||
49 | } | ||
50 | printk(KERN_INFO "%d pages of RAM\n", total); | ||
51 | printk(KERN_INFO "%d free pages\n", free); | ||
52 | printk(KERN_INFO "%d reserved pages\n", reserved); | ||
53 | printk(KERN_INFO "%d pages shared\n", shared); | ||
54 | printk(KERN_INFO "%d pages swap cached\n", cached); | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * Associate a large virtual page frame with a given physical page frame | ||
59 | * and protection flags for that frame. pfn is for the base of the page, | ||
60 | * vaddr is what the page gets mapped to - both must be properly aligned. | ||
61 | * The pmd must already be instantiated. Assumes PAE mode. | ||
62 | */ | ||
63 | void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags) | ||
64 | { | ||
65 | pgd_t *pgd; | ||
66 | pud_t *pud; | ||
67 | pmd_t *pmd; | ||
68 | |||
69 | if (vaddr & (PMD_SIZE-1)) { /* vaddr is misaligned */ | ||
70 | printk(KERN_ERR "set_pmd_pfn: vaddr misaligned\n"); | ||
71 | return; /* BUG(); */ | ||
72 | } | ||
73 | if (pfn & (PTRS_PER_PTE-1)) { /* pfn is misaligned */ | ||
74 | printk(KERN_ERR "set_pmd_pfn: pfn misaligned\n"); | ||
75 | return; /* BUG(); */ | ||
76 | } | ||
77 | pgd = swapper_pg_dir + pgd_index(vaddr); | ||
78 | if (pgd_none(*pgd)) { | ||
79 | printk(KERN_ERR "set_pmd_pfn: pgd_none\n"); | ||
80 | return; /* BUG(); */ | ||
81 | } | ||
82 | pud = pud_offset(pgd, vaddr); | ||
83 | pmd = pmd_offset(pud, vaddr); | ||
84 | set_pmd(pmd, pfn_pmd(pfn, flags)); | ||
85 | /* | ||
86 | * It's enough to flush this one mapping. | ||
87 | * (PGE mappings get flushed as well) | ||
88 | */ | ||
89 | __flush_tlb_one(vaddr); | ||
90 | } | ||
91 | |||
92 | pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) | ||
93 | { | ||
94 | pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); | ||
95 | if (pte) | ||
96 | clear_page(pte); | ||
97 | return pte; | ||
98 | } | ||
99 | |||
100 | struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) | ||
101 | { | ||
102 | struct page *pte; | ||
103 | |||
104 | #ifdef CONFIG_HIGHPTE | ||
105 | pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0); | ||
106 | #else | ||
107 | pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); | ||
108 | #endif | ||
109 | if (pte) | ||
110 | clear_highpage(pte); | ||
111 | return pte; | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * List of all pgd's needed for non-PAE so it can invalidate entries | ||
116 | * in both cached and uncached pgd's; not needed for PAE since the | ||
117 | * kernel pmd is shared. If PAE were not to share the pmd a similar | ||
118 | * tactic would be needed. This is essentially codepath-based locking | ||
119 | * against pageattr.c; it is the unique case in which a valid change | ||
120 | * of kernel pagetables can't be lazily synchronized by vmalloc faults. | ||
121 | * vmalloc faults work because attached pagetables are never freed. | ||
122 | * If the locking proves to be non-performant, a ticketing scheme with | ||
123 | * checks at dup_mmap(), exec(), and other mmlist addition points | ||
124 | * could be used. The locking scheme was chosen on the basis of | ||
125 | * manfred's recommendations and having no core impact whatsoever. | ||
126 | * -- wli | ||
127 | */ | ||
128 | DEFINE_SPINLOCK(pgd_lock); | ||
129 | struct page *pgd_list; | ||
130 | |||
131 | static inline void pgd_list_add(pgd_t *pgd) | ||
132 | { | ||
133 | struct page *page = virt_to_page(pgd); | ||
134 | page->index = (unsigned long) pgd_list; | ||
135 | if (pgd_list) | ||
136 | set_page_private(pgd_list, (unsigned long) &page->index); | ||
137 | pgd_list = page; | ||
138 | set_page_private(page, (unsigned long) &pgd_list); | ||
139 | } | ||
140 | |||
141 | static inline void pgd_list_del(pgd_t *pgd) | ||
142 | { | ||
143 | struct page *next, **pprev, *page = virt_to_page(pgd); | ||
144 | next = (struct page *) page->index; | ||
145 | pprev = (struct page **) page_private(page); | ||
146 | *pprev = next; | ||
147 | if (next) | ||
148 | set_page_private(next, (unsigned long) pprev); | ||
149 | } | ||
150 | |||
151 | void pgd_ctor(void *pgd) | ||
152 | { | ||
153 | unsigned long flags; | ||
154 | |||
155 | if (PTRS_PER_PMD == 1) | ||
156 | spin_lock_irqsave(&pgd_lock, flags); | ||
157 | |||
158 | memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, | ||
159 | swapper_pg_dir + USER_PTRS_PER_PGD, | ||
160 | (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); | ||
161 | |||
162 | if (PTRS_PER_PMD > 1) | ||
163 | return; | ||
164 | |||
165 | pgd_list_add(pgd); | ||
166 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
167 | memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); | ||
168 | } | ||
169 | |||
170 | /* never called when PTRS_PER_PMD > 1 */ | ||
171 | void pgd_dtor(void *pgd) | ||
172 | { | ||
173 | unsigned long flags; /* can be called from interrupt context */ | ||
174 | |||
175 | spin_lock_irqsave(&pgd_lock, flags); | ||
176 | pgd_list_del(pgd); | ||
177 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
178 | } | ||
179 | |||
180 | pgd_t *pgd_alloc(struct mm_struct *mm) | ||
181 | { | ||
182 | return quicklist_alloc(0, GFP_KERNEL, pgd_ctor); | ||
183 | } | ||
184 | |||
185 | void pgd_free(struct mm_struct *mm, pgd_t *pgd) | ||
186 | { | ||
187 | quicklist_free(0, pgd_dtor, pgd); | ||
188 | } | ||
189 | |||
190 | void __init pgtable_cache_init(void) | ||
191 | { | ||
192 | } | ||
193 | |||
194 | void check_pgt_cache(void) | ||
195 | { | ||
196 | quicklist_trim(0, pgd_dtor, 25, 16); | ||
197 | } | ||
diff --git a/arch/mn10300/mm/tlb-mn10300.S b/arch/mn10300/mm/tlb-mn10300.S new file mode 100644 index 000000000000..789208094e98 --- /dev/null +++ b/arch/mn10300/mm/tlb-mn10300.S | |||
@@ -0,0 +1,207 @@ | |||
1 | ############################################################################### | ||
2 | # | ||
3 | # TLB loading functions | ||
4 | # | ||
5 | # Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. | ||
6 | # Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | ||
7 | # Modified by David Howells (dhowells@redhat.com) | ||
8 | # | ||
9 | # This program is free software; you can redistribute it and/or | ||
10 | # modify it under the terms of the GNU General Public Licence | ||
11 | # as published by the Free Software Foundation; either version | ||
12 | # 2 of the Licence, or (at your option) any later version. | ||
13 | # | ||
14 | ############################################################################### | ||
15 | #include <linux/sys.h> | ||
16 | #include <linux/linkage.h> | ||
17 | #include <asm/smp.h> | ||
18 | #include <asm/intctl-regs.h> | ||
19 | #include <asm/frame.inc> | ||
20 | #include <asm/page.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | |||
23 | ############################################################################### | ||
24 | # | ||
25 | # Instruction TLB Miss handler entry point | ||
26 | # | ||
27 | ############################################################################### | ||
28 | .type itlb_miss,@function | ||
29 | ENTRY(itlb_miss) | ||
30 | and ~EPSW_NMID,epsw | ||
31 | #ifdef CONFIG_GDBSTUB | ||
32 | movm [d2,d3,a2],(sp) | ||
33 | #else | ||
34 | or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate | ||
35 | # register bank | ||
36 | nop | ||
37 | nop | ||
38 | nop | ||
39 | #endif | ||
40 | |||
41 | mov (IPTEU),d3 | ||
42 | mov (PTBR),a2 | ||
43 | mov d3,d2 | ||
44 | and 0xffc00000,d2 | ||
45 | lsr 20,d2 | ||
46 | mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] | ||
47 | btst _PAGE_VALID,a2 | ||
48 | beq itlb_miss_fault # jump if doesn't point anywhere | ||
49 | |||
50 | and ~(PAGE_SIZE-1),a2 | ||
51 | mov d3,d2 | ||
52 | and 0x003ff000,d2 | ||
53 | lsr 10,d2 | ||
54 | add d2,a2 | ||
55 | mov (a2),d2 # get pte from PTD[addr 21..12] | ||
56 | btst _PAGE_VALID,d2 | ||
57 | beq itlb_miss_fault # jump if doesn't point to a page | ||
58 | # (might be a swap id) | ||
59 | bset _PAGE_ACCESSED,(0,a2) | ||
60 | and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 | ||
61 | itlb_miss_set: | ||
62 | mov d2,(IPTEL) # change the TLB | ||
63 | #ifdef CONFIG_GDBSTUB | ||
64 | movm (sp),[d2,d3,a2] | ||
65 | #endif | ||
66 | rti | ||
67 | |||
68 | itlb_miss_fault: | ||
69 | mov _PAGE_VALID,d2 # force address error handler to be | ||
70 | # invoked | ||
71 | bra itlb_miss_set | ||
72 | |||
73 | .size itlb_miss, . - itlb_miss | ||
74 | |||
75 | ############################################################################### | ||
76 | # | ||
77 | # Data TLB Miss handler entry point | ||
78 | # | ||
79 | ############################################################################### | ||
80 | .type dtlb_miss,@function | ||
81 | ENTRY(dtlb_miss) | ||
82 | and ~EPSW_NMID,epsw | ||
83 | #ifdef CONFIG_GDBSTUB | ||
84 | movm [d2,d3,a2],(sp) | ||
85 | #else | ||
86 | or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate | ||
87 | # register bank | ||
88 | nop | ||
89 | nop | ||
90 | nop | ||
91 | #endif | ||
92 | |||
93 | mov (DPTEU),d3 | ||
94 | mov (PTBR),a2 | ||
95 | mov d3,d2 | ||
96 | and 0xffc00000,d2 | ||
97 | lsr 20,d2 | ||
98 | mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] | ||
99 | btst _PAGE_VALID,a2 | ||
100 | beq dtlb_miss_fault # jump if doesn't point anywhere | ||
101 | |||
102 | and ~(PAGE_SIZE-1),a2 | ||
103 | mov d3,d2 | ||
104 | and 0x003ff000,d2 | ||
105 | lsr 10,d2 | ||
106 | add d2,a2 | ||
107 | mov (a2),d2 # get pte from PTD[addr 21..12] | ||
108 | btst _PAGE_VALID,d2 | ||
109 | beq dtlb_miss_fault # jump if doesn't point to a page | ||
110 | # (might be a swap id) | ||
111 | bset _PAGE_ACCESSED,(0,a2) | ||
112 | and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 | ||
113 | dtlb_miss_set: | ||
114 | mov d2,(DPTEL) # change the TLB | ||
115 | #ifdef CONFIG_GDBSTUB | ||
116 | movm (sp),[d2,d3,a2] | ||
117 | #endif | ||
118 | rti | ||
119 | |||
120 | dtlb_miss_fault: | ||
121 | mov _PAGE_VALID,d2 # force address error handler to be | ||
122 | # invoked | ||
123 | bra dtlb_miss_set | ||
124 | .size dtlb_miss, . - dtlb_miss | ||
125 | |||
126 | ############################################################################### | ||
127 | # | ||
128 | # Instruction TLB Address Error handler entry point | ||
129 | # | ||
130 | ############################################################################### | ||
131 | .type itlb_aerror,@function | ||
132 | ENTRY(itlb_aerror) | ||
133 | and ~EPSW_NMID,epsw | ||
134 | add -4,sp | ||
135 | SAVE_ALL | ||
136 | add -4,sp # need to pass three params | ||
137 | |||
138 | # calculate the fault code | ||
139 | movhu (MMUFCR_IFC),d1 | ||
140 | or 0x00010000,d1 # it's an instruction fetch | ||
141 | |||
142 | # determine the page address | ||
143 | mov (IPTEU),a2 | ||
144 | mov a2,d0 | ||
145 | and PAGE_MASK,d0 | ||
146 | mov d0,(12,sp) | ||
147 | |||
148 | clr d0 | ||
149 | mov d0,(IPTEL) | ||
150 | |||
151 | and ~EPSW_NMID,epsw | ||
152 | or EPSW_IE,epsw | ||
153 | mov fp,d0 | ||
154 | call do_page_fault[],0 # do_page_fault(regs,code,addr | ||
155 | |||
156 | jmp ret_from_exception | ||
157 | .size itlb_aerror, . - itlb_aerror | ||
158 | |||
159 | ############################################################################### | ||
160 | # | ||
161 | # Data TLB Address Error handler entry point | ||
162 | # | ||
163 | ############################################################################### | ||
164 | .type dtlb_aerror,@function | ||
165 | ENTRY(dtlb_aerror) | ||
166 | and ~EPSW_NMID,epsw | ||
167 | add -4,sp | ||
168 | mov d1,(sp) | ||
169 | |||
170 | movhu (MMUFCR_DFC),d1 # is it the initial valid write | ||
171 | # to this page? | ||
172 | and MMUFCR_xFC_INITWR,d1 | ||
173 | beq dtlb_pagefault # jump if not | ||
174 | |||
175 | mov (DPTEL),d1 # set the dirty bit | ||
176 | # (don't replace with BSET!) | ||
177 | or _PAGE_DIRTY,d1 | ||
178 | mov d1,(DPTEL) | ||
179 | mov (sp),d1 | ||
180 | add 4,sp | ||
181 | rti | ||
182 | |||
183 | ALIGN | ||
184 | dtlb_pagefault: | ||
185 | mov (sp),d1 | ||
186 | SAVE_ALL | ||
187 | add -4,sp # need to pass three params | ||
188 | |||
189 | # calculate the fault code | ||
190 | movhu (MMUFCR_DFC),d1 | ||
191 | |||
192 | # determine the page address | ||
193 | mov (DPTEU),a2 | ||
194 | mov a2,d0 | ||
195 | and PAGE_MASK,d0 | ||
196 | mov d0,(12,sp) | ||
197 | |||
198 | clr d0 | ||
199 | mov d0,(DPTEL) | ||
200 | |||
201 | and ~EPSW_NMID,epsw | ||
202 | or EPSW_IE,epsw | ||
203 | mov fp,d0 | ||
204 | call do_page_fault[],0 # do_page_fault(regs,code,addr | ||
205 | |||
206 | jmp ret_from_exception | ||
207 | .size dtlb_aerror, . - dtlb_aerror | ||