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_fpc(const struct exception_table_entry *ex, struct pt_regs *regs) 54 { 55 fpu_sfpc(0); 56 regs->psw.addr = extable_fixup(ex); 57 return true; 58 } 59 60 struct insn_ssf { 61 u64 opc1 : 8; 62 u64 r3 : 4; 63 u64 opc2 : 4; 64 u64 b1 : 4; 65 u64 d1 : 12; 66 u64 b2 : 4; 67 u64 d2 : 12; 68 } __packed; 69 70 static bool ex_handler_ua_mvcos(const struct exception_table_entry *ex, 71 bool from, struct pt_regs *regs) 72 { 73 unsigned long uaddr, remainder; 74 struct insn_ssf *insn; 75 76 /* 77 * If the faulting user space access crossed a page boundary retry by 78 * limiting the access to the first page (adjust length accordingly). 79 * Then the mvcos instruction will either complete with condition code 80 * zero, or generate another fault where the user space access did not 81 * cross a page boundary. 82 * If the faulting user space access did not cross a page boundary set 83 * length to zero and retry. In this case no user space access will 84 * happen, and the mvcos instruction will complete with condition code 85 * zero. 86 * In both cases the instruction will complete with condition code 87 * zero (copying finished), and the register which contains the 88 * length, indicates the number of bytes copied. 89 */ 90 regs->psw.addr = extable_fixup(ex); 91 insn = (struct insn_ssf *)regs->psw.addr; 92 if (from) 93 uaddr = regs->gprs[insn->b2] + insn->d2; 94 else 95 uaddr = regs->gprs[insn->b1] + insn->d1; 96 remainder = PAGE_SIZE - (uaddr & (PAGE_SIZE - 1)); 97 if (regs->gprs[insn->r3] <= remainder) 98 remainder = 0; 99 regs->gprs[insn->r3] = remainder; 100 return true; 101 } 102 103 bool fixup_exception(struct pt_regs *regs) 104 { 105 const struct exception_table_entry *ex; 106 107 ex = s390_search_extables(instruction_pointer(regs)); 108 if (!ex) 109 return false; 110 switch (ex->type) { 111 case EX_TYPE_FIXUP: 112 return ex_handler_fixup(ex, regs); 113 case EX_TYPE_BPF: 114 return ex_handler_bpf(ex, regs); 115 case EX_TYPE_UA_FAULT: 116 return ex_handler_ua_fault(ex, regs); 117 case EX_TYPE_UA_LOAD_REG: 118 return ex_handler_ua_load_reg(ex, false, regs); 119 case EX_TYPE_UA_LOAD_REGPAIR: 120 return ex_handler_ua_load_reg(ex, true, regs); 121 case EX_TYPE_FPC: 122 return ex_handler_fpc(ex, regs); 123 case EX_TYPE_UA_MVCOS_TO: 124 return ex_handler_ua_mvcos(ex, false, regs); 125 case EX_TYPE_UA_MVCOS_FROM: 126 return ex_handler_ua_mvcos(ex, true, regs); 127 } 128 panic("invalid exception table entry"); 129 } 130