uaccess_mm.h 10.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6
#ifndef __M68K_UACCESS_H
#define __M68K_UACCESS_H

/*
 * User space memory access functions
 */
7
#include <linux/compiler.h>
Linus Torvalds's avatar
Linus Torvalds committed
8
#include <linux/errno.h>
9
#include <linux/types.h>
10
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15 16
#include <asm/segment.h>

#define VERIFY_READ	0
#define VERIFY_WRITE	1

/* We let the MMU do all checking */
17 18 19 20 21
static inline int access_ok(int type, const void __user *addr,
			    unsigned long size)
{
	return 1;
}
Linus Torvalds's avatar
Linus Torvalds committed
22

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * Not all varients of the 68k family support the notion of address spaces.
 * The traditional 680x0 parts do, and they use the sfc/dfc registers and
 * the "moves" instruction to access user space from kernel space. Other
 * family members like ColdFire don't support this, and only have a single
 * address space, and use the usual "move" instruction for user space access.
 *
 * Outside of this difference the user space access functions are the same.
 * So lets keep the code simple and just define in what we need to use.
 */
#ifdef CONFIG_CPU_HAS_ADDRESS_SPACES
#define	MOVES	"moves"
#else
#define	MOVES	"move"
#endif

Linus Torvalds's avatar
Linus Torvalds committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/*
 * The exception table consists of pairs of addresses: the first is the
 * address of an instruction that is allowed to fault, and the second is
 * the address at which the program should continue.  No registers are
 * modified, so it is entirely up to the continuation code to figure out
 * what to do.
 *
 * All the routines below use bits of fixup code that are out of line
 * with the main instruction path.  This means when everything is well,
 * we don't even have to jump over them.  Further, they do not intrude
 * on our cache or tlb entries.
 */

struct exception_table_entry
{
	unsigned long insn, fixup;
};

57 58 59 60 61
extern int __put_user_bad(void);
extern int __get_user_bad(void);

