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