xref: /openbmc/linux/arch/s390/mm/extable.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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