PPC32: This changeset adds preemptible kernel support for ppc32
[opensuse:kernel.git] / arch / ppc / kernel / align.c
1 /*
2  * BK Id: %F% %I% %G% %U% %#%
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)
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) <= 55)
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)
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         CHECK_FULL_REGS(regs);
204
205 #if defined(CONFIG_4xx)
206         /* The 4xx-family processors have no DSISR register,
207          * so we emulate it.
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, &current->thread.fpr[reg], &current->thread.fpscr);
327                 /* current->thread.fpr[reg] = data.f; */
328                 break;
329         case ST+F+S:
330                 enable_kernel_fp();
331                 cvt_df(&current->thread.fpr[reg], &data.f, &current->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 }