• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011-2012 Synopsys (www.synopsys.com)
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  * vineetg : May 2011
9  *  -Adapted (from .26 to .35)
10  *  -original contribution by Tim.yao@amlogic.com
11  *
12  */
13 
14 #include <linux/types.h>
15 #include <linux/ptrace.h>
16 #include <linux/uaccess.h>
17 #include <asm/disasm.h>
18 
19 #define __get8_unaligned_check(val, addr, err)		\
20 	__asm__(					\
21 	"1:	ldb.ab	%1, [%2, 1]\n"			\
22 	"2:\n"						\
23 	"	.section .fixup,\"ax\"\n"		\
24 	"	.align	4\n"				\
25 	"3:	mov	%0, 1\n"			\
26 	"	b	2b\n"				\
27 	"	.previous\n"				\
28 	"	.section __ex_table,\"a\"\n"		\
29 	"	.align	4\n"				\
30 	"	.long	1b, 3b\n"			\
31 	"	.previous\n"				\
32 	: "=r" (err), "=&r" (val), "=r" (addr)		\
33 	: "0" (err), "2" (addr))
34 
35 #define get16_unaligned_check(val, addr)		\
36 	do {						\
37 		unsigned int err = 0, v, a = addr;	\
38 		__get8_unaligned_check(v, a, err);	\
39 		val =  v ;				\
40 		__get8_unaligned_check(v, a, err);	\
41 		val |= v << 8;				\
42 		if (err)				\
43 			goto fault;			\
44 	} while (0)
45 
46 #define get32_unaligned_check(val, addr)		\
47 	do {						\
48 		unsigned int err = 0, v, a = addr;	\
49 		__get8_unaligned_check(v, a, err);	\
50 		val =  v << 0;				\
51 		__get8_unaligned_check(v, a, err);	\
52 		val |= v << 8;				\
53 		__get8_unaligned_check(v, a, err);	\
54 		val |= v << 16;				\
55 		__get8_unaligned_check(v, a, err);	\
56 		val |= v << 24;				\
57 		if (err)				\
58 			goto fault;			\
59 	} while (0)
60 
61 #define put16_unaligned_check(val, addr)		\
62 	do {						\
63 		unsigned int err = 0, v = val, a = addr;\
64 							\
65 		__asm__(				\
66 		"1:	stb.ab	%1, [%2, 1]\n"		\
67 		"	lsr %1, %1, 8\n"		\
68 		"2:	stb	%1, [%2]\n"		\
69 		"3:\n"					\
70 		"	.section .fixup,\"ax\"\n"	\
71 		"	.align	4\n"			\
72 		"4:	mov	%0, 1\n"		\
73 		"	b	3b\n"			\
74 		"	.previous\n"			\
75 		"	.section __ex_table,\"a\"\n"	\
76 		"	.align	4\n"			\
77 		"	.long	1b, 4b\n"		\
78 		"	.long	2b, 4b\n"		\
79 		"	.previous\n"			\
80 		: "=r" (err), "=&r" (v), "=&r" (a)	\
81 		: "0" (err), "1" (v), "2" (a));		\
82 							\
83 		if (err)				\
84 			goto fault;			\
85 	} while (0)
86 
87 #define put32_unaligned_check(val, addr)		\
88 	do {						\
89 		unsigned int err = 0, v = val, a = addr;\
90 		__asm__(				\
91 							\
92 		"1:	stb.ab	%1, [%2, 1]\n"		\
93 		"	lsr %1, %1, 8\n"		\
94 		"2:	stb.ab	%1, [%2, 1]\n"		\
95 		"	lsr %1, %1, 8\n"		\
96 		"3:	stb.ab	%1, [%2, 1]\n"		\
97 		"	lsr %1, %1, 8\n"		\
98 		"4:	stb	%1, [%2]\n"		\
99 		"5:\n"					\
100 		"	.section .fixup,\"ax\"\n"	\
101 		"	.align	4\n"			\
102 		"6:	mov	%0, 1\n"		\
103 		"	b	5b\n"			\
104 		"	.previous\n"			\
105 		"	.section __ex_table,\"a\"\n"	\
106 		"	.align	4\n"			\
107 		"	.long	1b, 6b\n"		\
108 		"	.long	2b, 6b\n"		\
109 		"	.long	3b, 6b\n"		\
110 		"	.long	4b, 6b\n"		\
111 		"	.previous\n"			\
112 		: "=r" (err), "=&r" (v), "=&r" (a)	\
113 		: "0" (err), "1" (v), "2" (a));		\
114 							\
115 		if (err)				\
116 			goto fault;			\
117 	} while (0)
118 
119 /* sysctl hooks */
120 int unaligned_enabled __read_mostly = 1;	/* Enabled by default */
121 int no_unaligned_warning __read_mostly = 1;	/* Only 1 warning by default */
122 
fixup_load(struct disasm_state * state,struct pt_regs * regs,struct callee_regs * cregs)123 static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
124 			struct callee_regs *cregs)
125 {
126 	int val;
127 
128 	/* register write back */
129 	if ((state->aa == 1) || (state->aa == 2)) {
130 		set_reg(state->wb_reg, state->src1 + state->src2, regs, cregs);
131 
132 		if (state->aa == 2)
133 			state->src2 = 0;
134 	}
135 
136 	if (state->zz == 0) {
137 		get32_unaligned_check(val, state->src1 + state->src2);
138 	} else {
139 		get16_unaligned_check(val, state->src1 + state->src2);
140 
141 		if (state->x)
142 			val = (val << 16) >> 16;
143 	}
144 
145 	if (state->pref == 0)
146 		set_reg(state->dest, val, regs, cregs);
147 
148 	return;
149 
150 fault:	state->fault = 1;
151 }
152 
fixup_store(struct disasm_state * state,struct pt_regs * regs,struct callee_regs * cregs)153 static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
154 			struct callee_regs *cregs)
155 {
156 	/* register write back */
157 	if ((state->aa == 1) || (state->aa == 2)) {
158 		set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
159 
160 		if (state->aa == 3)
161 			state->src3 = 0;
162 	} else if (state->aa == 3) {
163 		if (state->zz == 2) {
164 			set_reg(state->wb_reg, state->src2 + (state->src3 << 1),
165 				regs, cregs);
166 		} else if (!state->zz) {
167 			set_reg(state->wb_reg, state->src2 + (state->src3 << 2),
168 				regs, cregs);
169 		} else {
170 			goto fault;
171 		}
172 	}
173 
174 	/* write fix-up */
175 	if (!state->zz)
176 		put32_unaligned_check(state->src1, state->src2 + state->src3);
177 	else
178 		put16_unaligned_check(state->src1, state->src2 + state->src3);
179 
180 	return;
181 
182 fault:	state->fault = 1;
183 }
184 
185 /*
186  * Handle an unaligned access
187  * Returns 0 if successfully handled, 1 if some error happened
188  */
misaligned_fixup(unsigned long address,struct pt_regs * regs,unsigned long cause,struct callee_regs * cregs)189 int misaligned_fixup(unsigned long address, struct pt_regs *regs,
190 		     unsigned long cause, struct callee_regs *cregs)
191 {
192 	struct disasm_state state;
193 	char buf[TASK_COMM_LEN];
194 
195 	/* handle user mode only and only if enabled by sysadmin */
196 	if (!user_mode(regs) || !unaligned_enabled)
197 		return 1;
198 
199 	if (no_unaligned_warning) {
200 		pr_warn_once("%s(%d) made unaligned access which was emulated"
201 			     " by kernel assist\n. This can degrade application"
202 			     " performance significantly\n. To enable further"
203 			     " logging of such instances, please \n"
204 			     " echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n",
205 			     get_task_comm(buf, current), task_pid_nr(current));
206 	} else {
207 		/* Add rate limiting if it gets down to it */
208 		pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n",
209 			get_task_comm(buf, current), task_pid_nr(current),
210 			address, regs->ret);
211 
212 	}
213 
214 	disasm_instr(regs->ret, &state, 1, regs, cregs);
215 
216 	if (state.fault)
217 		goto fault;
218 
219 	/* ldb/stb should not have unaligned exception */
220 	if ((state.zz == 1) || (state.di))
221 		goto fault;
222 
223 	if (!state.write)
224 		fixup_load(&state, regs, cregs);
225 	else
226 		fixup_store(&state, regs, cregs);
227 
228 	if (state.fault)
229 		goto fault;
230 
231 	if (delay_mode(regs)) {
232 		regs->ret = regs->bta;
233 		regs->status32 &= ~STATUS_DE_MASK;
234 	} else {
235 		regs->ret += state.instr_len;
236 	}
237 
238 	return 0;
239 
240 fault:
241 	pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
242 		state.words[0], address);
243 
244 	return 1;
245 }
246