diff options
author | Mark Salyzyn <salyzyn@android.com> | 2015-01-16 19:01:10 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2015-01-16 19:01:10 -0500 |
commit | 9d5438f462abd6398cdb7b3211bdcec271873a3b (patch) | |
tree | feb8e50edd4180710602c0f4d2db9d76559da1f8 /fs/pstore/pmsg.c | |
parent | f44f96528a8cd4134a4f2c1a9d8c785600aa4888 (diff) |
pstore: Add pmsg - user-space accessible pstore object
A secured user-space accessible pstore object. Writes
to /dev/pmsg0 are appended to the buffer, on reboot
the persistent contents are available in
/sys/fs/pstore/pmsg-ramoops-[ID].
One possible use is syslogd, or other daemon, can
write messages, then on reboot provides a means to
triage user-space activities leading up to a panic
as a companion to the pstore dmesg or console logs.
Signed-off-by: Mark Salyzyn <salyzyn@android.com>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>
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 | } | ||