| /* 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; | 
 | } |