PageRenderTime 33ms CodeModel.GetById 1ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/ppc64/kernel/align.c

https://bitbucket.org/evzijst/gittest
C | 396 lines | 316 code | 32 blank | 48 comment | 39 complexity | c63c5035ebc342b75fadb763fb2acc2a MD5 | raw file
  1/* align.c - handle alignment exceptions for the Power PC.
  2 *
  3 * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
  4 * Copyright (c) 1998-1999 TiVo, Inc.
  5 *   PowerPC 403GCX modifications.
  6 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
  7 *   PowerPC 403GCX/405GP modifications.
  8 * Copyright (c) 2001-2002 PPC64 team, IBM Corp
  9 *   64-bit and Power4 support
 10 *
 11 * This program is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU General Public License
 13 * as published by the Free Software Foundation; either version
 14 * 2 of the License, or (at your option) any later version.
 15 */
 16
 17#include <linux/kernel.h>
 18#include <linux/mm.h>
 19#include <asm/processor.h>
 20#include <asm/uaccess.h>
 21#include <asm/system.h>
 22#include <asm/cache.h>
 23#include <asm/cputable.h>
 24
 25struct aligninfo {
 26	unsigned char len;
 27	unsigned char flags;
 28};
 29
 30#define IS_XFORM(inst)	(((inst) >> 26) == 31)
 31#define IS_DSFORM(inst)	(((inst) >> 26) >= 56)
 32
 33#define INVALID	{ 0, 0 }
 34
 35#define LD	1	/* load */
 36#define ST	2	/* store */
 37#define	SE	4	/* sign-extend value */
 38#define F	8	/* to/from fp regs */
 39#define U	0x10	/* update index register */
 40#define M	0x20	/* multiple load/store */
 41#define SW	0x40	/* byte swap */
 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 },		/* 00 0 1000: lfs */
 61	{ 8, LD+F },		/* 00 0 1001: lfd */
 62	{ 4, ST+F },		/* 00 0 1010: stfs */
 63	{ 8, ST+F },		/* 00 0 1011: stfd */
 64	INVALID,		/* 00 0 1100 */
 65	{ 8, LD },		/* 00 0 1101: ld */
 66	INVALID,		/* 00 0 1110 */
 67	{ 8, ST },		/* 00 0 1111: std */
 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+U },		/* 00 1 1000: lfsu */
 77	{ 8, LD+F+U },		/* 00 1 1001: lfdu */
 78	{ 4, ST+F+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	{ 8, LD },		/* 01 0 0000: ldx */
 85	INVALID,		/* 01 0 0001 */
 86	{ 8, ST },		/* 01 0 0010: stdx */
 87	INVALID,		/* 01 0 0011 */
 88	INVALID,		/* 01 0 0100 */
 89	{ 4, LD+SE },		/* 01 0 0101: lwax */
 90	INVALID,		/* 01 0 0110 */
 91	INVALID,		/* 01 0 0111 */
 92	{ 0, LD },		/* 01 0 1000: lswx */
 93	{ 0, LD },		/* 01 0 1001: lswi */
 94	{ 0, ST },		/* 01 0 1010: stswx */
 95	{ 0, ST },		/* 01 0 1011: stswi */
 96	INVALID,		/* 01 0 1100 */
 97	{ 8, LD+U },		/* 01 0 1101: ldu */
 98	INVALID,		/* 01 0 1110 */
 99	{ 8, ST+U },		/* 01 0 1111: stdu */
100	{ 8, LD+U },		/* 01 1 0000: ldux */
101	INVALID,		/* 01 1 0001 */
102	{ 8, ST+U },		/* 01 1 0010: stdux */
103	INVALID,		/* 01 1 0011 */
104	INVALID,		/* 01 1 0100 */
105	{ 4, LD+SE+U },		/* 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 },		/* 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+SW },		/* 10 0 1000: lwbrx */
125	INVALID,		/* 10 0 1001 */
126	{ 4, ST+SW },		/* 10 0 1010: stwbrx */
127	INVALID,		/* 10 0 1011 */
128	{ 2, LD+SW },		/* 10 0 1100: lhbrx */
129	{ 4, LD+SE },		/* 10 0 1101  lwa */
130	{ 2, ST+SW },		/* 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	{ L1_CACHE_BYTES, ST },	/* 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 },		/* 11 0 1000: lfsx */
157	{ 8, LD+F },		/* 11 0 1001: lfdx */
158	{ 4, ST+F },		/* 11 0 1010: stfsx */
159	{ 8, ST+F },		/* 11 0 1011: stfdx */
160	INVALID,		/* 11 0 1100 */
161	{ 8, LD+M },		/* 11 0 1101: lmd */
162	INVALID,		/* 11 0 1110 */
163	{ 8, ST+M },		/* 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+U },		/* 11 1 1000: lfsux */
173	{ 8, LD+F+U },		/* 11 1 1001: lfdux */
174	{ 4, ST+F+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
184static inline unsigned make_dsisr(unsigned instr)
185{
186	unsigned dsisr;
187	
188	/* create a DSISR value from the instruction */
189	dsisr = (instr & 0x03ff0000) >> 16;			/* bits  6:15 --> 22:31 */
190	
191	if ( IS_XFORM(instr) ) {
192		dsisr |= (instr & 0x00000006) << 14;		/* bits 29:30 --> 15:16 */
193		dsisr |= (instr & 0x00000040) << 8;		/* bit     25 -->    17 */
194		dsisr |= (instr & 0x00000780) << 3;		/* bits 21:24 --> 18:21 */
195	}
196	else {
197		dsisr |= (instr & 0x04000000) >> 12;		/* bit      5 -->    17 */
198		dsisr |= (instr & 0x78000000) >> 17;		/* bits  1: 4 --> 18:21 */
199		if ( IS_DSFORM(instr) ) {
200			dsisr |= (instr & 0x00000003) << 18;	/* bits 30:31 --> 12:13 */
201		}
202	}
203	
204	return dsisr;
205}
206
207int
208fix_alignment(struct pt_regs *regs)
209{
210	unsigned int instr, nb, flags;
211	int t;
212	unsigned long reg, areg;
213	unsigned long i;
214	int ret;
215	unsigned dsisr;
216	unsigned char __user *addr;
217	unsigned char __user *p;
218	unsigned long __user *lp;
219	union {
220		long ll;
221		double dd;
222		unsigned char v[8];
223		struct {
224			unsigned hi32;
225			int	 low32;
226		} x32;
227		struct {
228			unsigned char hi48[6];
229			short	      low16;
230		} x16;
231	} data;
232
233	/*
234	 * Return 1 on success
235	 * Return 0 if unable to handle the interrupt
236	 * Return -EFAULT if data address is bad
237	 */
238
239	dsisr = regs->dsisr;
240
241	if (cpu_has_feature(CPU_FTR_NODSISRALIGN)) {
242	    unsigned int real_instr;
243	    if (__get_user(real_instr, (unsigned int __user *)regs->nip))
244		return 0;
245	    dsisr = make_dsisr(real_instr);
246	}
247
248	/* extract the operation and registers from the dsisr */
249	reg = (dsisr >> 5) & 0x1f;	/* source/dest register */
250	areg = dsisr & 0x1f;		/* register to update */
251	instr = (dsisr >> 10) & 0x7f;
252	instr |= (dsisr >> 13) & 0x60;
253
254	/* Lookup the operation in our table */
255	nb = aligninfo[instr].len;
256	flags = aligninfo[instr].flags;
257
258	/* DAR has the operand effective address */
259	addr = (unsigned char __user *)regs->dar;
260
261	/* A size of 0 indicates an instruction we don't support */
262	/* we also don't support the multiples (lmw, stmw, lmd, stmd) */
263	if ((nb == 0) || (flags & M))
264		return 0;		/* too hard or invalid instruction */
265
266	/*
267	 * Special handling for dcbz
268	 * dcbz may give an alignment exception for accesses to caching inhibited
269	 * storage
270	 */
271	if (instr == DCBZ)
272		addr = (unsigned char __user *) ((unsigned long)addr & -L1_CACHE_BYTES);
273
274	/* Verify the address of the operand */
275	if (user_mode(regs)) {
276		if (!access_ok((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb))
277			return -EFAULT;	/* bad address */
278	}
279
280	/* Force the fprs into the save area so we can reference them */
281	if (flags & F) {
282		if (!user_mode(regs))
283			return 0;
284		flush_fp_to_thread(current);
285	}
286	
287	/* If we are loading, get the data from user space */
288	if (flags & LD) {
289		data.ll = 0;
290		ret = 0;
291		p = addr;
292		switch (nb) {
293		case 8:
294			ret |= __get_user(data.v[0], p++);
295			ret |= __get_user(data.v[1], p++);
296			ret |= __get_user(data.v[2], p++);
297			ret |= __get_user(data.v[3], p++);
298		case 4:
299			ret |= __get_user(data.v[4], p++);
300			ret |= __get_user(data.v[5], p++);
301		case 2:
302			ret |= __get_user(data.v[6], p++);
303			ret |= __get_user(data.v[7], p++);
304			if (ret)
305				return -EFAULT;
306		}
307	}
308	
309	/* If we are storing, get the data from the saved gpr or fpr */
310	if (flags & ST) {
311		if (flags & F) {
312			if (nb == 4) {
313				/* Doing stfs, have to convert to single */
314				preempt_disable();
315				enable_kernel_fp();
316				cvt_df(&current->thread.fpr[reg], (float *)&data.v[4], &current->thread.fpscr);
317				disable_kernel_fp();
318				preempt_enable();
319			}
320			else
321				data.dd = current->thread.fpr[reg];
322		}
323		else 
324			data.ll = regs->gpr[reg];
325	}
326	
327	/* Swap bytes as needed */
328	if (flags & SW) {
329		if (nb == 2)
330			SWAP(data.v[6], data.v[7]);
331		else {	/* nb must be 4 */
332			SWAP(data.v[4], data.v[7]);
333			SWAP(data.v[5], data.v[6]);
334		}
335	}
336	
337	/* Sign extend as needed */
338	if (flags & SE) {
339		if ( nb == 2 )
340			data.ll = data.x16.low16;
341		else	/* nb must be 4 */
342			data.ll = data.x32.low32;
343	}
344	
345	/* If we are loading, move the data to the gpr or fpr */
346	if (flags & LD) {
347		if (flags & F) {
348			if (nb == 4) {
349				/* Doing lfs, have to convert to double */
350				preempt_disable();
351				enable_kernel_fp();
352				cvt_fd((float *)&data.v[4], &current->thread.fpr[reg], &current->thread.fpscr);
353				disable_kernel_fp();
354				preempt_enable();
355			}
356			else
357				current->thread.fpr[reg] = data.dd;
358		}
359		else
360			regs->gpr[reg] = data.ll;
361	}
362	
363	/* If we are storing, copy the data to the user */
364	if (flags & ST) {
365		ret = 0;
366		p = addr;
367		switch (nb) {
368		case 128:	/* Special case - must be dcbz */
369			lp = (unsigned long __user *)p;
370			for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i)
371				ret |= __put_user(0, lp++);
372			break;
373		case 8:
374			ret |= __put_user(data.v[0], p++);
375			ret |= __put_user(data.v[1], p++);
376			ret |= __put_user(data.v[2], p++);
377			ret |= __put_user(data.v[3], p++);
378		case 4:
379			ret |= __put_user(data.v[4], p++);
380			ret |= __put_user(data.v[5], p++);
381		case 2:
382			ret |= __put_user(data.v[6], p++);
383			ret |= __put_user(data.v[7], p++);
384		}
385		if (ret)
386			return -EFAULT;
387	}
388	
389	/* Update RA as needed */
390	if (flags & U) {
391		regs->gpr[areg] = regs->dar;
392	}
393
394	return 1;
395}
396