1/*
2 * Copyright 2014  Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/cdev.h>
15#include <linux/device.h>
16#include <linux/fs.h>
17#include <linux/uaccess.h>
18#include <linux/vmalloc.h>
19#include "internal.h"
20
21static DEFINE_MUTEX(pmsg_lock);
22#define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE)
23
24static ssize_t write_pmsg(struct file *file, const char __user *buf,
25			  size_t count, loff_t *ppos)
26{
27	size_t i, buffer_size;
28	char *buffer;
29
30	if (!count)
31		return 0;
32
33	if (!access_ok(VERIFY_READ, buf, count))
34		return -EFAULT;
35
36	buffer_size = count;
37	if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE)
38		buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE;
39	buffer = vmalloc(buffer_size);
40
41	mutex_lock(&pmsg_lock);
42	for (i = 0; i < count; ) {
43		size_t c = min(count - i, buffer_size);
44		u64 id;
45		long ret;
46
47		ret = __copy_from_user(buffer, buf + i, c);
48		if (unlikely(ret != 0)) {
49			mutex_unlock(&pmsg_lock);
50			vfree(buffer);
51			return -EFAULT;
52		}
53		psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c,
54				  psinfo);
55
56		i += c;
57	}
58
59	mutex_unlock(&pmsg_lock);
60	vfree(buffer);
61	return count;
62}
63
64static const struct file_operations pmsg_fops = {
65	.owner		= THIS_MODULE,
66	.llseek		= noop_llseek,
67	.write		= write_pmsg,
68};
69
70static struct class *pmsg_class;
71static int pmsg_major;
72#define PMSG_NAME "pmsg"
73#undef pr_fmt
74#define pr_fmt(fmt) PMSG_NAME ": " fmt
75
76static char *pmsg_devnode(struct device *dev, umode_t *mode)
77{
78	if (mode)
79		*mode = 0220;
80	return NULL;
81}
82
83void pstore_register_pmsg(void)
84{
85	struct device *pmsg_device;
86
87	pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops);
88	if (pmsg_major < 0) {
89		pr_err("register_chrdev failed\n");
90		goto err;
91	}
92
93	pmsg_class = class_create(THIS_MODULE, PMSG_NAME);
94	if (IS_ERR(pmsg_class)) {
95		pr_err("device class file already in use\n");
96		goto err_class;
97	}
98	pmsg_class->devnode = pmsg_devnode;
99
100	pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0),
101					NULL, "%s%d", PMSG_NAME, 0);
102	if (IS_ERR(pmsg_device)) {
103		pr_err("failed to create device\n");
104		goto err_device;
105	}
106	return;
107
108err_device:
109	class_destroy(pmsg_class);
110err_class:
111	unregister_chrdev(pmsg_major, PMSG_NAME);
112err:
113	return;
114}
115