FILE
extable.c
File preview with syntax highlighting.
linux-6.19.14/arch/s390/mm/extable.c
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 12const 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 24static 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 30static 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 39static 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 53static 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 69static 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 76struct 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 86static 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 119bool 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}
~/
Read-only file preview.