linux/arch/frv/mm/fault.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/frv/mm/fault.c
   3 *
   4 * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
   5 * - Written by David Howells (dhowells@redhat.com)
   6 * - Derived from arch/m68knommu/mm/fault.c
   7 *   - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>,
   8 *   - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
   9 *
  10 *  Based on:
  11 *
  12 *  linux/arch/m68k/mm/fault.c
  13 *
  14 *  Copyright (C) 1995  Hamish Macdonald
  15 */
  16
  17#include <linux/mman.h>
  18#include <linux/mm.h>
  19#include <linux/kernel.h>
  20#include <linux/ptrace.h>
  21#include <linux/hardirq.h>
  22
  23#include <asm/pgtable.h>
  24#include <asm/uaccess.h>
  25#include <asm/gdb-stub.h>
  26
  27/*****************************************************************************/
  28/*
  29 * This routine handles page faults.  It determines the problem, and
  30 * then passes it off to one of the appropriate routines.
  31 */
  32asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear0)
  33{
  34        struct vm_area_struct *vma;
  35        struct mm_struct *mm;
  36        unsigned long _pme, lrai, lrad, fixup;
  37        unsigned long flags = 0;
  38        siginfo_t info;
  39        pgd_t *pge;
  40        pud_t *pue;
  41        pte_t *pte;
  42        int fault;
  43
  44#if 0
  45        const char *atxc[16] = {
  46                [0x0] = "mmu-miss", [0x8] = "multi-dat", [0x9] = "multi-sat",
  47                [0xa] = "tlb-miss", [0xc] = "privilege", [0xd] = "write-prot",
  48        };
  49
  50        printk("do_page_fault(%d,%lx [%s],%lx)\n",
  51               datammu, esr0, atxc[esr0 >> 20 & 0xf], ear0);
  52#endif
  53
  54        mm = current->mm;
  55
  56        /*
  57         * We fault-in kernel-space virtual memory on-demand. The
  58         * 'reference' page table is init_mm.pgd.
  59         *
  60         * NOTE! We MUST NOT take any locks for this case. We may
  61         * be in an interrupt or a critical region, and should
  62         * only copy the information from the master page table,
  63         * nothing more.
  64         *
  65         * This verifies that the fault happens in kernel space
  66         * and that the fault was a page not present (invalid) error
  67         */
  68        if (!user_mode(__frame) && (esr0 & ESR0_ATXC) == ESR0_ATXC_AMRTLB_MISS) {
  69                if (ear0 >= VMALLOC_START && ear0 < VMALLOC_END)
  70                        goto kernel_pte_fault;
  71                if (ear0 >= PKMAP_BASE && ear0 < PKMAP_END)
  72                        goto kernel_pte_fault;
  73        }
  74
  75        info.si_code = SEGV_MAPERR;
  76
  77        /*
  78         * If we're in an interrupt or have no user
  79         * context, we must not take the fault..
  80         */
  81        if (in_atomic() || !mm)
  82                goto no_context;
  83
  84        if (user_mode(__frame))
  85                flags |= FAULT_FLAG_USER;
  86
  87        down_read(&mm->mmap_sem);
  88
  89        vma = find_vma(mm, ear0);
  90        if (!vma)
  91                goto bad_area;
  92        if (vma->vm_start <= ear0)
  93                goto good_area;
  94        if (!(vma->vm_flags & VM_GROWSDOWN))
  95                goto bad_area;
  96
  97        if (user_mode(__frame)) {
  98                /*
  99                 * accessing the stack below %esp is always a bug.
 100                 * The "+ 32" is there due to some instructions (like
 101                 * pusha) doing post-decrement on the stack and that
 102                 * doesn't show up until later..
 103                 */
 104                if ((ear0 & PAGE_MASK) + 2 * PAGE_SIZE < __frame->sp) {
 105#if 0
 106                        printk("[%d] ### Access below stack @%lx (sp=%lx)\n",
 107                               current->pid, ear0, __frame->sp);
 108                        show_registers(__frame);
 109                        printk("[%d] ### Code: [%08lx] %02x %02x %02x %02x %02x %02x %02x %02x\n",
 110                               current->pid,
 111                               __frame->pc,
 112                               ((u8*)__frame->pc)[0],
 113                               ((u8*)__frame->pc)[1],
 114                               ((u8*)__frame->pc)[2],
 115                               ((u8*)__frame->pc)[3],
 116                               ((u8*)__frame->pc)[4],
 117                               ((u8*)__frame->pc)[5],
 118                               ((u8*)__frame->pc)[6],
 119                               ((u8*)__frame->pc)[7]
 120                               );
 121#endif
 122                        goto bad_area;
 123                }
 124        }
 125
 126        if (expand_stack(vma, ear0))
 127                goto bad_area;
 128
 129/*
 130 * Ok, we have a good vm_area for this memory access, so
 131 * we can handle it..
 132 */
 133 good_area:
 134        info.si_code = SEGV_ACCERR;
 135        switch (esr0 & ESR0_ATXC) {
 136        default:
 137                /* handle write to write protected page */
 138        case ESR0_ATXC_WP_EXCEP:
 139#ifdef TEST_VERIFY_AREA
 140                if (!(user_mode(__frame)))
 141                        printk("WP fault at %08lx\n", __frame->pc);
 142#endif
 143                if (!(vma->vm_flags & VM_WRITE))
 144                        goto bad_area;
 145                flags |= FAULT_FLAG_WRITE;
 146                break;
 147
 148                 /* handle read from protected page */
 149        case ESR0_ATXC_PRIV_EXCEP:
 150                goto bad_area;
 151
 152                 /* handle read, write or exec on absent page
 153                  * - can't support write without permitting read
 154                  * - don't support execute without permitting read and vice-versa
 155                  */
 156        case ESR0_ATXC_AMRTLB_MISS:
 157                if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
 158                        goto bad_area;
 159                break;
 160        }
 161
 162        /*
 163         * If for any reason at all we couldn't handle the fault,
 164         * make sure we exit gracefully rather than endlessly redo
 165         * the fault.
 166         */
 167        fault = handle_mm_fault(mm, vma, ear0, flags);
 168        if (unlikely(fault & VM_FAULT_ERROR)) {
 169                if (fault & VM_FAULT_OOM)
 170                        goto out_of_memory;
 171                else if (fault & VM_FAULT_SIGSEGV)
 172                        goto bad_area;
 173                else if (fault & VM_FAULT_SIGBUS)
 174                        goto do_sigbus;
 175                BUG();
 176        }
 177        if (fault & VM_FAULT_MAJOR)
 178                current->maj_flt++;
 179        else
 180                current->min_flt++;
 181
 182        up_read(&mm->mmap_sem);
 183        return;
 184
 185/*
 186 * Something tried to access memory that isn't in our memory map..
 187 * Fix it, but check if it's kernel or user first..
 188 */
 189 bad_area:
 190        up_read(&mm->mmap_sem);
 191
 192        /* User mode accesses just cause a SIGSEGV */
 193        if (user_mode(__frame)) {
 194                info.si_signo = SIGSEGV;
 195                info.si_errno = 0;
 196                /* info.si_code has been set above */
 197                info.si_addr = (void *) ear0;
 198                force_sig_info(SIGSEGV, &info, current);
 199                return;
 200        }
 201
 202 no_context:
 203        /* are we prepared to handle this kernel fault? */
 204        if ((fixup = search_exception_table(__frame->pc)) != 0) {
 205                __frame->pc = fixup;
 206                return;
 207        }
 208
 209/*
 210 * Oops. The kernel tried to access some bad page. We'll have to
 211 * terminate things with extreme prejudice.
 212 */
 213
 214        bust_spinlocks(1);
 215
 216        if (ear0 < PAGE_SIZE)
 217                printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
 218        else
 219                printk(KERN_ALERT "Unable to handle kernel paging request");
 220        printk(" at virtual addr %08lx\n", ear0);
 221        printk("  PC  : %08lx\n", __frame->pc);
 222        printk("  EXC : esr0=%08lx ear0=%08lx\n", esr0, ear0);
 223
 224        asm("lrai %1,%0,#1,#0,#0" : "=&r"(lrai) : "r"(ear0));
 225        asm("lrad %1,%0,#1,#0,#0" : "=&r"(lrad) : "r"(ear0));
 226
 227        printk(KERN_ALERT "  LRAI: %08lx\n", lrai);
 228        printk(KERN_ALERT "  LRAD: %08lx\n", lrad);
 229
 230        __break_hijack_kernel_event();
 231
 232        pge = pgd_offset(current->mm, ear0);
 233        pue = pud_offset(pge, ear0);
 234        _pme = pue->pue[0].ste[0];
 235
 236        printk(KERN_ALERT "  PGE : %8p { PME %08lx }\n", pge, _pme);
 237
 238        if (_pme & xAMPRx_V) {
 239                unsigned long dampr, damlr, val;
 240
 241                asm volatile("movsg dampr2,%0 ! movgs %2,dampr2 ! movsg damlr2,%1"
 242                             : "=&r"(dampr), "=r"(damlr)
 243                             : "r" (_pme | xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V)
 244                             );
 245
 246                pte = (pte_t *) damlr + __pte_index(ear0);
 247                val = pte_val(*pte);
 248
 249                asm volatile("movgs %0,dampr2" :: "r" (dampr));
 250
 251                printk(KERN_ALERT "  PTE : %8p { %08lx }\n", pte, val);
 252        }
 253
 254        die_if_kernel("Oops\n");
 255        do_exit(SIGKILL);
 256
 257/*
 258 * We ran out of memory, or some other thing happened to us that made
 259 * us unable to handle the page fault gracefully.
 260 */
 261 out_of_memory:
 262        up_read(&mm->mmap_sem);
 263        if (!user_mode(__frame))
 264                goto no_context;
 265        pagefault_out_of_memory();
 266        return;
 267
 268 do_sigbus:
 269        up_read(&mm->mmap_sem);
 270
 271        /*
 272         * Send a sigbus, regardless of whether we were in kernel
 273         * or user mode.
 274         */
 275        info.si_signo = SIGBUS;
 276        info.si_errno = 0;
 277        info.si_code = BUS_ADRERR;
 278        info.si_addr = (void *) ear0;
 279        force_sig_info(SIGBUS, &info, current);
 280
 281        /* Kernel mode? Handle exceptions or die */
 282        if (!user_mode(__frame))
 283                goto no_context;
 284        return;
 285
 286/*
 287 * The fault was caused by a kernel PTE (such as installed by vmalloc or kmap)
 288 */
 289 kernel_pte_fault:
 290        {
 291                /*
 292                 * Synchronize this task's top level page-table
 293                 * with the 'reference' page table.
 294                 *
 295                 * Do _not_ use "tsk" here. We might be inside
 296                 * an interrupt in the middle of a task switch..
 297                 */
 298                int index = pgd_index(ear0);
 299                pgd_t *pgd, *pgd_k;
 300                pud_t *pud, *pud_k;
 301                pmd_t *pmd, *pmd_k;
 302                pte_t *pte_k;
 303
 304                pgd = (pgd_t *) __get_TTBR();
 305                pgd = (pgd_t *)__va(pgd) + index;
 306                pgd_k = ((pgd_t *)(init_mm.pgd)) + index;
 307
 308                if (!pgd_present(*pgd_k))
 309                        goto no_context;
 310                //set_pgd(pgd, *pgd_k); /////// gcc ICE's on this line
 311
 312                pud_k = pud_offset(pgd_k, ear0);
 313                if (!pud_present(*pud_k))
 314                        goto no_context;
 315
 316                pmd_k = pmd_offset(pud_k, ear0);
 317                if (!pmd_present(*pmd_k))
 318                        goto no_context;
 319
 320                pud = pud_offset(pgd, ear0);
 321                pmd = pmd_offset(pud, ear0);
 322                set_pmd(pmd, *pmd_k);
 323
 324                pte_k = pte_offset_kernel(pmd_k, ear0);
 325                if (!pte_present(*pte_k))
 326                        goto no_context;
 327                return;
 328        }
 329} /* end do_page_fault() */
 330