1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 */ 5#include <linux/bitfield.h> 6#include <linux/extable.h> 7#include <linux/uaccess.h> 8#include <asm/asm-extable.h> 9#include <asm/branch.h> 10 11static inline unsigned long 12get_ex_fixup(const struct exception_table_entry *ex) 13{ 14 return ((unsigned long)&ex->fixup + ex->fixup); 15} 16 17static inline void regs_set_gpr(struct pt_regs *regs, 18 unsigned int offset, unsigned long val) 19{ 20 if (offset && offset <= MAX_REG_OFFSET) 21 *(unsigned long *)((unsigned long)regs + offset) = val; 22} 23 24static bool ex_handler_fixup(const struct exception_table_entry *ex, 25 struct pt_regs *regs) 26{ 27 regs->csr_era = get_ex_fixup(ex); 28 29 return true; 30} 31 32static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex, 33 struct pt_regs *regs) 34{ 35 int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 36 int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data); 37 38 regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT); 39 regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0); 40 regs->csr_era = get_ex_fixup(ex); 41 42 return true; 43} 44 45bool fixup_exception(struct pt_regs *regs) 46{ 47 const struct exception_table_entry *ex; 48 49 ex = search_exception_tables(exception_era(regs)); 50 if (!ex) 51 return false; 52 53 switch (ex->type) { 54 case EX_TYPE_FIXUP: 55 return ex_handler_fixup(ex, regs); 56 case EX_TYPE_UACCESS_ERR_ZERO: 57 return ex_handler_uaccess_err_zero(ex, regs); 58 case EX_TYPE_BPF: 59 return ex_handler_bpf(ex, regs); 60 } 61 62 BUG(); 63} 64