blob: 219fc821af6d789474567b7e26a4b2468f9ea76d [file] [log] [blame]
/*
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Andrey Ryabinin <a.ryabinin@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#define pr_fmt(fmt) "cfu test: %s " fmt, __func__
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/module.h>
static char pat[2 * PAGE_SIZE];
static void run_test(int n, int m, char __user *up, char *kp)
{
int i;
for (i = 0; i < 4; i++) {
int r;
memset(kp, 0, 2 * PAGE_SIZE);
r = __copy_from_user_inatomic(kp + i, up, m);
if (m <= n) {
if (r) {
pr_err("bogus fault (%d, %d, %d)\n", r, m, n);
return;
}
} else {
if (r < m - n) {
pr_err("claims too much (%d, %d, %d)\n", r, m, n);
return;
}
}
r = m - r; /* claim to have copied that much */
if (memcmp(kp + i, pat + PAGE_SIZE - n, r)) {
int j;
pr_err("crap in copy (%d, %d, %d)", r, m, n);
for (j = 0; j < r; j++) {
if (!kp[i+j]) {
if (!memcmp(kp + i + j, pat + PAGE_SIZE, PAGE_SIZE)) {
pr_cont(" only %d copied\n", j);
return;
}
break;
}
}
pr_cont("\n");
return;
}
if (memcmp(kp + i + r, pat + PAGE_SIZE, PAGE_SIZE)) {
pr_err("crap after copy (%d, %d, %d)\n", r, m, n);
return;
}
}
}
static int __init cfu_test(void)
{
char *kp;
char __user *up;
int i;
kp = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!kp)
return -EAGAIN;
up = (char __user *)vm_mmap(NULL, 0, 2 * PAGE_SIZE,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_PRIVATE, 0);
if (IS_ERR(up)) {
pr_err("Failed to allocate user memory\n");
kfree(kp);
return -EAGAIN;
}
vm_munmap((unsigned long)up + PAGE_SIZE, PAGE_SIZE);
for (i = 0; i < PAGE_SIZE; i++)
pat[i] = 128 | i;
if (copy_to_user(up, pat, PAGE_SIZE)) {
pr_err("failed to copy to user memory\n");
goto out;
}
for (i = 0; i <= 128; i++) {
int j;
pr_err("trying %d\n", i);
for (j = 0; j <= 128; j++)
run_test(i, j, up + PAGE_SIZE - i, kp);
}
out:
vm_munmap((unsigned long)up, PAGE_SIZE);
kfree(kp);
return -EAGAIN;
}
module_init(cfu_test);
MODULE_LICENSE("GPL");