xref: /openbmc/linux/arch/loongarch/mm/extable.c (revision dbcd7f5f)
109cfefb7SHuacai Chen // SPDX-License-Identifier: GPL-2.0
209cfefb7SHuacai Chen /*
309cfefb7SHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
409cfefb7SHuacai Chen  */
5672999cfSYouling Tang #include <linux/bitfield.h>
609cfefb7SHuacai Chen #include <linux/extable.h>
709cfefb7SHuacai Chen #include <linux/uaccess.h>
83d36f429SYouling Tang #include <asm/asm-extable.h>
93d36f429SYouling Tang #include <asm/branch.h>
1009cfefb7SHuacai Chen 
113d36f429SYouling Tang static inline unsigned long
get_ex_fixup(const struct exception_table_entry * ex)123d36f429SYouling Tang get_ex_fixup(const struct exception_table_entry *ex)
1309cfefb7SHuacai Chen {
143d36f429SYouling Tang 	return ((unsigned long)&ex->fixup + ex->fixup);
1509cfefb7SHuacai Chen }
1609cfefb7SHuacai Chen 
regs_set_gpr(struct pt_regs * regs,unsigned int offset,unsigned long val)17672999cfSYouling Tang static inline void regs_set_gpr(struct pt_regs *regs,
18672999cfSYouling Tang 				unsigned int offset, unsigned long val)
19672999cfSYouling Tang {
20672999cfSYouling Tang 	if (offset && offset <= MAX_REG_OFFSET)
21672999cfSYouling Tang 		*(unsigned long *)((unsigned long)regs + offset) = val;
22672999cfSYouling Tang }
23672999cfSYouling Tang 
ex_handler_fixup(const struct exception_table_entry * ex,struct pt_regs * regs)243d36f429SYouling Tang static bool ex_handler_fixup(const struct exception_table_entry *ex,
253d36f429SYouling Tang 			     struct pt_regs *regs)
263d36f429SYouling Tang {
273d36f429SYouling Tang 	regs->csr_era = get_ex_fixup(ex);
283d36f429SYouling Tang 
293d36f429SYouling Tang 	return true;
303d36f429SYouling Tang }
313d36f429SYouling Tang 
ex_handler_uaccess_err_zero(const struct exception_table_entry * ex,struct pt_regs * regs)32672999cfSYouling Tang static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
33672999cfSYouling Tang 					struct pt_regs *regs)
34672999cfSYouling Tang {
35672999cfSYouling Tang 	int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
36672999cfSYouling Tang 	int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
37672999cfSYouling Tang 
38672999cfSYouling Tang 	regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT);
39672999cfSYouling Tang 	regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0);
40672999cfSYouling Tang 	regs->csr_era = get_ex_fixup(ex);
41672999cfSYouling Tang 
42672999cfSYouling Tang 	return true;
43672999cfSYouling Tang }
443d36f429SYouling Tang 
fixup_exception(struct pt_regs * regs)453d36f429SYouling Tang bool fixup_exception(struct pt_regs *regs)
463d36f429SYouling Tang {
473d36f429SYouling Tang 	const struct exception_table_entry *ex;
483d36f429SYouling Tang 
493d36f429SYouling Tang 	ex = search_exception_tables(exception_era(regs));
503d36f429SYouling Tang 	if (!ex)
513d36f429SYouling Tang 		return false;
523d36f429SYouling Tang 
5326bc8244SYouling Tang 	switch (ex->type) {
5426bc8244SYouling Tang 	case EX_TYPE_FIXUP:
553d36f429SYouling Tang 		return ex_handler_fixup(ex, regs);
56672999cfSYouling Tang 	case EX_TYPE_UACCESS_ERR_ZERO:
57672999cfSYouling Tang 		return ex_handler_uaccess_err_zero(ex, regs);
58*dbcd7f5fSYouling Tang 	case EX_TYPE_BPF:
59*dbcd7f5fSYouling Tang 		return ex_handler_bpf(ex, regs);
6009cfefb7SHuacai Chen 	}
6126bc8244SYouling Tang 
6226bc8244SYouling Tang 	BUG();
6326bc8244SYouling Tang }
64