OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [sh/] [mm/] [fault.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* $Id: fault.c,v 1.1.1.1 2004-04-15 01:17:15 phoenix Exp $
2
 *
3
 *  linux/arch/sh/mm/fault.c
4
 *  Copyright (C) 1999  Niibe Yutaka
5
 *
6
 *  Based on linux/arch/i386/mm/fault.c:
7
 *   Copyright (C) 1995  Linus Torvalds
8
 */
9
 
10
#include <linux/signal.h>
11
#include <linux/sched.h>
12
#include <linux/kernel.h>
13
#include <linux/errno.h>
14
#include <linux/string.h>
15
#include <linux/types.h>
16
#include <linux/ptrace.h>
17
#include <linux/mman.h>
18
#include <linux/mm.h>
19
#include <linux/smp.h>
20
#include <linux/smp_lock.h>
21
#include <linux/interrupt.h>
22
 
23
#include <asm/system.h>
24
#include <asm/io.h>
25
#include <asm/uaccess.h>
26
#include <asm/pgalloc.h>
27
#include <asm/hardirq.h>
28
#include <asm/mmu_context.h>
29
 
30
#if defined(CONFIG_SH_KGDB)
31
#include <asm/kgdb.h>
32
#endif
33
 
34
extern void die(const char *,struct pt_regs *,long);
35
 
36
/*
37
 * Ugly, ugly, but the goto's result in better assembly..
38
 */
39
int __verify_write(const void * addr, unsigned long size)
40
{
41
        struct vm_area_struct * vma;
42
        unsigned long start = (unsigned long) addr;
43
 
44
        if (!size)
45
                return 1;
46
 
47
        vma = find_vma(current->mm, start);
48
        if (!vma)
49
                goto bad_area;
50
        if (vma->vm_start > start)
51
                goto check_stack;
52
 
53
good_area:
54
        if (!(vma->vm_flags & VM_WRITE))
55
                goto bad_area;
56
        size--;
57
        size += start & ~PAGE_MASK;
58
        size >>= PAGE_SHIFT;
59
        start &= PAGE_MASK;
60
 
61
        for (;;) {
62
                if (handle_mm_fault(current->mm, vma, start, 1) <= 0)
63
                        goto bad_area;
64
                if (!size)
65
                        break;
66
                size--;
67
                start += PAGE_SIZE;
68
                if (start < vma->vm_end)
69
                        continue;
70
                vma = vma->vm_next;
71
                if (!vma || vma->vm_start != start)
72
                        goto bad_area;
73
                if (!(vma->vm_flags & VM_WRITE))
74
                        goto bad_area;;
75
        }
76
        return 1;
77
 
78
check_stack:
79
        if (!(vma->vm_flags & VM_GROWSDOWN))
80
                goto bad_area;
81
        if (expand_stack(vma, start) == 0)
82
                goto good_area;
83
 
84
bad_area:
85
        return 0;
86
}
87
 
88
/*
89
 * This routine handles page faults.  It determines the address,
90
 * and the problem, and then passes it off to one of the appropriate
91
 * routines.
92
 */
93
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
94
                              unsigned long address)
