Skip to content

Commit d19f5e4

Browse files
committed
parisc: Clean up fixup routines for get_user()/put_user()
Al Viro noticed that userspace accesses via get_user()/put_user() can be simplified a lot with regard to usage of the exception handling. This patch implements a fixup routine for get_user() and put_user() in such that the exception handler will automatically load -EFAULT into the register %r8 (the error value) in case on a fault on userspace. Additionally the fixup routine will zero the target register on fault in case of a get_user() call. The target register is extracted out of the faulting assembly instruction. This patch brings a few benefits over the old implementation: 1. Exception handling gets much cleaner, easier and smaller in size. 2. Helper functions like fixup_get_user_skip_1 (all of fixup.S) can be dropped. 3. No need to hardcode %r9 as target register for get_user() any longer. This helps the compiler register allocator and thus creates less assembler statements. 4. No dependency on the exception_data contents any longer. 5. Nested faults will be handled cleanly. Reported-by: Al Viro <[email protected]> Cc: <[email protected]> # v4.9+ Signed-off-by: Helge Deller <[email protected]>
1 parent 554bfec commit d19f5e4

File tree

5 files changed

+52
-134
lines changed

5 files changed

+52
-134
lines changed

arch/parisc/include/asm/uaccess.h

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ struct exception_table_entry {
6464
".word (" #fault_addr " - .), (" #except_addr " - .)\n\t" \
6565
".previous\n"
6666

67+
/*
68+
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
69+
* (with lowest bit set) for which the fault handler in fixup_exception() will
70+
* load -EFAULT into %r8 for a read or write fault, and zeroes the target
71+
* register in case of a read fault in get_user().
72+
*/
73+
#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
74+
ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1)
75+
6776
/*
6877
* The page fault handler stores, in a per-cpu area, the following information
6978
* if a fixup routine is available.
@@ -91,7 +100,7 @@ struct exception_data {
91100
#define __get_user(x, ptr) \
92101
({ \
93102
register long __gu_err __asm__ ("r8") = 0; \
94-
register long __gu_val __asm__ ("r9") = 0; \
103+
register long __gu_val; \
95104
\
96105
load_sr2(); \
97106
switch (sizeof(*(ptr))) { \
@@ -107,22 +116,23 @@ struct exception_data {
107116
})
108117

109118
#define __get_user_asm(ldx, ptr) \
110-
__asm__("\n1:\t" ldx "\t0(%%sr2,%2),%0\n\t" \
111-
ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\
119+
__asm__("1: " ldx " 0(%%sr2,%2),%0\n" \
120+
"9:\n" \
121+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
112122
: "=r"(__gu_val), "=r"(__gu_err) \
113-
: "r"(ptr), "1"(__gu_err) \
114-
: "r1");
123+
: "r"(ptr), "1"(__gu_err));
115124

116125
#if !defined(CONFIG_64BIT)
117126

118127
#define __get_user_asm64(ptr) \
119-
__asm__("\n1:\tldw 0(%%sr2,%2),%0" \
120-
"\n2:\tldw 4(%%sr2,%2),%R0\n\t" \
121-
ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_2)\
122-
ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_get_user_skip_1)\
128+
__asm__(" copy %%r0,%R0\n" \
129+
"1: ldw 0(%%sr2,%2),%0\n" \
130+
"2: ldw 4(%%sr2,%2),%R0\n" \
131+
"9:\n" \
132+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
133+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
123134
: "=r"(__gu_val), "=r"(__gu_err) \
124-
: "r"(ptr), "1"(__gu_err) \
125-
: "r1");
135+
: "r"(ptr), "1"(__gu_err));
126136

127137
#endif /* !defined(CONFIG_64BIT) */
128138