#define __put_user_asm(res, x, ptr, bwl, reg, err)	\
asm volatile ("\n"					\
62
	"1:	"MOVES"."#bwl"	%2,%1\n"		\
63 64 65 66 67 68 69 70 71 72 73 74 75 76
	"2:\n"						\
	"	.section .fixup,\"ax\"\n"		\
	"	.even\n"				\
	"10:	moveq.l	%3,%0\n"			\
	"	jra 2b\n"				\
	"	.previous\n"				\
	"\n"						\
	"	.section __ex_table,\"a\"\n"		\
	"	.align	4\n"				\
	"	.long	1b,10b\n"			\
	"	.long	2b,10b\n"			\
	"	.previous"				\
	: "+d" (res), "=m" (*(ptr))			\
	: #reg (x), "i" (err))
Linus Torvalds's avatar
Linus Torvalds committed
77 78 79 80 81 82

/*
 * These are the main single-value transfer routines.  They automatically
 * use the right size if we just have the right pointer type.
 */

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
#define __put_user(x, ptr)						\
({									\
	typeof(*(ptr)) __pu_val = (x);					\
	int __pu_err = 0;						\
	__chk_user_ptr(ptr);						\
	switch (sizeof (*(ptr))) {					\
	case 1:								\
		__put_user_asm(__pu_err, __pu_val, ptr, b, d, -EFAULT);	\
		break;							\
	case 2:								\
		__put_user_asm(__pu_err, __pu_val, ptr, w, d, -EFAULT);	\
		break;							\
	case 4:								\
		__put_user_asm(__pu_err, __pu_val, ptr, l, r, -EFAULT);	\
		break;							\
	case 8:								\
 	    {								\
100
 		const void __user *__pu_ptr = (ptr);			\
101
		asm volatile ("\n"					\
102 103
			"1:	"MOVES".l	%2,(%1)+\n"		\
			"2:	"MOVES".l	%R2,(%1)\n"		\
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
			"3:\n"						\
			"	.section .fixup,\"ax\"\n"		\
			"	.even\n"				\
			"10:	movel %3,%0\n"				\
			"	jra 3b\n"				\
			"	.previous\n"				\
			"\n"						\
			"	.section __ex_table,\"a\"\n"		\
			"	.align 4\n"				\
			"	.long 1b,10b\n"				\
			"	.long 2b,10b\n"				\
			"	.long 3b,10b\n"				\
			"	.previous"				\
			: "+d" (__pu_err), "+a" (__pu_ptr)		\
			: "r" (__pu_val), "i" (-EFAULT)			\
			: "memory");					\
		break;							\
	    }								\
	default:							\
		__pu_err = __put_user_bad();				\
		break;							\
	}								\
	__pu_err;							\
Linus Torvalds's avatar
Linus Torvalds committed
127
})
128
#define put_user(x, ptr)	__put_user(x, ptr)
Linus Torvalds's avatar
Linus Torvalds committed
129 130


131 132 133
#define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({	\
	type __gu_val;						\
	asm volatile ("\n"					\
134
		"1:	"MOVES"."#bwl"	%2,%1\n"		\
135 136 137 138
		"2:\n"						\
		"	.section .fixup,\"ax\"\n"		\
		"	.even\n"				\
		"10:	move.l	%3,%0\n"			\
139
		"	sub.l	%1,%1\n"			\
140 141 142 143 144 145 146 147 148
		"	jra	2b\n"				\
		"	.previous\n"				\
		"\n"						\
		"	.section __ex_table,\"a\"\n"		\
		"	.align	4\n"				\
		"	.long	1b,10b\n"			\
		"	.previous"				\
		: "+d" (res), "=&" #reg (__gu_val)		\
		: "m" (*(ptr)), "i" (err));			\
149
	(x) = (typeof(*(ptr)))(unsigned long)__gu_val;		\
Linus Torvalds's avatar
Linus Torvalds committed
150 151
})

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#define __get_user(x, ptr)						\
({									\
	int __gu_err = 0;						\
	__chk_user_ptr(ptr);						\
	switch (sizeof(*(ptr))) {					\
	case 1:								\
		__get_user_asm(__gu_err, x, ptr, u8, b, d, -EFAULT);	\
		break;							\
	case 2:								\
		__get_user_asm(__gu_err, x, ptr, u16, w, d, -EFAULT);	\
		break;							\
	case 4:								\
		__get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT);	\
		break;							\
/*	case 8:	disabled because gcc-4.1 has a broken typeof		\
 	    {								\
 		const void *__gu_ptr = (ptr);				\
 		u64 __gu_val;						\
		asm volatile ("\n"					\
171 172
			"1:	"MOVES".l	(%2)+,%1\n"		\
			"2:	"MOVES".l	(%2),%R1\n"		\
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
			"3:\n"						\
			"	.section .fixup,\"ax\"\n"		\
			"	.even\n"				\
			"10:	move.l	%3,%0\n"			\
			"	sub.l	%1,%1\n"			\
			"	sub.l	%R1,%R1\n"			\
			"	jra	3b\n"				\
			"	.previous\n"				\
			"\n"						\
			"	.section __ex_table,\"a\"\n"		\
			"	.align	4\n"				\
			"	.long	1b,10b\n"			\
			"	.long	2b,10b\n"			\
			"	.previous"				\
			: "+d" (__gu_err), "=&r" (__gu_val),		\
			  "+a" (__gu_ptr)				\
			: "i" (-EFAULT)					\
			: "memory");					\
		(x) = (typeof(*(ptr)))__gu_val;				\
		break;							\
	    }	*/							\
	default:							\
		__gu_err = __get_user_bad();				\
		break;							\
	}								\
	__gu_err;							\
})
#define get_user(x, ptr) __get_user(x, ptr)
Linus Torvalds's avatar
Linus Torvalds committed
201

202 203
unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n);
unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n);
Linus Torvalds's avatar
Linus Torvalds committed
204