95
{
96
        struct task_struct *tsk;
97
        struct mm_struct *mm;
98
        struct vm_area_struct * vma;
99
        unsigned long page;
100
        unsigned long fixup;
101
 
102
#if defined(CONFIG_SH_KGDB)
103
        if (kgdb_nofault && kgdb_bus_err_hook)
104
          kgdb_bus_err_hook();
105
#endif
106
 
107
        tsk = current;
108
        mm = tsk->mm;
109
 
110
        /*
111
         * If we're in an interrupt or have no user
112
         * context, we must not take the fault..
113
         */
114
        if (in_interrupt() || !mm)
115
                goto no_context;
116
 
117
        down_read(&mm->mmap_sem);
118
 
119
        vma = find_vma(mm, address);
120
        if (!vma)
121
                goto bad_area;
122
        if (vma->vm_start <= address)
123
                goto good_area;
124
        if (!(vma->vm_flags & VM_GROWSDOWN))
125
                goto bad_area;
126
        if (expand_stack(vma, address))
127
                goto bad_area;
128
/*
129
 * Ok, we have a good vm_area for this memory access, so
130
 * we can handle it..
131
 */
132
good_area:
133
        if (writeaccess) {
134
                if (!(vma->vm_flags & VM_WRITE))
135
                        goto bad_area;
136
        } else {
137
                if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
138
                        goto bad_area;
139
        }
140
 
141
        /*
142
         * If for any reason at all we couldn't handle the fault,
143
         * make sure we exit gracefully rather than endlessly redo
144
         * the fault.
145
         */
146
survive:
147
        switch (handle_mm_fault(mm, vma, address, writeaccess)) {
148
        case 1:
149
                tsk->min_flt++;
150
                break;
151
        case 2:
152
                tsk->maj_flt++;
153
                break;
154
        case 0:
155
                goto do_sigbus;
156
        default:
157
                goto out_of_memory;
158
        }
159
 
160
        up_read(&mm->mmap_sem);
161
        return;
162
 
163
/*
164
 * Something tried to access memory that isn't in our memory map..
165
 * Fix it, but check if it's kernel or user first..
166
 */
167
bad_area:
168
        up_read(&mm->mmap_sem);
169
 
170
        if (user_mode(regs)) {
171
                tsk->thread.address = address;
172
                tsk->thread.error_code = writeaccess;
173
                force_sig(SIGSEGV, tsk);
174
                return;
175
        }
176
 
177
no_context:
178
        /* Are we prepared to handle this kernel fault?  */
179
        fixup = search_exception_table(regs->pc);
180
        if (fixup != 0) {
181
                regs->pc = fixup;
182
                return;
183
        }
184
 
185
/*
186
 * Oops. The kernel tried to access some bad page. We'll have to
187
 * terminate things with extreme prejudice.
188
 *
189
 */
190
        if (address < PAGE_SIZE)
191
                printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
192
        else
193
                printk(KERN_ALERT "Unable to handle kernel paging request");
194
        printk(" at virtual address %08lx\n", address);
195
        printk(KERN_ALERT "pc = %08lx\n", regs->pc);
196
        asm volatile("mov.l     %1, %0"
197
                     : "=r" (page)
198
                     : "m" (__m(MMU_TTB)));
199
        if (page) {
200
                page = ((unsigned long *) page)[address >> 22];
201
                printk(KERN_ALERT "*pde = %08lx\n", page);
202
                if (page & _PAGE_PRESENT) {
203
                        page &= PAGE_MASK;
204
                        address &= 0x003ff000;
205
                        page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
206
                        printk(KERN_ALERT "*pte = %08lx\n", page);
207
                }
208
        }
209
        die("Oops", regs, writeaccess);
210
        do_exit(SIGKILL);
211
 
212
/*
213
 * We ran out of memory, or some other thing happened to us that made
214
 * us unable to handle the page fault gracefully.
215
 */
216
out_of_memory:
217
        if (current->pid == 1) {
218
                yield();
219
                goto survive;
220
        }
221
        up_read(&mm->mmap_sem);
222
        printk("VM: killing process %s\n", tsk->comm);
223
        if (user_mode(regs))
224
                do_exit(SIGKILL);
225
        goto no_context;
226
 
227
do_sigbus:
228
        up_read(&mm->mmap_sem);
229
 
230
        /*
231
         * Send a sigbus, regardless of whether we were in kernel
232
         * or user mode.
233
         */
234
        tsk->thread.address = address;
235
        tsk->thread.error_code = writeaccess;
236
        tsk->thread.trap_no = 14;
237
        force_sig(SIGBUS, tsk);
238
 
239
        /* Kernel mode? Handle exceptions or die */
240
        if (!user_mode(regs))
241
                goto no_context;
242
}
243
 
244
/*
245
 * Called with interrupt disabled.
246
 */
247
asmlinkage int __do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
248
                               unsigned long address)
249
{
250
        pgd_t *dir;
251
        pmd_t *pmd;
252
        pte_t *pte;
253
        pte_t entry;
254
 
255
#if defined(CONFIG_SH_KGDB)
256
        if (kgdb_nofault && kgdb_bus_err_hook)
257
          kgdb_bus_err_hook();
258
#endif
259
        if (address >= P3SEG && address < P4SEG)
260
                dir = pgd_offset_k(address);
261
        else if (address >= TASK_SIZE)
262
                return 1;
263
        else if (!current->mm)
264
                return 1;
265
        else
266
                dir = pgd_offset(current->mm, address);
267
 
268
        pmd = pmd_offset(dir, address);
269
        if (pmd_none(*pmd))
270
                return 1;
271
        if (pmd_bad(*pmd)) {
272
                pmd_ERROR(*pmd);
273
                pmd_clear(pmd);
274
                return 1;
275
        }
276
        pte = pte_offset(pmd, address);
277
        entry = *pte;
278
        if (pte_none(entry) || pte_not_present(entry)
279
            || (writeaccess && !pte_write(entry)))
280
                return 1;
281
 
282
        if (writeaccess)
283
                entry = pte_mkdirty(entry);
284
        entry = pte_mkyoung(entry);
285
#if defined(__SH4__)
286
        /*
287
         * ITLB is not affected by "ldtlb" instruction.
288
         * So, we need to flush the entry by ourselves.
289
         */
290
        __flush_tlb_page(get_asid(), address&PAGE_MASK);
291
#endif
292
        set_pte(pte, entry);
293
        update_mmu_cache(NULL, address, entry);
294
        return 0;
295
}
296
 
297
void update_mmu_cache(struct vm_area_struct * vma,
298
                      unsigned long address, pte_t pte)
