blob: 7aa879b7c7ff57423ff792a6275807e5e725c34d [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3** Tablewalk MMU emulator
4**
5** by Toshiyasu Morita
6**
7** Started 1/16/98 @ 2:22 am
8*/
9
Geert Uytterhoevena4df02a2013-06-25 21:15:24 +020010#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/mman.h>
12#include <linux/mm.h>
13#include <linux/kernel.h>
14#include <linux/ptrace.h>
15#include <linux/delay.h>
Mike Rapoport57c8a662018-10-30 15:09:49 -070016#include <linux/memblock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/bitops.h>
18#include <linux/module.h>
Ingo Molnar6e84f312017-02-08 18:51:29 +010019#include <linux/sched/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21#include <asm/setup.h>
22#include <asm/traps.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080023#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <asm/page.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <asm/sun3mmu.h>
26#include <asm/segment.h>
27#include <asm/oplib.h>
28#include <asm/mmu_context.h>
29#include <asm/dvma.h>
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
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
Tejun Heo51e99be2009-12-09 17:43:19 +090049unsigned long m68k_vmalloc_end;
50EXPORT_SYMBOL(m68k_vmalloc_end);
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52unsigned long pmeg_vaddr[PMEGS_NUM];
53unsigned char pmeg_alloc[PMEGS_NUM];
54unsigned 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 */
Adrian Bunk07b81252008-07-17 21:16:27 +020058static struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 [0] = (struct mm_struct *)0xffffffff
60};
61
62/* has this context been mmdrop'd? */
63static 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. */
68unsigned long rom_pages[256];
69
70/* Print a PTE value in symbolic form. For debugging. */
71void print_pte (pte_t pte)
72{
73#if 0
74 /* Verbose version. */
75 unsigned long val = pte_val (pte);
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +010076 pr_cont(" pte=%lx [addr=%lx",
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +010078 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");
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 switch (val & SUN3_PAGE_TYPE_MASK) {
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +010085 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 }
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +010090 pr_cont("]\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070091#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
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100112 pr_cont(" pte=%08lx [%07lx %s %s]\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 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. */
118void print_pte_vaddr (unsigned long vaddr)
119{
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100120 pr_cont(" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 print_pte (__pte (sun3_get_pte (vaddr)));
122}
123
124/*
125 * Initialise the MMU emulator.
126 */
Geert Uytterhoevena4df02a2013-06-25 21:15:24 +0200127void __init mmu_emu_init(unsigned long bootmem_end)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
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
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100157 pr_info("freed:");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 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++) {
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100169 pr_info("mapped:");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 print_pte_vaddr (seg + (i*PAGE_SIZE));
171 break;
172 }
173#endif
174 // the lowest mapping here is the end of our
175 // vmalloc region
Tejun Heo51e99be2009-12-09 17:43:19 +0900176 if (!m68k_vmalloc_end)
177 m68k_vmalloc_end = seg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
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_fs(MAKE_MM_SEG(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_fs(KERNEL_DS);
201
202}
203
204/* erase the mappings for a dead context. Uses the pg_dir for hints
205 as the pmeg tables proved somewhat unreliable, and unmapping all of
206 TASK_SIZE was much slower and no more stable. */
207/* todo: find a better way to keep track of the pmegs used by a
208 context for when they're cleared */
209void clear_context(unsigned long context)
210{
211 unsigned char oldctx;
212 unsigned long i;
213
214 if(context) {
215 if(!ctx_alloc[context])
216 panic("clear_context: context not allocated\n");
217
218 ctx_alloc[context]->context = SUN3_INVALID_CONTEXT;
219 ctx_alloc[context] = (struct mm_struct *)0;
220 ctx_avail++;
221 }
222
223 oldctx = sun3_get_context();
224
225 sun3_put_context(context);
226
227 for(i = 0; i < SUN3_INVALID_PMEG; i++) {
228 if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
229 sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
230 pmeg_ctx[i] = 0;
231 pmeg_alloc[i] = 0;
232 pmeg_vaddr[i] = 0;
233 }
234 }
235
236 sun3_put_context(oldctx);
237}
238
239/* gets an empty context. if full, kills the next context listed to
240 die first */
241/* This context invalidation scheme is, well, totally arbitrary, I'm
Simon Arlott0c79cf62007-10-20 01:20:32 +0200242 sure it could be much more intelligent... but it gets the job done
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 for now without much overhead in making it's decision. */
244/* todo: come up with optimized scheme for flushing contexts */
245unsigned long get_free_context(struct mm_struct *mm)
246{
247 unsigned long new = 1;
248 static unsigned char next_to_die = 1;
249
250 if(!ctx_avail) {
251 /* kill someone to get our context */
252 new = next_to_die;
253 clear_context(new);
254 next_to_die = (next_to_die + 1) & 0x7;
255 if(!next_to_die)
256 next_to_die++;
257 } else {
258 while(new < CONTEXTS_NUM) {
259 if(ctx_alloc[new])
260 new++;
261 else
262 break;
263 }
264 // check to make sure one was really free...
265 if(new == CONTEXTS_NUM)
266 panic("get_free_context: failed to find free context");
267 }
268
269 ctx_alloc[new] = mm;
270 ctx_avail--;
271
272 return new;
273}
274
275/*
276 * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in
277 * `context'. Maintain internal PMEG management structures. This doesn't
278 * actually map the physical address, but does clear the old mappings.
279 */
280//todo: better allocation scheme? but is extra complexity worthwhile?
281//todo: only clear old entries if necessary? how to tell?
282
283inline void mmu_emu_map_pmeg (int context, int vaddr)
284{
285 static unsigned char curr_pmeg = 128;
286 int i;
287
288 /* Round address to PMEG boundary. */
289 vaddr &= ~SUN3_PMEG_MASK;
290
291 /* Find a spare one. */
292 while (pmeg_alloc[curr_pmeg] == 2)
293 ++curr_pmeg;
294
295
296#ifdef DEBUG_MMU_EMU
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100297 pr_info("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
298 curr_pmeg, context, vaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299#endif
300
301 /* Invalidate old mapping for the pmeg, if any */
302 if (pmeg_alloc[curr_pmeg] == 1) {
303 sun3_put_context(pmeg_ctx[curr_pmeg]);
304 sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
305 sun3_put_context(context);
306 }
307
308 /* Update PMEG management structures. */
309 // don't take pmeg's away from the kernel...
310 if(vaddr >= PAGE_OFFSET) {
311 /* map kernel pmegs into all contexts */
312 unsigned char i;
313
314 for(i = 0; i < CONTEXTS_NUM; i++) {
315 sun3_put_context(i);
316 sun3_put_segmap (vaddr, curr_pmeg);
317 }
318 sun3_put_context(context);
319 pmeg_alloc[curr_pmeg] = 2;
320 pmeg_ctx[curr_pmeg] = 0;
321
322 }
323 else {
324 pmeg_alloc[curr_pmeg] = 1;
325 pmeg_ctx[curr_pmeg] = context;
326 sun3_put_segmap (vaddr, curr_pmeg);
327
328 }
329 pmeg_vaddr[curr_pmeg] = vaddr;
330
331 /* Set hardware mapping and clear the old PTE entries. */
332 for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
333 sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
334
335 /* Consider a different one next time. */
336 ++curr_pmeg;
337}
338
339/*
340 * Handle a pagefault at virtual address `vaddr'; check if there should be a
341 * page there (specifically, whether the software pagetables indicate that
342 * there is). This is necessary due to the limited size of the second-level
343 * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a
344 * mapping present, we select a `spare' PMEG and use it to create a mapping.
345 * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero
346 * if we successfully handled the fault.
347 */
348//todo: should we bump minor pagefault counter? if so, here or in caller?
349//todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
350
351// kernel_fault is set when a kernel page couldn't be demand mapped,
352// and forces another try using the kernel page table. basically a
353// hack so that vmalloc would work correctly.
354
355int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault)
356{
357 unsigned long segment, offset;
358 unsigned char context;
359 pte_t *pte;
360 pgd_t * crp;
361
362 if(current->mm == NULL) {
363 crp = swapper_pg_dir;
364 context = 0;
365 } else {
366 context = current->mm->context;
367 if(kernel_fault)
368 crp = swapper_pg_dir;
369 else
370 crp = current->mm->pgd;
371 }
372
373#ifdef DEBUG_MMU_EMU
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100374 pr_info("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 vaddr, read_flag ? "read" : "write", crp);
376#endif
377
378 segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF;
379 offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
380
381#ifdef DEBUG_MMU_EMU
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100382 pr_info("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment,
383 offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384#endif
385
386 pte = (pte_t *) pgd_val (*(crp + segment));
387
388//todo: next line should check for valid pmd properly.
389 if (!pte) {
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100390// pr_info("mmu_emu_handle_fault: invalid pmd\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 return 0;
392 }
393
394 pte = (pte_t *) __va ((unsigned long)(pte + offset));
395
396 /* Make sure this is a valid page */
397 if (!(pte_val (*pte) & SUN3_PAGE_VALID))
398 return 0;
399
400 /* Make sure there's a pmeg allocated for the page */
401 if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
402 mmu_emu_map_pmeg (context, vaddr);
403
404 /* Write the pte value to hardware MMU */
405 sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
406
407 /* Update software copy of the pte value */
408// I'm not sure this is necessary. If this is required, we ought to simply
409// copy this out when we reuse the PMEG or at some other convenient time.
410// Doing it here is fairly meaningless, anyway, as we only know about the
411// first access to a given page. --m
412 if (!read_flag) {
413 if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
414 pte_val (*pte) |= (SUN3_PAGE_ACCESSED
415 | SUN3_PAGE_MODIFIED);
416 else
417 return 0; /* Write-protect error. */
418 } else
419 pte_val (*pte) |= SUN3_PAGE_ACCESSED;
420
421#ifdef DEBUG_MMU_EMU
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100422 pr_info("seg:%ld crp:%p ->", get_fs().seg, crp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 print_pte_vaddr (vaddr);
Geert Uytterhoeven56bbd8622016-11-09 15:40:58 +0100424 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425#endif
426
427 return 1;
428}