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, ¤t->thread.fpr[reg], ¤t->thread.fpscr);
324 /* current->thread.fpr[reg] = data.f; */
325 break;
326 case ST+F+S:
327 enable_kernel_fp();
328 cvt_df(¤t->thread.fpr[reg], &data.f, ¤t->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