PageRenderTime 29ms CodeModel.GetById 8ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/ppc/kernel/align.c

https://bitbucket.org/evzijst/gittest
C | 398 lines | 333 code | 29 blank | 36 comment | 68 complexity | b28181ccb6b79196a80f3e40c93dee10 MD5 | raw file
  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
 19struct 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 */
 51static 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
184int
185fix_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}