10741ec11SHeiko Carstens // SPDX-License-Identifier: GPL-2.0
20741ec11SHeiko Carstens
3454ede3fSHeiko Carstens #include <linux/bitfield.h>
40741ec11SHeiko Carstens #include <linux/extable.h>
5454ede3fSHeiko Carstens #include <linux/string.h>
6484a8ed8SHeiko Carstens #include <linux/errno.h>
73d66718cSHeiko Carstens #include <linux/panic.h>
83d66718cSHeiko Carstens #include <asm/asm-extable.h>
90741ec11SHeiko Carstens #include <asm/extable.h>
100741ec11SHeiko Carstens
s390_search_extables(unsigned long addr)110741ec11SHeiko Carstens const struct exception_table_entry *s390_search_extables(unsigned long addr)
120741ec11SHeiko Carstens {
130741ec11SHeiko Carstens const struct exception_table_entry *fixup;
140741ec11SHeiko Carstens size_t num;
150741ec11SHeiko Carstens
160741ec11SHeiko Carstens fixup = search_exception_tables(addr);
170741ec11SHeiko Carstens if (fixup)
180741ec11SHeiko Carstens return fixup;
190741ec11SHeiko Carstens num = __stop_amode31_ex_table - __start_amode31_ex_table;
200741ec11SHeiko Carstens return search_extable(__start_amode31_ex_table, num, addr);
210741ec11SHeiko Carstens }
2246fee16fSHeiko Carstens
ex_handler_fixup(const struct exception_table_entry * ex,struct pt_regs * regs)233d66718cSHeiko Carstens static bool ex_handler_fixup(const struct exception_table_entry *ex, struct pt_regs *regs)
243d66718cSHeiko Carstens {
253d66718cSHeiko Carstens regs->psw.addr = extable_fixup(ex);
263d66718cSHeiko Carstens return true;
273d66718cSHeiko Carstens }
283d66718cSHeiko Carstens
ex_handler_ua_store(const struct exception_table_entry * ex,struct pt_regs * regs)29454ede3fSHeiko Carstens static bool ex_handler_ua_store(const struct exception_table_entry *ex, struct pt_regs *regs)
30484a8ed8SHeiko Carstens {
31454ede3fSHeiko Carstens unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
32454ede3fSHeiko Carstens
33454ede3fSHeiko Carstens regs->gprs[reg_err] = -EFAULT;
34454ede3fSHeiko Carstens regs->psw.addr = extable_fixup(ex);
35454ede3fSHeiko Carstens return true;
36454ede3fSHeiko Carstens }
37454ede3fSHeiko Carstens
ex_handler_ua_load_mem(const struct exception_table_entry * ex,struct pt_regs * regs)38454ede3fSHeiko Carstens static bool ex_handler_ua_load_mem(const struct exception_table_entry *ex, struct pt_regs *regs)
39454ede3fSHeiko Carstens {
40454ede3fSHeiko Carstens unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
41454ede3fSHeiko Carstens unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
42454ede3fSHeiko Carstens size_t len = FIELD_GET(EX_DATA_LEN, ex->data);
43454ede3fSHeiko Carstens
44454ede3fSHeiko Carstens regs->gprs[reg_err] = -EFAULT;
45454ede3fSHeiko Carstens memset((void *)regs->gprs[reg_addr], 0, len);
46454ede3fSHeiko Carstens regs->psw.addr = extable_fixup(ex);
47454ede3fSHeiko Carstens return true;
48454ede3fSHeiko Carstens }
49454ede3fSHeiko Carstens
ex_handler_ua_load_reg(const struct exception_table_entry * ex,bool pair,struct pt_regs * regs)50*f39a8c4aSHeiko Carstens static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex,
51*f39a8c4aSHeiko Carstens bool pair, struct pt_regs *regs)
52454ede3fSHeiko Carstens {
53454ede3fSHeiko Carstens unsigned int reg_zero = FIELD_GET(EX_DATA_REG_ADDR, ex->data);
54454ede3fSHeiko Carstens unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
55454ede3fSHeiko Carstens
56454ede3fSHeiko Carstens regs->gprs[reg_err] = -EFAULT;
57454ede3fSHeiko Carstens regs->gprs[reg_zero] = 0;
58*f39a8c4aSHeiko Carstens if (pair)
59*f39a8c4aSHeiko Carstens regs->gprs[reg_zero + 1] = 0;
60484a8ed8SHeiko Carstens regs->psw.addr = extable_fixup(ex);
61484a8ed8SHeiko Carstens return true;
62484a8ed8SHeiko Carstens }
63484a8ed8SHeiko Carstens
fixup_exception(struct pt_regs * regs)6446fee16fSHeiko Carstens bool fixup_exception(struct pt_regs *regs)
6546fee16fSHeiko Carstens {
6646fee16fSHeiko Carstens const struct exception_table_entry *ex;
6746fee16fSHeiko Carstens
6846fee16fSHeiko Carstens ex = s390_search_extables(instruction_pointer(regs));
6946fee16fSHeiko Carstens if (!ex)
7046fee16fSHeiko Carstens return false;
713d66718cSHeiko Carstens switch (ex->type) {
723d66718cSHeiko Carstens case EX_TYPE_FIXUP:
733d66718cSHeiko Carstens return ex_handler_fixup(ex, regs);
743d66718cSHeiko Carstens case EX_TYPE_BPF:
753d66718cSHeiko Carstens return ex_handler_bpf(ex, regs);
76454ede3fSHeiko Carstens case EX_TYPE_UA_STORE:
77454ede3fSHeiko Carstens return ex_handler_ua_store(ex, regs);
78454ede3fSHeiko Carstens case EX_TYPE_UA_LOAD_MEM:
79454ede3fSHeiko Carstens return ex_handler_ua_load_mem(ex, regs);
80454ede3fSHeiko Carstens case EX_TYPE_UA_LOAD_REG:
81*f39a8c4aSHeiko Carstens return ex_handler_ua_load_reg(ex, false, regs);
82*f39a8c4aSHeiko Carstens case EX_TYPE_UA_LOAD_REGPAIR:
83*f39a8c4aSHeiko Carstens return ex_handler_ua_load_reg(ex, true, regs);
843d66718cSHeiko Carstens }
853d66718cSHeiko Carstens panic("invalid exception table entry");
8646fee16fSHeiko Carstens }
87