299
{
300
        unsigned long flags;
301
        unsigned long pteval;
302
        unsigned long vpn;
303
#if defined(__SH4__)
304
        struct page *page;
305
        unsigned long ptea;
306
#endif
307
 
308
        /* Ptrace may call this routine. */
309
        if (vma && current->active_mm != vma->vm_mm)
310
                return;
311
 
312
#if defined(__SH4__)
313
        page = pte_page(pte);
314
        if (VALID_PAGE(page) && !test_bit(PG_mapped, &page->flags)) {
315
                unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
316
                __flush_wback_region((void *)P1SEGADDR(phys), PAGE_SIZE);
317
                __set_bit(PG_mapped, &page->flags);
318
        }
319
#endif
320
 
321
        save_and_cli(flags);
322
 
323
        /* Set PTEH register */
324
        vpn = (address & MMU_VPN_MASK) | get_asid();
325
        ctrl_outl(vpn, MMU_PTEH);
326
 
327
        pteval = pte_val(pte);
328
#if defined(__SH4__)
329
        /* Set PTEA register */
330
        /* TODO: make this look less hacky */
331
        ptea = ((pteval >> 28) & 0xe) | (pteval & 0x1);
332
        ctrl_outl(ptea, MMU_PTEA);
333
#endif
334
 
335
        /* Set PTEL register */
336
        pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
337
        /* conveniently, we want all the software flags to be 0 anyway */
338
        ctrl_outl(pteval, MMU_PTEL);
339
 
340
        /* Load the TLB */
341
        asm volatile("ldtlb": /* no output */ : /* no input */ : "memory");
342
        restore_flags(flags);
343
}
344
 
345
void __flush_tlb_page(unsigned long asid, unsigned long page)
346
{
347
        unsigned long addr, data;
348
 
349
        /*
350
         * NOTE: PTEH.ASID should be set to this MM
351
         *       _AND_ we need to write ASID to the array.
352
         *
353
         * It would be simple if we didn't need to set PTEH.ASID...
354
         */
355
#if defined(__sh3__)
356
        addr = MMU_TLB_ADDRESS_ARRAY |(page & 0x1F000)| MMU_PAGE_ASSOC_BIT;
357
        data = (page & 0xfffe0000) | asid; /* VALID bit is off */
358
        ctrl_outl(data, addr);
359
#elif defined(__SH4__)
360
        addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT;
361
        data = page | asid; /* VALID bit is off */
362
        jump_to_P2();
363
        ctrl_outl(data, addr);
364
        back_to_P1();
365
#endif
366
}
367
 
368
void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
369
{
370
        if (vma->vm_mm && vma->vm_mm->context != NO_CONTEXT) {
371
                unsigned long flags;
372
                unsigned long asid;
373
                unsigned long saved_asid = MMU_NO_ASID;
374
 
375
                asid = vma->vm_mm->context & MMU_CONTEXT_ASID_MASK;
376
                page &= PAGE_MASK;
377
 
378
                save_and_cli(flags);
379
                if (vma->vm_mm != current->mm) {
380
                        saved_asid = get_asid();
381
                        set_asid(asid);
382
                }
383
                __flush_tlb_page(asid, page);
384
                if (saved_asid != MMU_NO_ASID)
385
                        set_asid(saved_asid);
386
                restore_flags(flags);
387
        }
388
}
389
 
390
void flush_tlb_range(struct mm_struct *mm, unsigned long start,
391
                     unsigned long end)
392
{
393
        if (mm->context != NO_CONTEXT) {
394
                unsigned long flags;
395
                int size;
396
 
397
                save_and_cli(flags);
398
                size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
399
                if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
400
                        mm->context = NO_CONTEXT;
401
                        if (mm == current->mm)
402
                                activate_context(mm);
403
                } else {
404
                        unsigned long asid = mm->context&MMU_CONTEXT_ASID_MASK;
405
                        unsigned long saved_asid = MMU_NO_ASID;
406
 
407
                        start &= PAGE_MASK;
408
                        end += (PAGE_SIZE - 1);
409
                        end &= PAGE_MASK;
410
                        if (mm != current->mm) {
411
                                saved_asid = get_asid();
412
                                set_asid(asid);
413
                        }
414
                        while (start < end) {
415
                                __flush_tlb_page(asid, start);
416
                                start += PAGE_SIZE;
417
                        }
418
                        if (saved_asid != MMU_NO_ASID)
419
                                set_asid(saved_asid);
420
                }
421
                restore_flags(flags);
422
        }
423
}
424
 
425
void flush_tlb_mm(struct mm_struct *mm)
426
{
427
        /* Invalidate all TLB of this process. */
428
        /* Instead of invalidating each TLB, we get new MMU context. */
429
        if (mm->context != NO_CONTEXT) {
430
                unsigned long flags;
431
 
432
                save_and_cli(flags);
433
                mm->context = NO_CONTEXT;
434
                if (mm == current->mm)
435
                        activate_context(mm);
436
                restore_flags(flags);
437
        }
438
}
439
 
440
void flush_tlb_all(void)
441
{
442
        unsigned long flags, status;
443
 
444
        /*
445
         * Flush all the TLB.
446
         *
447
         * Write to the MMU control register's bit:
448
         *      TF-bit for SH-3, TI-bit for SH-4.
449
         *      It's same position, bit #2.
450
         */
451
        save_and_cli(flags);
452
        status = ctrl_inl(MMUCR);
453
        status |= 0x04;
454
        ctrl_outl(status, MMUCR);
455
        restore_flags(flags);
456
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.