1587064b6SPunit Agrawal /*
2587064b6SPunit Agrawal  *  Copyright (C) 2014 ARM Limited
3587064b6SPunit Agrawal  *
4587064b6SPunit Agrawal  * This program is free software; you can redistribute it and/or modify
5587064b6SPunit Agrawal  * it under the terms of the GNU General Public License version 2 as
6587064b6SPunit Agrawal  * published by the Free Software Foundation.
7587064b6SPunit Agrawal  */
8587064b6SPunit Agrawal 
9c852f320SPunit Agrawal #include <linux/cpu.h>
10587064b6SPunit Agrawal #include <linux/init.h>
11587064b6SPunit Agrawal #include <linux/list.h>
12bd35a4adSPunit Agrawal #include <linux/perf_event.h>
13bd35a4adSPunit Agrawal #include <linux/sched.h>
14587064b6SPunit Agrawal #include <linux/slab.h>
15587064b6SPunit Agrawal #include <linux/sysctl.h>
16587064b6SPunit Agrawal 
17bd35a4adSPunit Agrawal #include <asm/insn.h>
18bd35a4adSPunit Agrawal #include <asm/opcodes.h>
19bd35a4adSPunit Agrawal #include <asm/system_misc.h>
20587064b6SPunit Agrawal #include <asm/traps.h>
21bd35a4adSPunit Agrawal #include <asm/uaccess.h>
22587064b6SPunit Agrawal 
23587064b6SPunit Agrawal /*
24587064b6SPunit Agrawal  * The runtime support for deprecated instruction support can be in one of
25587064b6SPunit Agrawal  * following three states -
26587064b6SPunit Agrawal  *
27587064b6SPunit Agrawal  * 0 = undef
28587064b6SPunit Agrawal  * 1 = emulate (software emulation)
29587064b6SPunit Agrawal  * 2 = hw (supported in hardware)
30587064b6SPunit Agrawal  */
31587064b6SPunit Agrawal enum insn_emulation_mode {
32587064b6SPunit Agrawal 	INSN_UNDEF,
33587064b6SPunit Agrawal 	INSN_EMULATE,
34587064b6SPunit Agrawal 	INSN_HW,
35587064b6SPunit Agrawal };
36587064b6SPunit Agrawal 
37587064b6SPunit Agrawal enum legacy_insn_status {
38587064b6SPunit Agrawal 	INSN_DEPRECATED,
39587064b6SPunit Agrawal 	INSN_OBSOLETE,
40587064b6SPunit Agrawal };
41587064b6SPunit Agrawal 
42587064b6SPunit Agrawal struct insn_emulation_ops {
43587064b6SPunit Agrawal 	const char		*name;
44587064b6SPunit Agrawal 	enum legacy_insn_status	status;
45587064b6SPunit Agrawal 	struct undef_hook	*hooks;
46587064b6SPunit Agrawal 	int			(*set_hw_mode)(bool enable);
47587064b6SPunit Agrawal };
48587064b6SPunit Agrawal 
49587064b6SPunit Agrawal struct insn_emulation {
50587064b6SPunit Agrawal 	struct list_head node;
51587064b6SPunit Agrawal 	struct insn_emulation_ops *ops;
52587064b6SPunit Agrawal 	int current_mode;
53587064b6SPunit Agrawal 	int min;
54587064b6SPunit Agrawal 	int max;
55587064b6SPunit Agrawal };
56587064b6SPunit Agrawal 
57587064b6SPunit Agrawal static LIST_HEAD(insn_emulation);
58587064b6SPunit Agrawal static int nr_insn_emulated;
59587064b6SPunit Agrawal static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
60587064b6SPunit Agrawal 
61587064b6SPunit Agrawal static void register_emulation_hooks(struct insn_emulation_ops *ops)
62587064b6SPunit Agrawal {
63587064b6SPunit Agrawal 	struct undef_hook *hook;
64587064b6SPunit Agrawal 
65587064b6SPunit Agrawal 	BUG_ON(!ops->hooks);
66587064b6SPunit Agrawal 
67587064b6SPunit Agrawal 	for (hook = ops->hooks; hook->instr_mask; hook++)
68587064b6SPunit Agrawal 		register_undef_hook(hook);
69587064b6SPunit Agrawal 
70587064b6SPunit Agrawal 	pr_notice("Registered %s emulation handler\n", ops->name);
71587064b6SPunit Agrawal }
72587064b6SPunit Agrawal 
73587064b6SPunit Agrawal static void remove_emulation_hooks(struct insn_emulation_ops *ops)
74587064b6SPunit Agrawal {
75587064b6SPunit Agrawal 	struct undef_hook *hook;
76587064b6SPunit Agrawal 
77587064b6SPunit Agrawal 	BUG_ON(!ops->hooks);
78587064b6SPunit Agrawal 
79587064b6SPunit Agrawal 	for (hook = ops->hooks; hook->instr_mask; hook++)
80587064b6SPunit Agrawal 		unregister_undef_hook(hook);
81587064b6SPunit Agrawal 
82587064b6SPunit Agrawal 	pr_notice("Removed %s emulation handler\n", ops->name);
83587064b6SPunit Agrawal }
84587064b6SPunit Agrawal 
85587064b6SPunit Agrawal static int update_insn_emulation_mode(struct insn_emulation *insn,
86587064b6SPunit Agrawal 				       enum insn_emulation_mode prev)
87587064b6SPunit Agrawal {
88587064b6SPunit Agrawal 	int ret = 0;
89587064b6SPunit Agrawal 
90587064b6SPunit Agrawal 	switch (prev) {
91587064b6SPunit Agrawal 	case INSN_UNDEF: /* Nothing to be done */
92587064b6SPunit Agrawal 		break;
93587064b6SPunit Agrawal 	case INSN_EMULATE:
94587064b6SPunit Agrawal 		remove_emulation_hooks(insn->ops);
95587064b6SPunit Agrawal 		break;
96587064b6SPunit Agrawal 	case INSN_HW:
97587064b6SPunit Agrawal 		if (insn->ops->set_hw_mode) {
98587064b6SPunit Agrawal 			insn->ops->set_hw_mode(false);
99587064b6SPunit Agrawal 			pr_notice("Disabled %s support\n", insn->ops->name);
100587064b6SPunit Agrawal 		}
101587064b6SPunit Agrawal 		break;
102587064b6SPunit Agrawal 	}
103587064b6SPunit Agrawal 
104587064b6SPunit Agrawal 	switch (insn->current_mode) {
105587064b6SPunit Agrawal 	case INSN_UNDEF:
106587064b6SPunit Agrawal 		break;
107587064b6SPunit Agrawal 	case INSN_EMULATE:
108587064b6SPunit Agrawal 		register_emulation_hooks(insn->ops);
109587064b6SPunit Agrawal 		break;
110587064b6SPunit Agrawal 	case INSN_HW:
111587064b6SPunit Agrawal 		if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true))
112587064b6SPunit Agrawal 			pr_notice("Enabled %s support\n", insn->ops->name);
113587064b6SPunit Agrawal 		else
114587064b6SPunit Agrawal 			ret = -EINVAL;
115587064b6SPunit Agrawal 		break;
116587064b6SPunit Agrawal 	}
117587064b6SPunit Agrawal 
118587064b6SPunit Agrawal 	return ret;
119587064b6SPunit Agrawal }
120587064b6SPunit Agrawal 
121587064b6SPunit Agrawal static void register_insn_emulation(struct insn_emulation_ops *ops)
122587064b6SPunit Agrawal {
123587064b6SPunit Agrawal 	unsigned long flags;
124587064b6SPunit Agrawal 	struct insn_emulation *insn;
125587064b6SPunit Agrawal 
126587064b6SPunit Agrawal 	insn = kzalloc(sizeof(*insn), GFP_KERNEL);
127587064b6SPunit Agrawal 	insn->ops = ops;
128587064b6SPunit Agrawal 	insn->min = INSN_UNDEF;
129587064b6SPunit Agrawal 
130587064b6SPunit Agrawal 	switch (ops->status) {
131587064b6SPunit Agrawal 	case INSN_DEPRECATED:
132587064b6SPunit Agrawal 		insn->current_mode = INSN_EMULATE;
133587064b6SPunit Agrawal 		insn->max = INSN_HW;
134587064b6SPunit Agrawal 		break;
135587064b6SPunit Agrawal 	case INSN_OBSOLETE:
136587064b6SPunit Agrawal 		insn->current_mode = INSN_UNDEF;
137587064b6SPunit Agrawal 		insn->max = INSN_EMULATE;
138587064b6SPunit Agrawal 		break;
139587064b6SPunit Agrawal 	}
140587064b6SPunit Agrawal 
141587064b6SPunit Agrawal 	raw_spin_lock_irqsave(&insn_emulation_lock, flags);
142587064b6SPunit Agrawal 	list_add(&insn->node, &insn_emulation);
143587064b6SPunit Agrawal 	nr_insn_emulated++;
144587064b6SPunit Agrawal 	raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
145587064b6SPunit Agrawal 
146587064b6SPunit Agrawal 	/* Register any handlers if required */
147587064b6SPunit Agrawal 	update_insn_emulation_mode(insn, INSN_UNDEF);
148587064b6SPunit Agrawal }
149587064b6SPunit Agrawal 
150587064b6SPunit Agrawal static int emulation_proc_handler(struct ctl_table *table, int write,
151587064b6SPunit Agrawal 				  void __user *buffer, size_t *lenp,
152587064b6SPunit Agrawal 				  loff_t *ppos)
153587064b6SPunit Agrawal {
154587064b6SPunit Agrawal 	int ret = 0;
155587064b6SPunit Agrawal 	struct insn_emulation *insn = (struct insn_emulation *) table->data;
156587064b6SPunit Agrawal 	enum insn_emulation_mode prev_mode = insn->current_mode;
157587064b6SPunit Agrawal 
158587064b6SPunit Agrawal 	table->data = &insn->current_mode;
159587064b6SPunit Agrawal 	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
160587064b6SPunit Agrawal 
161587064b6SPunit Agrawal 	if (ret || !write || prev_mode == insn->current_mode)
162587064b6SPunit Agrawal 		goto ret;
163587064b6SPunit Agrawal 
164587064b6SPunit Agrawal 	ret = update_insn_emulation_mode(insn, prev_mode);
165587064b6SPunit Agrawal 	if (!ret) {
166587064b6SPunit Agrawal 		/* Mode change failed, revert to previous mode. */
167587064b6SPunit Agrawal 		insn->current_mode = prev_mode;
168587064b6SPunit Agrawal 		update_insn_emulation_mode(insn, INSN_UNDEF);
169587064b6SPunit Agrawal 	}
170587064b6SPunit Agrawal ret:
171587064b6SPunit Agrawal 	table->data = insn;
172587064b6SPunit Agrawal 	return ret;
173587064b6SPunit Agrawal }
174587064b6SPunit Agrawal 
175587064b6SPunit Agrawal static struct ctl_table ctl_abi[] = {
176587064b6SPunit Agrawal 	{
177587064b6SPunit Agrawal 		.procname = "abi",
178587064b6SPunit Agrawal 		.mode = 0555,
179587064b6SPunit Agrawal 	},
180587064b6SPunit Agrawal 	{ }
181587064b6SPunit Agrawal };
182587064b6SPunit Agrawal 
183587064b6SPunit Agrawal static void register_insn_emulation_sysctl(struct ctl_table *table)
184587064b6SPunit Agrawal {
185587064b6SPunit Agrawal 	unsigned long flags;
186587064b6SPunit Agrawal 	int i = 0;
187587064b6SPunit Agrawal 	struct insn_emulation *insn;
188587064b6SPunit Agrawal 	struct ctl_table *insns_sysctl, *sysctl;
189587064b6SPunit Agrawal 
190587064b6SPunit Agrawal 	insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1),
191587064b6SPunit Agrawal 			      GFP_KERNEL);
192587064b6SPunit Agrawal 
193587064b6SPunit Agrawal 	raw_spin_lock_irqsave(&insn_emulation_lock, flags);
194587064b6SPunit Agrawal 	list_for_each_entry(insn, &insn_emulation, node) {
195587064b6SPunit Agrawal 		sysctl = &insns_sysctl[i];
196587064b6SPunit Agrawal 
197587064b6SPunit Agrawal 		sysctl->mode = 0644;
198587064b6SPunit Agrawal 		sysctl->maxlen = sizeof(int);
199587064b6SPunit Agrawal 
200587064b6SPunit Agrawal 		sysctl->procname = insn->ops->name;
201587064b6SPunit Agrawal 		sysctl->data = insn;
202587064b6SPunit Agrawal 		sysctl->extra1 = &insn->min;
203587064b6SPunit Agrawal 		sysctl->extra2 = &insn->max;
204587064b6SPunit Agrawal 		sysctl->proc_handler = emulation_proc_handler;
205587064b6SPunit Agrawal 		i++;
206587064b6SPunit Agrawal 	}
207587064b6SPunit Agrawal 	raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
208587064b6SPunit Agrawal 
209587064b6SPunit Agrawal 	table->child = insns_sysctl;
210587064b6SPunit Agrawal 	register_sysctl_table(table);
211587064b6SPunit Agrawal }
212587064b6SPunit Agrawal 
213587064b6SPunit Agrawal /*
214bd35a4adSPunit Agrawal  *  Implement emulation of the SWP/SWPB instructions using load-exclusive and
215bd35a4adSPunit Agrawal  *  store-exclusive.
216bd35a4adSPunit Agrawal  *
217bd35a4adSPunit Agrawal  *  Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
218bd35a4adSPunit Agrawal  *  Where: Rt  = destination
219bd35a4adSPunit Agrawal  *	   Rt2 = source
220bd35a4adSPunit Agrawal  *	   Rn  = address
221bd35a4adSPunit Agrawal  */
222bd35a4adSPunit Agrawal 
223bd35a4adSPunit Agrawal /*
224bd35a4adSPunit Agrawal  * Error-checking SWP macros implemented using ldxr{b}/stxr{b}
225bd35a4adSPunit Agrawal  */
226bd35a4adSPunit Agrawal #define __user_swpX_asm(data, addr, res, temp, B)		\
227bd35a4adSPunit Agrawal 	__asm__ __volatile__(					\
228bd35a4adSPunit Agrawal 	"	mov		%w2, %w1\n"			\
229bd35a4adSPunit Agrawal 	"0:	ldxr"B"		%w1, [%3]\n"			\
230bd35a4adSPunit Agrawal 	"1:	stxr"B"		%w0, %w2, [%3]\n"		\
231bd35a4adSPunit Agrawal 	"	cbz		%w0, 2f\n"			\
232bd35a4adSPunit Agrawal 	"	mov		%w0, %w4\n"			\
233bd35a4adSPunit Agrawal 	"2:\n"							\
234bd35a4adSPunit Agrawal 	"	.pushsection	 .fixup,\"ax\"\n"		\
235bd35a4adSPunit Agrawal 	"	.align		2\n"				\
236bd35a4adSPunit Agrawal 	"3:	mov		%w0, %w5\n"			\
237bd35a4adSPunit Agrawal 	"	b		2b\n"				\
238bd35a4adSPunit Agrawal 	"	.popsection"					\
239bd35a4adSPunit Agrawal 	"	.pushsection	 __ex_table,\"a\"\n"		\
240bd35a4adSPunit Agrawal 	"	.align		3\n"				\
241bd35a4adSPunit Agrawal 	"	.quad		0b, 3b\n"			\
242bd35a4adSPunit Agrawal 	"	.quad		1b, 3b\n"			\
243bd35a4adSPunit Agrawal 	"	.popsection"					\
244bd35a4adSPunit Agrawal 	: "=&r" (res), "+r" (data), "=&r" (temp)		\
245bd35a4adSPunit Agrawal 	: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT)		\
246bd35a4adSPunit Agrawal 	: "memory")
247bd35a4adSPunit Agrawal 
248bd35a4adSPunit Agrawal #define __user_swp_asm(data, addr, res, temp) \
249bd35a4adSPunit Agrawal 	__user_swpX_asm(data, addr, res, temp, "")
250bd35a4adSPunit Agrawal #define __user_swpb_asm(data, addr, res, temp) \
251bd35a4adSPunit Agrawal 	__user_swpX_asm(data, addr, res, temp, "b")
252bd35a4adSPunit Agrawal 
253bd35a4adSPunit Agrawal /*
254bd35a4adSPunit Agrawal  * Bit 22 of the instruction encoding distinguishes between
255bd35a4adSPunit Agrawal  * the SWP and SWPB variants (bit set means SWPB).
256bd35a4adSPunit Agrawal  */
257bd35a4adSPunit Agrawal #define TYPE_SWPB (1 << 22)
258bd35a4adSPunit Agrawal 
259bd35a4adSPunit Agrawal /*
260bd35a4adSPunit Agrawal  * Set up process info to signal segmentation fault - called on access error.
261bd35a4adSPunit Agrawal  */
262bd35a4adSPunit Agrawal static void set_segfault(struct pt_regs *regs, unsigned long addr)
263bd35a4adSPunit Agrawal {
264bd35a4adSPunit Agrawal 	siginfo_t info;
265bd35a4adSPunit Agrawal 
266bd35a4adSPunit Agrawal 	down_read(&current->mm->mmap_sem);
267bd35a4adSPunit Agrawal 	if (find_vma(current->mm, addr) == NULL)
268bd35a4adSPunit Agrawal 		info.si_code = SEGV_MAPERR;
269bd35a4adSPunit Agrawal 	else
270bd35a4adSPunit Agrawal 		info.si_code = SEGV_ACCERR;
271bd35a4adSPunit Agrawal 	up_read(&current->mm->mmap_sem);
272bd35a4adSPunit Agrawal 
273bd35a4adSPunit Agrawal 	info.si_signo = SIGSEGV;
274bd35a4adSPunit Agrawal 	info.si_errno = 0;
275bd35a4adSPunit Agrawal 	info.si_addr  = (void *) instruction_pointer(regs);
276bd35a4adSPunit Agrawal 
277bd35a4adSPunit Agrawal 	pr_debug("SWP{B} emulation: access caused memory abort!\n");
278bd35a4adSPunit Agrawal 	arm64_notify_die("Illegal memory access", regs, &info, 0);
279bd35a4adSPunit Agrawal }
280bd35a4adSPunit Agrawal 
281bd35a4adSPunit Agrawal static int emulate_swpX(unsigned int address, unsigned int *data,
282bd35a4adSPunit Agrawal 			unsigned int type)
283bd35a4adSPunit Agrawal {
284bd35a4adSPunit Agrawal 	unsigned int res = 0;
285bd35a4adSPunit Agrawal 
286bd35a4adSPunit Agrawal 	if ((type != TYPE_SWPB) && (address & 0x3)) {
287bd35a4adSPunit Agrawal 		/* SWP to unaligned address not permitted */
288bd35a4adSPunit Agrawal 		pr_debug("SWP instruction on unaligned pointer!\n");
289bd35a4adSPunit Agrawal 		return -EFAULT;
290bd35a4adSPunit Agrawal 	}
291bd35a4adSPunit Agrawal 
292bd35a4adSPunit Agrawal 	while (1) {
293bd35a4adSPunit Agrawal 		unsigned long temp;
294bd35a4adSPunit Agrawal 
295bd35a4adSPunit Agrawal 		if (type == TYPE_SWPB)
296bd35a4adSPunit Agrawal 			__user_swpb_asm(*data, address, res, temp);
297bd35a4adSPunit Agrawal 		else
298bd35a4adSPunit Agrawal 			__user_swp_asm(*data, address, res, temp);
299bd35a4adSPunit Agrawal 
300bd35a4adSPunit Agrawal 		if (likely(res != -EAGAIN) || signal_pending(current))
301bd35a4adSPunit Agrawal 			break;
302bd35a4adSPunit Agrawal 
303bd35a4adSPunit Agrawal 		cond_resched();
304bd35a4adSPunit Agrawal 	}
305bd35a4adSPunit Agrawal 
306bd35a4adSPunit Agrawal 	return res;
307bd35a4adSPunit Agrawal }
308bd35a4adSPunit Agrawal 
309bd35a4adSPunit Agrawal /*
310bd35a4adSPunit Agrawal  * swp_handler logs the id of calling process, dissects the instruction, sanity
311bd35a4adSPunit Agrawal  * checks the memory location, calls emulate_swpX for the actual operation and
312bd35a4adSPunit Agrawal  * deals with fixup/error handling before returning
313bd35a4adSPunit Agrawal  */
314bd35a4adSPunit Agrawal static int swp_handler(struct pt_regs *regs, u32 instr)
315bd35a4adSPunit Agrawal {
316bd35a4adSPunit Agrawal 	u32 destreg, data, type, address = 0;
317bd35a4adSPunit Agrawal 	int rn, rt2, res = 0;
318bd35a4adSPunit Agrawal 
319bd35a4adSPunit Agrawal 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
320bd35a4adSPunit Agrawal 
321bd35a4adSPunit Agrawal 	type = instr & TYPE_SWPB;
322bd35a4adSPunit Agrawal 
323bd35a4adSPunit Agrawal 	switch (arm_check_condition(instr, regs->pstate)) {
324bd35a4adSPunit Agrawal 	case ARM_OPCODE_CONDTEST_PASS:
325bd35a4adSPunit Agrawal 		break;
326bd35a4adSPunit Agrawal 	case ARM_OPCODE_CONDTEST_FAIL:
327bd35a4adSPunit Agrawal 		/* Condition failed - return to next instruction */
328bd35a4adSPunit Agrawal 		goto ret;
329bd35a4adSPunit Agrawal 	case ARM_OPCODE_CONDTEST_UNCOND:
330bd35a4adSPunit Agrawal 		/* If unconditional encoding - not a SWP, undef */
331bd35a4adSPunit Agrawal 		return -EFAULT;
332bd35a4adSPunit Agrawal 	default:
333bd35a4adSPunit Agrawal 		return -EINVAL;
334bd35a4adSPunit Agrawal 	}
335bd35a4adSPunit Agrawal 
336bd35a4adSPunit Agrawal 	rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET);
337bd35a4adSPunit Agrawal 	rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET);
338bd35a4adSPunit Agrawal 
339bd35a4adSPunit Agrawal 	address = (u32)regs->user_regs.regs[rn];
340bd35a4adSPunit Agrawal 	data	= (u32)regs->user_regs.regs[rt2];
341bd35a4adSPunit Agrawal 	destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET);
342bd35a4adSPunit Agrawal 
343bd35a4adSPunit Agrawal 	pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
344bd35a4adSPunit Agrawal 		rn, address, destreg,
345bd35a4adSPunit Agrawal 		aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data);
346bd35a4adSPunit Agrawal 
347bd35a4adSPunit Agrawal 	/* Check access in reasonable access range for both SWP and SWPB */
348bd35a4adSPunit Agrawal 	if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
349bd35a4adSPunit Agrawal 		pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n",
350bd35a4adSPunit Agrawal 			address);
351bd35a4adSPunit Agrawal 		goto fault;
352bd35a4adSPunit Agrawal 	}
353bd35a4adSPunit Agrawal 
354bd35a4adSPunit Agrawal 	res = emulate_swpX(address, &data, type);
355bd35a4adSPunit Agrawal 	if (res == -EFAULT)
356bd35a4adSPunit Agrawal 		goto fault;
357bd35a4adSPunit Agrawal 	else if (res == 0)
358bd35a4adSPunit Agrawal 		regs->user_regs.regs[destreg] = data;
359bd35a4adSPunit Agrawal 
360bd35a4adSPunit Agrawal ret:
361bd35a4adSPunit Agrawal 	pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
362bd35a4adSPunit Agrawal 			current->comm, (unsigned long)current->pid, regs->pc);
363bd35a4adSPunit Agrawal 
364bd35a4adSPunit Agrawal 	regs->pc += 4;
365bd35a4adSPunit Agrawal 	return 0;
366bd35a4adSPunit Agrawal 
367bd35a4adSPunit Agrawal fault:
368bd35a4adSPunit Agrawal 	set_segfault(regs, address);
369bd35a4adSPunit Agrawal 
370bd35a4adSPunit Agrawal 	return 0;
371bd35a4adSPunit Agrawal }
372bd35a4adSPunit Agrawal 
373bd35a4adSPunit Agrawal /*
374bd35a4adSPunit Agrawal  * Only emulate SWP/SWPB executed in ARM state/User mode.
375bd35a4adSPunit Agrawal  * The kernel must be SWP free and SWP{B} does not exist in Thumb.
376bd35a4adSPunit Agrawal  */
377bd35a4adSPunit Agrawal static struct undef_hook swp_hooks[] = {
378bd35a4adSPunit Agrawal 	{
379bd35a4adSPunit Agrawal 		.instr_mask	= 0x0fb00ff0,
380bd35a4adSPunit Agrawal 		.instr_val	= 0x01000090,
381bd35a4adSPunit Agrawal 		.pstate_mask	= COMPAT_PSR_MODE_MASK,
382bd35a4adSPunit Agrawal 		.pstate_val	= COMPAT_PSR_MODE_USR,
383bd35a4adSPunit Agrawal 		.fn		= swp_handler
384bd35a4adSPunit Agrawal 	},
385bd35a4adSPunit Agrawal 	{ }
386bd35a4adSPunit Agrawal };
387bd35a4adSPunit Agrawal 
388bd35a4adSPunit Agrawal static struct insn_emulation_ops swp_ops = {
389bd35a4adSPunit Agrawal 	.name = "swp",
390bd35a4adSPunit Agrawal 	.status = INSN_OBSOLETE,
391bd35a4adSPunit Agrawal 	.hooks = swp_hooks,
392bd35a4adSPunit Agrawal 	.set_hw_mode = NULL,
393bd35a4adSPunit Agrawal };
394bd35a4adSPunit Agrawal 
395c852f320SPunit Agrawal static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
396c852f320SPunit Agrawal {
397c852f320SPunit Agrawal 	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
398c852f320SPunit Agrawal 
399c852f320SPunit Agrawal 	switch (arm_check_condition(instr, regs->pstate)) {
400c852f320SPunit Agrawal 	case ARM_OPCODE_CONDTEST_PASS:
401c852f320SPunit Agrawal 		break;
402c852f320SPunit Agrawal 	case ARM_OPCODE_CONDTEST_FAIL:
403c852f320SPunit Agrawal 		/* Condition failed - return to next instruction */
404c852f320SPunit Agrawal 		goto ret;
405c852f320SPunit Agrawal 	case ARM_OPCODE_CONDTEST_UNCOND:
406c852f320SPunit Agrawal 		/* If unconditional encoding - not a barrier instruction */
407c852f320SPunit Agrawal 		return -EFAULT;
408c852f320SPunit Agrawal 	default:
409c852f320SPunit Agrawal 		return -EINVAL;
410c852f320SPunit Agrawal 	}
411c852f320SPunit Agrawal 
412c852f320SPunit Agrawal 	switch (aarch32_insn_mcr_extract_crm(instr)) {
413c852f320SPunit Agrawal 	case 10:
414c852f320SPunit Agrawal 		/*
415c852f320SPunit Agrawal 		 * dmb - mcr p15, 0, Rt, c7, c10, 5
416c852f320SPunit Agrawal 		 * dsb - mcr p15, 0, Rt, c7, c10, 4
417c852f320SPunit Agrawal 		 */
418c852f320SPunit Agrawal 		if (aarch32_insn_mcr_extract_opc2(instr) == 5)
419c852f320SPunit Agrawal 			dmb(sy);
420c852f320SPunit Agrawal 		else
421c852f320SPunit Agrawal 			dsb(sy);
422c852f320SPunit Agrawal 		break;
423c852f320SPunit Agrawal 	case 5:
424c852f320SPunit Agrawal 		/*
425c852f320SPunit Agrawal 		 * isb - mcr p15, 0, Rt, c7, c5, 4
426c852f320SPunit Agrawal 		 *
427c852f320SPunit Agrawal 		 * Taking an exception or returning from one acts as an
428c852f320SPunit Agrawal 		 * instruction barrier. So no explicit barrier needed here.
429c852f320SPunit Agrawal 		 */
430c852f320SPunit Agrawal 		break;
431c852f320SPunit Agrawal 	}
432c852f320SPunit Agrawal 
433c852f320SPunit Agrawal ret:
434c852f320SPunit Agrawal 	pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
435c852f320SPunit Agrawal 			current->comm, (unsigned long)current->pid, regs->pc);
436c852f320SPunit Agrawal 
437c852f320SPunit Agrawal 	regs->pc += 4;
438c852f320SPunit Agrawal 	return 0;
439c852f320SPunit Agrawal }
440c852f320SPunit Agrawal 
441c852f320SPunit Agrawal #define SCTLR_EL1_CP15BEN (1 << 5)
442c852f320SPunit Agrawal 
443c852f320SPunit Agrawal static inline void config_sctlr_el1(u32 clear, u32 set)
444c852f320SPunit Agrawal {
445c852f320SPunit Agrawal 	u32 val;
446c852f320SPunit Agrawal 
447c852f320SPunit Agrawal 	asm volatile("mrs %0, sctlr_el1" : "=r" (val));
448c852f320SPunit Agrawal 	val &= ~clear;
449c852f320SPunit Agrawal 	val |= set;
450c852f320SPunit Agrawal 	asm volatile("msr sctlr_el1, %0" : : "r" (val));
451c852f320SPunit Agrawal }
452c852f320SPunit Agrawal 
453c852f320SPunit Agrawal static void enable_cp15_ben(void *info)
454c852f320SPunit Agrawal {
455c852f320SPunit Agrawal 	config_sctlr_el1(0, SCTLR_EL1_CP15BEN);
456c852f320SPunit Agrawal }
457c852f320SPunit Agrawal 
458c852f320SPunit Agrawal static void disable_cp15_ben(void *info)
459c852f320SPunit Agrawal {
460c852f320SPunit Agrawal 	config_sctlr_el1(SCTLR_EL1_CP15BEN, 0);
461c852f320SPunit Agrawal }
462c852f320SPunit Agrawal 
463c852f320SPunit Agrawal static int cpu_hotplug_notify(struct notifier_block *b,
464c852f320SPunit Agrawal 			      unsigned long action, void *hcpu)
465c852f320SPunit Agrawal {
466c852f320SPunit Agrawal 	switch (action) {
467c852f320SPunit Agrawal 	case CPU_STARTING:
468c852f320SPunit Agrawal 	case CPU_STARTING_FROZEN:
469c852f320SPunit Agrawal 		enable_cp15_ben(NULL);
470c852f320SPunit Agrawal 		return NOTIFY_DONE;
471c852f320SPunit Agrawal 	case CPU_DYING:
472c852f320SPunit Agrawal 	case CPU_DYING_FROZEN:
473c852f320SPunit Agrawal 		disable_cp15_ben(NULL);
474c852f320SPunit Agrawal 		return NOTIFY_DONE;
475c852f320SPunit Agrawal 	}
476c852f320SPunit Agrawal 
477c852f320SPunit Agrawal 	return NOTIFY_OK;
478c852f320SPunit Agrawal }
479c852f320SPunit Agrawal 
480c852f320SPunit Agrawal static struct notifier_block cpu_hotplug_notifier = {
481c852f320SPunit Agrawal 	.notifier_call = cpu_hotplug_notify,
482c852f320SPunit Agrawal };
483c852f320SPunit Agrawal 
484c852f320SPunit Agrawal static int cp15_barrier_set_hw_mode(bool enable)
485c852f320SPunit Agrawal {
486c852f320SPunit Agrawal 	if (enable) {
487c852f320SPunit Agrawal 		register_cpu_notifier(&cpu_hotplug_notifier);
488c852f320SPunit Agrawal 		on_each_cpu(enable_cp15_ben, NULL, true);
489c852f320SPunit Agrawal 	} else {
490c852f320SPunit Agrawal 		unregister_cpu_notifier(&cpu_hotplug_notifier);
491c852f320SPunit Agrawal 		on_each_cpu(disable_cp15_ben, NULL, true);
492c852f320SPunit Agrawal 	}
493c852f320SPunit Agrawal 
494c852f320SPunit Agrawal 	return true;
495c852f320SPunit Agrawal }
496c852f320SPunit Agrawal 
497c852f320SPunit Agrawal static struct undef_hook cp15_barrier_hooks[] = {
498c852f320SPunit Agrawal 	{
499c852f320SPunit Agrawal 		.instr_mask	= 0x0fff0fdf,
500c852f320SPunit Agrawal 		.instr_val	= 0x0e070f9a,
501c852f320SPunit Agrawal 		.pstate_mask	= COMPAT_PSR_MODE_MASK,
502c852f320SPunit Agrawal 		.pstate_val	= COMPAT_PSR_MODE_USR,
503c852f320SPunit Agrawal 		.fn		= cp15barrier_handler,
504c852f320SPunit Agrawal 	},
505c852f320SPunit Agrawal 	{
506c852f320SPunit Agrawal 		.instr_mask	= 0x0fff0fff,
507c852f320SPunit Agrawal 		.instr_val	= 0x0e070f95,
508c852f320SPunit Agrawal 		.pstate_mask	= COMPAT_PSR_MODE_MASK,
509c852f320SPunit Agrawal 		.pstate_val	= COMPAT_PSR_MODE_USR,
510c852f320SPunit Agrawal 		.fn		= cp15barrier_handler,
511c852f320SPunit Agrawal 	},
512c852f320SPunit Agrawal 	{ }
513c852f320SPunit Agrawal };
514c852f320SPunit Agrawal 
515c852f320SPunit Agrawal static struct insn_emulation_ops cp15_barrier_ops = {
516c852f320SPunit Agrawal 	.name = "cp15_barrier",
517c852f320SPunit Agrawal 	.status = INSN_DEPRECATED,
518c852f320SPunit Agrawal 	.hooks = cp15_barrier_hooks,
519c852f320SPunit Agrawal 	.set_hw_mode = cp15_barrier_set_hw_mode,
520c852f320SPunit Agrawal };
521c852f320SPunit Agrawal 
522bd35a4adSPunit Agrawal /*
523587064b6SPunit Agrawal  * Invoked as late_initcall, since not needed before init spawned.
524587064b6SPunit Agrawal  */
525587064b6SPunit Agrawal static int __init armv8_deprecated_init(void)
526587064b6SPunit Agrawal {
527bd35a4adSPunit Agrawal 	if (IS_ENABLED(CONFIG_SWP_EMULATION))
528bd35a4adSPunit Agrawal 		register_insn_emulation(&swp_ops);
529bd35a4adSPunit Agrawal 
530c852f320SPunit Agrawal 	if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))
531c852f320SPunit Agrawal 		register_insn_emulation(&cp15_barrier_ops);
532c852f320SPunit Agrawal 
533587064b6SPunit Agrawal 	register_insn_emulation_sysctl(ctl_abi);
534587064b6SPunit Agrawal 
535587064b6SPunit Agrawal 	return 0;
536587064b6SPunit Agrawal }
537587064b6SPunit Agrawal 
538587064b6SPunit Agrawal late_initcall(armv8_deprecated_init);
539