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