xref: /openbmc/linux/arch/csky/abiv1/alignment.c (revision 081860b970adb69888734df0c8aa849b00ea7254)
1*081860b9SGuo Ren // SPDX-License-Identifier: GPL-2.0
2*081860b9SGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3*081860b9SGuo Ren 
4*081860b9SGuo Ren #include <linux/kernel.h>
5*081860b9SGuo Ren #include <linux/uaccess.h>
6*081860b9SGuo Ren #include <linux/ptrace.h>
7*081860b9SGuo Ren 
8*081860b9SGuo Ren static int align_enable = 1;
9*081860b9SGuo Ren static int align_count;
10*081860b9SGuo Ren 
11*081860b9SGuo Ren static inline uint32_t get_ptreg(struct pt_regs *regs, uint32_t rx)
12*081860b9SGuo Ren {
13*081860b9SGuo Ren 	return rx == 15 ? regs->lr : *((uint32_t *)&(regs->a0) - 2 + rx);
14*081860b9SGuo Ren }
15*081860b9SGuo Ren 
16*081860b9SGuo Ren static inline void put_ptreg(struct pt_regs *regs, uint32_t rx, uint32_t val)
17*081860b9SGuo Ren {
18*081860b9SGuo Ren 	if (rx == 15)
19*081860b9SGuo Ren 		regs->lr = val;
20*081860b9SGuo Ren 	else
21*081860b9SGuo Ren 		*((uint32_t *)&(regs->a0) - 2 + rx) = val;
22*081860b9SGuo Ren }
23*081860b9SGuo Ren 
24*081860b9SGuo Ren /*
25*081860b9SGuo Ren  * Get byte-value from addr and set it to *valp.
26*081860b9SGuo Ren  *
27*081860b9SGuo Ren  * Success: return 0
28*081860b9SGuo Ren  * Failure: return 1
29*081860b9SGuo Ren  */
30*081860b9SGuo Ren static int ldb_asm(uint32_t addr, uint32_t *valp)
31*081860b9SGuo Ren {
32*081860b9SGuo Ren 	uint32_t val;
33*081860b9SGuo Ren 	int err;
34*081860b9SGuo Ren 
35*081860b9SGuo Ren 	if (!access_ok(VERIFY_READ, (void *)addr, 1))
36*081860b9SGuo Ren 		return 1;
37*081860b9SGuo Ren 
38*081860b9SGuo Ren 	asm volatile (
39*081860b9SGuo Ren 		"movi	%0, 0\n"
40*081860b9SGuo Ren 		"1:\n"
41*081860b9SGuo Ren 		"ldb	%1, (%2)\n"
42*081860b9SGuo Ren 		"br	3f\n"
43*081860b9SGuo Ren 		"2:\n"
44*081860b9SGuo Ren 		"movi	%0, 1\n"
45*081860b9SGuo Ren 		"br	3f\n"
46*081860b9SGuo Ren 		".section __ex_table,\"a\"\n"
47*081860b9SGuo Ren 		".align 2\n"
48*081860b9SGuo Ren 		".long	1b, 2b\n"
49*081860b9SGuo Ren 		".previous\n"
50*081860b9SGuo Ren 		"3:\n"
51*081860b9SGuo Ren 		: "=&r"(err), "=r"(val)
52*081860b9SGuo Ren 		: "r" (addr)
53*081860b9SGuo Ren 	);
54*081860b9SGuo Ren 
55*081860b9SGuo Ren 	*valp = val;
56*081860b9SGuo Ren 
57*081860b9SGuo Ren 	return err;
58*081860b9SGuo Ren }
59*081860b9SGuo Ren 
60*081860b9SGuo Ren /*
61*081860b9SGuo Ren  * Put byte-value to addr.
62*081860b9SGuo Ren  *
63*081860b9SGuo Ren  * Success: return 0
64*081860b9SGuo Ren  * Failure: return 1
65*081860b9SGuo Ren  */
66*081860b9SGuo Ren static int stb_asm(uint32_t addr, uint32_t val)
67*081860b9SGuo Ren {
68*081860b9SGuo Ren 	int err;
69*081860b9SGuo Ren 
70*081860b9SGuo Ren 	if (!access_ok(VERIFY_WRITE, (void *)addr, 1))
71*081860b9SGuo Ren 		return 1;
72*081860b9SGuo Ren 
73*081860b9SGuo Ren 	asm volatile (
74*081860b9SGuo Ren 		"movi	%0, 0\n"
75*081860b9SGuo Ren 		"1:\n"
76*081860b9SGuo Ren 		"stb	%1, (%2)\n"
77*081860b9SGuo Ren 		"br	3f\n"
78*081860b9SGuo Ren 		"2:\n"
79*081860b9SGuo Ren 		"movi	%0, 1\n"
80*081860b9SGuo Ren 		"br	3f\n"
81*081860b9SGuo Ren 		".section __ex_table,\"a\"\n"
82*081860b9SGuo Ren 		".align 2\n"
83*081860b9SGuo Ren 		".long	1b, 2b\n"
84*081860b9SGuo Ren 		".previous\n"
85*081860b9SGuo Ren 		"3:\n"
86*081860b9SGuo Ren 		: "=&r"(err)
87*081860b9SGuo Ren 		: "r"(val), "r" (addr)
88*081860b9SGuo Ren 	);
89*081860b9SGuo Ren 
90*081860b9SGuo Ren 	return err;
91*081860b9SGuo Ren }
92*081860b9SGuo Ren 
93*081860b9SGuo Ren /*
94*081860b9SGuo Ren  * Get half-word from [rx + imm]
95*081860b9SGuo Ren  *
96*081860b9SGuo Ren  * Success: return 0
97*081860b9SGuo Ren  * Failure: return 1
98*081860b9SGuo Ren  */
99*081860b9SGuo Ren static int ldh_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
100*081860b9SGuo Ren {
101*081860b9SGuo Ren 	uint32_t byte0, byte1;
102*081860b9SGuo Ren 
103*081860b9SGuo Ren 	if (ldb_asm(addr, &byte0))
104*081860b9SGuo Ren 		return 1;
105*081860b9SGuo Ren 	addr += 1;
106*081860b9SGuo Ren 	if (ldb_asm(addr, &byte1))
107*081860b9SGuo Ren 		return 1;
108*081860b9SGuo Ren 
109*081860b9SGuo Ren 	byte0 |= byte1 << 8;
110*081860b9SGuo Ren 	put_ptreg(regs, rz, byte0);
111*081860b9SGuo Ren 
112*081860b9SGuo Ren 	return 0;
113*081860b9SGuo Ren }
114*081860b9SGuo Ren 
115*081860b9SGuo Ren /*
116*081860b9SGuo Ren  * Store half-word to [rx + imm]
117*081860b9SGuo Ren  *
118*081860b9SGuo Ren  * Success: return 0
119*081860b9SGuo Ren  * Failure: return 1
120*081860b9SGuo Ren  */
121*081860b9SGuo Ren static int sth_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
122*081860b9SGuo Ren {
123*081860b9SGuo Ren 	uint32_t byte0, byte1;
124*081860b9SGuo Ren 
125*081860b9SGuo Ren 	byte0 = byte1 = get_ptreg(regs, rz);
126*081860b9SGuo Ren 
127*081860b9SGuo Ren 	byte0 &= 0xff;
128*081860b9SGuo Ren 
129*081860b9SGuo Ren 	if (stb_asm(addr, byte0))
130*081860b9SGuo Ren 		return 1;
131*081860b9SGuo Ren 
132*081860b9SGuo Ren 	addr += 1;
133*081860b9SGuo Ren 	byte1 = (byte1 >> 8) & 0xff;
134*081860b9SGuo Ren 	if (stb_asm(addr, byte1))
135*081860b9SGuo Ren 		return 1;
136*081860b9SGuo Ren 
137*081860b9SGuo Ren 	return 0;
138*081860b9SGuo Ren }
139*081860b9SGuo Ren 
140*081860b9SGuo Ren /*
141*081860b9SGuo Ren  * Get word from [rx + imm]
142*081860b9SGuo Ren  *
143*081860b9SGuo Ren  * Success: return 0
144*081860b9SGuo Ren  * Failure: return 1
145*081860b9SGuo Ren  */
146*081860b9SGuo Ren static int ldw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
147*081860b9SGuo Ren {
148*081860b9SGuo Ren 	uint32_t byte0, byte1, byte2, byte3;
149*081860b9SGuo Ren 
150*081860b9SGuo Ren 	if (ldb_asm(addr, &byte0))
151*081860b9SGuo Ren 		return 1;
152*081860b9SGuo Ren 
153*081860b9SGuo Ren 	addr += 1;
154*081860b9SGuo Ren 	if (ldb_asm(addr, &byte1))
155*081860b9SGuo Ren 		return 1;
156*081860b9SGuo Ren 
157*081860b9SGuo Ren 	addr += 1;
158*081860b9SGuo Ren 	if (ldb_asm(addr, &byte2))
159*081860b9SGuo Ren 		return 1;
160*081860b9SGuo Ren 
161*081860b9SGuo Ren 	addr += 1;
162*081860b9SGuo Ren 	if (ldb_asm(addr, &byte3))
163*081860b9SGuo Ren 		return 1;
164*081860b9SGuo Ren 
165*081860b9SGuo Ren 	byte0 |= byte1 << 8;
166*081860b9SGuo Ren 	byte0 |= byte2 << 16;
167*081860b9SGuo Ren 	byte0 |= byte3 << 24;
168*081860b9SGuo Ren 
169*081860b9SGuo Ren 	put_ptreg(regs, rz, byte0);
170*081860b9SGuo Ren 
171*081860b9SGuo Ren 	return 0;
172*081860b9SGuo Ren }
173*081860b9SGuo Ren 
174*081860b9SGuo Ren /*
175*081860b9SGuo Ren  * Store word to [rx + imm]
176*081860b9SGuo Ren  *
177*081860b9SGuo Ren  * Success: return 0
178*081860b9SGuo Ren  * Failure: return 1
179*081860b9SGuo Ren  */
180*081860b9SGuo Ren static int stw_c(struct pt_regs *regs, uint32_t rz, uint32_t addr)
181*081860b9SGuo Ren {
182*081860b9SGuo Ren 	uint32_t byte0, byte1, byte2, byte3;
183*081860b9SGuo Ren 
184*081860b9SGuo Ren 	byte0 = byte1 = byte2 = byte3 = get_ptreg(regs, rz);
185*081860b9SGuo Ren 
186*081860b9SGuo Ren 	byte0 &= 0xff;
187*081860b9SGuo Ren 
188*081860b9SGuo Ren 	if (stb_asm(addr, byte0))
189*081860b9SGuo Ren 		return 1;
190*081860b9SGuo Ren 
191*081860b9SGuo Ren 	addr += 1;
192*081860b9SGuo Ren 	byte1 = (byte1 >> 8) & 0xff;
193*081860b9SGuo Ren 	if (stb_asm(addr, byte1))
194*081860b9SGuo Ren 		return 1;
195*081860b9SGuo Ren 
196*081860b9SGuo Ren 	addr += 1;
197*081860b9SGuo Ren 	byte2 = (byte2 >> 16) & 0xff;
198*081860b9SGuo Ren 	if (stb_asm(addr, byte2))
199*081860b9SGuo Ren 		return 1;
200*081860b9SGuo Ren 
201*081860b9SGuo Ren 	addr += 1;
202*081860b9SGuo Ren 	byte3 = (byte3 >> 24) & 0xff;
203*081860b9SGuo Ren 	if (stb_asm(addr, byte3))
204*081860b9SGuo Ren 		return 1;
205*081860b9SGuo Ren 
206*081860b9SGuo Ren 	align_count++;
207*081860b9SGuo Ren 
208*081860b9SGuo Ren 	return 0;
209*081860b9SGuo Ren }
210*081860b9SGuo Ren 
211*081860b9SGuo Ren extern int fixup_exception(struct pt_regs *regs);
212*081860b9SGuo Ren 
213*081860b9SGuo Ren #define OP_LDH 0xc000
214*081860b9SGuo Ren #define OP_STH 0xd000
215*081860b9SGuo Ren #define OP_LDW 0x8000
216*081860b9SGuo Ren #define OP_STW 0x9000
217*081860b9SGuo Ren 
218*081860b9SGuo Ren void csky_alignment(struct pt_regs *regs)
219*081860b9SGuo Ren {
220*081860b9SGuo Ren 	int ret;
221*081860b9SGuo Ren 	uint16_t tmp;
222*081860b9SGuo Ren 	uint32_t opcode = 0;
223*081860b9SGuo Ren 	uint32_t rx     = 0;
224*081860b9SGuo Ren 	uint32_t rz     = 0;
225*081860b9SGuo Ren 	uint32_t imm    = 0;
226*081860b9SGuo Ren 	uint32_t addr   = 0;
227*081860b9SGuo Ren 
228*081860b9SGuo Ren 	if (!user_mode(regs))
229*081860b9SGuo Ren 		goto bad_area;
230*081860b9SGuo Ren 
231*081860b9SGuo Ren 	ret = get_user(tmp, (uint16_t *)instruction_pointer(regs));
232*081860b9SGuo Ren 	if (ret) {
233*081860b9SGuo Ren 		pr_err("%s get_user failed.\n", __func__);
234*081860b9SGuo Ren 		goto bad_area;
235*081860b9SGuo Ren 	}
236*081860b9SGuo Ren 
237*081860b9SGuo Ren 	opcode = (uint32_t)tmp;
238*081860b9SGuo Ren 
239*081860b9SGuo Ren 	rx  = opcode & 0xf;
240*081860b9SGuo Ren 	imm = (opcode >> 4) & 0xf;
241*081860b9SGuo Ren 	rz  = (opcode >> 8) & 0xf;
242*081860b9SGuo Ren 	opcode &= 0xf000;
243*081860b9SGuo Ren 
244*081860b9SGuo Ren 	if (rx == 0 || rx == 1 || rz == 0 || rz == 1)
245*081860b9SGuo Ren 		goto bad_area;
246*081860b9SGuo Ren 
247*081860b9SGuo Ren 	switch (opcode) {
248*081860b9SGuo Ren 	case OP_LDH:
249*081860b9SGuo Ren 		addr = get_ptreg(regs, rx) + (imm << 1);
250*081860b9SGuo Ren 		ret = ldh_c(regs, rz, addr);
251*081860b9SGuo Ren 		break;
252*081860b9SGuo Ren 	case OP_LDW:
253*081860b9SGuo Ren 		addr = get_ptreg(regs, rx) + (imm << 2);
254*081860b9SGuo Ren 		ret = ldw_c(regs, rz, addr);
255*081860b9SGuo Ren 		break;
256*081860b9SGuo Ren 	case OP_STH:
257*081860b9SGuo Ren 		addr = get_ptreg(regs, rx) + (imm << 1);
258*081860b9SGuo Ren 		ret = sth_c(regs, rz, addr);
259*081860b9SGuo Ren 		break;
260*081860b9SGuo Ren 	case OP_STW:
261*081860b9SGuo Ren 		addr = get_ptreg(regs, rx) + (imm << 2);
262*081860b9SGuo Ren 		ret = stw_c(regs, rz, addr);
263*081860b9SGuo Ren 		break;
264*081860b9SGuo Ren 	}
265*081860b9SGuo Ren 
266*081860b9SGuo Ren 	if (ret)
267*081860b9SGuo Ren 		goto bad_area;
268*081860b9SGuo Ren 
269*081860b9SGuo Ren 	regs->pc += 2;
270*081860b9SGuo Ren 
271*081860b9SGuo Ren 	return;
272*081860b9SGuo Ren 
273*081860b9SGuo Ren bad_area:
274*081860b9SGuo Ren 	if (!user_mode(regs)) {
275*081860b9SGuo Ren 		if (fixup_exception(regs))
276*081860b9SGuo Ren 			return;
277*081860b9SGuo Ren 
278*081860b9SGuo Ren 		bust_spinlocks(1);
279*081860b9SGuo Ren 		pr_alert("%s opcode: %x, rz: %d, rx: %d, imm: %d, addr: %x.\n",
280*081860b9SGuo Ren 				__func__, opcode, rz, rx, imm, addr);
281*081860b9SGuo Ren 		show_regs(regs);
282*081860b9SGuo Ren 		bust_spinlocks(0);
283*081860b9SGuo Ren 		do_exit(SIGKILL);
284*081860b9SGuo Ren 	}
285*081860b9SGuo Ren 
286*081860b9SGuo Ren 	force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current);
287*081860b9SGuo Ren }
288*081860b9SGuo Ren 
289*081860b9SGuo Ren static struct ctl_table alignment_tbl[4] = {
290*081860b9SGuo Ren 	{
291*081860b9SGuo Ren 		.procname = "enable",
292*081860b9SGuo Ren 		.data = &align_enable,
293*081860b9SGuo Ren 		.maxlen = sizeof(align_enable),
294*081860b9SGuo Ren 		.mode = 0666,
295*081860b9SGuo Ren 		.proc_handler = &proc_dointvec
296*081860b9SGuo Ren 	},
297*081860b9SGuo Ren 	{
298*081860b9SGuo Ren 		.procname = "count",
299*081860b9SGuo Ren 		.data = &align_count,
300*081860b9SGuo Ren 		.maxlen = sizeof(align_count),
301*081860b9SGuo Ren 		.mode = 0666,
302*081860b9SGuo Ren 		.proc_handler = &proc_dointvec
303*081860b9SGuo Ren 	},
304*081860b9SGuo Ren 	{}
305*081860b9SGuo Ren };
306*081860b9SGuo Ren 
307*081860b9SGuo Ren static struct ctl_table sysctl_table[2] = {
308*081860b9SGuo Ren 	{
309*081860b9SGuo Ren 	 .procname = "csky_alignment",
310*081860b9SGuo Ren 	 .mode = 0555,
311*081860b9SGuo Ren 	 .child = alignment_tbl},
312*081860b9SGuo Ren 	{}
313*081860b9SGuo Ren };
314*081860b9SGuo Ren 
315*081860b9SGuo Ren static struct ctl_path sysctl_path[2] = {
316*081860b9SGuo Ren 	{.procname = "csky"},
317*081860b9SGuo Ren 	{}
318*081860b9SGuo Ren };
319*081860b9SGuo Ren 
320*081860b9SGuo Ren static int __init csky_alignment_init(void)
321*081860b9SGuo Ren {
322*081860b9SGuo Ren 	register_sysctl_paths(sysctl_path, sysctl_table);
323*081860b9SGuo Ren 	return 0;
324*081860b9SGuo Ren }
325*081860b9SGuo Ren 
326*081860b9SGuo Ren arch_initcall(csky_alignment_init);
327