armv8_deprecated.c revision f2e97ae6b98e61fa4f1539a47661baef41992b04
1/*
2 *  Copyright (C) 2014 ARM Limited
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/init.h>
10#include <linux/list.h>
11#include <linux/perf_event.h>
12#include <linux/sched.h>
13#include <linux/slab.h>
14#include <linux/sysctl.h>
15
16#include <asm/insn.h>
17#include <asm/opcodes.h>
18#include <asm/system_misc.h>
19#include <asm/traps.h>
20#include <asm/uaccess.h>
21
22/*
23 * The runtime support for deprecated instruction support can be in one of
24 * following three states -
25 *
26 * 0 = undef
27 * 1 = emulate (software emulation)
28 * 2 = hw (supported in hardware)
29 */
30enum insn_emulation_mode {
31	INSN_UNDEF,
32	INSN_EMULATE,
33	INSN_HW,
34};
35
36enum legacy_insn_status {
37	INSN_DEPRECATED,
38	INSN_OBSOLETE,
39};
40
41struct insn_emulation_ops {
42	const char		*name;
43	enum legacy_insn_status	status;
44	struct undef_hook	*hooks;
45	int			(*set_hw_mode)(bool enable);
46};
47
48struct insn_emulation {
49	struct list_head node;
50	struct insn_emulation_ops *ops;
51	int current_mode;
52	int min;
53	int max;
54};
55
56static LIST_HEAD(insn_emulation);
57static int nr_insn_emulated;
58static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
59
60static void register_emulation_hooks(struct insn_emulation_ops *ops)
61{
62	struct undef_hook *hook;
63
64	BUG_ON(!ops->hooks);
65
66	for (hook = ops->hooks; hook->instr_mask; hook++)
67		register_undef_hook(hook);
68
69	pr_notice("Registered %s emulation handler\n", ops->name);
70}
71
72static void remove_emulation_hooks(struct insn_emulation_ops *ops)
73{
74	struct undef_hook *hook;
75
76	BUG_ON(!ops->hooks);
77
78	for (hook = ops->hooks; hook->instr_mask; hook++)
79		unregister_undef_hook(hook);
80
81	pr_notice("Removed %s emulation handler\n", ops->name);
82}
83
84static int update_insn_emulation_mode(struct insn_emulation *insn,
85				       enum insn_emulation_mode prev)
86{
87	int ret = 0;
88
89	switch (prev) {
90	case INSN_UNDEF: /* Nothing to be done */
91		break;
92	case INSN_EMULATE:
93		remove_emulation_hooks(insn->ops);
94		break;
95	case INSN_HW:
96		if (insn->ops->set_hw_mode) {
97			insn->ops->set_hw_mode(false);
98			pr_notice("Disabled %s support\n", insn->ops->name);
99		}
100		break;
101	}
102
103	switch (insn->current_mode) {
104	case INSN_UNDEF:
105		break;
106	case INSN_EMULATE:
107		register_emulation_hooks(insn->ops);
108		break;
109	case INSN_HW:
110		if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true))
111			pr_notice("Enabled %s support\n", insn->ops->name);
112		else
113			ret = -EINVAL;
114		break;
115	}
116
117	return ret;
118}
119
120static void register_insn_emulation(struct insn_emulation_ops *ops)
121{
122	unsigned long flags;
123	struct insn_emulation *insn;
124
125	insn = kzalloc(sizeof(*insn), GFP_KERNEL);
126	insn->ops = ops;
127	insn->min = INSN_UNDEF;
128
129	switch (ops->status) {
130	case INSN_DEPRECATED:
131		insn->current_mode = INSN_EMULATE;
132		insn->max = INSN_HW;
133		break;
134	case INSN_OBSOLETE:
135		insn->current_mode = INSN_UNDEF;
136		insn->max = INSN_EMULATE;
137		break;
138	}
139
140	raw_spin_lock_irqsave(&insn_emulation_lock, flags);
141	list_add(&insn->node, &insn_emulation);
142	nr_insn_emulated++;
143	raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
144
145	/* Register any handlers if required */
146	update_insn_emulation_mode(insn, INSN_UNDEF);
147}
148
149static int emulation_proc_handler(struct ctl_table *table, int write,
150				  void __user *buffer, size_t *lenp,
151				  loff_t *ppos)
152{
153	int ret = 0;
154	struct insn_emulation *insn = (struct insn_emulation *) table->data;
155	enum insn_emulation_mode prev_mode = insn->current_mode;
156
157	table->data = &insn->current_mode;
158	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
159
160	if (ret || !write || prev_mode == insn->current_mode)
161		goto ret;
162
163	ret = update_insn_emulation_mode(insn, prev_mode);
164	if (!ret) {
165		/* Mode change failed, revert to previous mode. */
166		insn->current_mode = prev_mode;
167		update_insn_emulation_mode(insn, INSN_UNDEF);
168	}
169ret:
170	table->data = insn;
171	return ret;
172}
173
174static struct ctl_table ctl_abi[] = {
175	{
176		.procname = "abi",
177		.mode = 0555,
178	},
179	{ }
180};
181
182static void register_insn_emulation_sysctl(struct ctl_table *table)
183{
184	unsigned long flags;
185	int i = 0;
186	struct insn_emulation *insn;
187	struct ctl_table *insns_sysctl, *sysctl;
188
189	insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1),
190			      GFP_KERNEL);
191
192	raw_spin_lock_irqsave(&insn_emulation_lock, flags);
193	list_for_each_entry(insn, &insn_emulation, node) {
194		sysctl = &insns_sysctl[i];
195
196		sysctl->mode = 0644;
197		sysctl->maxlen = sizeof(int);
198
199		sysctl->procname = insn->ops->name;
200		sysctl->data = insn;
201		sysctl->extra1 = &insn->min;
202		sysctl->extra2 = &insn->max;
203		sysctl->proc_handler = emulation_proc_handler;
204		i++;
205	}
206	raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
207
208	table->child = insns_sysctl;
209	register_sysctl_table(table);
210}
211
212/*
213 *  Implement emulation of the SWP/SWPB instructions using load-exclusive and
214 *  store-exclusive.
215 *
216 *  Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
217 *  Where: Rt  = destination
218 *	   Rt2 = source
219 *	   Rn  = address
220 */
221
222/*
223 * Error-checking SWP macros implemented using ldxr{b}/stxr{b}
224 */
225#define __user_swpX_asm(data, addr, res, temp, B)		\
226	__asm__ __volatile__(					\
227	"	mov		%w2, %w1\n"			\
228	"0:	ldxr"B"		%w1, [%3]\n"			\
229	"1:	stxr"B"		%w0, %w2, [%3]\n"		\
230	"	cbz		%w0, 2f\n"			\
231	"	mov		%w0, %w4\n"			\
232	"2:\n"							\
233	"	.pushsection	 .fixup,\"ax\"\n"		\
234	"	.align		2\n"				\
235	"3:	mov		%w0, %w5\n"			\
236	"	b		2b\n"				\
237	"	.popsection"					\
238	"	.pushsection	 __ex_table,\"a\"\n"		\
239	"	.align		3\n"				\
240	"	.quad		0b, 3b\n"			\
241	"	.quad		1b, 3b\n"			\
242	"	.popsection"					\
243	: "=&r" (res), "+r" (data), "=&r" (temp)		\
244	: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT)		\
245	: "memory")
246
247#define __user_swp_asm(data, addr, res, temp) \
248	__user_swpX_asm(data, addr, res, temp, "")
249#define __user_swpb_asm(data, addr, res, temp) \
250	__user_swpX_asm(data, addr, res, temp, "b")
251
252/*
253 * Bit 22 of the instruction encoding distinguishes between
254 * the SWP and SWPB variants (bit set means SWPB).
255 */
256#define TYPE_SWPB (1 << 22)
257
258/*
259 * Set up process info to signal segmentation fault - called on access error.
260 */
261static void set_segfault(struct pt_regs *regs, unsigned long addr)
262{
263	siginfo_t info;
264
265	down_read(&current->mm->mmap_sem);
266	if (find_vma(current->mm, addr) == NULL)
267		info.si_code = SEGV_MAPERR;
268	else
269		info.si_code = SEGV_ACCERR;
270	up_read(&current->mm->mmap_sem);
271
272	info.si_signo = SIGSEGV;
273	info.si_errno = 0;
274	info.si_addr  = (void *) instruction_pointer(regs);
275
276	pr_debug("SWP{B} emulation: access caused memory abort!\n");
277	arm64_notify_die("Illegal memory access", regs, &info, 0);
278}
279
280static int emulate_swpX(unsigned int address, unsigned int *data,
281			unsigned int type)
282{
283	unsigned int res = 0;
284
285	if ((type != TYPE_SWPB) && (address & 0x3)) {
286		/* SWP to unaligned address not permitted */
287		pr_debug("SWP instruction on unaligned pointer!\n");
288		return -EFAULT;
289	}
290
291	while (1) {
292		unsigned long temp;
293
294		if (type == TYPE_SWPB)
295			__user_swpb_asm(*data, address, res, temp);
296		else
297			__user_swp_asm(*data, address, res, temp);
298
299		if (likely(res != -EAGAIN) || signal_pending(current))
300			break;
301
302		cond_resched();
303	}
304
305	return res;
306}
307
308/*
309 * swp_handler logs the id of calling process, dissects the instruction, sanity
310 * checks the memory location, calls emulate_swpX for the actual operation and
311 * deals with fixup/error handling before returning
312 */
313static int swp_handler(struct pt_regs *regs, u32 instr)
314{
315	u32 destreg, data, type, address = 0;
316	int rn, rt2, res = 0;
317
318	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
319
320	type = instr & TYPE_SWPB;
321
322	switch (arm_check_condition(instr, regs->pstate)) {
323	case ARM_OPCODE_CONDTEST_PASS:
324		break;
325	case ARM_OPCODE_CONDTEST_FAIL:
326		/* Condition failed - return to next instruction */
327		goto ret;
328	case ARM_OPCODE_CONDTEST_UNCOND:
329		/* If unconditional encoding - not a SWP, undef */
330		return -EFAULT;
331	default:
332		return -EINVAL;
333	}
334
335	rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET);
336	rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET);
337
338	address = (u32)regs->user_regs.regs[rn];
339	data	= (u32)regs->user_regs.regs[rt2];
340	destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET);
341
342	pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
343		rn, address, destreg,
344		aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data);
345
346	/* Check access in reasonable access range for both SWP and SWPB */
347	if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
348		pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n",
349			address);
350		goto fault;
351	}
352
353	res = emulate_swpX(address, &data, type);
354	if (res == -EFAULT)
355		goto fault;
356	else if (res == 0)
357		regs->user_regs.regs[destreg] = data;
358
359ret:
360	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
361			current->comm, (unsigned long)current->pid, regs->pc);
362
363	regs->pc += 4;
364	return 0;
365
366fault:
367	set_segfault(regs, address);
368
369	return 0;
370}
371
372/*
373 * Only emulate SWP/SWPB executed in ARM state/User mode.
374 * The kernel must be SWP free and SWP{B} does not exist in Thumb.
375 */
376static struct undef_hook swp_hooks[] = {
377	{
378		.instr_mask	= 0x0fb00ff0,
379		.instr_val	= 0x01000090,
380		.pstate_mask	= COMPAT_PSR_MODE_MASK,
381		.pstate_val	= COMPAT_PSR_MODE_USR,
382		.fn		= swp_handler
383	},
384	{ }
385};
386
387static struct insn_emulation_ops swp_ops = {
388	.name = "swp",
389	.status = INSN_OBSOLETE,
390	.hooks = swp_hooks,
391	.set_hw_mode = NULL,
392};
393
394/*
395 * Invoked as late_initcall, since not needed before init spawned.
396 */
397static int __init armv8_deprecated_init(void)
398{
399	if (IS_ENABLED(CONFIG_SWP_EMULATION))
400		register_insn_emulation(&swp_ops);
401
402	register_insn_emulation_sysctl(ctl_abi);
403
404	return 0;
405}
406
407late_initcall(armv8_deprecated_init);
408