xref: /openbmc/linux/arch/riscv/mm/extable.c (revision c9933d49)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
4  *  Lennox Wu <lennox.wu@sunplusct.com>
5  *  Chen Liqin <liqin.chen@sunplusct.com>
6  * Copyright (C) 2013 Regents of the University of California
7  */
8 
9 
10 #include <linux/bitfield.h>
11 #include <linux/extable.h>
12 #include <linux/module.h>
13 #include <linux/uaccess.h>
14 #include <asm/asm-extable.h>
15 #include <asm/ptrace.h>
16 
17 static inline unsigned long
18 get_ex_fixup(const struct exception_table_entry *ex)
19 {
20 	return ((unsigned long)&ex->fixup + ex->fixup);
21 }
22 
23 static bool ex_handler_fixup(const struct exception_table_entry *ex,
24 			     struct pt_regs *regs)
25 {
26 	regs->epc = get_ex_fixup(ex);
27 	return true;
28 }
29 
30 static inline void regs_set_gpr(struct pt_regs *regs, unsigned int offset,
31 				unsigned long val)
32 {
33 	if (unlikely(offset > MAX_REG_OFFSET))
34 		return;
35 
36 	if (offset)
37 		*(unsigned long *)((unsigned long)regs + offset) = val;
38 }
39 
40 static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
41 					struct pt_regs *regs)
42 {
43 	int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
44 	int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
45 
46 	regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT);
47 	regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0);
48 
49 	regs->epc = get_ex_fixup(ex);
50 	return true;
51 }
52 
53 bool fixup_exception(struct pt_regs *regs)
54 {
55 	const struct exception_table_entry *ex;
56 
57 	ex = search_exception_tables(regs->epc);
58 	if (!ex)
59 		return false;
60 
61 	switch (ex->type) {
62 	case EX_TYPE_FIXUP:
63 		return ex_handler_fixup(ex, regs);
64 	case EX_TYPE_BPF:
65 		return ex_handler_bpf(ex, regs);
66 	case EX_TYPE_UACCESS_ERR_ZERO:
67 		return ex_handler_uaccess_err_zero(ex, regs);
68 	}
69 
70 	BUG();
71 }
72