@@ -148,32 +158,31 @@ struct exception_data {
148158
* The "__put_user/kernel_asm()" macros tell gcc they read from memory
149159
* instead of writing. This is because they do not write to any memory
150160
* gcc knows about, so there are no aliasing issues. These macros must
151-
* also be aware that "fixup_put_user_skip_[12]" are executed in the
152-
* context of the fault, and any registers used there must be listed
153-
* as clobbers. In this case only "r1" is used by the current routines.
154-
* r8/r9 are already listed as err/val.
161+
* also be aware that fixups are executed in the context of the fault,
162+
* and any registers used there must be listed as clobbers.
163+
* r8 is already listed as err.
155164
*/
156165

157166
#define __put_user_asm(stx, x, ptr) \
158167
__asm__ __volatile__ ( \
159-
"\n1:\t" stx "\t%2,0(%%sr2,%1)\n\t" \
160-
ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\
168+
"1: " stx " %2,0(%%sr2,%1)\n" \
169+
"9:\n" \
170+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
161171
: "=r"(__pu_err) \
162-
: "r"(ptr), "r"(x), "0"(__pu_err) \
163-
: "r1")
172+
: "r"(ptr), "r"(x), "0"(__pu_err))
164173

165174

166175
#if !defined(CONFIG_64BIT)
167176

168177
#define __put_user_asm64(__val, ptr) do { \
169178
__asm__ __volatile__ ( \
170-
"\n1:\tstw %2,0(%%sr2,%1)" \
171-
"\n2:\tstw %R2,4(%%sr2,%1)\n\t" \
172-
ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\
173-
ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\
179+
"1: stw %2,0(%%sr2,%1)\n" \
180+
"2: stw %R2,4(%%sr2,%1)\n" \
181+
"9:\n" \
182+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
183+
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
174184
: "=r"(__pu_err) \
175-
: "r"(ptr), "r"(__val), "0"(__pu_err) \
176-
: "r1"); \
185+
: "r"(ptr), "r"(__val), "0"(__pu_err)); \
177186
} while (0)
178187

179188
#endif /* !defined(CONFIG_64BIT) */

arch/parisc/kernel/parisc_ksyms.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,6 @@ EXPORT_SYMBOL(__cmpxchg_u64);
4747
EXPORT_SYMBOL(lclear_user);
4848
EXPORT_SYMBOL(lstrnlen_user);
4949

50-
/* Global fixups - defined as int to avoid creation of function pointers */
51-
extern int fixup_get_user_skip_1;
52-
extern int fixup_get_user_skip_2;
53-
extern int fixup_put_user_skip_1;
54-
extern int fixup_put_user_skip_2;
55-
EXPORT_SYMBOL(fixup_get_user_skip_1);
56-
EXPORT_SYMBOL(fixup_get_user_skip_2);
57-
EXPORT_SYMBOL(fixup_put_user_skip_1);
58-
EXPORT_SYMBOL(fixup_put_user_skip_2);
59-
6050
#ifndef CONFIG_64BIT
6151
/* Needed so insmod can set dp value */
6252
extern int $global$;

arch/parisc/lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Makefile for parisc-specific library files
33
#
44

5-
lib-y := lusercopy.o bitops.o checksum.o io.o memset.o fixup.o memcpy.o \
5+
lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \
66
ucmpdi2.o delay.o
77

88
obj-y := iomap.o

arch/parisc/lib/fixup.S

Lines changed: 0 additions & 98 deletions
This file was deleted.

arch/parisc/mm/fault.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,23 @@ int fixup_exception(struct pt_regs *regs)
150150
d->fault_space = regs->isr;
151151
d->fault_addr = regs->ior;
152152

153+
/*
154+
* Fix up get_user() and put_user().
155+
* ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
156+
* bit in the relative address of the fixup routine to indicate
157+
* that %r8 should be loaded with -EFAULT to report a userspace
158+
* access error.
159+
*/
160+
if (fix->fixup & 1) {
161+
regs->gr[8] = -EFAULT;
162+
163+
/* zero target register for get_user() */
164+
if (parisc_acctyp(0, regs->iir) == VM_READ) {
165+
int treg = regs->iir & 0x1f;
166+
regs->gr[treg] = 0;
167+
}
168+
}
169+
153170
regs->iaoq[0] = (unsigned long)&fix->fixup + fix->fixup;
154171
regs->iaoq[0] &= ~3;
155172
/*

0 commit comments

Comments
 (0)