205 206
#define __constant_copy_from_user_asm(res, to, from, tmp, n, s1, s2, s3)\
	asm volatile ("\n"						\
207
		"1:	"MOVES"."#s1"	(%2)+,%3\n"			\
208
		"	move."#s1"	%3,(%1)+\n"			\
209
		"2:	"MOVES"."#s2"	(%2)+,%3\n"			\
210 211
		"	move."#s2"	%3,(%1)+\n"			\
		"	.ifnc	\""#s3"\",\"\"\n"			\
212
		"3:	"MOVES"."#s3"	(%2)+,%3\n"			\
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
		"	move."#s3"	%3,(%1)+\n"			\
		"	.endif\n"					\
		"4:\n"							\
		"	.section __ex_table,\"a\"\n"			\
		"	.align	4\n"					\
		"	.long	1b,10f\n"				\
		"	.long	2b,20f\n"				\
		"	.ifnc	\""#s3"\",\"\"\n"			\
		"	.long	3b,30f\n"				\
		"	.endif\n"					\
		"	.previous\n"					\
		"\n"							\
		"	.section .fixup,\"ax\"\n"			\
		"	.even\n"					\
		"10:	clr."#s1"	(%1)+\n"			\
		"20:	clr."#s2"	(%1)+\n"			\
		"	.ifnc	\""#s3"\",\"\"\n"			\
		"30:	clr."#s3"	(%1)+\n"			\
		"	.endif\n"					\
		"	moveq.l	#"#n",%0\n"				\
		"	jra	4b\n"					\
		"	.previous\n"					\
		: "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp)	\
		: : "memory")

238 239
static __always_inline unsigned long
__constant_copy_from_user(void *to, const void __user *from, unsigned long n)
Linus Torvalds's avatar
Linus Torvalds committed
240
{
241
	unsigned long res = 0, tmp;
Linus Torvalds's avatar
Linus Torvalds committed
242

243
	switch (n) {
Linus Torvalds's avatar
Linus Torvalds committed
244
	case 1:
245
		__get_user_asm(res, *(u8 *)to, (u8 __user *)from, u8, b, d, 1);
246
		break;
Linus Torvalds's avatar
Linus Torvalds committed
247
	case 2:
248
		__get_user_asm(res, *(u16 *)to, (u16 __user *)from, u16, w, d, 2);
249 250 251 252
		break;
	case 3:
		__constant_copy_from_user_asm(res, to, from, tmp, 3, w, b,);
		break;
253
	case 4:
254
		__get_user_asm(res, *(u32 *)to, (u32 __user *)from, u32, l, r, 4);
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
		break;
	case 5:
		__constant_copy_from_user_asm(res, to, from, tmp, 5, l, b,);
		break;
	case 6:
		__constant_copy_from_user_asm(res, to, from, tmp, 6, l, w,);
		break;
	case 7:
		__constant_copy_from_user_asm(res, to, from, tmp, 7, l, w, b);
		break;
	case 8:
		__constant_copy_from_user_asm(res, to, from, tmp, 8, l, l,);
		break;
	case 9:
		__constant_copy_from_user_asm(res, to, from, tmp, 9, l, l, b);
		break;
	case 10:
		__constant_copy_from_user_asm(res, to, from, tmp, 10, l, l, w);
		break;
	case 12:
		__constant_copy_from_user_asm(res, to, from, tmp, 12, l, l, l);
		break;
	default:
		/* we limit the inlined version to 3 moves */
		return __generic_copy_from_user(to, from, n);
Linus Torvalds's avatar
Linus Torvalds committed
280 281
	}

282 283
	return res;
}
Linus Torvalds's avatar
Linus Torvalds committed
284

285 286 287
#define __constant_copy_to_user_asm(res, to, from, tmp, n, s1, s2, s3)	\
	asm volatile ("\n"						\
		"	move."#s1"	(%2)+,%3\n"			\
288
		"11:	"MOVES"."#s1"	%3,(%1)+\n"			\
289
		"12:	move."#s2"	(%2)+,%3\n"			\
290
		"21:	"MOVES"."#s2"	%3,(%1)+\n"			\
291 292 293
		"22:\n"							\
		"	.ifnc	\""#s3"\",\"\"\n"			\
		"	move."#s3"	(%2)+,%3\n"			\
294
		"31:	"MOVES"."#s3"	%3,(%1)+\n"			\
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
		"32:\n"							\
		"	.endif\n"					\
		"4:\n"							\
		"\n"							\
		"	.section __ex_table,\"a\"\n"			\
		"	.align	4\n"					\
		"	.long	11b,5f\n"				\
		"	.long	12b,5f\n"				\
		"	.long	21b,5f\n"				\
		"	.long	22b,5f\n"				\
		"	.ifnc	\""#s3"\",\"\"\n"			\
		"	.long	31b,5f\n"				\
		"	.long	32b,5f\n"				\
		"	.endif\n"					\
		"	.previous\n"					\
		"\n"							\
		"	.section .fixup,\"ax\"\n"			\
		"	.even\n"					\
		"5:	moveq.l	#"#n",%0\n"				\
		"	jra	4b\n"					\
		"	.previous\n"					\
		: "+d" (res), "+a" (to), "+a" (from), "=&d" (tmp)	\
		: : "memory")

319
static __always_inline unsigned long
320
__constant_copy_to_user(void __user *to, const void *from, unsigned long n)
Linus Torvalds's avatar
Linus Torvalds committed
321
{
322 323 324
	unsigned long res = 0, tmp;

	switch (n) {
Linus Torvalds's avatar
Linus Torvalds committed
325
	case 1:
326
		__put_user_asm(res, *(u8 *)from, (u8 __user *)to, b, d, 1);
327
		break;
Linus Torvalds's avatar
Linus Torvalds committed
328
	case 2:
329
		__put_user_asm(res, *(u16 *)from, (u16 __user *)to, w, d, 2);
330 331 332 333
		break;
	case 3:
		__constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,);
		break;
334
	case 4:
335
		__put_user_asm(res, *(u32 *)from, (u32 __user *)to, l, r, 4);
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
		break;
	case 5:
		__constant_copy_to_user_asm(res, to, from, tmp, 5, l, b,);
		break;
	case 6:
		__constant_copy_to_user_asm(res, to, from, tmp, 6, l, w,);
		break;
	case 7:
		__constant_copy_to_user_asm(res, to, from, tmp, 7, l, w, b);
		break;
	case 8:
		__constant_copy_to_user_asm(res, to, from, tmp, 8, l, l,);
		break;
	case 9:
		__constant_copy_to_user_asm(res, to, from, tmp, 9, l, l, b);
		break;
	case 10:
		__constant_copy_to_user_asm(res, to, from, tmp, 10, l, l, w);
		break;
	case 12:
		__constant_copy_to_user_asm(res, to, from, tmp, 12, l, l, l);
		break;
	default:
		/* limit the inlined version to 3 moves */
		return __generic_copy_to_user(to, from, n);
Linus Torvalds's avatar
Linus Torvalds committed
361
	}
362 363

	return res;
Linus Torvalds's avatar
Linus Torvalds committed
364 365
}

