ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ppc / kernel / align.c
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
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 __user *addr;
193         union {
194                 long l;
195                 float f;
196                 double d;
197                 unsigned char v[8];
198         } data;
199
200         CHECK_FULL_REGS(regs);
201
202 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4)
203         /* The 4xx-family processors have no DSISR register,
204          * so we emulate it.
205          * The POWER4 has a DSISR register but doesn't set it on
206          * an alignment fault.  -- paulus
207          */
208
209         instr = *((unsigned int *)regs->nip);
210         opcode = OPCD(instr);
211         reg = RS(instr);
212         areg = RA(instr);
213
214         if (!IS_XFORM(opcode)) {
215                 f1 = 0;
216                 f2 = (instr & 0x04000000) >> 26;
217                 f3 = (instr & 0x78000000) >> 27;
218         } else {
219                 f1 = (instr & 0x00000006) >> 1;
220                 f2 = (instr & 0x00000040) >> 6;
221                 f3 = (instr & 0x00000780) >> 7;
222         }
223
224         instr = ((f1 << 5) | (f2 << 4) | f3);
225 #else
226         reg = (regs->dsisr >> 5) & 0x1f;        /* source/dest register */
227         areg = regs->dsisr & 0x1f;              /* register to update */
228         instr = (regs->dsisr >> 10) & 0x7f;
229 #endif
230
231         nb = aligninfo[instr].len;
232         if (nb == 0) {
233                 long *p;
234                 int i;
235
236                 if (instr != DCBZ)
237                         return 0;       /* too hard or invalid instruction */
238                 /*
239                  * The dcbz (data cache block zero) instruction
240                  * gives an alignment fault if used on non-cacheable
241                  * memory.  We handle the fault mainly for the
242                  * case when we are running with the cache disabled
243                  * for debugging.
244                  */
245                 p = (long *) (regs->dar & -L1_CACHE_BYTES);
246                 for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i)
247                         p[i] = 0;
248                 return 1;
249         }
250
251         flags = aligninfo[instr].flags;
252
253         /* For the 4xx-family processors, the 'dar' field of the
254          * pt_regs structure is overloaded and is really from the DEAR.
255          */
256
257         addr = (unsigned char __user *)regs->dar;
258
259         /* Verify the address of the operand */
260         if (user_mode(regs)) {
261                 if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb))
262                         return -EFAULT; /* bad address */
263         }
264
265         if (flags & F) {
266                 preempt_disable();
267                 if (regs->msr & MSR_FP)
268                         giveup_fpu(current);
269                 preempt_enable();
270         }
271         if (flags & M)
272                 return 0;               /* too hard for now */
273
274         /* If we read the operand, copy it in */
275         if (flags & LD) {
276                 if (nb == 2) {
277                         data.v[0] = data.v[1] = 0;
278                         if (__get_user(data.v[2], addr)
279                             || __get_user(data.v[3], addr+1))
280                                 return -EFAULT;
281                 } else {
282                         for (i = 0; i < nb; ++i)
283                                 if (__get_user(data.v[i], addr+i))
284                                         return -EFAULT;
285                 }
286         }
287
288         switch (flags & ~U) {
289         case LD+SE:
290                 if (data.v[2] >= 0x80)
291                         data.v[0] = data.v[1] = -1;
292                 /* fall through */
293         case LD:
294                 regs->gpr[reg] = data.l;
295                 break;
296         case LD+S:
297                 if (nb == 2) {
298                         SWAP(data.v[2], data.v[3]);
299                 } else {
300                         SWAP(data.v[0], data.v[3]);
301                         SWAP(data.v[1], data.v[2]);
302                 }
303                 regs->gpr[reg] = data.l;
304                 break;
305         case ST:
306                 data.l = regs->gpr[reg];
307                 break;
308         case ST+S:
309                 data.l = regs->gpr[reg];
310                 if (nb == 2) {
311                         SWAP(data.v[2], data.v[3]);
312                 } else {
313                         SWAP(data.v[0], data.v[3]);
314                         SWAP(data.v[1], data.v[2]);
315                 }
316                 break;
317         case LD+F:
318                 current->thread.fpr[reg] = data.d;
319                 break;
320         case ST+F:
321                 data.d = current->thread.fpr[reg];
322                 break;
323         /* these require some floating point conversions... */
324         /* we'd like to use the assignment, but we have to compile
325          * the kernel with -msoft-float so it doesn't use the
326          * fp regs for copying 8-byte objects. */
327         case LD+F+S:
328                 preempt_disable();
329                 enable_kernel_fp();
330                 cvt_fd(&data.f, &current->thread.fpr[reg], &current->thread.fpscr);
331                 /* current->thread.fpr[reg] = data.f; */
332                 preempt_enable();
333                 break;
334         case ST+F+S:
335                 preempt_disable();
336                 enable_kernel_fp();
337                 cvt_df(&current->thread.fpr[reg], &data.f, &current->thread.fpscr);
338                 /* data.f = current->thread.fpr[reg]; */
339                 preempt_enable();
340                 break;
341         default:
342                 printk("align: can't handle flags=%x\n", flags);
343                 return 0;
344         }
345
346         if (flags & ST) {
347                 if (nb == 2) {
348                         if (__put_user(data.v[2], addr)
349                             || __put_user(data.v[3], addr+1))
350                                 return -EFAULT;
351                 } else {
352                         for (i = 0; i < nb; ++i)
353                                 if (__put_user(data.v[i], addr+i))
354                                         return -EFAULT;
355                 }
356         }
357
358         if (flags & U) {
359                 regs->gpr[areg] = regs->dar;
360         }
361
362         return 1;
363 }