2006-09-27 06:13:36 +00:00
|
|
|
/*
|
|
|
|
* Page fault handler for SH with an MMU.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 1999 Niibe Yutaka
|
2009-07-04 18:18:47 +00:00
|
|
|
* Copyright (C) 2003 - 2009 Paul Mundt
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* Based on linux/arch/i386/mm/fault.c:
|
|
|
|
* Copyright (C) 1995 Linus Torvalds
|
2006-09-27 06:13:36 +00:00
|
|
|
*
|
|
|
|
* 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.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/mm.h>
|
2006-09-27 08:03:56 +00:00
|
|
|
#include <linux/hardirq.h>
|
|
|
|
#include <linux/kprobes.h>
|
perf: Do the big rename: Performance Counters -> Performance Events
Bye-bye Performance Counters, welcome Performance Events!
In the past few months the perfcounters subsystem has grown out its
initial role of counting hardware events, and has become (and is
becoming) a much broader generic event enumeration, reporting, logging,
monitoring, analysis facility.
Naming its core object 'perf_counter' and naming the subsystem
'perfcounters' has become more and more of a misnomer. With pending
code like hw-breakpoints support the 'counter' name is less and
less appropriate.
All in one, we've decided to rename the subsystem to 'performance
events' and to propagate this rename through all fields, variables
and API names. (in an ABI compatible fashion)
The word 'event' is also a bit shorter than 'counter' - which makes
it slightly more convenient to write/handle as well.
Thanks goes to Stephane Eranian who first observed this misnomer and
suggested a rename.
User-space tooling and ABI compatibility is not affected - this patch
should be function-invariant. (Also, defconfigs were not touched to
keep the size down.)
This patch has been generated via the following script:
FILES=$(find * -type f | grep -vE 'oprofile|[^K]config')
sed -i \
-e 's/PERF_EVENT_/PERF_RECORD_/g' \
-e 's/PERF_COUNTER/PERF_EVENT/g' \
-e 's/perf_counter/perf_event/g' \
-e 's/nb_counters/nb_events/g' \
-e 's/swcounter/swevent/g' \
-e 's/tpcounter_event/tp_event/g' \
$FILES
for N in $(find . -name perf_counter.[ch]); do
M=$(echo $N | sed 's/perf_counter/perf_event/g')
mv $N $M
done
FILES=$(find . -name perf_event.*)
sed -i \
-e 's/COUNTER_MASK/REG_MASK/g' \
-e 's/COUNTER/EVENT/g' \
-e 's/\<event\>/event_id/g' \
-e 's/counter/event/g' \
-e 's/Counter/Event/g' \
$FILES
... to keep it as correct as possible. This script can also be
used by anyone who has pending perfcounters patches - it converts
a Linux kernel tree over to the new naming. We tried to time this
change to the point in time where the amount of pending patches
is the smallest: the end of the merge window.
Namespace clashes were fixed up in a preparatory patch - and some
stylistic fallout will be fixed up in a subsequent patch.
( NOTE: 'counters' are still the proper terminology when we deal
with hardware registers - and these sed scripts are a bit
over-eager in renaming them. I've undone some of that, but
in case there's something left where 'counter' would be
better than 'event' we can undo that on an individual basis
instead of touching an otherwise nicely automated patch. )
Suggested-by: Stephane Eranian <eranian@google.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Paul Mackerras <paulus@samba.org>
Reviewed-by: Arjan van de Ven <arjan@linux.intel.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: David Howells <dhowells@redhat.com>
Cc: Kyle McMartin <kyle@mcmartin.ca>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: <linux-arch@vger.kernel.org>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-09-21 10:02:48 +00:00
|
|
|
#include <linux/perf_event.h>
|
2008-02-07 11:18:21 +00:00
|
|
|
#include <asm/io_trapped.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <asm/mmu_context.h>
|
2007-02-14 05:13:10 +00:00
|
|
|
#include <asm/tlbflush.h>
|
2012-03-28 17:30:03 +00:00
|
|
|
#include <asm/traps.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-06-24 17:30:10 +00:00
|
|
|
static inline int notify_page_fault(struct pt_regs *regs, int trap)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2009-07-04 17:50:10 +00:00
|
|
|
if (kprobes_built_in() && !user_mode(regs)) {
|
2009-06-24 17:30:10 +00:00
|
|
|
preempt_disable();
|
|
|
|
if (kprobe_running() && kprobe_fault_handler(regs, trap))
|
|
|
|
ret = 1;
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address)
|
|
|
|
{
|
|
|
|
unsigned index = pgd_index(address);
|
|
|
|
pgd_t *pgd_k;
|
|
|
|
pud_t *pud, *pud_k;
|
|
|
|
pmd_t *pmd, *pmd_k;
|
|
|
|
|
|
|
|
pgd += index;
|
|
|
|
pgd_k = init_mm.pgd + index;
|
|
|
|
|
|
|
|
if (!pgd_present(*pgd_k))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pud = pud_offset(pgd, address);
|
|
|
|
pud_k = pud_offset(pgd_k, address);
|
|
|
|
if (!pud_present(*pud_k))
|
|
|
|
return NULL;
|
|
|
|
|
2009-12-13 14:38:50 +00:00
|
|
|
if (!pud_present(*pud))
|
|
|
|
set_pud(pud, *pud_k);
|
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
pmd = pmd_offset(pud, address);
|
|
|
|
pmd_k = pmd_offset(pud_k, address);
|
|
|
|
if (!pmd_present(*pmd_k))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!pmd_present(*pmd))
|
|
|
|
set_pmd(pmd, *pmd_k);
|
2009-07-13 11:38:04 +00:00
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* The page tables are fully synchronised so there must
|
|
|
|
* be another reason for the fault. Return NULL here to
|
|
|
|
* signal that we have not taken care of the fault.
|
|
|
|
*/
|
2009-07-04 18:18:47 +00:00
|
|
|
BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
|
2009-07-13 11:38:04 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-07-04 18:18:47 +00:00
|
|
|
|
|
|
|
return pmd_k;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle a fault on the vmalloc or module mapping area
|
|
|
|
*/
|
|
|
|
static noinline int vmalloc_fault(unsigned long address)
|
|
|
|
{
|
|
|
|
pgd_t *pgd_k;
|
|
|
|
pmd_t *pmd_k;
|
|
|
|
pte_t *pte_k;
|
|
|
|
|
2009-09-03 08:21:10 +00:00
|
|
|
/* Make sure we are in vmalloc/module/P3 area: */
|
sh: Fix error synchronising kernel page tables
The problem is caused by the interaction of two features in the Linux
memory management code.
A processes address space is described by a struct mm_struct, and
every thread has a pointer to the mm it should run in. The exception
to this are kernel threads, which don't have an mm, and so borrow
the mm from the last thread which ran. The system is bootstrapped
by the initial kernel thread using init's mm (even though init hasn't
been created yet, its mm is the static init_mm).
The other feature is how the kernel handles the page table which
describes the portion of the address space which is only visible when
executing inside the kernel, and which is shared by all threads. On
the SH4 the only portion of the kernel's address space which described
using the page table is called P3, from 0xc0000000 to 0xdfffffff. This
portion of the address space is divided into three:
- mappings for dma_alloc_coherent()
- mappings for vmalloc() and ioremap()
- fixmap mappings, primarily used in copy_user_pages() to create
kernel mappings of user pages with the correct cache colour.
To optimise the TLB miss handler we don't want to add an additional
condition which checks whether the faulting address is in the user or
the kernel portion of the address space, and so all page tables have a
common portion which describes the kernel part of the address
space. As the SH4 uses a two level page table, only the kernel portion
of first level page table (the pgd entries) is duplicated. These all
point to the same second level entries (the pte's), and so no memory
is wasted.
The reference page table for the kernel is called the swapper_pg_dir,
and when a new page table is created for a new process the kernel
portion of the page table is copied from swapper_pg_dir. This works
fine when changes only occur in the second level of the kernel's page
table, or the first level entries are created before any new user
processes. However if a change occurs to the first level of the page
table, and there are existing processes which don't have this entry in
their page table, this new entry needs to be added. This is done on
demand, when the kernel accesses a P3 address which isn't mapped using
the current page table, the code in vmalloc_fault() copies the entry
from the reference page table (swapper_pg_dir) into the current
processes page table.
The bug which this patch addresses is that the code in vmalloc_fault()
was not copying addresses which fell in the dma_alloc_coherent()
portion of the address space, and it should have been copying any P3
address.
Why we hadn't seen this before, and what made this hard to reproduce,
is that normally the kernel will have called dma_alloc_coherent(), and
accessed the memory mapping created, before any user process
runs. Typically drivers such as USB or SATA will have created and used
mappings of this type during the kernel initialisation, when probing
for the attached devices, before init runs. Ethernet is slightly
different, as it normally only creates and accesses
dma_alloc_coherent() mappings when the network is brought up, but if
kernel level IP configuration is used this will also occur before any
user space process runs. So the first reproduction of this problem
which we saw was occurred when USB and SATA were removed from the
kernel, and then bring up Ethernet from user space using ifconfig.
I'd like to thank Joseph Bormolini who did the hard work reducing the
problem to this simple to reproduce criteria.
In your case the situation is slightly different, and turns out to
depends on the exact kernel configuration (which we had) and your
ramdisk contents (which we didn't - hence the need for some assumptions).
In this case the problem is a side effect of kernel level module
loading. Kernel subsystems sometimes trigger the load of kernel
modules directly, for example the crypto subsystem tries to load the
cryptomgr and MTD tries to load modules for Flash partitioning if
these are not built into the kernel. This is done by the kernel
creating a user process which runs insmod to try and load the
appropriate module.
In order for this to cause problems the system must be running with a
initrd or initramfs, which contains an insmod executable - if the
kernel can't find an insmod to run, no user process is created, and
the problem doesn't occur. If an insmod is found, a process is
created to run it, which will inherit the kernel portion of the
swapper_pg_dir first level page table. It doesn't matter whether the
inmod is successful or not, but when the the kernel scheduler context
switches back to the kernel initialisation thread, the insmod's mm is
'borrowed' by the kernel thread, as it doesn't have an address space
of its own. (Reference counting is used to ensure this mm is not
destroyed, even though the user process which caused its creation may no
longer exist.) If this address space doesn't have a first level page
table entry for the consistent mappings, and a driver tries to access
such a mapping, we are in the same situation as described above,
except this time in a kernel thread rather than a user thread
executing inside the kernel.
See bugzilla: 15425, 15836, 15862, 16106, 16793
Signed-off-by: Stuart Menefy <stuart.menefy@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
2012-02-14 11:29:11 +00:00
|
|
|
if (!(address >= P3SEG && address < P3_ADDR_MAX))
|
2009-07-04 18:18:47 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Synchronize this task's top level page-table
|
|
|
|
* with the 'reference' page table.
|
|
|
|
*
|
|
|
|
* Do _not_ use "current" here. We might be inside
|
|
|
|
* an interrupt in the middle of a task switch..
|
|
|
|
*/
|
|
|
|
pgd_k = get_TTB();
|
2009-07-13 11:38:04 +00:00
|
|
|
pmd_k = vmalloc_sync_one(pgd_k, address);
|
2009-07-04 18:18:47 +00:00
|
|
|
if (!pmd_k)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pte_k = pte_offset_kernel(pmd_k, address);
|
|
|
|
if (!pte_present(*pte_k))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fault_in_kernel_space(unsigned long address)
|
|
|
|
{
|
|
|
|
return address >= TASK_SIZE;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* This routine handles page faults. It determines the address,
|
|
|
|
* and the problem, and then passes it off to one of the appropriate
|
|
|
|
* routines.
|
|
|
|
*/
|
2006-11-21 04:34:04 +00:00
|
|
|
asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
|
|
|
|
unsigned long writeaccess,
|
|
|
|
unsigned long address)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-07-04 18:18:47 +00:00
|
|
|
unsigned long vec;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct task_struct *tsk;
|
|
|
|
struct mm_struct *mm;
|
|
|
|
struct vm_area_struct * vma;
|
2006-11-21 04:34:04 +00:00
|
|
|
int si_code;
|
2007-07-19 08:47:05 +00:00
|
|
|
int fault;
|
2006-11-21 04:34:04 +00:00
|
|
|
siginfo_t info;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
tsk = current;
|
2009-07-04 18:18:47 +00:00
|
|
|
mm = tsk->mm;
|
2006-11-21 04:34:04 +00:00
|
|
|
si_code = SEGV_MAPERR;
|
2009-07-04 18:18:47 +00:00
|
|
|
vec = lookup_exception_vector();
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
/*
|
|
|
|
* We fault-in kernel-space virtual memory on-demand. The
|
|
|
|
* 'reference' page table is init_mm.pgd.
|
|
|
|
*
|
|
|
|
* NOTE! We MUST NOT take any locks for this case. We may
|
|
|
|
* be in an interrupt or a critical region, and should
|
|
|
|
* only copy the information from the master page table,
|
|
|
|
* nothing more.
|
|
|
|
*/
|
|
|
|
if (unlikely(fault_in_kernel_space(address))) {
|
|
|
|
if (vmalloc_fault(address) >= 0)
|
2006-11-21 06:38:05 +00:00
|
|
|
return;
|
2009-07-04 18:18:47 +00:00
|
|
|
if (notify_page_fault(regs, vec))
|
2008-09-05 07:17:15 +00:00
|
|
|
return;
|
2006-11-21 06:38:05 +00:00
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
goto bad_area_nosemaphore;
|
2006-11-21 06:38:05 +00:00
|
|
|
}
|
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
if (unlikely(notify_page_fault(regs, vec)))
|
2009-06-24 17:30:10 +00:00
|
|
|
return;
|
|
|
|
|
2008-07-02 08:51:23 +00:00
|
|
|
/* Only enable interrupts if they were on before the fault */
|
2009-06-24 17:30:10 +00:00
|
|
|
if ((regs->sr & SR_IMASK) != SR_IMASK)
|
2008-07-02 08:51:23 +00:00
|
|
|
local_irq_enable();
|
|
|
|
|
2011-06-27 12:41:57 +00:00
|
|
|
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
|
2008-07-02 08:51:23 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2009-07-04 18:18:47 +00:00
|
|
|
* If we're in an interrupt, have no user context or are running
|
|
|
|
* in an atomic region then we must not take the fault:
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
if (in_atomic() || !mm)
|
|
|
|
goto no_context;
|
|
|
|
|
|
|
|
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 (expand_stack(vma, address))
|
|
|
|
goto bad_area;
|
2009-07-04 18:18:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, we have a good vm_area for this memory access, so
|
|
|
|
* we can handle it..
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
good_area:
|
2006-11-21 04:34:04 +00:00
|
|
|
si_code = SEGV_ACCERR;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (writeaccess) {
|
|
|
|
if (!(vma->vm_flags & VM_WRITE))
|
|
|
|
goto bad_area;
|
|
|
|
} else {
|
[PATCH] make PROT_WRITE imply PROT_READ
Make PROT_WRITE imply PROT_READ for a number of architectures which don't
support write only in hardware.
While looking at this, I noticed that some architectures which do not
support write only mappings already take the exact same approach. For
example, in arch/alpha/mm/fault.c:
"
if (cause < 0) {
if (!(vma->vm_flags & VM_EXEC))
goto bad_area;
} else if (!cause) {
/* Allow reads even for write-only mappings */
if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
goto bad_area;
} else {
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
}
"
Thus, this patch brings other architectures which do not support write only
mappings in-line and consistent with the rest. I've verified the patch on
ia64, x86_64 and x86.
Additional discussion:
Several architectures, including x86, can not support write-only mappings.
The pte for x86 reserves a single bit for protection and its two states are
read only or read/write. Thus, write only is not supported in h/w.
Currently, if i 'mmap' a page write-only, the first read attempt on that page
creates a page fault and will SEGV. That check is enforced in
arch/blah/mm/fault.c. However, if i first write that page it will fault in
and the pte will be set to read/write. Thus, any subsequent reads to the page
will succeed. It is this inconsistency in behavior that this patch is
attempting to address. Furthermore, if the page is swapped out, and then
brought back the first read will also cause a SEGV. Thus, any arbitrary read
on a page can potentially result in a SEGV.
According to the SuSv3 spec, "if the application requests only PROT_WRITE, the
implementation may also allow read access." Also as mentioned, some
archtectures, such as alpha, shown above already take the approach that i am
suggesting.
The counter-argument to this raised by Arjan, is that the kernel is enforcing
the write only mapping the best it can given the h/w limitations. This is
true, however Alan Cox, and myself would argue that the inconsitency in
behavior, that is applications can sometimes work/sometimes fails is highly
undesireable. If you read through the thread, i think people, came to an
agreement on the last patch i posted, as nobody has objected to it...
Signed-off-by: Jason Baron <jbaron@redhat.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Andi Kleen <ak@muc.de>
Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Cc: Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
Cc: Ian Molton <spyro@f2s.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-09-29 08:58:58 +00:00
|
|
|
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
|
2005-04-16 22:20:36 +00:00
|
|
|
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.
|
|
|
|
*/
|
2009-04-10 16:01:23 +00:00
|
|
|
fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0);
|
2007-07-19 08:47:05 +00:00
|
|
|
if (unlikely(fault & VM_FAULT_ERROR)) {
|
|
|
|
if (fault & VM_FAULT_OOM)
|
2005-04-16 22:20:36 +00:00
|
|
|
goto out_of_memory;
|
2007-07-19 08:47:05 +00:00
|
|
|
else if (fault & VM_FAULT_SIGBUS)
|
|
|
|
goto do_sigbus;
|
|
|
|
BUG();
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-06-24 17:30:10 +00:00
|
|
|
if (fault & VM_FAULT_MAJOR) {
|
2007-07-19 08:47:05 +00:00
|
|
|
tsk->maj_flt++;
|
2011-06-27 12:41:57 +00:00
|
|
|
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
|
2009-06-24 17:30:10 +00:00
|
|
|
regs, address);
|
|
|
|
} else {
|
2007-07-19 08:47:05 +00:00
|
|
|
tsk->min_flt++;
|
2011-06-27 12:41:57 +00:00
|
|
|
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
|
2009-06-24 17:30:10 +00:00
|
|
|
regs, address);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
up_read(&mm->mmap_sem);
|
|
|
|
return;
|
|
|
|
|
2009-07-04 18:18:47 +00:00
|
|
|
/*
|
|
|
|
* Something tried to access memory that isn't in our memory map..
|
|
|
|
* Fix it, but check if it's kernel or user first..
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
bad_area:
|
|
|
|
up_read(&mm->mmap_sem);
|
|
|
|
|
2006-11-21 06:38:05 +00:00
|
|
|
bad_area_nosemaphore:
|
2005-04-16 22:20:36 +00:00
|
|
|
if (user_mode(regs)) {
|
2006-11-21 04:34:04 +00:00
|
|
|
info.si_signo = SIGSEGV;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = si_code;
|
|
|
|
info.si_addr = (void *) address;
|
|
|
|
force_sig_info(SIGSEGV, &info, tsk);
|
2005-04-16 22:20:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
no_context:
|
|
|
|
/* Are we prepared to handle this kernel fault? */
|
|
|
|
if (fixup_exception(regs))
|
|
|
|
return;
|
|
|
|
|
2008-02-07 11:18:21 +00:00
|
|
|
if (handle_trapped_io(regs, address))
|
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Oops. The kernel tried to access some bad page. We'll have to
|
|
|
|
* terminate things with extreme prejudice.
|
|
|
|
*
|
|
|
|
*/
|
2007-06-18 10:02:47 +00:00
|
|
|
|
|
|
|
bust_spinlocks(1);
|
|
|
|
|
|
|
|
if (oops_may_print()) {
|
2008-01-10 05:07:03 +00:00
|
|
|
unsigned long page;
|
2007-06-18 10:02:47 +00:00
|
|
|
|
|
|
|
if (address < PAGE_SIZE)
|
|
|
|
printk(KERN_ALERT "Unable to handle kernel NULL "
|
|
|
|
"pointer dereference");
|
|
|
|
else
|
|
|
|
printk(KERN_ALERT "Unable to handle kernel paging "
|
|
|
|
"request");
|
|
|
|
printk(" at virtual address %08lx\n", address);
|
|
|
|
printk(KERN_ALERT "pc = %08lx\n", regs->pc);
|
|
|
|
page = (unsigned long)get_TTB();
|
|
|
|
if (page) {
|
2007-08-01 07:39:51 +00:00
|
|
|
page = ((__typeof__(page) *)page)[address >> PGDIR_SHIFT];
|
2007-06-18 10:02:47 +00:00
|
|
|
printk(KERN_ALERT "*pde = %08lx\n", page);
|
|
|
|
if (page & _PAGE_PRESENT) {
|
|
|
|
page &= PAGE_MASK;
|
|
|
|
address &= 0x003ff000;
|
|
|
|
page = ((__typeof__(page) *)
|
|
|
|
__va(page))[address >>
|
|
|
|
PAGE_SHIFT];
|
|
|
|
printk(KERN_ALERT "*pte = %08lx\n", page);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2007-06-18 10:02:47 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
die("Oops", regs, writeaccess);
|
2007-06-18 10:02:47 +00:00
|
|
|
bust_spinlocks(0);
|
2005-04-16 22:20:36 +00:00
|
|
|
do_exit(SIGKILL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
up_read(&mm->mmap_sem);
|
2010-04-22 16:06:26 +00:00
|
|
|
if (!user_mode(regs))
|
|
|
|
goto no_context;
|
|
|
|
pagefault_out_of_memory();
|
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
do_sigbus:
|
|
|
|
up_read(&mm->mmap_sem);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send a sigbus, regardless of whether we were in kernel
|
|
|
|
* or user mode.
|
|
|
|
*/
|
2006-11-21 04:34:04 +00:00
|
|
|
info.si_signo = SIGBUS;
|
|
|
|
info.si_errno = 0;
|
|
|
|
info.si_code = BUS_ADRERR;
|
|
|
|
info.si_addr = (void *)address;
|
|
|
|
force_sig_info(SIGBUS, &info, tsk);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Kernel mode? Handle exceptions or die */
|
|
|
|
if (!user_mode(regs))
|
|
|
|
goto no_context;
|
|
|
|
}
|
2007-02-14 05:13:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Called with interrupts disabled.
|
|
|
|
*/
|
2009-08-14 17:49:40 +00:00
|
|
|
asmlinkage int __kprobes
|
|
|
|
handle_tlbmiss(struct pt_regs *regs, unsigned long writeaccess,
|
|
|
|
unsigned long address)
|
2007-02-14 05:13:10 +00:00
|
|
|
{
|
|
|
|
pgd_t *pgd;
|
|
|
|
pud_t *pud;
|
|
|
|
pmd_t *pmd;
|
|
|
|
pte_t *pte;
|
|
|
|
pte_t entry;
|
2008-09-21 04:56:39 +00:00
|
|
|
|
2007-02-14 05:13:10 +00:00
|
|
|
/*
|
|
|
|
* We don't take page faults for P1, P2, and parts of P4, these
|
|
|
|
* are always mapped, whether it be due to legacy behaviour in
|
|
|
|
* 29-bit mode, or due to PMB configuration in 32-bit mode.
|
|
|
|
*/
|
|
|
|
if (address >= P3SEG && address < P3_ADDR_MAX) {
|
|
|
|
pgd = pgd_offset_k(address);
|
|
|
|
} else {
|
2007-11-19 04:05:18 +00:00
|
|
|
if (unlikely(address >= TASK_SIZE || !current->mm))
|
2009-08-14 18:06:41 +00:00
|
|
|
return 1;
|
2007-02-14 05:13:10 +00:00
|
|
|
|
2007-11-19 04:05:18 +00:00
|
|
|
pgd = pgd_offset(current->mm, address);
|
2007-02-14 05:13:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pud = pud_offset(pgd, address);
|
|
|
|
if (pud_none_or_clear_bad(pud))
|
2009-08-14 18:06:41 +00:00
|
|
|
return 1;
|
2007-02-14 05:13:10 +00:00
|
|
|
pmd = pmd_offset(pud, address);
|
|
|
|
if (pmd_none_or_clear_bad(pmd))
|
2009-08-14 18:06:41 +00:00
|
|
|
return 1;
|
2007-11-19 04:05:18 +00:00
|
|
|
pte = pte_offset_kernel(pmd, address);
|
2007-02-14 05:13:10 +00:00
|
|
|
entry = *pte;
|
|
|
|
if (unlikely(pte_none(entry) || pte_not_present(entry)))
|
2009-08-14 18:06:41 +00:00
|
|
|
return 1;
|
2007-02-14 05:13:10 +00:00
|
|
|
if (unlikely(writeaccess && !pte_write(entry)))
|
2009-08-14 18:06:41 +00:00
|
|
|
return 1;
|
2007-02-14 05:13:10 +00:00
|
|
|
|
|
|
|
if (writeaccess)
|
|
|
|
entry = pte_mkdirty(entry);
|
|
|
|
entry = pte_mkyoung(entry);
|
|
|
|
|
2009-08-14 18:06:41 +00:00
|
|
|
set_pte(pte, entry);
|
|
|
|
|
2008-02-14 05:45:08 +00:00
|
|
|
#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SMP)
|
|
|
|
/*
|
2009-08-14 18:06:41 +00:00
|
|
|
* SH-4 does not set MMUCR.RC to the corresponding TLB entry in
|
|
|
|
* the case of an initial page write exception, so we need to
|
|
|
|
* flush it in order to avoid potential TLB entry duplication.
|
2008-02-14 05:45:08 +00:00
|
|
|
*/
|
2009-08-14 18:06:41 +00:00
|
|
|
if (writeaccess == 2)
|
|
|
|
local_flush_tlb_one(get_asid(), address & PAGE_MASK);
|
2008-02-14 05:45:08 +00:00
|
|
|
#endif
|
|
|
|
|
MM: Pass a PTE pointer to update_mmu_cache() rather than the PTE itself
On VIVT ARM, when we have multiple shared mappings of the same file
in the same MM, we need to ensure that we have coherency across all
copies. We do this via make_coherent() by making the pages
uncacheable.
This used to work fine, until we allowed highmem with highpte - we
now have a page table which is mapped as required, and is not available
for modification via update_mmu_cache().
Ralf Beache suggested getting rid of the PTE value passed to
update_mmu_cache():
On MIPS update_mmu_cache() calls __update_tlb() which walks pagetables
to construct a pointer to the pte again. Passing a pte_t * is much
more elegant. Maybe we might even replace the pte argument with the
pte_t?
Ben Herrenschmidt would also like the pte pointer for PowerPC:
Passing the ptep in there is exactly what I want. I want that
-instead- of the PTE value, because I have issue on some ppc cases,
for I$/D$ coherency, where set_pte_at() may decide to mask out the
_PAGE_EXEC.
So, pass in the mapped page table pointer into update_mmu_cache(), and
remove the PTE value, updating all implementations and call sites to
suit.
Includes a fix from Stephen Rothwell:
sparc: fix fallout from update_mmu_cache API change
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2009-12-18 16:40:18 +00:00
|
|
|
update_mmu_cache(NULL, address, pte);
|
2007-11-19 04:05:18 +00:00
|
|
|
|
2009-08-14 18:06:41 +00:00
|
|
|
return 0;
|
2007-02-14 05:13:10 +00:00
|
|
|
}
|