aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHelge Deller <deller@gmx.de>2017-03-25 06:59:15 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 03:30:34 -0400
commit09b931fcb87c8aad178475a7db1d4bfc939f7faa (patch)
tree077051ef17e98a0655d3690508c30dda5ec30974
parent3967cf7e6a9180e09b42ecc154731a570efafa49 (diff)
parisc: Clean up fixup routines for get_user()/put_user()
commit d19f5e41b344a057bb2450024a807476f30978d2 upstream. 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 <viro@ZenIV.linux.org.uk> Signed-off-by: Helge Deller <deller@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/parisc/include/asm/uaccess.h59
-rw-r--r--arch/parisc/kernel/parisc_ksyms.c10
-rw-r--r--arch/parisc/lib/Makefile2
-rw-r--r--arch/parisc/lib/fixup.S98
-rw-r--r--arch/parisc/mm/fault.c17
5 files changed, 52 insertions, 134 deletions
diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index 9a2aee1b90fc..7fcf5128996a 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -68,6 +68,15 @@ struct exception_table_entry {
68 ".previous\n" 68 ".previous\n"
69 69
70/* 70/*
71 * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() creates a special exception table entry
72 * (with lowest bit set) for which the fault handler in fixup_exception() will
73 * load -EFAULT into %r8 for a read or write fault, and zeroes the target
74 * register in case of a read fault in get_user().
75 */
76#define ASM_EXCEPTIONTABLE_ENTRY_EFAULT( fault_addr, except_addr )\
77 ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr + 1)
78
79/*
71 * The page fault handler stores, in a per-cpu area, the following information 80 * The page fault handler stores, in a per-cpu area, the following information
72 * if a fixup routine is available. 81 * if a fixup routine is available.
73 */ 82 */
@@ -94,7 +103,7 @@ struct exception_data {
94#define __get_user(x, ptr) \ 103#define __get_user(x, ptr) \
95({ \ 104({ \
96 register long __gu_err __asm__ ("r8") = 0; \ 105 register long __gu_err __asm__ ("r8") = 0; \
97 register long __gu_val __asm__ ("r9") = 0; \ 106 register long __gu_val; \
98 \ 107 \
99 load_sr2(); \ 108 load_sr2(); \
100 switch (sizeof(*(ptr))) { \ 109 switch (sizeof(*(ptr))) { \
@@ -110,22 +119,23 @@ struct exception_data {
110}) 119})
111 120
112#define __get_user_asm(ldx, ptr) \ 121#define __get_user_asm(ldx, ptr) \
113 __asm__("\n1:\t" ldx "\t0(%%sr2,%2),%0\n\t" \ 122 __asm__("1: " ldx " 0(%%sr2,%2),%0\n" \
114 ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_1)\ 123 "9:\n" \
124 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
115 : "=r"(__gu_val), "=r"(__gu_err) \ 125 : "=r"(__gu_val), "=r"(__gu_err) \
116 : "r"(ptr), "1"(__gu_err) \ 126 : "r"(ptr), "1"(__gu_err));
117 : "r1");
118 127
119#if !defined(CONFIG_64BIT) 128#if !defined(CONFIG_64BIT)
120 129
121#define __get_user_asm64(ptr) \ 130#define __get_user_asm64(ptr) \
122 __asm__("\n1:\tldw 0(%%sr2,%2),%0" \ 131 __asm__(" copy %%r0,%R0\n" \
123 "\n2:\tldw 4(%%sr2,%2),%R0\n\t" \ 132 "1: ldw 0(%%sr2,%2),%0\n" \
124 ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_get_user_skip_2)\ 133 "2: ldw 4(%%sr2,%2),%R0\n" \
125 ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_get_user_skip_1)\ 134 "9:\n" \
135 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
136 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
126 : "=r"(__gu_val), "=r"(__gu_err) \ 137 : "=r"(__gu_val), "=r"(__gu_err) \
127 : "r"(ptr), "1"(__gu_err) \ 138 : "r"(ptr), "1"(__gu_err));
128 : "r1");
129 139
130#endif /* !defined(CONFIG_64BIT) */ 140#endif /* !defined(CONFIG_64BIT) */
131 141
@@ -151,32 +161,31 @@ struct exception_data {
151 * The "__put_user/kernel_asm()" macros tell gcc they read from memory 161 * The "__put_user/kernel_asm()" macros tell gcc they read from memory
152 * instead of writing. This is because they do not write to any memory 162 * instead of writing. This is because they do not write to any memory
153 * gcc knows about, so there are no aliasing issues. These macros must 163 * gcc knows about, so there are no aliasing issues. These macros must
154 * also be aware that "fixup_put_user_skip_[12]" are executed in the 164 * also be aware that fixups are executed in the context of the fault,
155 * context of the fault, and any registers used there must be listed 165 * and any registers used there must be listed as clobbers.
156 * as clobbers. In this case only "r1" is used by the current routines. 166 * r8 is already listed as err.
157 * r8/r9 are already listed as err/val.
158 */ 167 */
159 168
160#define __put_user_asm(stx, x, ptr) \ 169#define __put_user_asm(stx, x, ptr) \
161 __asm__ __volatile__ ( \ 170 __asm__ __volatile__ ( \
162 "\n1:\t" stx "\t%2,0(%%sr2,%1)\n\t" \ 171 "1: " stx " %2,0(%%sr2,%1)\n" \
163 ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_1)\ 172 "9:\n" \
173 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
164 : "=r"(__pu_err) \ 174 : "=r"(__pu_err) \
165 : "r"(ptr), "r"(x), "0"(__pu_err) \ 175 : "r"(ptr), "r"(x), "0"(__pu_err))
166 : "r1")
167 176
168 177
169#if !defined(CONFIG_64BIT) 178#if !defined(CONFIG_64BIT)
170 179
171#define __put_user_asm64(__val, ptr) do { \ 180#define __put_user_asm64(__val, ptr) do { \
172 __asm__ __volatile__ ( \ 181 __asm__ __volatile__ ( \
173 "\n1:\tstw %2,0(%%sr2,%1)" \ 182 "1: stw %2,0(%%sr2,%1)\n" \
174 "\n2:\tstw %R2,4(%%sr2,%1)\n\t" \ 183 "2: stw %R2,4(%%sr2,%1)\n" \
175 ASM_EXCEPTIONTABLE_ENTRY(1b, fixup_put_user_skip_2)\ 184 "9:\n" \
176 ASM_EXCEPTIONTABLE_ENTRY(2b, fixup_put_user_skip_1)\ 185 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
186 ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
177 : "=r"(__pu_err) \ 187 : "=r"(__pu_err) \
178 : "r"(ptr), "r"(__val), "0"(__pu_err) \ 188 : "r"(ptr), "r"(__val), "0"(__pu_err)); \
179 : "r1"); \
180} while (0) 189} while (0)
181 190
182#endif /* !defined(CONFIG_64BIT) */ 191#endif /* !defined(CONFIG_64BIT) */
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index 3cad8aadc69e..4e6f0d93154f 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -47,16 +47,6 @@ EXPORT_SYMBOL(__cmpxchg_u64);
47EXPORT_SYMBOL(lclear_user); 47EXPORT_SYMBOL(lclear_user);
48EXPORT_SYMBOL(lstrnlen_user); 48EXPORT_SYMBOL(lstrnlen_user);
49 49
50/* Global fixups - defined as int to avoid creation of function pointers */
51extern int fixup_get_user_skip_1;
52extern int fixup_get_user_skip_2;
53extern int fixup_put_user_skip_1;
54extern int fixup_put_user_skip_2;
55EXPORT_SYMBOL(fixup_get_user_skip_1);
56EXPORT_SYMBOL(fixup_get_user_skip_2);
57EXPORT_SYMBOL(fixup_put_user_skip_1);
58EXPORT_SYMBOL(fixup_put_user_skip_2);
59
60#ifndef CONFIG_64BIT 50#ifndef CONFIG_64BIT
61/* Needed so insmod can set dp value */ 51/* Needed so insmod can set dp value */
62extern int $global$; 52extern int $global$;
diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile
index 8fa92b8d839a..f2dac4d73b1b 100644
--- a/arch/parisc/lib/Makefile
+++ b/arch/parisc/lib/Makefile
@@ -2,7 +2,7 @@
2# Makefile for parisc-specific library files 2# Makefile for parisc-specific library files
3# 3#
4 4
5lib-y := lusercopy.o bitops.o checksum.o io.o memset.o fixup.o memcpy.o \ 5lib-y := lusercopy.o bitops.o checksum.o io.o memset.o memcpy.o \
6 ucmpdi2.o delay.o 6 ucmpdi2.o delay.o
7 7
8obj-y := iomap.o 8obj-y := iomap.o
diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S
deleted file mode 100644
index a5b72f22c7a6..000000000000
--- a/arch/parisc/lib/fixup.S
+++ /dev/null
@@ -1,98 +0,0 @@
1/*
2 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
3 *
4 * Copyright (C) 2004 Randolph Chung <tausq@debian.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Fixup routines for kernel exception handling.
21 */
22#include <asm/asm-offsets.h>
23#include <asm/assembly.h>
24#include <asm/errno.h>
25#include <linux/linkage.h>
26
27#ifdef CONFIG_SMP
28 .macro get_fault_ip t1 t2
29 loadgp
30 addil LT%__per_cpu_offset,%r27
31 LDREG RT%__per_cpu_offset(%r1),\t1
32 /* t2 = smp_processor_id() */
33 mfctl 30,\t2
34 ldw TI_CPU(\t2),\t2
35#ifdef CONFIG_64BIT
36 extrd,u \t2,63,32,\t2
37#endif
38 /* t2 = &__per_cpu_offset[smp_processor_id()]; */
39 LDREGX \t2(\t1),\t2
40 addil LT%exception_data,%r27
41 LDREG RT%exception_data(%r1),\t1
42 /* t1 = this_cpu_ptr(&exception_data) */
43 add,l \t1,\t2,\t1
44 /* %r27 = t1->fault_gp - restore gp */
45 LDREG EXCDATA_GP(\t1), %r27
46 /* t1 = t1->fault_ip */
47 LDREG EXCDATA_IP(\t1), \t1
48 .endm
49#else
50 .macro get_fault_ip t1 t2
51 loadgp
52 /* t1 = this_cpu_ptr(&exception_data) */
53 addil LT%exception_data,%r27
54 LDREG RT%exception_data(%r1),\t2
55 /* %r27 = t2->fault_gp - restore gp */
56 LDREG EXCDATA_GP(\t2), %r27
57 /* t1 = t2->fault_ip */
58 LDREG EXCDATA_IP(\t2), \t1
59 .endm
60#endif
61
62 .level LEVEL
63
64 .text
65 .section .fixup, "ax"
66
67 /* get_user() fixups, store -EFAULT in r8, and 0 in r9 */
68ENTRY_CFI(fixup_get_user_skip_1)
69 get_fault_ip %r1,%r8
70 ldo 4(%r1), %r1
71 ldi -EFAULT, %r8
72 bv %r0(%r1)
73 copy %r0, %r9
74ENDPROC_CFI(fixup_get_user_skip_1)
75
76ENTRY_CFI(fixup_get_user_skip_2)
77 get_fault_ip %r1,%r8
78 ldo 8(%r1), %r1
79 ldi -EFAULT, %r8
80 bv %r0(%r1)
81 copy %r0, %r9
82ENDPROC_CFI(fixup_get_user_skip_2)
83
84 /* put_user() fixups, store -EFAULT in r8 */
85ENTRY_CFI(fixup_put_user_skip_1)
86 get_fault_ip %r1,%r8
87 ldo 4(%r1), %r1
88 bv %r0(%r1)
89 ldi -EFAULT, %r8
90ENDPROC_CFI(fixup_put_user_skip_1)
91
92ENTRY_CFI(fixup_put_user_skip_2)
93 get_fault_ip %r1,%r8
94 ldo 8(%r1), %r1
95 bv %r0(%r1)
96 ldi -EFAULT, %r8
97ENDPROC_CFI(fixup_put_user_skip_2)
98
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index 1a0b4f63f0e9..040c48fc5391 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -149,6 +149,23 @@ int fixup_exception(struct pt_regs *regs)
149 d->fault_space = regs->isr; 149 d->fault_space = regs->isr;
150 d->fault_addr = regs->ior; 150 d->fault_addr = regs->ior;
151 151
152 /*
153 * Fix up get_user() and put_user().
154 * ASM_EXCEPTIONTABLE_ENTRY_EFAULT() sets the least-significant
155 * bit in the relative address of the fixup routine to indicate
156 * that %r8 should be loaded with -EFAULT to report a userspace
157 * access error.
158 */
159 if (fix->fixup & 1) {
160 regs->gr[8] = -EFAULT;
161
162 /* zero target register for get_user() */
163 if (parisc_acctyp(0, regs->iir) == VM_READ) {
164 int treg = regs->iir & 0x1f;
165 regs->gr[treg] = 0;
166 }
167 }
168
152 regs->iaoq[0] = (unsigned long)&fix->fixup + fix->fixup; 169 regs->iaoq[0] = (unsigned long)&fix->fixup + fix->fixup;
153 regs->iaoq[0] &= ~3; 170 regs->iaoq[0] &= ~3;
154 /* 171 /*