From: Michal Simek <monstr@monstr.eu>
To: linux-kernel@vger.kernel.org
Cc: john.williams@petalogix.com, microblaze-uclinux@itee.uq.edu.au,
michal.simek@petalogix.com, Michal Simek <monstr@monstr.eu>
Subject: [PATCH 07/29] microblaze_mmu_v2: Page fault handling high level - fault.c
Date: Wed, 27 May 2009 08:47:32 +0200 [thread overview]
Message-ID: <1243406874-3917-8-git-send-email-monstr@monstr.eu> (raw)
In-Reply-To: <1243406874-3917-7-git-send-email-monstr@monstr.eu>
Signed-off-by: Michal Simek <monstr@monstr.eu>
---
arch/microblaze/mm/fault.c | 304 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 304 insertions(+), 0 deletions(-)
create mode 100644 arch/microblaze/mm/fault.c
diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c
new file mode 100644
index 0000000..5e67cd1
--- /dev/null
+++ b/arch/microblaze/mm/fault.c
@@ -0,0 +1,304 @@
+/*
+ * arch/microblaze/mm/fault.c
+ *
+ * Copyright (C) 2007 Xilinx, Inc. All rights reserved.
+ *
+ * Derived from "arch/ppc/mm/fault.c"
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Derived from "arch/i386/mm/fault.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * Modified by Cort Dougan and Paul Mackerras.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <asm/exceptions.h>
+
+#if defined(CONFIG_KGDB)
+int debugger_kernel_faults = 1;
+#endif
+
+static unsigned long pte_misses; /* updated by do_page_fault() */
+static unsigned long pte_errors; /* updated by do_page_fault() */
+
+/*
+ * Check whether the instruction at regs->pc is a store using
+ * an update addressing form which will update r1.
+ */
+static int store_updates_sp(struct pt_regs *regs)
+{
+ unsigned int inst;
+
+ if (get_user(inst, (unsigned int *)regs->pc))
+ return 0;
+ /* check for 1 in the rD field */
+ if (((inst >> 21) & 0x1f) != 1)
+ return 0;
+ /* check for store opcodes */
+ if ((inst & 0xd0000000) == 0xd0000000)
+ return 1;
+ return 0;
+}
+
+
+/*
+ * bad_page_fault is called when we have a bad access from the kernel.
+ * It is called from do_page_fault above and from some of the procedures
+ * in traps.c.
+ */
+static void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
+{
+ const struct exception_table_entry *fixup;
+/* MS: no context */
+ /* Are we prepared to handle this fault? */
+ fixup = search_exception_tables(regs->pc);
+ if (fixup) {
+ regs->pc = fixup->fixup;
+ return;
+ }
+
+ /* kernel has accessed a bad area */
+#if defined(CONFIG_KGDB)
+ if (debugger_kernel_faults)
+ debugger(regs);
+#endif
+ die("kernel access of bad area", regs, sig);
+}
+
+/*
+ * The error_code parameter is ESR for a data fault,
+ * 0 for an instruction fault.
+ */
+void do_page_fault(struct pt_regs *regs, unsigned long address,
+ unsigned long error_code)
+{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+ siginfo_t info;
+ int code = SEGV_MAPERR;
+ int is_write = error_code & ESR_S;
+ int fault;
+
+ regs->ear = address;
+ regs->esr = error_code;
+
+ /* On a kernel SLB miss we can only check for a valid exception entry */
+ if (kernel_mode(regs) && (address >= TASK_SIZE)) {
+ printk(KERN_WARNING "kernel task_size exceed");
+ _exception(SIGSEGV, regs, code, address);
+ }
+
+ /* for instr TLB miss and instr storage exception ESR_S is undefined */
+ if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11)
+ is_write = 0;
+
+#if defined(CONFIG_KGDB)
+ if (debugger_fault_handler && regs->trap == 0x300) {
+ debugger_fault_handler(regs);
+ return;
+ }
+#endif /* CONFIG_KGDB */
+
+ if (in_atomic() || mm == NULL) {
+ /* FIXME */
+ if (kernel_mode(regs)) {
+ printk(KERN_EMERG
+ "Page fault in kernel mode - Oooou!!! pid %d\n",
+ current->pid);
+ _exception(SIGSEGV, regs, code, address);
+ return;
+ }
+ /* in_atomic() in user mode is really bad,
+ as is current->mm == NULL. */
+ printk(KERN_EMERG "Page fault in user mode with "
+ "in_atomic(), mm = %p\n", mm);
+ printk(KERN_EMERG "r15 = %lx MSR = %lx\n",
+ regs->r15, regs->msr);
+ die("Weird page fault", regs, SIGSEGV);
+ }
+
+ /* When running in the kernel we expect faults to occur only to
+ * addresses in user space. All other faults represent errors in the
+ * kernel and should generate an OOPS. Unfortunately, in the case of an
+ * erroneous fault occurring in a code path which already holds mmap_sem
+ * we will deadlock attempting to validate the fault against the
+ * address space. Luckily the kernel only validly references user
+ * space from well defined areas of code, which are listed in the
+ * exceptions table.
+ *
+ * As the vast majority of faults will be valid we will only perform
+ * the source reference check when there is a possibility of a deadlock.
+ * Attempt to lock the address space, if we cannot we then validate the
+ * source. If this is invalid we can skip the address space check,
+ * thus avoiding the deadlock.
+ */
+ if (!down_read_trylock(&mm->mmap_sem)) {
+ if (kernel_mode(regs) && !search_exception_tables(regs->pc))
+ goto bad_area_nosemaphore;
+
+ down_read(&mm->mmap_sem);
+ }
+
+ vma = find_vma(mm, address);
+ if (!vma)
+ goto bad_area;
+
+ if (vma->vm_start <= address)
+ goto good_area;
+
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+
+ if (!is_write)
+ goto bad_area;
+
+ /*
+ * N.B. The ABI allows programs to access up to
+ * a few hundred bytes below the stack pointer (TBD).
+ * The kernel signal delivery code writes up to about 1.5kB
+ * below the stack pointer (r1) before decrementing it.
+ * The exec code can write slightly over 640kB to the stack
+ * before setting the user r1. Thus we allow the stack to
+ * expand to 1MB without further checks.
+ */
+ if (address + 0x100000 < vma->vm_end) {
+
+ /* get user regs even if this fault is in kernel mode */
+ struct pt_regs *uregs = current->thread.regs;
+ if (uregs == NULL)
+ goto bad_area;
+
+ /*
+ * A user-mode access to an address a long way below
+ * the stack pointer is only valid if the instruction
+ * is one which would update the stack pointer to the
+ * address accessed if the instruction completed,
+ * i.e. either stwu rs,n(r1) or stwux rs,r1,rb
+ * (or the byte, halfword, float or double forms).
+ *
+ * If we don't check this then any write to the area
+ * between the last mapped region and the stack will
+ * expand the stack rather than segfaulting.
+ */
+ if (address + 2048 < uregs->r1
+ && (kernel_mode(regs) || !store_updates_sp(regs)))
+ goto bad_area;
+ }
+ if (expand_stack(vma, address))
+ goto bad_area;
+
+good_area:
+ code = SEGV_ACCERR;
+
+ /* a write */
+ if (is_write) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ /* a read */
+ } else {
+ /* protection fault */
+ if (error_code & 0x08000000)
+ goto bad_area;
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+survive:
+ fault = handle_mm_fault(mm, vma, address, is_write);
+ if (unlikely(fault & VM_FAULT_ERROR)) {
+ if (fault & VM_FAULT_OOM)
+ goto out_of_memory;
+ else if (fault & VM_FAULT_SIGBUS)
+ goto do_sigbus;
+ BUG();
+ }
+ if (fault & VM_FAULT_MAJOR)
+ current->maj_flt++;
+ else
+ current->min_flt++;
+ up_read(&mm->mmap_sem);
+ /*
+ * keep track of tlb+htab misses that are good addrs but
+ * just need pte's created via handle_mm_fault()
+ * -- Cort
+ */
+ pte_misses++;
+ return;
+
+bad_area:
+ up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+ pte_errors++;
+
+ /* User mode accesses cause a SIGSEGV */
+ if (user_mode(regs)) {
+ _exception(SIGSEGV, regs, code, address);
+/* info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = code;
+ info.si_addr = (void *) address;
+ force_sig_info(SIGSEGV, &info, current);*/
+ return;
+ }
+
+ bad_page_fault(regs, address, SIGSEGV);
+ return;
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ if (current->pid == 1) {
+ yield();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
+ up_read(&mm->mmap_sem);
+ printk(KERN_WARNING "VM: killing process %s\n", current->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
+ bad_page_fault(regs, address, SIGKILL);
+ return;
+
+do_sigbus:
+ up_read(&mm->mmap_sem);
+ if (user_mode(regs)) {
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRERR;
+ info.si_addr = (void __user *)address;
+ force_sig_info(SIGBUS, &info, current);
+ return;
+ }
+ bad_page_fault(regs, address, SIGBUS);
+}
--
1.5.5.1
next prev parent reply other threads:[~2009-05-27 7:33 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-27 6:47 Microblaze MMU v2 Michal Simek
2009-05-27 6:47 ` [PATCH 01/29] microblaze_mmu_v2: Add mmu_defconfig Michal Simek
2009-05-27 6:47 ` [PATCH 02/29] microblaze_mmu_v2: MMU update for startup code Michal Simek
2009-05-27 6:47 ` [PATCH 03/29] microblaze_mmu_v2: Alocate TLB for early console Michal Simek
2009-05-27 6:47 ` [PATCH 04/29] microblaze_mmu_v2: TLB low level code Michal Simek
2009-05-27 6:47 ` [PATCH 05/29] microblaze_mmu_v2: MMU initialization Michal Simek
2009-05-27 6:47 ` [PATCH 06/29] microblaze_mmu_v2: mmu.h update Michal Simek
2009-05-27 6:47 ` Michal Simek [this message]
2009-05-27 6:47 ` [PATCH 08/29] microblaze_mmu_v2: Context handling - mmu_context.c/h Michal Simek
2009-05-27 6:47 ` [PATCH 09/29] microblaze_mmu_v2: Page table - ioremap - pgtable.c/h, section update Michal Simek
2009-05-27 6:47 ` [PATCH 10/29] microblaze_mmu_v2: io.h MMU update Michal Simek
2009-05-27 6:47 ` [PATCH 11/29] microblaze_mmu_v2: pgalloc.h and page.h Michal Simek
2009-05-27 6:47 ` [PATCH 12/29] microblaze_mmu_v2: Update process creation for MMU Michal Simek
2009-05-27 6:47 ` [PATCH 13/29] microblaze_mmu_v2: Update tlb.h and tlbflush.h Michal Simek
2009-05-27 6:47 ` [PATCH 14/29] microblaze_mmu_v2: MMU asm offset update Michal Simek
2009-05-27 6:47 ` [PATCH 15/29] microblaze_mmu_v2: Add CURRENT_TASK for entry.S Michal Simek
2009-05-27 6:47 ` [PATCH 16/29] microblaze_mmu_v2: entry.S, entry.h Michal Simek
2009-05-27 6:47 ` [PATCH 17/29] microblaze_mmu_v2: Update exception handling - MMU exception Michal Simek
2009-05-27 6:47 ` [PATCH 18/29] microblaze_mmu_v2: uaccess MMU update Michal Simek
2009-05-27 6:47 ` [PATCH 19/29] microblaze_mmu_v2: Add MMU related exceptions handling Michal Simek
2009-05-27 6:47 ` [PATCH 20/29] microblaze_mmu_v2: Update linker script for MMU Michal Simek
2009-05-27 6:47 ` [PATCH 21/29] microblaze_mmu_v2: Enable fork syscall for MMU and add fork as vfork for noMMU Michal Simek
2009-05-27 6:47 ` [PATCH 22/29] microblaze_mmu_v2: Traps MMU update Michal Simek
2009-05-27 6:47 ` [PATCH 23/29] microblaze_mmu_v2: Update signal returning address Michal Simek
2009-05-27 6:47 ` [PATCH 24/29] microblaze_mmu_v2: Update cacheflush.h Michal Simek
2009-05-27 6:47 ` [PATCH 25/29] microblaze_mmu_v2: Update dma.h for MMU Michal Simek
2009-05-27 6:47 ` [PATCH 26/29] microblaze_mmu_v2: Elf update Michal Simek
2009-05-27 6:47 ` [PATCH 27/29] microblaze_mmu_v2: stat.h MMU update Michal Simek
2009-05-27 6:47 ` [PATCH 28/29] microblaze_mmu_v2: Kconfig update Michal Simek
2009-05-27 6:47 ` [PATCH 29/29] microblaze_mmu_v2: Makefiles Michal Simek
2009-05-27 11:08 ` [PATCH 08/29] microblaze_mmu_v2: Context handling - mmu_context.c/h Arnd Bergmann
2009-05-27 11:59 ` Michal Simek
2009-05-29 14:39 ` [microblaze-uclinux] Microblaze MMU v2 John Linn
2009-06-11 8:46 ` John Williams
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1243406874-3917-8-git-send-email-monstr@monstr.eu \
--to=monstr@monstr.eu \
--cc=john.williams@petalogix.com \
--cc=linux-kernel@vger.kernel.org \
--cc=michal.simek@petalogix.com \
--cc=microblaze-uclinux@itee.uq.edu.au \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®