aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/ramoops.c
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2012-05-03 01:45:02 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-16 11:06:33 -0400
commit9ba80d99c86f1b76df891afdf39b44df38bbd35b (patch)
treef011fd0ac2adb5cf2a36b15cce0d0b135acaaf09 /drivers/char/ramoops.c
parent7dd8e9be9dba1dc5f0dfec67e37076b9c56a97d7 (diff)
ramoops: use pstore interface
Instead of using /dev/mem directly and forcing userspace to know (or extract) where the platform has defined persistent memory, how many slots it has, the sizes, etc, use the common pstore infrastructure to handle Oops gathering and extraction. This presents a much easier to use filesystem-based view to the memory region. This also means that any other tools that are written to understand pstore will automatically be able to process ramoops too. Signed-off-by: Kees Cook <keescook@chromium.org> Cc: Tony Luck <tony.luck@intel.com> Cc: Marco Stornelli <marco.stornelli@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/char/ramoops.c')
-rw-r--r--drivers/char/ramoops.c203
1 files changed, 157 insertions, 46 deletions
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c
index 2a5e45d2a9f8..a6796c0610d2 100644
--- a/drivers/char/ramoops.c
+++ b/drivers/char/ramoops.c
@@ -2,6 +2,7 @@
2 * RAM Oops/Panic logger 2 * RAM Oops/Panic logger
3 * 3 *
4 * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com> 4 * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
5 * Copyright (C) 2011 Kees Cook <keescook@chromium.org>
5 * 6 *
6 * This program is free software; you can redistribute it and/or 7 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License 8 * modify it under the terms of the GNU General Public License
@@ -24,7 +25,7 @@
24#include <linux/kernel.h> 25#include <linux/kernel.h>
25#include <linux/err.h> 26#include <linux/err.h>
26#include <linux/module.h> 27#include <linux/module.h>
27#include <linux/kmsg_dump.h> 28#include <linux/pstore.h>
28#include <linux/time.h> 29#include <linux/time.h>
29#include <linux/io.h> 30#include <linux/io.h>
30#include <linux/ioport.h> 31#include <linux/ioport.h>
@@ -55,73 +56,156 @@ module_param(dump_oops, int, 0600);
55MODULE_PARM_DESC(dump_oops, 56MODULE_PARM_DESC(dump_oops,
56 "set to 1 to dump oopses, 0 to only dump panics (default 1)"); 57 "set to 1 to dump oopses, 0 to only dump panics (default 1)");
57 58
58static struct ramoops_context { 59struct ramoops_context {
59 struct kmsg_dumper dump;
60 void *virt_addr; 60 void *virt_addr;
61 phys_addr_t phys_addr; 61 phys_addr_t phys_addr;
62 unsigned long size; 62 unsigned long size;
63 unsigned long record_size; 63 size_t record_size;
64 int dump_oops; 64 int dump_oops;
65 int count; 65 unsigned int count;
66 int max_count; 66 unsigned int max_count;
67} oops_cxt; 67 unsigned int read_count;
68 struct pstore_info pstore;
69};
68 70
69static struct platform_device *dummy; 71static struct platform_device *dummy;
70static struct ramoops_platform_data *dummy_data; 72static struct ramoops_platform_data *dummy_data;
71 73
72static void ramoops_do_dump(struct kmsg_dumper *dumper, 74static int ramoops_pstore_open(struct pstore_info *psi)
73 enum kmsg_dump_reason reason, const char *s1, unsigned long l1, 75{
74 const char *s2, unsigned long l2) 76 struct ramoops_context *cxt = psi->data;
77
78 cxt->read_count = 0;
79 return 0;
80}
81
82static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
83 struct timespec *time,
84 char **buf,
85 struct pstore_info *psi)
75{ 86{
76 struct ramoops_context *cxt = container_of(dumper, 87 ssize_t size;
77 struct ramoops_context, dump); 88 char *rambuf;
78 unsigned long s1_start, s2_start; 89 struct ramoops_context *cxt = psi->data;
79 unsigned long l1_cpy, l2_cpy; 90
80 int res, hdr_size; 91 if (cxt->read_count >= cxt->max_count)
81 char *buf, *buf_orig; 92 return -EINVAL;
93 *id = cxt->read_count++;
94 /* Only supports dmesg output so far. */
95 *type = PSTORE_TYPE_DMESG;
96 /* TODO(kees): Bogus time for the moment. */
97 time->tv_sec = 0;
98 time->tv_nsec = 0;
99
100 rambuf = cxt->virt_addr + (*id * cxt->record_size);
101 size = strnlen(rambuf, cxt->record_size);
102 *buf = kmalloc(size, GFP_KERNEL);
103 if (*buf == NULL)
104 return -ENOMEM;
105 memcpy(*buf, rambuf, size);
106
107 return size;
108}
109
110static int ramoops_pstore_write(enum pstore_type_id type,
111 enum kmsg_dump_reason reason,
112 u64 *id,
113 unsigned int part,
114 size_t size, struct pstore_info *psi)
115{
116 char *buf;
117 size_t res;
82 struct timeval timestamp; 118 struct timeval timestamp;
119 struct ramoops_context *cxt = psi->data;
120 size_t available = cxt->record_size;
121
122 /* Currently ramoops is designed to only store dmesg dumps. */
123 if (type != PSTORE_TYPE_DMESG)
124 return -EINVAL;
83 125
126 /* Out of the various dmesg dump types, ramoops is currently designed
127 * to only store crash logs, rather than storing general kernel logs.
128 */
84 if (reason != KMSG_DUMP_OOPS && 129 if (reason != KMSG_DUMP_OOPS &&
85 reason != KMSG_DUMP_PANIC) 130 reason != KMSG_DUMP_PANIC)
86 return; 131 return -EINVAL;
87 132
88 /* Only dump oopses if dump_oops is set */ 133 /* Skip Oopes when configured to do so. */
89 if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) 134 if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
90 return; 135 return -EINVAL;
136
137 /* Explicitly only take the first part of any new crash.
138 * If our buffer is larger than kmsg_bytes, this can never happen,
139 * and if our buffer is smaller than kmsg_bytes, we don't want the
140 * report split across multiple records.
141 */
142 if (part != 1)
143 return -ENOSPC;
91 144
92 buf = cxt->virt_addr + (cxt->count * cxt->record_size); 145 buf = cxt->virt_addr + (cxt->count * cxt->record_size);
93 buf_orig = buf;
94 146
95 memset(buf, '\0', cxt->record_size);
96 res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); 147 res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
97 buf += res; 148 buf += res;
149 available -= res;
150
98 do_gettimeofday(&timestamp); 151 do_gettimeofday(&timestamp);
99 res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); 152 res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec);
100 buf += res; 153 buf += res;
154 available -= res;
101 155
102 hdr_size = buf - buf_orig; 156 if (size > available)
103 l2_cpy = min(l2, cxt->record_size - hdr_size); 157 size = available;
104 l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy);
105 158
106 s2_start = l2 - l2_cpy; 159 memcpy(buf, cxt->pstore.buf, size);
107 s1_start = l1 - l1_cpy; 160 memset(buf + size, '\0', available - size);
108
109 memcpy(buf, s1 + s1_start, l1_cpy);
110 memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy);
111 161
112 cxt->count = (cxt->count + 1) % cxt->max_count; 162 cxt->count = (cxt->count + 1) % cxt->max_count;
163
164 return 0;
165}
166
167static int ramoops_pstore_erase(enum pstore_type_id type, u64 id,
168 struct pstore_info *psi)
169{
170 char *buf;
171 struct ramoops_context *cxt = psi->data;
172
173 if (id >= cxt->max_count)
174 return -EINVAL;
175
176 buf = cxt->virt_addr + (id * cxt->record_size);
177 memset(buf, '\0', cxt->record_size);
178
179 return 0;
113} 180}
114 181
182static struct ramoops_context oops_cxt = {
183 .pstore = {
184 .owner = THIS_MODULE,
185 .name = "ramoops",
186 .open = ramoops_pstore_open,
187 .read = ramoops_pstore_read,
188 .write = ramoops_pstore_write,
189 .erase = ramoops_pstore_erase,
190 },
191};
192
115static int __init ramoops_probe(struct platform_device *pdev) 193static int __init ramoops_probe(struct platform_device *pdev)
116{ 194{
117 struct ramoops_platform_data *pdata = pdev->dev.platform_data; 195 struct ramoops_platform_data *pdata = pdev->dev.platform_data;
118 struct ramoops_context *cxt = &oops_cxt; 196 struct ramoops_context *cxt = &oops_cxt;
119 int err = -EINVAL; 197 int err = -EINVAL;
120 198
199 /* Only a single ramoops area allowed at a time, so fail extra
200 * probes.
201 */
202 if (cxt->max_count)
203 goto fail_out;
204
121 if (!pdata->mem_size || !pdata->record_size) { 205 if (!pdata->mem_size || !pdata->record_size) {
122 pr_err("The memory size and the record size must be " 206 pr_err("The memory size and the record size must be "
123 "non-zero\n"); 207 "non-zero\n");
124 goto fail3; 208 goto fail_out;
125 } 209 }
126 210
127 pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); 211 pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
@@ -130,14 +214,15 @@ static int __init ramoops_probe(struct platform_device *pdev)
130 /* Check for the minimum memory size */ 214 /* Check for the minimum memory size */
131 if (pdata->mem_size < MIN_MEM_SIZE && 215 if (pdata->mem_size < MIN_MEM_SIZE &&
132 pdata->record_size < MIN_MEM_SIZE) { 216 pdata->record_size < MIN_MEM_SIZE) {
133 pr_err("memory size too small, minium is %lu\n", MIN_MEM_SIZE); 217 pr_err("memory size too small, minimum is %lu\n",
134 goto fail3; 218 MIN_MEM_SIZE);
219 goto fail_out;
135 } 220 }
136 221
137 if (pdata->mem_size < pdata->record_size) { 222 if (pdata->mem_size < pdata->record_size) {
138 pr_err("The memory size must be larger than the " 223 pr_err("The memory size must be larger than the "
139 "records size\n"); 224 "records size\n");
140 goto fail3; 225 goto fail_out;
141 } 226 }
142 227
143 cxt->max_count = pdata->mem_size / pdata->record_size; 228 cxt->max_count = pdata->mem_size / pdata->record_size;
@@ -147,23 +232,32 @@ static int __init ramoops_probe(struct platform_device *pdev)
147 cxt->record_size = pdata->record_size; 232 cxt->record_size = pdata->record_size;
148 cxt->dump_oops = pdata->dump_oops; 233 cxt->dump_oops = pdata->dump_oops;
149 234
235 cxt->pstore.data = cxt;
236 cxt->pstore.bufsize = cxt->record_size;
237 cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL);
238 spin_lock_init(&cxt->pstore.buf_lock);
239 if (!cxt->pstore.buf) {
240 pr_err("cannot allocate pstore buffer\n");
241 goto fail_clear;
242 }
243
150 if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { 244 if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
151 pr_err("request mem region failed\n"); 245 pr_err("request mem region (0x%lx@0x%llx) failed\n",
246 cxt->size, cxt->phys_addr);
152 err = -EINVAL; 247 err = -EINVAL;
153 goto fail3; 248 goto fail_buf;
154 } 249 }
155 250
156 cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); 251 cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size);
157 if (!cxt->virt_addr) { 252 if (!cxt->virt_addr) {
158 pr_err("ioremap failed\n"); 253 pr_err("ioremap failed\n");
159 goto fail2; 254 goto fail_mem_region;
160 } 255 }
161 256
162 cxt->dump.dump = ramoops_do_dump; 257 err = pstore_register(&cxt->pstore);
163 err = kmsg_dump_register(&cxt->dump);
164 if (err) { 258 if (err) {
165 pr_err("registering kmsg dumper failed\n"); 259 pr_err("registering with pstore failed\n");
166 goto fail1; 260 goto fail_iounmap;
167 } 261 }
168 262
169 /* 263 /*
@@ -175,26 +269,43 @@ static int __init ramoops_probe(struct platform_device *pdev)
175 record_size = pdata->record_size; 269 record_size = pdata->record_size;
176 dump_oops = pdata->dump_oops; 270 dump_oops = pdata->dump_oops;
177 271
272 pr_info("attached 0x%lx@0x%llx (%ux0x%zx)\n",
273 cxt->size, cxt->phys_addr, cxt->max_count, cxt->record_size);
274
178 return 0; 275 return 0;
179 276
180fail1: 277fail_iounmap:
181 iounmap(cxt->virt_addr); 278 iounmap(cxt->virt_addr);
182fail2: 279fail_mem_region:
183 release_mem_region(cxt->phys_addr, cxt->size); 280 release_mem_region(cxt->phys_addr, cxt->size);
184fail3: 281fail_buf:
282 kfree(cxt->pstore.buf);
283fail_clear:
284 cxt->pstore.bufsize = 0;
285 cxt->max_count = 0;
286fail_out:
185 return err; 287 return err;
186} 288}
187 289
188static int __exit ramoops_remove(struct platform_device *pdev) 290static int __exit ramoops_remove(struct platform_device *pdev)
189{ 291{
292#if 0
293 /* TODO(kees): We cannot unload ramoops since pstore doesn't support
294 * unregistering yet.
295 */
190 struct ramoops_context *cxt = &oops_cxt; 296 struct ramoops_context *cxt = &oops_cxt;
191 297
192 if (kmsg_dump_unregister(&cxt->dump) < 0)
193 pr_warn("could not unregister kmsg_dumper\n");
194
195 iounmap(cxt->virt_addr); 298 iounmap(cxt->virt_addr);
196 release_mem_region(cxt->phys_addr, cxt->size); 299 release_mem_region(cxt->phys_addr, cxt->size);
300 cxt->max_count = 0;
301
302 /* TODO(kees): When pstore supports unregistering, call it here. */
303 kfree(cxt->pstore.buf);
304 cxt->pstore.bufsize = 0;
305
197 return 0; 306 return 0;
307#endif
308 return -EBUSY;
198} 309}
199 310
200static struct platform_driver ramoops_driver = { 311static struct platform_driver ramoops_driver = {