Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.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) || defined(CONFIG_BOOKE)
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 SX      0x40    /* byte count in XER */
41 #define HARD    0x80    /* string, stwcx. */
42
43 #define DCBZ    0x5f    /* 8xx/82xx dcbz faults when cache not enabled */
44
45 /*
46  * The PowerPC stores certain bits of the instruction that caused the
47  * alignment exception in the DSISR register.  This array maps those
48  * bits to information about the operand length and what the
49  * instruction would do.
50  */
51 static struct aligninfo aligninfo[128] = {
52         { 4, LD },              /* 00 0 0000: lwz / lwarx */
53         INVALID,                /* 00 0 0001 */
54         { 4, ST },              /* 00 0 0010: stw */
55         INVALID,                /* 00 0 0011 */
56         { 2, LD },              /* 00 0 0100: lhz */
57         { 2, LD+SE },           /* 00 0 0101: lha */
58         { 2, ST },              /* 00 0 0110: sth */
59         { 4, LD+M },            /* 00 0 0111: lmw */
60         { 4, LD+F+S },          /* 00 0 1000: lfs */
61         { 8, LD+F },            /* 00 0 1001: lfd */
62         { 4, ST+F+S },          /* 00 0 1010: stfs */
63         { 8, ST+F },            /* 00 0 1011: stfd */
64         INVALID,                /* 00 0 1100 */
65         INVALID,                /* 00 0 1101: ld/ldu/lwa */
66         INVALID,                /* 00 0 1110 */
67         INVALID,                /* 00 0 1111: std/stdu */
68         { 4, LD+U },            /* 00 1 0000: lwzu */
69         INVALID,                /* 00 1 0001 */
70         { 4, ST+U },            /* 00 1 0010: stwu */
71         INVALID,                /* 00 1 0011 */
72         { 2, LD+U },            /* 00 1 0100: lhzu */
73         { 2, LD+SE+U },         /* 00 1 0101: lhau */
74         { 2, ST+U },            /* 00 1 0110: sthu */
75         { 4, ST+M },            /* 00 1 0111: stmw */
76         { 4, LD+F+S+U },        /* 00 1 1000: lfsu */
77         { 8, LD+F+U },          /* 00 1 1001: lfdu */
78         { 4, ST+F+S+U },        /* 00 1 1010: stfsu */
79         { 8, ST+F+U },          /* 00 1 1011: stfdu */
80         INVALID,                /* 00 1 1100 */
81         INVALID,                /* 00 1 1101 */
82         INVALID,                /* 00 1 1110 */
83         INVALID,                /* 00 1 1111 */
84         INVALID,                /* 01 0 0000: ldx */
85         INVALID,                /* 01 0 0001 */
86         INVALID,                /* 01 0 0010: stdx */
87         INVALID,                /* 01 0 0011 */
88         INVALID,                /* 01 0 0100 */
89         INVALID,                /* 01 0 0101: lwax */
90         INVALID,                /* 01 0 0110 */
91         INVALID,                /* 01 0 0111 */
92         { 4, LD+M+HARD+SX },    /* 01 0 1000: lswx */
93         { 4, LD+M+HARD },       /* 01 0 1001: lswi */
94         { 4, ST+M+HARD+SX },    /* 01 0 1010: stswx */
95         { 4, ST+M+HARD },       /* 01 0 1011: stswi */
96         INVALID,                /* 01 0 1100 */
97         INVALID,                /* 01 0 1101 */
98         INVALID,                /* 01 0 1110 */
99         INVALID,                /* 01 0 1111 */
100         INVALID,                /* 01 1 0000: ldux */
101         INVALID,                /* 01 1 0001 */
102         INVALID,                /* 01 1 0010: stdux */
103         INVALID,                /* 01 1 0011 */
104         INVALID,                /* 01 1 0100 */
105         INVALID,                /* 01 1 0101: lwaux */
106         INVALID,                /* 01 1 0110 */
107         INVALID,                /* 01 1 0111 */
108         INVALID,                /* 01 1 1000 */
109         INVALID,                /* 01 1 1001 */
110         INVALID,                /* 01 1 1010 */
111         INVALID,                /* 01 1 1011 */
112         INVALID,                /* 01 1 1100 */
113         INVALID,                /* 01 1 1101 */
114         INVALID,                /* 01 1 1110 */
115         INVALID,                /* 01 1 1111 */
116         INVALID,                /* 10 0 0000 */
117         INVALID,                /* 10 0 0001 */
118         { 0, ST+HARD },         /* 10 0 0010: stwcx. */
119         INVALID,                /* 10 0 0011 */
120         INVALID,                /* 10 0 0100 */
121         INVALID,                /* 10 0 0101 */
122         INVALID,                /* 10 0 0110 */
123         INVALID,                /* 10 0 0111 */
124         { 4, LD+S },            /* 10 0 1000: lwbrx */
125         INVALID,                /* 10 0 1001 */
126         { 4, ST+S },            /* 10 0 1010: stwbrx */
127         INVALID,                /* 10 0 1011 */
128         { 2, LD+S },            /* 10 0 1100: lhbrx */
129         INVALID,                /* 10 0 1101 */
130         { 2, ST+S },            /* 10 0 1110: sthbrx */
131         INVALID,                /* 10 0 1111 */
132         INVALID,                /* 10 1 0000 */
133         INVALID,                /* 10 1 0001 */
134         INVALID,                /* 10 1 0010 */
135         INVALID,                /* 10 1 0011 */
136         INVALID,                /* 10 1 0100 */
137         INVALID,                /* 10 1 0101 */
138         INVALID,                /* 10 1 0110 */
139         INVALID,                /* 10 1 0111 */
140         INVALID,                /* 10 1 1000 */
141         INVALID,                /* 10 1 1001 */
142         INVALID,                /* 10 1 1010 */
143         INVALID,                /* 10 1 1011 */
144         INVALID,                /* 10 1 1100 */
145         INVALID,                /* 10 1 1101 */
146         INVALID,                /* 10 1 1110 */
147         { 0, ST+HARD },         /* 10 1 1111: dcbz */
148         { 4, LD },              /* 11 0 0000: lwzx */
149         INVALID,                /* 11 0 0001 */
150         { 4, ST },              /* 11 0 0010: stwx */
151         INVALID,                /* 11 0 0011 */
152         { 2, LD },              /* 11 0 0100: lhzx */
153         { 2, LD+SE },           /* 11 0 0101: lhax */
154         { 2, ST },              /* 11 0 0110: sthx */
155         INVALID,                /* 11 0 0111 */
156         { 4, LD+F+S },          /* 11 0 1000: lfsx */
157         { 8, LD+F },            /* 11 0 1001: lfdx */
158         { 4, ST+F+S },          /* 11 0 1010: stfsx */
159         { 8, ST+F },            /* 11 0 1011: stfdx */
160         INVALID,                /* 11 0 1100 */
161         INVALID,                /* 11 0 1101: lmd */
162         INVALID,                /* 11 0 1110 */
163         INVALID,                /* 11 0 1111: stmd */
164         { 4, LD+U },            /* 11 1 0000: lwzux */
165         INVALID,                /* 11 1 0001 */
166         { 4, ST+U },            /* 11 1 0010: stwux */
167         INVALID,                /* 11 1 0011 */
168         { 2, LD+U },            /* 11 1 0100: lhzux */
169         { 2, LD+SE+U },         /* 11 1 0101: lhaux */
170         { 2, ST+U },            /* 11 1 0110: sthux */
171         INVALID,                /* 11 1 0111 */
172         { 4, LD+F+S+U },        /* 11 1 1000: lfsux */
173         { 8, LD+F+U },          /* 11 1 1001: lfdux */
174         { 4, ST+F+S+U },        /* 11 1 1010: stfsux */
175         { 8, ST+F+U },          /* 11 1 1011: stfdux */
176         INVALID,                /* 11 1 1100 */
177         INVALID,                /* 11 1 1101 */
178         INVALID,                /* 11 1 1110 */
179         INVALID,                /* 11 1 1111 */
180 };
181
182 #define SWAP(a, b)      (t = (a), (a) = (b), (b) = t)
183
184 int
185 fix_alignment(struct pt_regs *regs)
186 {
187         int instr, nb, flags;
188 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4) || defined(CONFIG_BOOKE)
189         int opcode, f1, f2, f3;
190 #endif
191         int i, t;
192         int reg, areg;
193         int offset, nb0;
194         unsigned char __user *addr;
195         unsigned char *rptr;
196         union {
197                 long l;
198                 float f;
199                 double d;
200                 unsigned char v[8];
201         } data;
202
203         CHECK_FULL_REGS(regs);
204
205 #if defined(CONFIG_4xx) || defined(CONFIG_POWER4) || defined(CONFIG_BOOKE)
206         /* The 4xx-family & Book-E processors have no DSISR register,
207          * so we emulate it.
208          * The POWER4 has a DSISR register but doesn't set it on
209          * an alignment fault.  -- paulus
210          */
211
212         if (__get_user(instr, (unsigned int __user *) regs->nip))
213                 return 0;
214         opcode = OPCD(instr);
215         reg = RS(instr);
216         areg = RA(instr);
217
218         if (!IS_XFORM(opcode)) {
219                 f1 = 0;
220                 f2 = (instr & 0x04000000) >> 26;
221                 f3 = (instr & 0x78000000) >> 27;
222         } else {
223                 f1 = (instr & 0x00000006) >> 1;
224                 f2 = (instr & 0x00000040) >> 6;
225                 f3 = (instr & 0x00000780) >> 7;
226         }
227
228         instr = ((f1 << 5) | (f2 << 4) | f3);
229 #else
230         reg = (regs->dsisr >> 5) & 0x1f;        /* source/dest register */
231         areg = regs->dsisr & 0x1f;              /* register to update */
232         instr = (regs->dsisr >> 10) & 0x7f;
233 #endif
234
235         nb = aligninfo[instr].len;
236         if (nb == 0) {
237                 long __user *p;
238                 int i;
239
240                 if (instr != DCBZ)
241                         return 0;       /* too hard or invalid instruction */
242                 /*
243                  * The dcbz (data cache block zero) instruction
244                  * gives an alignment fault if used on non-cacheable
245                  * memory.  We handle the fault mainly for the
246                  * case when we are running with the cache disabled
247                  * for debugging.
248                  */
249                 p = (long __user *) (regs->dar & -L1_CACHE_BYTES);
250                 if (user_mode(regs)
251                     && !access_ok(VERIFY_WRITE, p, L1_CACHE_BYTES))
252                         return -EFAULT;
253                 for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i)
254                         if (__put_user(0, p+i))
255                                 return -EFAULT;
256                 return 1;
257         }
258
259         flags = aligninfo[instr].flags;
260         if ((flags & (LD|ST)) == 0)
261                 return 0;
262
263         /* For the 4xx-family & Book-E processors, the 'dar' field of the
264          * pt_regs structure is overloaded and is really from the DEAR.
265          */
266
267         addr = (unsigned char __user *)regs->dar;
268
269         if (flags & M) {
270                 /* lmw, stmw, lswi/x, stswi/x */
271                 nb0 = 0;
272                 if (flags & HARD) {
273                         if (flags & SX) {
274                                 nb = regs->xer & 127;
275                                 if (nb == 0)
276                                         return 1;
277                         } else {
278                                 if (__get_user(instr,
279                                             (unsigned int __user *)regs->nip))
280                                         return 0;
281                                 nb = (instr >> 11) & 0x1f;
282                                 if (nb == 0)
283                                         nb = 32;
284                         }
285                         if (nb + reg * 4 > 128) {
286                                 nb0 = nb + reg * 4 - 128;
287                                 nb = 128 - reg * 4;
288                         }
289                 } else {
290                         /* lwm, stmw */
291                         nb = (32 - reg) * 4;
292                 }
293                 rptr = (unsigned char *) &regs->gpr[reg];
294                 if (flags & LD) {
295                         for (i = 0; i < nb; ++i)
296                                 if (__get_user(rptr[i], addr+i))
297                                         return -EFAULT;
298                         if (nb0 > 0) {
299                                 rptr = (unsigned char *) &regs->gpr[0];
300                                 addr += nb;
301                                 for (i = 0; i < nb0; ++i)
302                                         if (__get_user(rptr[i], addr+i))
303                                                 return -EFAULT;
304                         }
305                         for (; (i & 3) != 0; ++i)
306                                 rptr[i] = 0;
307                 } else {
308                         for (i = 0; i < nb; ++i)
309                                 if (__put_user(rptr[i], addr+i))
310                                         return -EFAULT;
311                         if (nb0 > 0) {
312                                 rptr = (unsigned char *) &regs->gpr[0];
313                                 addr += nb;
314                                 for (i = 0; i < nb0; ++i)
315                                         if (__put_user(rptr[i], addr+i))
316                                                 return -EFAULT;
317                         }
318                 }
319                 return 1;
320         }
321
322         offset = 0;
323         if (nb < 4) {
324                 /* read/write the least significant bits */
325                 data.l = 0;
326                 offset = 4 - nb;
327         }
328
329         /* Verify the address of the operand */
330         if (user_mode(regs)) {
331                 if (!access_ok((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb))
332                         return -EFAULT; /* bad address */
333         }
334
335         if (flags & F) {
336                 preempt_disable();
337                 if (regs->msr & MSR_FP)
338                         giveup_fpu(current);
339                 preempt_enable();
340         }
341
342         /* If we read the operand, copy it in, else get register values */
343         if (flags & LD) {
344                 for (i = 0; i < nb; ++i)
345                         if (__get_user(data.v[offset+i], addr+i))
346                                 return -EFAULT;
347         } else if (flags & F) {
348                 data.d = current->thread.fpr[reg];
349         } else {
350                 data.l = regs->gpr[reg];
351         }
352
353         switch (flags & ~U) {
354         case LD+SE:     /* sign extend */
355                 if (data.v[2] >= 0x80)
356                         data.v[0] = data.v[1] = -1;
357                 break;
358
359         case LD+S:      /* byte-swap */
360         case ST+S:
361                 if (nb == 2) {
362                         SWAP(data.v[2], data.v[3]);
363                 } else {
364                         SWAP(data.v[0], data.v[3]);
365                         SWAP(data.v[1], data.v[2]);
366                 }
367                 break;
368
369         /* Single-precision FP load and store require conversions... */
370         case LD+F+S:
371                 preempt_disable();
372                 enable_kernel_fp();
373                 cvt_fd(&data.f, &data.d, &current->thread.fpscr);
374                 preempt_enable();
375                 break;
376         case ST+F+S:
377                 preempt_disable();
378                 enable_kernel_fp();
379                 cvt_df(&data.d, &data.f, &current->thread.fpscr);
380                 preempt_enable();
381                 break;
382         }
383
384         if (flags & ST) {
385                 for (i = 0; i < nb; ++i)
386                         if (__put_user(data.v[offset+i], addr+i))
387                                 return -EFAULT;
388         } else if (flags & F) {
389                 current->thread.fpr[reg] = data.d;
390         } else {
391                 regs->gpr[reg] = data.l;
392         }
393
394         if (flags & U)
395                 regs->gpr[areg] = regs->dar;
396
397         return 1;
398 }