1588cb88cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22129a235SPalmer Dabbelt /*
32129a235SPalmer Dabbelt * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
42129a235SPalmer Dabbelt * Lennox Wu <lennox.wu@sunplusct.com>
52129a235SPalmer Dabbelt * Chen Liqin <liqin.chen@sunplusct.com>
62129a235SPalmer Dabbelt * Copyright (C) 2013 Regents of the University of California
72129a235SPalmer Dabbelt */
82129a235SPalmer Dabbelt
92129a235SPalmer Dabbelt
1020802d8dSJisheng Zhang #include <linux/bitfield.h>
112129a235SPalmer Dabbelt #include <linux/extable.h>
122129a235SPalmer Dabbelt #include <linux/module.h>
132129a235SPalmer Dabbelt #include <linux/uaccess.h>
142bf847dbSJisheng Zhang #include <asm/asm-extable.h>
1520802d8dSJisheng Zhang #include <asm/ptrace.h>
162bf847dbSJisheng Zhang
172bf847dbSJisheng Zhang static inline unsigned long
get_ex_fixup(const struct exception_table_entry * ex)182bf847dbSJisheng Zhang get_ex_fixup(const struct exception_table_entry *ex)
192bf847dbSJisheng Zhang {
202bf847dbSJisheng Zhang return ((unsigned long)&ex->fixup + ex->fixup);
212bf847dbSJisheng Zhang }
222bf847dbSJisheng Zhang
ex_handler_fixup(const struct exception_table_entry * ex,struct pt_regs * regs)232bf847dbSJisheng Zhang static bool ex_handler_fixup(const struct exception_table_entry *ex,
242bf847dbSJisheng Zhang struct pt_regs *regs)
252bf847dbSJisheng Zhang {
262bf847dbSJisheng Zhang regs->epc = get_ex_fixup(ex);
272bf847dbSJisheng Zhang return true;
282bf847dbSJisheng Zhang }
292129a235SPalmer Dabbelt
regs_get_gpr(struct pt_regs * regs,unsigned int offset)30*d0fdc20bSJisheng Zhang static inline unsigned long regs_get_gpr(struct pt_regs *regs, unsigned int offset)
31*d0fdc20bSJisheng Zhang {
32*d0fdc20bSJisheng Zhang if (unlikely(!offset || offset > MAX_REG_OFFSET))
33*d0fdc20bSJisheng Zhang return 0;
34*d0fdc20bSJisheng Zhang
35*d0fdc20bSJisheng Zhang return *(unsigned long *)((unsigned long)regs + offset);
36*d0fdc20bSJisheng Zhang }
37*d0fdc20bSJisheng Zhang
regs_set_gpr(struct pt_regs * regs,unsigned int offset,unsigned long val)3820802d8dSJisheng Zhang static inline void regs_set_gpr(struct pt_regs *regs, unsigned int offset,
3920802d8dSJisheng Zhang unsigned long val)
4020802d8dSJisheng Zhang {
4120802d8dSJisheng Zhang if (unlikely(offset > MAX_REG_OFFSET))
4220802d8dSJisheng Zhang return;
4320802d8dSJisheng Zhang
44f81393a5SJisheng Zhang if (offset)
4520802d8dSJisheng Zhang *(unsigned long *)((unsigned long)regs + offset) = val;
4620802d8dSJisheng Zhang }
4720802d8dSJisheng Zhang
ex_handler_uaccess_err_zero(const struct exception_table_entry * ex,struct pt_regs * regs)4820802d8dSJisheng Zhang static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
4920802d8dSJisheng Zhang struct pt_regs *regs)
5020802d8dSJisheng Zhang {
5120802d8dSJisheng Zhang int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
5220802d8dSJisheng Zhang int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
5320802d8dSJisheng Zhang
54f81393a5SJisheng Zhang regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT);
55f81393a5SJisheng Zhang regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0);
5620802d8dSJisheng Zhang
5720802d8dSJisheng Zhang regs->epc = get_ex_fixup(ex);
5820802d8dSJisheng Zhang return true;
5920802d8dSJisheng Zhang }
6020802d8dSJisheng Zhang
61*d0fdc20bSJisheng Zhang static bool
ex_handler_load_unaligned_zeropad(const struct exception_table_entry * ex,struct pt_regs * regs)62*d0fdc20bSJisheng Zhang ex_handler_load_unaligned_zeropad(const struct exception_table_entry *ex,
63*d0fdc20bSJisheng Zhang struct pt_regs *regs)
64*d0fdc20bSJisheng Zhang {
65*d0fdc20bSJisheng Zhang int reg_data = FIELD_GET(EX_DATA_REG_DATA, ex->data);
66*d0fdc20bSJisheng Zhang int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
67*d0fdc20bSJisheng Zhang unsigned long data, addr, offset;
68*d0fdc20bSJisheng Zhang
69*d0fdc20bSJisheng Zhang addr = regs_get_gpr(regs, reg_addr * sizeof(unsigned long));
70*d0fdc20bSJisheng Zhang
71*d0fdc20bSJisheng Zhang offset = addr & 0x7UL;
72*d0fdc20bSJisheng Zhang addr &= ~0x7UL;
73*d0fdc20bSJisheng Zhang
74*d0fdc20bSJisheng Zhang data = *(unsigned long *)addr >> (offset * 8);
75*d0fdc20bSJisheng Zhang
76*d0fdc20bSJisheng Zhang regs_set_gpr(regs, reg_data * sizeof(unsigned long), data);
77*d0fdc20bSJisheng Zhang
78*d0fdc20bSJisheng Zhang regs->epc = get_ex_fixup(ex);
79*d0fdc20bSJisheng Zhang return true;
80*d0fdc20bSJisheng Zhang }
81*d0fdc20bSJisheng Zhang
fixup_exception(struct pt_regs * regs)82ef127bcaSJisheng Zhang bool fixup_exception(struct pt_regs *regs)
832129a235SPalmer Dabbelt {
844c2e7ce8SJisheng Zhang const struct exception_table_entry *ex;
852129a235SPalmer Dabbelt
864c2e7ce8SJisheng Zhang ex = search_exception_tables(regs->epc);
874c2e7ce8SJisheng Zhang if (!ex)
88ef127bcaSJisheng Zhang return false;
89252c765bSTong Tiangen
902bf847dbSJisheng Zhang switch (ex->type) {
912bf847dbSJisheng Zhang case EX_TYPE_FIXUP:
922bf847dbSJisheng Zhang return ex_handler_fixup(ex, regs);
932bf847dbSJisheng Zhang case EX_TYPE_BPF:
942bf847dbSJisheng Zhang return ex_handler_bpf(ex, regs);
9520802d8dSJisheng Zhang case EX_TYPE_UACCESS_ERR_ZERO:
9620802d8dSJisheng Zhang return ex_handler_uaccess_err_zero(ex, regs);
97*d0fdc20bSJisheng Zhang case EX_TYPE_LOAD_UNALIGNED_ZEROPAD:
98*d0fdc20bSJisheng Zhang return ex_handler_load_unaligned_zeropad(ex, regs);
992bf847dbSJisheng Zhang }
100252c765bSTong Tiangen
1012bf847dbSJisheng Zhang BUG();
1022129a235SPalmer Dabbelt }
103