| /* Copyright (c) 2015 Google Inc | 
 |  * Davide Libenzi <dlibenzi@google.com> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * Part of this code coming from a Linux kernel file: | 
 |  * | 
 |  * linux/arch/x86/include/asm/uaccess.h | 
 |  * | 
 |  * Which, even though missing specific copyright, it is supposed to be | 
 |  * ruled by the overall Linux copyright. | 
 |  */ | 
 |  | 
 | #pragma once | 
 |  | 
 | #include <ros/errno.h> | 
 | #include <compiler.h> | 
 | #include <stdint.h> | 
 | #include <umem.h> | 
 | #include <arch/fixup.h> | 
 |  | 
 | #define __m(x) *(x) | 
 |  | 
 | struct extable_ip_fixup { | 
 | 	uint64_t insn; | 
 | 	uint64_t fixup; | 
 | }; | 
 |  | 
 | #define __read_msr_asm(eax, edx, addr, err, errret)						\ | 
 | 	asm volatile(ASM_STAC "\n"											\ | 
 | 	             "1:		rdmsr\n"									\ | 
 | 	             "			mfence\n"									\ | 
 | 	             "2: " ASM_CLAC "\n"									\ | 
 | 	             ".section .fixup,\"ax\"\n"								\ | 
 | 	             "3:		mov %4,%0\n"								\ | 
 | 	             "	jmp 2b\n"											\ | 
 | 	             ".previous\n"											\ | 
 | 	             _ASM_EXTABLE(1b, 3b)									\ | 
 | 	             : "=r" (err), "=d" (edx), "=a" (eax)					\ | 
 | 	             : "c" (addr), "i" (errret), "0" (err)) | 
 |  | 
 | #define __write_msr_asm(val, addr, err, errret)							\ | 
 | 	asm volatile(ASM_STAC "\n"											\ | 
 | 				 "1:		wrmsr\n"									\ | 
 | 				 "2: " ASM_CLAC "\n"									\ | 
 | 				 ".section .fixup,\"ax\"\n"								\ | 
 | 				 "3:		mov %4,%0\n"								\ | 
 | 				 "	jmp 2b\n"											\ | 
 | 				 ".previous\n"											\ | 
 | 				 _ASM_EXTABLE(1b, 3b)									\ | 
 | 				 : "=r" (err)											\ | 
 | 				 : "d" ((uint32_t) (val >> 32)),						\ | 
 | 				   "a" ((uint32_t) (val & 0xffffffff)), "c" (addr),		\ | 
 | 				   "i" (errret), "0" (err)) | 
 |  | 
 | #define __put_user_asm(x, addr, err, itype, rtype, ltype, errret)		\ | 
 | 	asm volatile(ASM_STAC "\n"											\ | 
 | 				 "1:		mov"itype" %"rtype"1,%2\n"					\ | 
 | 				 "2: " ASM_CLAC "\n"									\ | 
 | 				 ".section .fixup,\"ax\"\n"								\ | 
 | 				 "3:		mov %3,%0\n"								\ | 
 | 				 "	jmp 2b\n"											\ | 
 | 				 ".previous\n"											\ | 
 | 				 _ASM_EXTABLE(1b, 3b)									\ | 
 | 				 : "=r"(err)											\ | 
 | 				 : ltype(x), "m" (__m(addr)), "i" (errret), "0" (err)) | 
 |  | 
 | #define __get_user_asm(x, addr, err, itype, rtype, ltype, errret)	\ | 
 | 	asm volatile(ASM_STAC "\n"										\ | 
 | 				 "1:		mov"itype" %2,%"rtype"1\n"				\ | 
 | 				 "2: " ASM_CLAC "\n"								\ | 
 | 				 ".section .fixup,\"ax\"\n"							\ | 
 | 				 "3:		mov %3,%0\n"							\ | 
 | 				 "	xor"itype" %"rtype"1,%"rtype"1\n"				\ | 
 | 				 "	jmp 2b\n"										\ | 
 | 				 ".previous\n"										\ | 
 | 				 _ASM_EXTABLE(1b, 3b)								\ | 
 | 				 : "=r" (err), ltype(x)								\ | 
 | 				 : "m" (__m(addr)), "i" (errret), "0" (err)) | 
 |  | 
 | #define __user_memcpy(dst, src, count, err, errret)						\ | 
 | 	asm volatile(ASM_STAC "\n"											\ | 
 | 				 "  		cld\n"										\ | 
 | 				 "1:		rep movsb\n"								\ | 
 | 				 "2: " ASM_CLAC "\n"									\ | 
 | 				 ".section .fixup,\"ax\"\n"								\ | 
 | 				 "3:		mov %4,%0\n"								\ | 
 | 				 "	jmp 2b\n"											\ | 
 | 				 ".previous\n"											\ | 
 | 				 _ASM_EXTABLE(1b, 3b)									\ | 
 | 				 : "=r"(err), "+D" (dst), "+S" (src), "+c" (count)		\ | 
 | 				 : "i" (errret), "0" (err)								\ | 
 | 				 : "memory") | 
 |  | 
 | static inline int __put_user(void *dst, const void *src, unsigned int count) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	switch (count) { | 
 | 	case 1: | 
 | 		__put_user_asm(*(const uint8_t *) src, (uint8_t *) dst, err, "b", | 
 | 		               "b", "iq", -EFAULT); | 
 | 		break; | 
 | 	case 2: | 
 | 		__put_user_asm(*(const uint16_t *) src, (uint16_t *) dst, err, "w", | 
 | 		               "w", "ir", -EFAULT); | 
 | 		break; | 
 | 	case 4: | 
 | 		__put_user_asm(*(const uint32_t *) src, (uint32_t *) dst, err, "l", | 
 | 		               "k", "ir", -EFAULT); | 
 | 		break; | 
 | 	case 8: | 
 | 		__put_user_asm(*(const uint64_t *) src, (uint64_t *) dst, err, "q", | 
 | 		               "", "er", -EFAULT); | 
 | 		break; | 
 | 	default: | 
 | 		__user_memcpy(dst, src, count, err, -EFAULT); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline int copy_to_user(void *dst, const void *src, unsigned int count) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (unlikely(!is_user_rwaddr(dst, count))) { | 
 | 		err = -EFAULT; | 
 | 	} else if (!__builtin_constant_p(count)) { | 
 | 		__user_memcpy(dst, src, count, err, -EFAULT); | 
 | 	} else { | 
 | 		err = __put_user(dst, src, count); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline int __get_user(void *dst, const void *src, unsigned int count) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	switch (count) { | 
 | 	case 1: | 
 | 		__get_user_asm(*(uint8_t *) dst, (const uint8_t *) src, err, "b", | 
 | 		               "b", "=q", -EFAULT); | 
 | 		break; | 
 | 	case 2: | 
 | 		__get_user_asm(*(uint16_t *) dst, (const uint16_t *) src, err, "w", | 
 | 		               "w", "=r", -EFAULT); | 
 | 		break; | 
 | 	case 4: | 
 | 		__get_user_asm(*(uint32_t *) dst, (const uint32_t *) src, err, "l", | 
 | 		               "k", "=r", -EFAULT); | 
 | 		break; | 
 | 	case 8: | 
 | 		__get_user_asm(*(uint64_t *) dst, (const uint64_t *) src, err, "q", | 
 | 		               "", "=r", -EFAULT); | 
 | 		break; | 
 | 	default: | 
 | 		__user_memcpy(dst, src, count, err, -EFAULT); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline int copy_from_user(void *dst, const void *src, | 
 |                                  unsigned int count) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	if (unlikely(!is_user_raddr((void *) src, count))) { | 
 | 		err = -EFAULT; | 
 | 	} else if (!__builtin_constant_p(count)) { | 
 | 		__user_memcpy(dst, src, count, err, -EFAULT); | 
 | 	} else { | 
 | 		err = __get_user(dst, src, count); | 
 | 	} | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline int read_msr_safe(uint32_t addr, uint64_t *value) | 
 | { | 
 | 	int err = 0; | 
 | 	uint32_t edx, eax; | 
 |  | 
 | 	__read_msr_asm(eax, edx, addr, err, -EFAULT); | 
 | 	if (likely(err == 0)) | 
 | 		*value = ((uint64_t) edx << 32) | eax; | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline int write_msr_safe(uint32_t addr, uint64_t value) | 
 | { | 
 | 	int err = 0; | 
 |  | 
 | 	__write_msr_asm(value, addr, err, -EFAULT); | 
 |  | 
 | 	return err; | 
 | } | 
 |  | 
 | static inline uintptr_t ex_insn_addr(const struct extable_ip_fixup *x) | 
 | { | 
 | 	return (uintptr_t) &x->insn + x->insn; | 
 | } | 
 |  | 
 | static inline uintptr_t ex_fixup_addr(const struct extable_ip_fixup *x) | 
 | { | 
 | 	return (uintptr_t) &x->fixup + x->fixup; | 
 | } |