File Index
Files and directories
Sign in
FILE

extable.c

File preview with syntax highlighting.

linux-6.19.14/arch/s390/mm/extable.c 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.