diff options
Diffstat (limited to 'arch/sh/mm/tlbex_32.c')
| -rw-r--r-- | arch/sh/mm/tlbex_32.c | 78 | 
1 files changed, 78 insertions, 0 deletions
diff --git a/arch/sh/mm/tlbex_32.c b/arch/sh/mm/tlbex_32.c new file mode 100644 index 00000000000..382262dc0c4 --- /dev/null +++ b/arch/sh/mm/tlbex_32.c @@ -0,0 +1,78 @@ +/* + * TLB miss handler for SH with an MMU. + * + *  Copyright (C) 1999  Niibe Yutaka + *  Copyright (C) 2003 - 2012  Paul Mundt + * + * 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/kernel.h> +#include <linux/mm.h> +#include <linux/kprobes.h> +#include <linux/kdebug.h> +#include <asm/mmu_context.h> +#include <asm/thread_info.h> + +/* + * Called with interrupts disabled. + */ +asmlinkage int __kprobes +handle_tlbmiss(struct pt_regs *regs, unsigned long error_code, +	       unsigned long address) +{ +	pgd_t *pgd; +	pud_t *pud; +	pmd_t *pmd; +	pte_t *pte; +	pte_t entry; + +	/* +	 * 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 { +		if (unlikely(address >= TASK_SIZE || !current->mm)) +			return 1; + +		pgd = pgd_offset(current->mm, address); +	} + +	pud = pud_offset(pgd, address); +	if (pud_none_or_clear_bad(pud)) +		return 1; +	pmd = pmd_offset(pud, address); +	if (pmd_none_or_clear_bad(pmd)) +		return 1; +	pte = pte_offset_kernel(pmd, address); +	entry = *pte; +	if (unlikely(pte_none(entry) || pte_not_present(entry))) +		return 1; +	if (unlikely(error_code && !pte_write(entry))) +		return 1; + +	if (error_code) +		entry = pte_mkdirty(entry); +	entry = pte_mkyoung(entry); + +	set_pte(pte, entry); + +#if defined(CONFIG_CPU_SH4) && !defined(CONFIG_SMP) +	/* +	 * 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. +	 */ +	if (error_code == FAULT_CODE_INITIAL) +		local_flush_tlb_one(get_asid(), address & PAGE_MASK); +#endif + +	set_thread_fault_code(error_code); +	update_mmu_cache(NULL, address, pte); + +	return 0; +}  |