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, &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     }
357