1 /*
2  * align.c - handle alignment exceptions for the Power PC.
3  *
4  * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
5  * Copyright (c) 1998-1999 TiVo, Inc.
6  *   PowerPC 403GCX modifications.
7  * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
8  *   PowerPC 403GCX/405GP modifications.
9  */
10 #include <linux/config.h>
11 #include <linux/kernel.h>
12 #include <linux/mm.h>
13 #include <asm/ptrace.h>
14 #include <asm/processor.h>
15 #include <asm/uaccess.h>
16 #include <asm/system.h>
17 #include <asm/cache.h>
18 
19 struct aligninfo {
20 	unsigned char len;
21 	unsigned char flags;
22 };
23 
24 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
25 #define	OPCD(inst)	(((inst) & 0xFC000000) >> 26)
26 #define	RS(inst)	(((inst) & 0x03E00000) >> 21)
27 #define	RA(inst)	(((inst) & 0x001F0000) >> 16)
28 #define	IS_XFORM(code)	((code) == 31)
29 #endif
30 
31 #define INVALID	{ 0, 0 }
32 
33 #define LD	1	/* load */
34 #define ST	2	/* store */
35 #define	SE	4	/* sign-extend value */
36 #define F	8	/* to/from fp regs */
37 #define U	0x10	/* update index register */
38 #define M	0x20	/* multiple load/store */
39 #define S	0x40	/* single-precision fp, or byte-swap value */
40 #define HARD	0x80	/* string, stwcx. */
41 
42 #define DCBZ	0x5f	/* 8xx/82xx dcbz faults when cache not enabled */
43 
44 /*
45  * The PowerPC stores certain bits of the instruction that caused the
46  * alignment exception in the DSISR register.  This array maps those
47  * bits to information about the operand length and what the
48  * instruction would do.
49  */
50 static struct aligninfo aligninfo[128] = {
51 	{ 4, LD },		/* 00 0 0000: lwz / lwarx */
52 	INVALID,		/* 00 0 0001 */
53 	{ 4, ST },		/* 00 0 0010: stw */
54 	INVALID,		/* 00 0 0011 */
55 	{ 2, LD },		/* 00 0 0100: lhz */
56 	{ 2, LD+SE },		/* 00 0 0101: lha */
57 	{ 2, ST },		/* 00 0 0110: sth */
58 	{ 4, LD+M },		/* 00 0 0111: lmw */
59 	{ 4, LD+F+S },		/* 00 0 1000: lfs */
60 	{ 8, LD+F },		/* 00 0 1001: lfd */
61 	{ 4, ST+F+S },		/* 00 0 1010: stfs */
62 	{ 8, ST+F },		/* 00 0 1011: stfd */
63 	INVALID,		/* 00 0 1100 */
64 	INVALID,		/* 00 0 1101: ld/ldu/lwa */
65 	INVALID,		/* 00 0 1110 */
66 	INVALID,		/* 00 0 1111: std/stdu */
67 	{ 4, LD+U },		/* 00 1 0000: lwzu */
68 	INVALID,		/* 00 1 0001 */
69 	{ 4, ST+U },		/* 00 1 0010: stwu */
70 	INVALID,		/* 00 1 0011 */
71 	{ 2, LD+U },		/* 00 1 0100: lhzu */
72 	{ 2, LD+SE+U },		/* 00 1 0101: lhau */
73 	{ 2, ST+U },		/* 00 1 0110: sthu */
74 	{ 4, ST+M },		/* 00 1 0111: stmw */
75 	{ 4, LD+F+S+U },	/* 00 1 1000: lfsu */
76 	{ 8, LD+F+U },		/* 00 1 1001: lfdu */
77 	{ 4, ST+F+S+U },	/* 00 1 1010: stfsu */
78 	{ 8, ST+F+U },		/* 00 1 1011: stfdu */
79 	INVALID,		/* 00 1 1100 */
80 	INVALID,		/* 00 1 1101 */
81 	INVALID,		/* 00 1 1110 */
82 	INVALID,		/* 00 1 1111 */
83 	INVALID,		/* 01 0 0000: ldx */
84 	INVALID,		/* 01 0 0001 */
85 	INVALID,		/* 01 0 0010: stdx */
86 	INVALID,		/* 01 0 0011 */
87 	INVALID,		/* 01 0 0100 */
88 	INVALID,		/* 01 0 0101: lwax */
89 	INVALID,		/* 01 0 0110 */
90 	INVALID,		/* 01 0 0111 */
91 	{ 0, LD+HARD },		/* 01 0 1000: lswx */
92 	{ 0, LD+HARD },		/* 01 0 1001: lswi */
93 	{ 0, ST+HARD },		/* 01 0 1010: stswx */
94 	{ 0, ST+HARD },		/* 01 0 1011: stswi */
95 	INVALID,		/* 01 0 1100 */
96 	INVALID,		/* 01 0 1101 */
97 	INVALID,		/* 01 0 1110 */
98 	INVALID,		/* 01 0 1111 */
99 	INVALID,		/* 01 1 0000: ldux */
100 	INVALID,		/* 01 1 0001 */
101 	INVALID,		/* 01 1 0010: stdux */
102 	INVALID,		/* 01 1 0011 */
103 	INVALID,		/* 01 1 0100 */
104 	INVALID,		/* 01 1 0101: lwaux */
105 	INVALID,		/* 01 1 0110 */
106 	INVALID,		/* 01 1 0111 */
107 	INVALID,		/* 01 1 1000 */
108 	INVALID,		/* 01 1 1001 */
109 	INVALID,		/* 01 1 1010 */
110 	INVALID,		/* 01 1 1011 */
111 	INVALID,		/* 01 1 1100 */
112 	INVALID,		/* 01 1 1101 */
113 	INVALID,		/* 01 1 1110 */
114 	INVALID,		/* 01 1 1111 */
115 	INVALID,		/* 10 0 0000 */
116 	INVALID,		/* 10 0 0001 */
117 	{ 0, ST+HARD },		/* 10 0 0010: stwcx. */
118 	INVALID,		/* 10 0 0011 */
119 	INVALID,		/* 10 0 0100 */
120 	INVALID,		/* 10 0 0101 */
121 	INVALID,		/* 10 0 0110 */
122 	INVALID,		/* 10 0 0111 */
123 	{ 4, LD+S },		/* 10 0 1000: lwbrx */
124 	INVALID,		/* 10 0 1001 */
125 	{ 4, ST+S },		/* 10 0 1010: stwbrx */
126 	INVALID,		/* 10 0 1011 */
127 	{ 2, LD+S },		/* 10 0 1100: lhbrx */
128 	INVALID,		/* 10 0 1101 */
129 	{ 2, ST+S },		/* 10 0 1110: sthbrx */
130 	INVALID,		/* 10 0 1111 */
131 	INVALID,		/* 10 1 0000 */
132 	INVALID,		/* 10 1 0001 */
133 	INVALID,		/* 10 1 0010 */
134 	INVALID,		/* 10 1 0011 */
135 	INVALID,		/* 10 1 0100 */
136 	INVALID,		/* 10 1 0101 */
137 	INVALID,		/* 10 1 0110 */
138 	INVALID,		/* 10 1 0111 */
139 	INVALID,		/* 10 1 1000 */
140 	INVALID,		/* 10 1 1001 */
141 	INVALID,		/* 10 1 1010 */
142 	INVALID,		/* 10 1 1011 */
143 	INVALID,		/* 10 1 1100 */
144 	INVALID,		/* 10 1 1101 */
145 	INVALID,		/* 10 1 1110 */
146 	{ 0, ST+HARD },		/* 10 1 1111: dcbz */
147 	{ 4, LD },		/* 11 0 0000: lwzx */
148 	INVALID,		/* 11 0 0001 */
149 	{ 4, ST },		/* 11 0 0010: stwx */
150 	INVALID,		/* 11 0 0011 */
151 	{ 2, LD },		/* 11 0 0100: lhzx */
152 	{ 2, LD+SE },		/* 11 0 0101: lhax */
153 	{ 2, ST },		/* 11 0 0110: sthx */
154 	INVALID,		/* 11 0 0111 */
155 	{ 4, LD+F+S },		/* 11 0 1000: lfsx */
156 	{ 8, LD+F },		/* 11 0 1001: lfdx */
157 	{ 4, ST+F+S },		/* 11 0 1010: stfsx */
158 	{ 8, ST+F },		/* 11 0 1011: stfdx */
159 	INVALID,		/* 11 0 1100 */
160 	INVALID,		/* 11 0 1101: lmd */
161 	INVALID,		/* 11 0 1110 */
162 	INVALID,		/* 11 0 1111: stmd */
163 	{ 4, LD+U },		/* 11 1 0000: lwzux */
164 	INVALID,		/* 11 1 0001 */
165 	{ 4, ST+U },		/* 11 1 0010: stwux */
166 	INVALID,		/* 11 1 0011 */
167 	{ 2, LD+U },		/* 11 1 0100: lhzux */
168 	{ 2, LD+SE+U },		/* 11 1 0101: lhaux */
169 	{ 2, ST+U },		/* 11 1 0110: sthux */
170 	INVALID,		/* 11 1 0111 */
171 	{ 4, LD+F+S+U },	/* 11 1 1000: lfsux */
172 	{ 8, LD+F+U },		/* 11 1 1001: lfdux */
173 	{ 4, ST+F+S+U },	/* 11 1 1010: stfsux */
174 	{ 8, ST+F+U },		/* 11 1 1011: stfdux */
175 	INVALID,		/* 11 1 1100 */
176 	INVALID,		/* 11 1 1101 */
177 	INVALID,		/* 11 1 1110 */
178 	INVALID,		/* 11 1 1111 */
179 };
180 
181 #define SWAP(a, b)	(t = (a), (a) = (b), (b) = t)
182 
183 int
fix_alignment(struct pt_regs * regs)184 fix_alignment(struct pt_regs *regs)
185 {
186 	int instr, nb, flags;
187 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
188 	int opcode, f1, f2, f3;
189 #endif
190 	int i, t;
191 	int reg, areg;
192 	unsigned char *addr;
193 	union {
194 		long l;
195 		float f;
196 		double d;
197 		unsigned char v[8];
198 	} data;
199 
200 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
201 	/* The 4xx-family processors have no DSISR register,
202 	 * so we emulate it.
203 	 * The POWER4 has a DSISR register but doesn't set it on
204 	 * an alignment fault.  -- paulus
205 	 */
206 
207 	instr = *((unsigned int *)regs->nip);
208 	opcode = OPCD(instr);
209 	reg = RS(instr);
210 	areg = RA(instr);
211 
212 	if (!IS_XFORM(opcode)) {
213 		f1 = 0;
214 		f2 = (instr & 0x04000000) >> 26;
215 		f3 = (instr & 0x78000000) >> 27;
216 	} else {
217 		f1 = (instr & 0x00000006) >> 1;
218 		f2 = (instr & 0x00000040) >> 6;
219 		f3 = (instr & 0x00000780) >> 7;
220 	}
221 
222 	instr = ((f1 << 5) | (f2 << 4) | f3);
223 #else
224 	reg = (regs->dsisr >> 5) & 0x1f;	/* source/dest register */
225 	areg = regs->dsisr & 0x1f;		/* register to update */
226 	instr = (regs->dsisr >> 10) & 0x7f;
227 #endif
228 
229 	nb = aligninfo[instr].len;
230 	if (nb == 0) {
231 		long *p;
232 		int i;
233 
234 		if (instr != DCBZ)
235 			return 0;	/* too hard or invalid instruction */
236 		/*
237 		 * The dcbz (data cache block zero) instruction
238 		 * gives an alignment fault if used on non-cacheable
239 		 * memory.  We handle the fault mainly for the
240 		 * case when we are running with the cache disabled
241 		 * for debugging.
242 		 */
243 		p = (long *) (regs->dar & -L1_CACHE_BYTES);
244 		for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i)
245 			p[i] = 0;
246 		return 1;
247 	}
248 
249 	flags = aligninfo[instr].flags;
250 
251 	/* For the 4xx-family processors, the 'dar' field of the
252 	 * pt_regs structure is overloaded and is really from the DEAR.
253 	 */
254 
255 	addr = (unsigned char *)regs->dar;
256 
257 	/* Verify the address of the operand */
258 	if (user_mode(regs)) {
259 		if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb))
260 			return -EFAULT;	/* bad address */
261 	}
262 
263 	if ((flags & F) && (regs->msr & MSR_FP))
264 		giveup_fpu(current);
265 	if (flags & M)
266 		return 0;		/* too hard for now */
267 
268 	/* If we read the operand, copy it in */
269 	if (flags & LD) {
270 		if (nb == 2) {
271 			data.v[0] = data.v[1] = 0;
272 			if (__get_user(data.v[2], addr)
273 			    || __get_user(data.v[3], addr+1))
274 				return -EFAULT;
275 		} else {
276 			for (i = 0; i < nb; ++i)
277 				if (__get_user(data.v[i], addr+i))
278 					return -EFAULT;
279 		}
280 	}
281 
282 	switch (flags & ~U) {
283 	case LD+SE:
284 		if (data.v[2] >= 0x80)
285 			data.v[0] = data.v[1] = -1;
286 		/* fall through */
287 	case LD:
288 		regs->gpr[reg] = data.l;
289 		break;
290 	case LD+S:
291 		if (nb == 2) {
292 			SWAP(data.v[2], data.v[3]);
293 		} else {
294 			SWAP(data.v[0], data.v[3]);
295 			SWAP(data.v[1], data.v[2]);
296 		}
297 		regs->gpr[reg] = data.l;
298 		break;
299 	case ST:
300 		data.l = regs->gpr[reg];
301 		break;
302 	case ST+S:
303 		data.l = regs->gpr[reg];
304 		if (nb == 2) {
305 			SWAP(data.v[2], data.v[3]);
306 		} else {
307 			SWAP(data.v[0], data.v[3]);
308 			SWAP(data.v[1], data.v[2]);
309 		}
310 		break;
311 	case LD+F:
312 		current->thread.fpr[reg] = data.d;
313 		break;
314 	case ST+F:
315 		data.d = current->thread.fpr[reg];
316 		break;
317 	/* these require some floating point conversions... */
318 	/* we'd like to use the assignment, but we have to compile
319 	 * the kernel with -msoft-float so it doesn't use the
320 	 * fp regs for copying 8-byte objects. */
321 	case LD+F+S:
322 		enable_kernel_fp();
323 		cvt_fd(&data.f, &current->thread.fpr[reg], &current->thread.fpscr);
324 		/* current->thread.fpr[reg] = data.f; */
325 		break;
326 	case ST+F+S:
327 		enable_kernel_fp();
328 		cvt_df(&current->thread.fpr[reg], &data.f, &current->thread.fpscr);
329 		/* data.f = current->thread.fpr[reg]; */
330 		break;
331 	default:
332 		printk("align: can't handle flags=%x\n", flags);
333 		return 0;
334 	}
335 
336 	if (flags & ST) {
337 		if (nb == 2) {
338 			if (__put_user(data.v[2], addr)
339 			    || __put_user(data.v[3], addr+1))
340 				return -EFAULT;
341 		} else {
342 			for (i = 0; i < nb; ++i)
343 				if (__put_user(data.v[i], addr+i))
344 					return -EFAULT;
345 		}
346 	}
347 
348 	if (flags & U) {
349 		regs->gpr[areg] = regs->dar;
350 	}
351 
352 	return 1;
353 }
354