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