mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
sparc32: switch to generic extables
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
c4da8e0dc6
commit
b4edf06c8a
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include <asm/extable_64.h>
|
|
||||||
#include <asm/spitfire.h>
|
#include <asm/spitfire.h>
|
||||||
#include <asm/adi.h>
|
#include <asm/adi.h>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef __ASM_EXTABLE64_H
|
#ifndef __ASM_EXTABLE_H
|
||||||
#define __ASM_EXTABLE64_H
|
#define __ASM_EXTABLE_H
|
||||||
/*
|
/*
|
||||||
* The exception table consists of pairs of addresses: the first is the
|
* The exception table consists of pairs of addresses: the first is the
|
||||||
* address of an instruction that is allowed to fault, and the second is
|
* address of an instruction that is allowed to fault, and the second is
|
@ -1,6 +1,9 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef ___ASM_SPARC_UACCESS_H
|
#ifndef ___ASM_SPARC_UACCESS_H
|
||||||
#define ___ASM_SPARC_UACCESS_H
|
#define ___ASM_SPARC_UACCESS_H
|
||||||
|
|
||||||
|
#include <asm/extable.h>
|
||||||
|
|
||||||
#if defined(__sparc__) && defined(__arch64__)
|
#if defined(__sparc__) && defined(__arch64__)
|
||||||
#include <asm/uaccess_64.h>
|
#include <asm/uaccess_64.h>
|
||||||
#else
|
#else
|
||||||
|
@ -13,9 +13,6 @@
|
|||||||
|
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
|
|
||||||
#define ARCH_HAS_SORT_EXTABLE
|
|
||||||
#define ARCH_HAS_SEARCH_EXTABLE
|
|
||||||
|
|
||||||
/* Sparc is not segmented, however we need to be able to fool access_ok()
|
/* Sparc is not segmented, however we need to be able to fool access_ok()
|
||||||
* when doing system calls from kernel mode legitimately.
|
* when doing system calls from kernel mode legitimately.
|
||||||
*
|
*
|
||||||
@ -40,36 +37,6 @@
|
|||||||
#define __access_ok(addr, size) (__user_ok((addr) & get_fs().seg, (size)))
|
#define __access_ok(addr, size) (__user_ok((addr) & get_fs().seg, (size)))
|
||||||
#define access_ok(addr, size) __access_ok((unsigned long)(addr), size)
|
#define access_ok(addr, size) __access_ok((unsigned long)(addr), size)
|
||||||
|
|
||||||
/*
|
|
||||||
* The exception table consists of pairs of addresses: the first is the
|
|
||||||
* address of an instruction that is allowed to fault, and the second is
|
|
||||||
* the address at which the program should continue. No registers are
|
|
||||||
* modified, so it is entirely up to the continuation code to figure out
|
|
||||||
* what to do.
|
|
||||||
*
|
|
||||||
* All the routines below use bits of fixup code that are out of line
|
|
||||||
* with the main instruction path. This means when everything is well,
|
|
||||||
* we don't even have to jump over them. Further, they do not intrude
|
|
||||||
* on our cache or tlb entries.
|
|
||||||
*
|
|
||||||
* There is a special way how to put a range of potentially faulting
|
|
||||||
* insns (like twenty ldd/std's with now intervening other instructions)
|
|
||||||
* You specify address of first in insn and 0 in fixup and in the next
|
|
||||||
* exception_table_entry you specify last potentially faulting insn + 1
|
|
||||||
* and in fixup the routine which should handle the fault.
|
|
||||||
* That fixup code will get
|
|
||||||
* (faulting_insn_address - first_insn_in_the_range_address)/4
|
|
||||||
* in %g2 (ie. index of the faulting instruction in the range).
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct exception_table_entry
|
|
||||||
{
|
|
||||||
unsigned long insn, fixup;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Returns 0 if exception not found and fixup otherwise. */
|
|
||||||
unsigned long search_extables_range(unsigned long addr, unsigned long *g2);
|
|
||||||
|
|
||||||
/* Uh, these should become the main single-value transfer routines..
|
/* Uh, these should become the main single-value transfer routines..
|
||||||
* They automatically use the right size if we just have the right
|
* They automatically use the right size if we just have the right
|
||||||
* pointer type..
|
* pointer type..
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <asm/asi.h>
|
#include <asm/asi.h>
|
||||||
#include <asm/spitfire.h>
|
#include <asm/spitfire.h>
|
||||||
#include <asm/extable_64.h>
|
|
||||||
|
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
#include <linux/extable.h>
|
||||||
|
|
||||||
#include <asm/setup.h>
|
#include <asm/setup.h>
|
||||||
|
|
||||||
@ -213,10 +214,10 @@ static inline int ok_for_kernel(unsigned int insn)
|
|||||||
|
|
||||||
static void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
|
static void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
|
||||||
{
|
{
|
||||||
unsigned long g2 = regs->u_regs [UREG_G2];
|
const struct exception_table_entry *entry;
|
||||||
unsigned long fixup = search_extables_range(regs->pc, &g2);
|
|
||||||
|
|
||||||
if (!fixup) {
|
entry = search_exception_tables(regs->pc);
|
||||||
|
if (!entry) {
|
||||||
unsigned long address = compute_effective_address(regs, insn);
|
unsigned long address = compute_effective_address(regs, insn);
|
||||||
if(address < PAGE_SIZE) {
|
if(address < PAGE_SIZE) {
|
||||||
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
|
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
|
||||||
@ -232,9 +233,8 @@ static void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
|
|||||||
die_if_kernel("Oops", regs);
|
die_if_kernel("Oops", regs);
|
||||||
/* Not reached */
|
/* Not reached */
|
||||||
}
|
}
|
||||||
regs->pc = fixup;
|
regs->pc = entry->fixup;
|
||||||
regs->npc = regs->pc + 4;
|
regs->npc = regs->pc + 4;
|
||||||
regs->u_regs [UREG_G2] = g2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
|
asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
|
||||||
|
@ -8,7 +8,7 @@ ccflags-y := -Werror
|
|||||||
obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o
|
obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o
|
||||||
obj-y += fault_$(BITS).o
|
obj-y += fault_$(BITS).o
|
||||||
obj-y += init_$(BITS).o
|
obj-y += init_$(BITS).o
|
||||||
obj-$(CONFIG_SPARC32) += extable.o srmmu.o iommu.o io-unit.o
|
obj-$(CONFIG_SPARC32) += srmmu.o iommu.o io-unit.o
|
||||||
obj-$(CONFIG_SPARC32) += srmmu_access.o
|
obj-$(CONFIG_SPARC32) += srmmu_access.o
|
||||||
obj-$(CONFIG_SPARC32) += hypersparc.o viking.o tsunami.o swift.o
|
obj-$(CONFIG_SPARC32) += hypersparc.o viking.o tsunami.o swift.o
|
||||||
obj-$(CONFIG_SPARC32) += leon_mm.o
|
obj-$(CONFIG_SPARC32) += leon_mm.o
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/*
|
|
||||||
* linux/arch/sparc/mm/extable.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/extable.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
|
|
||||||
void sort_extable(struct exception_table_entry *start,
|
|
||||||
struct exception_table_entry *finish)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caller knows they are in a range if ret->fixup == 0 */
|
|
||||||
const struct exception_table_entry *
|
|
||||||
search_extable(const struct exception_table_entry *base,
|
|
||||||
const size_t num,
|
|
||||||
unsigned long value)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Single insn entries are encoded as:
|
|
||||||
* word 1: insn address
|
|
||||||
* word 2: fixup code address
|
|
||||||
*
|
|
||||||
* Range entries are encoded as:
|
|
||||||
* word 1: first insn address
|
|
||||||
* word 2: 0
|
|
||||||
* word 3: last insn address + 4 bytes
|
|
||||||
* word 4: fixup code address
|
|
||||||
*
|
|
||||||
* Deleted entries are encoded as:
|
|
||||||
* word 1: unused
|
|
||||||
* word 2: -1
|
|
||||||
*
|
|
||||||
* See asm/uaccess.h for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* 1. Try to find an exact match. */
|
|
||||||
for (i = 0; i < num; i++) {
|
|
||||||
if (base[i].fixup == 0) {
|
|
||||||
/* A range entry, skip both parts. */
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A deleted entry; see trim_init_extable */
|
|
||||||
if (base[i].fixup == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (base[i].insn == value)
|
|
||||||
return &base[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. Try to find a range match. */
|
|
||||||
for (i = 0; i < (num - 1); i++) {
|
|
||||||
if (base[i].fixup)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (base[i].insn <= value && base[i + 1].insn > value)
|
|
||||||
return &base[i];
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_MODULES
|
|
||||||
/* We could memmove them around; easier to mark the trimmed ones. */
|
|
||||||
void trim_init_extable(struct module *m)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
bool range;
|
|
||||||
|
|
||||||
for (i = 0; i < m->num_exentries; i += range ? 2 : 1) {
|
|
||||||
range = m->extable[i].fixup == 0;
|
|
||||||
|
|
||||||
if (within_module_init(m->extable[i].insn, m)) {
|
|
||||||
m->extable[i].fixup = -1;
|
|
||||||
if (range)
|
|
||||||
m->extable[i+1].fixup = -1;
|
|
||||||
}
|
|
||||||
if (range)
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_MODULES */
|
|
||||||
|
|
||||||
/* Special extable search, which handles ranges. Returns fixup */
|
|
||||||
unsigned long search_extables_range(unsigned long addr, unsigned long *g2)
|
|
||||||
{
|
|
||||||
const struct exception_table_entry *entry;
|
|
||||||
|
|
||||||
entry = search_exception_tables(addr);
|
|
||||||
if (!entry)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Inside range? Fix g2 and return correct fixup */
|
|
||||||
if (!entry->fixup) {
|
|
||||||
*g2 = (addr - entry->insn) / 4;
|
|
||||||
return (entry + 1)->fixup;
|
|
||||||
}
|
|
||||||
|
|
||||||
return entry->fixup;
|
|
||||||
}
|
|
@ -23,6 +23,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kdebug.h>
|
#include <linux/kdebug.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/extable.h>
|
||||||
|
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/openprom.h>
|
#include <asm/openprom.h>
|
||||||
@ -114,8 +115,6 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
|
|||||||
struct vm_area_struct *vma;
|
struct vm_area_struct *vma;
|
||||||
struct task_struct *tsk = current;
|
struct task_struct *tsk = current;
|
||||||
struct mm_struct *mm = tsk->mm;
|
struct mm_struct *mm = tsk->mm;
|
||||||
unsigned int fixup;
|
|
||||||
unsigned long g2;
|
|
||||||
int from_user = !(regs->psr & PSR_PS);
|
int from_user = !(regs->psr & PSR_PS);
|
||||||
int code;
|
int code;
|
||||||
vm_fault_t fault;
|
vm_fault_t fault;
|
||||||
@ -233,22 +232,19 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write,
|
|||||||
|
|
||||||
/* Is this in ex_table? */
|
/* Is this in ex_table? */
|
||||||
no_context:
|
no_context:
|
||||||
g2 = regs->u_regs[UREG_G2];
|
|
||||||
if (!from_user) {
|
if (!from_user) {
|
||||||
fixup = search_extables_range(regs->pc, &g2);
|
const struct exception_table_entry *entry;
|
||||||
/* Values below 10 are reserved for other things */
|
|
||||||
if (fixup > 10) {
|
entry = search_exception_tables(regs->pc);
|
||||||
#ifdef DEBUG_EXCEPTIONS
|
#ifdef DEBUG_EXCEPTIONS
|
||||||
printk("Exception: PC<%08lx> faddr<%08lx>\n",
|
printk("Exception: PC<%08lx> faddr<%08lx>\n",
|
||||||
regs->pc, address);
|
regs->pc, address);
|
||||||
printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
|
printk("EX_TABLE: insn<%08lx> fixup<%08x>\n",
|
||||||
regs->pc, fixup, g2);
|
regs->pc, entry->fixup);
|
||||||
#endif
|
#endif
|
||||||
regs->u_regs[UREG_G2] = g2;
|
regs->pc = entry->fixup;
|
||||||
regs->pc = fixup;
|
regs->npc = regs->pc + 4;
|
||||||
regs->npc = regs->pc + 4;
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unhandled_fault(address, tsk, regs);
|
unhandled_fault(address, tsk, regs);
|
||||||
|
@ -21,7 +21,6 @@ static inline unsigned long ex_to_insn(const struct exception_table_entry *x)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ARCH_HAS_SORT_EXTABLE
|
|
||||||
#ifndef ARCH_HAS_RELATIVE_EXTABLE
|
#ifndef ARCH_HAS_RELATIVE_EXTABLE
|
||||||
#define swap_ex NULL
|
#define swap_ex NULL
|
||||||
#else
|
#else
|
||||||
@ -88,9 +87,6 @@ void trim_init_extable(struct module *m)
|
|||||||
m->num_exentries--;
|
m->num_exentries--;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_MODULES */
|
#endif /* CONFIG_MODULES */
|
||||||
#endif /* !ARCH_HAS_SORT_EXTABLE */
|
|
||||||
|
|
||||||
#ifndef ARCH_HAS_SEARCH_EXTABLE
|
|
||||||
|
|
||||||
static int cmp_ex_search(const void *key, const void *elt)
|
static int cmp_ex_search(const void *key, const void *elt)
|
||||||
{
|
{
|
||||||
@ -120,4 +116,3 @@ search_extable(const struct exception_table_entry *base,
|
|||||||
return bsearch(&value, base, num,
|
return bsearch(&value, base, num,
|
||||||
sizeof(struct exception_table_entry), cmp_ex_search);
|
sizeof(struct exception_table_entry), cmp_ex_search);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
Loading…
Reference in New Issue
Block a user