blob: 4a1927d98c4957e2c8dbd4c8c01e329817562781 [file] [log] [blame]
/* 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;
}