Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * linux/arch/arm/kernel/swp_emulate.c
4 : : *
5 : : * Copyright (C) 2009 ARM Limited
6 : : * __user_* functions adapted from include/asm/uaccess.h
7 : : *
8 : : * Implements emulation of the SWP/SWPB instructions using load-exclusive and
9 : : * store-exclusive for processors that have them disabled (or future ones that
10 : : * might not implement them).
11 : : *
12 : : * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
13 : : * Where: Rt = destination
14 : : * Rt2 = source
15 : : * Rn = address
16 : : */
17 : :
18 : : #include <linux/init.h>
19 : : #include <linux/kernel.h>
20 : : #include <linux/proc_fs.h>
21 : : #include <linux/seq_file.h>
22 : : #include <linux/sched.h>
23 : : #include <linux/sched/mm.h>
24 : : #include <linux/syscalls.h>
25 : : #include <linux/perf_event.h>
26 : :
27 : : #include <asm/opcodes.h>
28 : : #include <asm/system_info.h>
29 : : #include <asm/traps.h>
30 : : #include <linux/uaccess.h>
31 : :
32 : : /*
33 : : * Error-checking SWP macros implemented using ldrex{b}/strex{b}
34 : : */
35 : : #define __user_swpX_asm(data, addr, res, temp, B) \
36 : : __asm__ __volatile__( \
37 : : "0: ldrex"B" %2, [%3]\n" \
38 : : "1: strex"B" %0, %1, [%3]\n" \
39 : : " cmp %0, #0\n" \
40 : : " moveq %1, %2\n" \
41 : : " movne %0, %4\n" \
42 : : "2:\n" \
43 : : " .section .text.fixup,\"ax\"\n" \
44 : : " .align 2\n" \
45 : : "3: mov %0, %5\n" \
46 : : " b 2b\n" \
47 : : " .previous\n" \
48 : : " .section __ex_table,\"a\"\n" \
49 : : " .align 3\n" \
50 : : " .long 0b, 3b\n" \
51 : : " .long 1b, 3b\n" \
52 : : " .previous" \
53 : : : "=&r" (res), "+r" (data), "=&r" (temp) \
54 : : : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
55 : : : "cc", "memory")
56 : :
57 : : #define __user_swp_asm(data, addr, res, temp) \
58 : : __user_swpX_asm(data, addr, res, temp, "")
59 : : #define __user_swpb_asm(data, addr, res, temp) \
60 : : __user_swpX_asm(data, addr, res, temp, "b")
61 : :
62 : : /*
63 : : * Macros/defines for extracting register numbers from instruction.
64 : : */
65 : : #define EXTRACT_REG_NUM(instruction, offset) \
66 : : (((instruction) & (0xf << (offset))) >> (offset))
67 : : #define RN_OFFSET 16
68 : : #define RT_OFFSET 12
69 : : #define RT2_OFFSET 0
70 : : /*
71 : : * Bit 22 of the instruction encoding distinguishes between
72 : : * the SWP and SWPB variants (bit set means SWPB).
73 : : */
74 : : #define TYPE_SWPB (1 << 22)
75 : :
76 : : static unsigned long swpcounter;
77 : : static unsigned long swpbcounter;
78 : : static unsigned long abtcounter;
79 : : static pid_t previous_pid;
80 : :
81 : : #ifdef CONFIG_PROC_FS
82 : 0 : static int proc_status_show(struct seq_file *m, void *v)
83 : : {
84 : 0 : seq_printf(m, "Emulated SWP:\t\t%lu\n", swpcounter);
85 : 0 : seq_printf(m, "Emulated SWPB:\t\t%lu\n", swpbcounter);
86 : 0 : seq_printf(m, "Aborted SWP{B}:\t\t%lu\n", abtcounter);
87 : 0 : if (previous_pid != 0)
88 : 0 : seq_printf(m, "Last process:\t\t%d\n", previous_pid);
89 : 0 : return 0;
90 : : }
91 : : #endif
92 : :
93 : : /*
94 : : * Set up process info to signal segmentation fault - called on access error.
95 : : */
96 : 0 : static void set_segfault(struct pt_regs *regs, unsigned long addr)
97 : : {
98 : : int si_code;
99 : :
100 : 0 : down_read(¤t->mm->mmap_sem);
101 : 0 : if (find_vma(current->mm, addr) == NULL)
102 : : si_code = SEGV_MAPERR;
103 : : else
104 : : si_code = SEGV_ACCERR;
105 : 0 : up_read(¤t->mm->mmap_sem);
106 : :
107 : : pr_debug("SWP{B} emulation: access caused memory abort!\n");
108 : 0 : arm_notify_die("Illegal memory access", regs,
109 : : SIGSEGV, si_code,
110 : 0 : (void __user *)instruction_pointer(regs),
111 : : 0, 0);
112 : :
113 : 0 : abtcounter++;
114 : 0 : }
115 : :
116 : 0 : static int emulate_swpX(unsigned int address, unsigned int *data,
117 : : unsigned int type)
118 : : {
119 : : unsigned int res = 0;
120 : :
121 : 0 : if ((type != TYPE_SWPB) && (address & 0x3)) {
122 : : /* SWP to unaligned address not permitted */
123 : : pr_debug("SWP instruction on unaligned pointer!\n");
124 : : return -EFAULT;
125 : : }
126 : :
127 : : while (1) {
128 : : unsigned long temp;
129 : : unsigned int __ua_flags;
130 : :
131 : : __ua_flags = uaccess_save_and_enable();
132 : 0 : if (type == TYPE_SWPB)
133 : 0 : __user_swpb_asm(*data, address, res, temp);
134 : : else
135 : 0 : __user_swp_asm(*data, address, res, temp);
136 : : uaccess_restore(__ua_flags);
137 : :
138 : 0 : if (likely(res != -EAGAIN) || signal_pending(current))
139 : : break;
140 : :
141 : 0 : cond_resched();
142 : 0 : }
143 : :
144 : 0 : if (res == 0) {
145 : 0 : if (type == TYPE_SWPB)
146 : 0 : swpbcounter++;
147 : : else
148 : 0 : swpcounter++;
149 : : }
150 : :
151 : 0 : return res;
152 : : }
153 : :
154 : : /*
155 : : * swp_handler logs the id of calling process, dissects the instruction, sanity
156 : : * checks the memory location, calls emulate_swpX for the actual operation and
157 : : * deals with fixup/error handling before returning
158 : : */
159 : 0 : static int swp_handler(struct pt_regs *regs, unsigned int instr)
160 : : {
161 : : unsigned int address, destreg, data, type;
162 : : unsigned int res = 0;
163 : :
164 : 0 : perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
165 : :
166 : 0 : res = arm_check_condition(instr, regs->ARM_cpsr);
167 : 0 : switch (res) {
168 : : case ARM_OPCODE_CONDTEST_PASS:
169 : : break;
170 : : case ARM_OPCODE_CONDTEST_FAIL:
171 : : /* Condition failed - return to next instruction */
172 : 0 : regs->ARM_pc += 4;
173 : 0 : return 0;
174 : : case ARM_OPCODE_CONDTEST_UNCOND:
175 : : /* If unconditional encoding - not a SWP, undef */
176 : : return -EFAULT;
177 : : default:
178 : 0 : return -EINVAL;
179 : : }
180 : :
181 : 0 : if (current->pid != previous_pid) {
182 : : pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
183 : : current->comm, (unsigned long)current->pid);
184 : 0 : previous_pid = current->pid;
185 : : }
186 : :
187 : 0 : address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
188 : 0 : data = regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
189 : 0 : destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
190 : :
191 : 0 : type = instr & TYPE_SWPB;
192 : :
193 : : pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
194 : : EXTRACT_REG_NUM(instr, RN_OFFSET), address,
195 : : destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
196 : :
197 : : /* Check access in reasonable access range for both SWP and SWPB */
198 : 0 : if (!access_ok((address & ~3), 4)) {
199 : : pr_debug("SWP{B} emulation: access to %p not allowed!\n",
200 : : (void *)address);
201 : : res = -EFAULT;
202 : : } else {
203 : 0 : res = emulate_swpX(address, &data, type);
204 : : }
205 : :
206 : 0 : if (res == 0) {
207 : : /*
208 : : * On successful emulation, revert the adjustment to the PC
209 : : * made in kernel/traps.c in order to resume execution at the
210 : : * instruction following the SWP{B}.
211 : : */
212 : 0 : regs->ARM_pc += 4;
213 : 0 : regs->uregs[destreg] = data;
214 : 0 : } else if (res == -EFAULT) {
215 : : /*
216 : : * Memory errors do not mean emulation failed.
217 : : * Set up signal info to return SEGV, then return OK
218 : : */
219 : 0 : set_segfault(regs, address);
220 : : }
221 : :
222 : : return 0;
223 : : }
224 : :
225 : : /*
226 : : * Only emulate SWP/SWPB executed in ARM state/User mode.
227 : : * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
228 : : */
229 : : static struct undef_hook swp_hook = {
230 : : .instr_mask = 0x0fb00ff0,
231 : : .instr_val = 0x01000090,
232 : : .cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
233 : : .cpsr_val = USR_MODE,
234 : : .fn = swp_handler
235 : : };
236 : :
237 : : /*
238 : : * Register handler and create status file in /proc/cpu
239 : : * Invoked as late_initcall, since not needed before init spawned.
240 : : */
241 : 3 : static int __init swp_emulation_init(void)
242 : : {
243 : 3 : if (cpu_architecture() < CPU_ARCH_ARMv7)
244 : : return 0;
245 : :
246 : : #ifdef CONFIG_PROC_FS
247 : 3 : if (!proc_create_single("cpu/swp_emulation", S_IRUGO, NULL,
248 : : proc_status_show))
249 : : return -ENOMEM;
250 : : #endif /* CONFIG_PROC_FS */
251 : :
252 : 3 : pr_notice("Registering SWP/SWPB emulation handler\n");
253 : 3 : register_undef_hook(&swp_hook);
254 : :
255 : 3 : return 0;
256 : : }
257 : :
258 : : late_initcall(swp_emulation_init);
|