366
#define __copy_from_user(to, from, n)		\
Linus Torvalds's avatar
Linus Torvalds committed
367 368 369 370
(__builtin_constant_p(n) ?			\
 __constant_copy_from_user(to, from, n) :	\
 __generic_copy_from_user(to, from, n))

371
#define __copy_to_user(to, from, n)		\
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375
(__builtin_constant_p(n) ?			\
 __constant_copy_to_user(to, from, n) :		\
 __generic_copy_to_user(to, from, n))

376 377
#define __copy_to_user_inatomic		__copy_to_user
#define __copy_from_user_inatomic	__copy_from_user
Linus Torvalds's avatar
Linus Torvalds committed
378

379 380
#define copy_from_user(to, from, n)	__copy_from_user(to, from, n)
#define copy_to_user(to, from, n)	__copy_to_user(to, from, n)
Linus Torvalds's avatar
Linus Torvalds committed
381

382 383 384 385 386 387 388
#define user_addr_max() \
	(segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL)

extern long strncpy_from_user(char *dst, const char __user *src, long count);
extern __must_check long strlen_user(const char __user *str);
extern __must_check long strnlen_user(const char __user *str, long n);

389 390 391
unsigned long __clear_user(void __user *to, unsigned long n);

#define clear_user	__clear_user
Linus Torvalds's avatar
Linus Torvalds committed
392 393

#endif /* _M68K_UACCESS_H */