xref: /linux/arch/riscv/mm/extable.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
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