Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/mm/extable.c
26444 views
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
149