1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/bitfield.h> 4 #include <linux/extable.h> 5 #include <linux/string.h> 6 #include <linux/errno.h> 7 #include <linux/panic.h> 8 #include <asm/asm-extable.h> 9 #include <asm/extable.h> 10 #include <asm/fpu.h> 11 12 const struct exception_table_entry *s390_search_extables(unsigned long addr) 13 { 14 const struct exception_table_entry *fixup; 15 size_t num; 16 17 fixup = search_exception_tables(addr); 18 if (fixup) 19 return fixup; 20 num = __stop_amode31_ex_table - __start_amode31_ex_table; 21 return search_extable(__start_amode31_ex_table, num, addr); 22 } 23 24 static bool ex_handler_fixup(const struct exception_table_entry *ex, struct pt_regs *regs) 25 { 26 regs->psw.addr = extable_fixup(ex); 27 return true; 28 } 29 30 static bool ex_handler_ua_fault(const struct exception_table_entry *ex, struct pt_regs *regs) 31 { 32 unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 33 34 regs->gprs[reg_err] = -EFAULT; 35 regs->psw.addr = extable_fixup(ex); 36 return true; 37 } 38 39 static bool ex_handler_ua_load_reg(const struct exception_table_entry *ex, 40 bool pair, struct pt_regs *regs) 41 { 42 unsigned int reg_zero = FIELD_GET(EX_DATA_REG_ADDR, ex->data); 43 unsigned int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); 44 45 regs->gprs[reg_err] = -EFAULT; 46 regs->gprs[reg_zero] = 0; 47 if (pair) 48 regs->gprs[reg_zero + 1] = 0; 49 regs->psw.addr = extable_fixup(ex); 50 return true; 51 } 52 53 static bool ex_handler_zeropad(const struct exception_table_entry *ex, struct pt_regs *regs) 54 { 55 unsigned int reg_addr = FIELD_GET(EX_DATA_REG_ADDR, ex->data); 56 unsigned int reg_data = FIELD_GET(EX_DATA_REG_ERR, ex->data); 57 unsigned long data, addr, offset; 58 59 addr = regs->gprs[reg_addr]; 60 offset = addr & (sizeof(unsigned long) - 1); 61 addr &= ~(sizeof(unsigned long) - 1); 62 data = *(unsigned long *)addr; 63 data <<= BITS_PER_BYTE * offset; 64 regs->gprs[reg_data] = data; 65 regs->psw.addr = extable_fixup(ex); 66 return true; 67 } 68 69 static bool ex_handler_fpc(const struct exception_table_entry *ex, struct pt_regs *regs) 70 { 71 fpu_sfpc(0); 72 regs->psw.addr = extable_fixup(ex); 73 return true; 74 } 75 76 bool fixup_exception(struct pt_regs *regs) 77 { 78 const struct exception_table_entry *ex; 79 80 ex = s390_search_extables(instruction_pointer(regs)); 81 if (!ex) 82 return false; 83 switch (ex->type) { 84 case EX_TYPE_FIXUP: 85 return ex_handler_fixup(ex, regs); 86 case EX_TYPE_BPF: 87 return ex_handler_bpf(ex, regs); 88 case EX_TYPE_UA_FAULT: 89 return ex_handler_ua_fault(ex, regs); 90 case EX_TYPE_UA_LOAD_REG: 91 return ex_handler_ua_load_reg(ex, false, regs); 92 case EX_TYPE_UA_LOAD_REGPAIR: 93 return ex_handler_ua_load_reg(ex, true, regs); 94 case EX_TYPE_ZEROPAD: 95 return ex_handler_zeropad(ex, regs); 96 case EX_TYPE_FPC: 97 return ex_handler_fpc(ex, regs); 98 } 99 panic("invalid exception table entry"); 100 } 101