1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | ** Tablewalk MMU emulator |
4 | ** |
5 | ** by Toshiyasu Morita |
6 | ** |
7 | ** Started 1/16/98 @ 2:22 am |
8 | */ |
9 | |
10 | #include <linux/init.h> |
11 | #include <linux/mman.h> |
12 | #include <linux/mm.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/ptrace.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/memblock.h> |
17 | #include <linux/bitops.h> |
18 | #include <linux/module.h> |
19 | #include <linux/sched/mm.h> |
20 | |
21 | #include <asm/setup.h> |
22 | #include <asm/traps.h> |
23 | #include <linux/uaccess.h> |
24 | #include <asm/page.h> |
25 | #include <asm/sun3mmu.h> |
26 | #include <asm/oplib.h> |
27 | #include <asm/mmu_context.h> |
28 | #include <asm/dvma.h> |
29 | |
30 | #include "sun3.h" |
31 | |
32 | #undef DEBUG_MMU_EMU |
33 | #define DEBUG_PROM_MAPS |
34 | |
35 | /* |
36 | ** Defines |
37 | */ |
38 | |
39 | #define CONTEXTS_NUM 8 |
40 | #define SEGMAPS_PER_CONTEXT_NUM 2048 |
41 | #define PAGES_PER_SEGMENT 16 |
42 | #define PMEGS_NUM 256 |
43 | #define PMEG_MASK 0xFF |
44 | |
45 | /* |
46 | ** Globals |
47 | */ |
48 | |
49 | unsigned long m68k_vmalloc_end; |
50 | EXPORT_SYMBOL(m68k_vmalloc_end); |
51 | |
52 | unsigned long pmeg_vaddr[PMEGS_NUM]; |
53 | unsigned char pmeg_alloc[PMEGS_NUM]; |
54 | unsigned char pmeg_ctx[PMEGS_NUM]; |
55 | |
56 | /* pointers to the mm structs for each task in each |
57 | context. 0xffffffff is a marker for kernel context */ |
58 | static struct mm_struct *ctx_alloc[CONTEXTS_NUM] = { |
59 | [0] = (struct mm_struct *)0xffffffff |
60 | }; |
61 | |
62 | /* has this context been mmdrop'd? */ |
63 | static unsigned char ctx_avail = CONTEXTS_NUM-1; |
64 | |
65 | /* array of pages to be marked off for the rom when we do mem_init later */ |
66 | /* 256 pages lets the rom take up to 2mb of physical ram.. I really |
67 | hope it never wants mote than that. */ |
68 | unsigned long rom_pages[256]; |
69 | |
70 | /* Print a PTE value in symbolic form. For debugging. */ |
71 | static void print_pte(pte_t pte) |
72 | { |
73 | #if 0 |
74 | /* Verbose version. */ |
75 | unsigned long val = pte_val (pte); |
76 | pr_cont(" pte=%lx [addr=%lx" , |
77 | val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT); |
78 | if (val & SUN3_PAGE_VALID) pr_cont(" valid" ); |
79 | if (val & SUN3_PAGE_WRITEABLE) pr_cont(" write" ); |
80 | if (val & SUN3_PAGE_SYSTEM) pr_cont(" sys" ); |
81 | if (val & SUN3_PAGE_NOCACHE) pr_cont(" nocache" ); |
82 | if (val & SUN3_PAGE_ACCESSED) pr_cont(" accessed" ); |
83 | if (val & SUN3_PAGE_MODIFIED) pr_cont(" modified" ); |
84 | switch (val & SUN3_PAGE_TYPE_MASK) { |
85 | case SUN3_PAGE_TYPE_MEMORY: pr_cont(" memory" ); break; |
86 | case SUN3_PAGE_TYPE_IO: pr_cont(" io" ); break; |
87 | case SUN3_PAGE_TYPE_VME16: pr_cont(" vme16" ); break; |
88 | case SUN3_PAGE_TYPE_VME32: pr_cont(" vme32" ); break; |
89 | } |
90 | pr_cont("]\n" ); |
91 | #else |
92 | /* Terse version. More likely to fit on a line. */ |
93 | unsigned long val = pte_val (pte); |
94 | char flags[7], *type; |
95 | |
96 | flags[0] = (val & SUN3_PAGE_VALID) ? 'v' : '-'; |
97 | flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-'; |
98 | flags[2] = (val & SUN3_PAGE_SYSTEM) ? 's' : '-'; |
99 | flags[3] = (val & SUN3_PAGE_NOCACHE) ? 'x' : '-'; |
100 | flags[4] = (val & SUN3_PAGE_ACCESSED) ? 'a' : '-'; |
101 | flags[5] = (val & SUN3_PAGE_MODIFIED) ? 'm' : '-'; |
102 | flags[6] = '\0'; |
103 | |
104 | switch (val & SUN3_PAGE_TYPE_MASK) { |
105 | case SUN3_PAGE_TYPE_MEMORY: type = "memory" ; break; |
106 | case SUN3_PAGE_TYPE_IO: type = "io" ; break; |
107 | case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break; |
108 | case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break; |
109 | default: type = "unknown?" ; break; |
110 | } |
111 | |
112 | pr_cont(" pte=%08lx [%07lx %s %s]\n" , |
113 | val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type); |
114 | #endif |
115 | } |
116 | |
117 | /* Print the PTE value for a given virtual address. For debugging. */ |
118 | void print_pte_vaddr (unsigned long vaddr) |
119 | { |
120 | pr_cont(" vaddr=%lx [%02lx]" , vaddr, sun3_get_segmap (vaddr)); |
121 | print_pte (pte: __pte (val: sun3_get_pte (vaddr))); |
122 | } |
123 | |
124 | /* |
125 | * Initialise the MMU emulator. |
126 | */ |
127 | void __init mmu_emu_init(unsigned long bootmem_end) |
128 | { |
129 | unsigned long seg, num; |
130 | int i,j; |
131 | |
132 | memset(rom_pages, 0, sizeof(rom_pages)); |
133 | memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr)); |
134 | memset(pmeg_alloc, 0, sizeof(pmeg_alloc)); |
135 | memset(pmeg_ctx, 0, sizeof(pmeg_ctx)); |
136 | |
137 | /* pmeg align the end of bootmem, adding another pmeg, |
138 | * later bootmem allocations will likely need it */ |
139 | bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK; |
140 | |
141 | /* mark all of the pmegs used thus far as reserved */ |
142 | for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i) |
143 | pmeg_alloc[i] = 2; |
144 | |
145 | |
146 | /* I'm thinking that most of the top pmeg's are going to be |
147 | used for something, and we probably shouldn't risk it */ |
148 | for(num = 0xf0; num <= 0xff; num++) |
149 | pmeg_alloc[num] = 2; |
150 | |
151 | /* liberate all existing mappings in the rest of kernel space */ |
152 | for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) { |
153 | i = sun3_get_segmap(seg); |
154 | |
155 | if(!pmeg_alloc[i]) { |
156 | #ifdef DEBUG_MMU_EMU |
157 | pr_info("freed:" ); |
158 | print_pte_vaddr (seg); |
159 | #endif |
160 | sun3_put_segmap(seg, SUN3_INVALID_PMEG); |
161 | } |
162 | } |
163 | |
164 | j = 0; |
165 | for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) { |
166 | if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) { |
167 | #ifdef DEBUG_PROM_MAPS |
168 | for(i = 0; i < 16; i++) { |
169 | pr_info("mapped:" ); |
170 | print_pte_vaddr (vaddr: seg + (i*PAGE_SIZE)); |
171 | break; |
172 | } |
173 | #endif |
174 | // the lowest mapping here is the end of our |
175 | // vmalloc region |
176 | if (!m68k_vmalloc_end) |
177 | m68k_vmalloc_end = seg; |
178 | |
179 | // mark the segmap alloc'd, and reserve any |
180 | // of the first 0xbff pages the hardware is |
181 | // already using... does any sun3 support > 24mb? |
182 | pmeg_alloc[sun3_get_segmap(seg)] = 2; |
183 | } |
184 | } |
185 | |
186 | dvma_init(); |
187 | |
188 | |
189 | /* blank everything below the kernel, and we've got the base |
190 | mapping to start all the contexts off with... */ |
191 | for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE) |
192 | sun3_put_segmap(seg, SUN3_INVALID_PMEG); |
193 | |
194 | set_fc(3); |
195 | for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) { |
196 | i = sun3_get_segmap(seg); |
197 | for(j = 1; j < CONTEXTS_NUM; j++) |
198 | (*(romvec->pv_setctxt))(j, (void *)seg, i); |
199 | } |
200 | set_fc(USER_DATA); |
201 | } |
202 | |
203 | /* erase the mappings for a dead context. Uses the pg_dir for hints |
204 | as the pmeg tables proved somewhat unreliable, and unmapping all of |
205 | TASK_SIZE was much slower and no more stable. */ |
206 | /* todo: find a better way to keep track of the pmegs used by a |
207 | context for when they're cleared */ |
208 | void clear_context(unsigned long context) |
209 | { |
210 | unsigned char oldctx; |
211 | unsigned long i; |
212 | |
213 | if (context) { |
214 | if (!ctx_alloc[context]) |
215 | panic(fmt: "%s: context not allocated\n" , __func__); |
216 | |
217 | ctx_alloc[context]->context = SUN3_INVALID_CONTEXT; |
218 | ctx_alloc[context] = (struct mm_struct *)0; |
219 | ctx_avail++; |
220 | } |
221 | |
222 | oldctx = sun3_get_context(); |
223 | |
224 | sun3_put_context(context); |
225 | |
226 | for (i = 0; i < SUN3_INVALID_PMEG; i++) { |
227 | if ((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) { |
228 | sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG); |
229 | pmeg_ctx[i] = 0; |
230 | pmeg_alloc[i] = 0; |
231 | pmeg_vaddr[i] = 0; |
232 | } |
233 | } |
234 | |
235 | sun3_put_context(oldctx); |
236 | } |
237 | |
238 | /* gets an empty context. if full, kills the next context listed to |
239 | die first */ |
240 | /* This context invalidation scheme is, well, totally arbitrary, I'm |
241 | sure it could be much more intelligent... but it gets the job done |
242 | for now without much overhead in making it's decision. */ |
243 | /* todo: come up with optimized scheme for flushing contexts */ |
244 | unsigned long get_free_context(struct mm_struct *mm) |
245 | { |
246 | unsigned long new = 1; |
247 | static unsigned char next_to_die = 1; |
248 | |
249 | if(!ctx_avail) { |
250 | /* kill someone to get our context */ |
251 | new = next_to_die; |
252 | clear_context(context: new); |
253 | next_to_die = (next_to_die + 1) & 0x7; |
254 | if(!next_to_die) |
255 | next_to_die++; |
256 | } else { |
257 | while(new < CONTEXTS_NUM) { |
258 | if(ctx_alloc[new]) |
259 | new++; |
260 | else |
261 | break; |
262 | } |
263 | // check to make sure one was really free... |
264 | if(new == CONTEXTS_NUM) |
265 | panic(fmt: "%s: failed to find free context" , __func__); |
266 | } |
267 | |
268 | ctx_alloc[new] = mm; |
269 | ctx_avail--; |
270 | |
271 | return new; |
272 | } |
273 | |
274 | /* |
275 | * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in |
276 | * `context'. Maintain internal PMEG management structures. This doesn't |
277 | * actually map the physical address, but does clear the old mappings. |
278 | */ |
279 | //todo: better allocation scheme? but is extra complexity worthwhile? |
280 | //todo: only clear old entries if necessary? how to tell? |
281 | |
282 | inline void mmu_emu_map_pmeg (int context, int vaddr) |
283 | { |
284 | static unsigned char curr_pmeg = 128; |
285 | int i; |
286 | |
287 | /* Round address to PMEG boundary. */ |
288 | vaddr &= ~SUN3_PMEG_MASK; |
289 | |
290 | /* Find a spare one. */ |
291 | while (pmeg_alloc[curr_pmeg] == 2) |
292 | ++curr_pmeg; |
293 | |
294 | |
295 | #ifdef DEBUG_MMU_EMU |
296 | pr_info("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n" , |
297 | curr_pmeg, context, vaddr); |
298 | #endif |
299 | |
300 | /* Invalidate old mapping for the pmeg, if any */ |
301 | if (pmeg_alloc[curr_pmeg] == 1) { |
302 | sun3_put_context(pmeg_ctx[curr_pmeg]); |
303 | sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG); |
304 | sun3_put_context(context); |
305 | } |
306 | |
307 | /* Update PMEG management structures. */ |
308 | // don't take pmeg's away from the kernel... |
309 | if(vaddr >= PAGE_OFFSET) { |
310 | /* map kernel pmegs into all contexts */ |
311 | unsigned char i; |
312 | |
313 | for(i = 0; i < CONTEXTS_NUM; i++) { |
314 | sun3_put_context(i); |
315 | sun3_put_segmap (vaddr, curr_pmeg); |
316 | } |
317 | sun3_put_context(context); |
318 | pmeg_alloc[curr_pmeg] = 2; |
319 | pmeg_ctx[curr_pmeg] = 0; |
320 | |
321 | } |
322 | else { |
323 | pmeg_alloc[curr_pmeg] = 1; |
324 | pmeg_ctx[curr_pmeg] = context; |
325 | sun3_put_segmap (vaddr, curr_pmeg); |
326 | |
327 | } |
328 | pmeg_vaddr[curr_pmeg] = vaddr; |
329 | |
330 | /* Set hardware mapping and clear the old PTE entries. */ |
331 | for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE) |
332 | sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM); |
333 | |
334 | /* Consider a different one next time. */ |
335 | ++curr_pmeg; |
336 | } |
337 | |
338 | /* |
339 | * Handle a pagefault at virtual address `vaddr'; check if there should be a |
340 | * page there (specifically, whether the software pagetables indicate that |
341 | * there is). This is necessary due to the limited size of the second-level |
342 | * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a |
343 | * mapping present, we select a `spare' PMEG and use it to create a mapping. |
344 | * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero |
345 | * if we successfully handled the fault. |
346 | */ |
347 | //todo: should we bump minor pagefault counter? if so, here or in caller? |
348 | //todo: possibly inline this into bus_error030 in <asm/buserror.h> ? |
349 | |
350 | // kernel_fault is set when a kernel page couldn't be demand mapped, |
351 | // and forces another try using the kernel page table. basically a |
352 | // hack so that vmalloc would work correctly. |
353 | |
354 | int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault) |
355 | { |
356 | unsigned long segment, offset; |
357 | unsigned char context; |
358 | pte_t *pte; |
359 | pgd_t * crp; |
360 | |
361 | if(current->mm == NULL) { |
362 | crp = swapper_pg_dir; |
363 | context = 0; |
364 | } else { |
365 | context = current->mm->context; |
366 | if(kernel_fault) |
367 | crp = swapper_pg_dir; |
368 | else |
369 | crp = current->mm->pgd; |
370 | } |
371 | |
372 | #ifdef DEBUG_MMU_EMU |
373 | pr_info("%s: vaddr=%lx type=%s crp=%p\n" , __func__, vaddr, |
374 | read_flag ? "read" : "write" , crp); |
375 | #endif |
376 | |
377 | segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF; |
378 | offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF; |
379 | |
380 | #ifdef DEBUG_MMU_EMU |
381 | pr_info("%s: segment=%lx offset=%lx\n" , __func__, segment, offset); |
382 | #endif |
383 | |
384 | pte = (pte_t *) pgd_val (pgd: *(crp + segment)); |
385 | |
386 | //todo: next line should check for valid pmd properly. |
387 | if (!pte) { |
388 | // pr_info("mmu_emu_handle_fault: invalid pmd\n"); |
389 | return 0; |
390 | } |
391 | |
392 | pte = (pte_t *) __va ((unsigned long)(pte + offset)); |
393 | |
394 | /* Make sure this is a valid page */ |
395 | if (!(pte_val (*pte) & SUN3_PAGE_VALID)) |
396 | return 0; |
397 | |
398 | /* Make sure there's a pmeg allocated for the page */ |
399 | if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG) |
400 | mmu_emu_map_pmeg (context, vaddr); |
401 | |
402 | /* Write the pte value to hardware MMU */ |
403 | sun3_put_pte (vaddr&PAGE_MASK, pte_val (pte: *pte)); |
404 | |
405 | /* Update software copy of the pte value */ |
406 | // I'm not sure this is necessary. If this is required, we ought to simply |
407 | // copy this out when we reuse the PMEG or at some other convenient time. |
408 | // Doing it here is fairly meaningless, anyway, as we only know about the |
409 | // first access to a given page. --m |
410 | if (!read_flag) { |
411 | if (pte_val (*pte) & SUN3_PAGE_WRITEABLE) |
412 | pte_val (*pte) |= (SUN3_PAGE_ACCESSED |
413 | | SUN3_PAGE_MODIFIED); |
414 | else |
415 | return 0; /* Write-protect error. */ |
416 | } else |
417 | pte_val (*pte) |= SUN3_PAGE_ACCESSED; |
418 | |
419 | #ifdef DEBUG_MMU_EMU |
420 | pr_info("seg:%ld crp:%p ->" , get_fs().seg, crp); |
421 | print_pte_vaddr (vaddr); |
422 | pr_cont("\n" ); |
423 | #endif |
424 | |
425 | return 1; |
426 | } |
427 | |