diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2005-11-17 22:09:41 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2005-11-17 22:39:23 -0500 |
commit | 5daf9071b527089b1bd5d9cb3a5354b83121550e (patch) | |
tree | 3abf10c1f67975f3a7d0def22de261f395c325f1 /arch/ppc | |
parent | 6defa38b3754c84cd3449447477aed81ea979407 (diff) |
[PATCH] powerpc: merge align.c
This patch merges align.c, the result isn't quite what was in ppc64 nor
what was in ppc32 :) It should implement all the functionalities of both
though. Kumar, since you played with that in the past, I suppose you
have some test cases for verifying that it works properly before I dig
out the 601 machine ? :)
Since it's likely that I won't be able to test all scenario, code
inspection is much welcome.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/ppc')
-rw-r--r-- | arch/ppc/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/ppc/kernel/align.c | 410 |
2 files changed, 2 insertions, 412 deletions
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 17a4da65e275..0bb23fce4293 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile | |||
@@ -13,7 +13,7 @@ extra-$(CONFIG_POWER4) += idle_power4.o | |||
13 | extra-y += vmlinux.lds | 13 | extra-y += vmlinux.lds |
14 | 14 | ||
15 | obj-y := entry.o traps.o idle.o time.o misc.o \ | 15 | obj-y := entry.o traps.o idle.o time.o misc.o \ |
16 | process.o align.o \ | 16 | process.o \ |
17 | setup.o \ | 17 | setup.o \ |
18 | ppc_htab.o | 18 | ppc_htab.o |
19 | obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o | 19 | obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o |
@@ -38,7 +38,7 @@ endif | |||
38 | # These are here while we do the architecture merge | 38 | # These are here while we do the architecture merge |
39 | 39 | ||
40 | else | 40 | else |
41 | obj-y := idle.o align.o | 41 | obj-y := idle.o |
42 | obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o | 42 | obj-$(CONFIG_6xx) += l2cr.o cpu_setup_6xx.o |
43 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o | 43 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o |
44 | obj-$(CONFIG_MODULES) += module.o | 44 | obj-$(CONFIG_MODULES) += module.o |
diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c deleted file mode 100644 index ab398c4b70b6..000000000000 --- a/arch/ppc/kernel/align.c +++ /dev/null | |||
@@ -1,410 +0,0 @@ | |||
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 | |||
294 | if (!access_ok((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb+nb0)) | ||
295 | return -EFAULT; /* bad address */ | ||
296 | |||
297 | rptr = (unsigned char *) ®s->gpr[reg]; | ||
298 | if (flags & LD) { | ||
299 | for (i = 0; i < nb; ++i) | ||
300 | if (__get_user(rptr[i], addr+i)) | ||
301 | return -EFAULT; | ||
302 | if (nb0 > 0) { | ||
303 | rptr = (unsigned char *) ®s->gpr[0]; | ||
304 | addr += nb; | ||
305 | for (i = 0; i < nb0; ++i) | ||
306 | if (__get_user(rptr[i], addr+i)) | ||
307 | return -EFAULT; | ||
308 | } | ||
309 | for (; (i & 3) != 0; ++i) | ||
310 | rptr[i] = 0; | ||
311 | } else { | ||
312 | for (i = 0; i < nb; ++i) | ||
313 | if (__put_user(rptr[i], addr+i)) | ||
314 | return -EFAULT; | ||
315 | if (nb0 > 0) { | ||
316 | rptr = (unsigned char *) ®s->gpr[0]; | ||
317 | addr += nb; | ||
318 | for (i = 0; i < nb0; ++i) | ||
319 | if (__put_user(rptr[i], addr+i)) | ||
320 | return -EFAULT; | ||
321 | } | ||
322 | } | ||
323 | return 1; | ||
324 | } | ||
325 | |||
326 | offset = 0; | ||
327 | if (nb < 4) { | ||
328 | /* read/write the least significant bits */ | ||
329 | data.l = 0; | ||
330 | offset = 4 - nb; | ||
331 | } | ||
332 | |||
333 | /* Verify the address of the operand */ | ||
334 | if (user_mode(regs)) { | ||
335 | if (!access_ok((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) | ||
336 | return -EFAULT; /* bad address */ | ||
337 | } | ||
338 | |||
339 | if (flags & F) { | ||
340 | preempt_disable(); | ||
341 | if (regs->msr & MSR_FP) | ||
342 | giveup_fpu(current); | ||
343 | preempt_enable(); | ||
344 | } | ||
345 | |||
346 | /* If we read the operand, copy it in, else get register values */ | ||
347 | if (flags & LD) { | ||
348 | for (i = 0; i < nb; ++i) | ||
349 | if (__get_user(data.v[offset+i], addr+i)) | ||
350 | return -EFAULT; | ||
351 | } else if (flags & F) { | ||
352 | data.d = current->thread.fpr[reg]; | ||
353 | } else { | ||
354 | data.l = regs->gpr[reg]; | ||
355 | } | ||
356 | |||
357 | switch (flags & ~U) { | ||
358 | case LD+SE: /* sign extend */ | ||
359 | if (data.v[2] >= 0x80) | ||
360 | data.v[0] = data.v[1] = -1; | ||
361 | break; | ||
362 | |||
363 | case LD+S: /* byte-swap */ | ||
364 | case ST+S: | ||
365 | if (nb == 2) { | ||
366 | SWAP(data.v[2], data.v[3]); | ||
367 | } else { | ||
368 | SWAP(data.v[0], data.v[3]); | ||
369 | SWAP(data.v[1], data.v[2]); | ||
370 | } | ||
371 | break; | ||
372 | |||
373 | /* Single-precision FP load and store require conversions... */ | ||
374 | case LD+F+S: | ||
375 | #ifdef CONFIG_PPC_FPU | ||
376 | preempt_disable(); | ||
377 | enable_kernel_fp(); | ||
378 | cvt_fd(&data.f, &data.d, ¤t->thread); | ||
379 | preempt_enable(); | ||
380 | #else | ||
381 | return 0; | ||
382 | #endif | ||
383 | break; | ||
384 | case ST+F+S: | ||
385 | #ifdef CONFIG_PPC_FPU | ||
386 | preempt_disable(); | ||
387 | enable_kernel_fp(); | ||
388 | cvt_df(&data.d, &data.f, ¤t->thread); | ||
389 | preempt_enable(); | ||
390 | #else | ||
391 | return 0; | ||
392 | #endif | ||
393 | break; | ||
394 | } | ||
395 | |||
396 | if (flags & ST) { | ||
397 | for (i = 0; i < nb; ++i) | ||
398 | if (__put_user(data.v[offset+i], addr+i)) | ||
399 | return -EFAULT; | ||
400 | } else if (flags & F) { | ||
401 | current->thread.fpr[reg] = data.d; | ||
402 | } else { | ||
403 | regs->gpr[reg] = data.l; | ||
404 | } | ||
405 | |||
406 | if (flags & U) | ||
407 | regs->gpr[areg] = regs->dar; | ||
408 | |||
409 | return 1; | ||
410 | } | ||