18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Loongson Technology Corporation Limited 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 68c2ecf20Sopenharmony_ci#include <linux/extable.h> 78c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 88c2ecf20Sopenharmony_ci#include <asm/asm-extable.h> 98c2ecf20Sopenharmony_ci#include <asm/branch.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic inline unsigned long 128c2ecf20Sopenharmony_ciget_ex_fixup(const struct exception_table_entry *ex) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci return ((unsigned long)&ex->fixup + ex->fixup); 158c2ecf20Sopenharmony_ci} 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic inline void regs_set_gpr(struct pt_regs *regs, 188c2ecf20Sopenharmony_ci unsigned int offset, unsigned long val) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci if (offset && offset <= MAX_REG_OFFSET) 218c2ecf20Sopenharmony_ci *(unsigned long *)((unsigned long)regs + offset) = val; 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic bool ex_handler_fixup(const struct exception_table_entry *ex, 258c2ecf20Sopenharmony_ci struct pt_regs *regs) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci regs->csr_era = get_ex_fixup(ex); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return true; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex, 338c2ecf20Sopenharmony_ci struct pt_regs *regs) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 368c2ecf20Sopenharmony_ci int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT); 398c2ecf20Sopenharmony_ci regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0); 408c2ecf20Sopenharmony_ci regs->csr_era = get_ex_fixup(ex); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return true; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cibool fixup_exception(struct pt_regs *regs) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci const struct exception_table_entry *ex; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci ex = search_exception_tables(exception_era(regs)); 508c2ecf20Sopenharmony_ci if (!ex) 518c2ecf20Sopenharmony_ci return false; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci switch (ex->type) { 548c2ecf20Sopenharmony_ci case EX_TYPE_FIXUP: 558c2ecf20Sopenharmony_ci return ex_handler_fixup(ex, regs); 568c2ecf20Sopenharmony_ci case EX_TYPE_UACCESS_ERR_ZERO: 578c2ecf20Sopenharmony_ci return ex_handler_uaccess_err_zero(ex, regs); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci BUG(); 618c2ecf20Sopenharmony_ci} 62