diff options
Diffstat (limited to 'fs/pstore/pmsg.c')
| -rw-r--r-- | fs/pstore/pmsg.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c new file mode 100644 index 000000000000..feb5dd2948b4 --- /dev/null +++ b/fs/pstore/pmsg.c | |||
| @@ -0,0 +1,114 @@ | |||
| 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 | |||
| 21 | static DEFINE_MUTEX(pmsg_lock); | ||
| 22 | #define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE) | ||
| 23 | |||
| 24 | static 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 | |||
| 64 | static const struct file_operations pmsg_fops = { | ||
| 65 | .owner = THIS_MODULE, | ||
| 66 | .llseek = noop_llseek, | ||
| 67 | .write = write_pmsg, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static struct class *pmsg_class; | ||
| 71 | static int pmsg_major; | ||
| 72 | #define PMSG_NAME "pmsg" | ||
| 73 | #undef pr_fmt | ||
| 74 | #define pr_fmt(fmt) PMSG_NAME ": " fmt | ||
| 75 | |||
| 76 | static char *pmsg_devnode(struct device *dev, umode_t *mode) | ||
| 77 | { | ||
| 78 | if (mode) | ||
| 79 | *mode = 0220; | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | |||
| 83 | void 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 | |||
| 108 | err_device: | ||
| 109 | class_destroy(pmsg_class); | ||
| 110 | err_class: | ||
| 111 | unregister_chrdev(pmsg_major, PMSG_NAME); | ||
| 112 | err: | ||
| 113 | return; | ||
| 114 | } | ||
