diff options
author | Anton Vorontsov <anton.vorontsov@linaro.org> | 2012-05-16 08:43:08 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-16 11:06:37 -0400 |
commit | 1894a253db97059bc299b834b76f665bc6586b1d (patch) | |
tree | 791ad2d33cc1d28c32fa340b0a8f0c2a645c4b26 /fs/pstore/ram.c | |
parent | d109a674a3685d43f16da5cc4cb8b927d07c436d (diff) |
ramoops: Move to fs/pstore/ram.c
Since ramoops was converted to pstore, it has nothing to do with character
devices nowadays. Instead, today it is just a RAM backend for pstore.
The patch just moves things around. There are a few changes were needed
because of the move:
1. Kconfig and Makefiles fixups, of course.
2. In pstore/ram.c we have to play a bit with MODULE_PARAM_PREFIX, this
is needed to keep user experience the same as with ramoops driver
(i.e. so that ramoops.foo kernel command line arguments would still
work).
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Acked-by: Marco Stornelli <marco.stornelli@gmail.com>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/pstore/ram.c')
-rw-r--r-- | fs/pstore/ram.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c new file mode 100644 index 000000000000..e443c9c6914f --- /dev/null +++ b/fs/pstore/ram.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * RAM Oops/Panic logger | ||
3 | * | ||
4 | * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com> | ||
5 | * Copyright (C) 2011 Kees Cook <keescook@chromium.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/pstore.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/pstore_ram.h> | ||
35 | |||
36 | #define RAMOOPS_KERNMSG_HDR "====" | ||
37 | #define MIN_MEM_SIZE 4096UL | ||
38 | |||
39 | static ulong record_size = MIN_MEM_SIZE; | ||
40 | module_param(record_size, ulong, 0400); | ||
41 | MODULE_PARM_DESC(record_size, | ||
42 | "size of each dump done on oops/panic"); | ||
43 | |||
44 | static ulong mem_address; | ||
45 | module_param(mem_address, ulong, 0400); | ||
46 | MODULE_PARM_DESC(mem_address, | ||
47 | "start of reserved RAM used to store oops/panic logs"); | ||
48 | |||
49 | static ulong mem_size; | ||
50 | module_param(mem_size, ulong, 0400); | ||
51 | MODULE_PARM_DESC(mem_size, | ||
52 | "size of reserved RAM used to store oops/panic logs"); | ||
53 | |||
54 | static int dump_oops = 1; | ||
55 | module_param(dump_oops, int, 0600); | ||
56 | MODULE_PARM_DESC(dump_oops, | ||
57 | "set to 1 to dump oopses, 0 to only dump panics (default 1)"); | ||
58 | |||
59 | struct ramoops_context { | ||
60 | void *virt_addr; | ||
61 | phys_addr_t phys_addr; | ||
62 | unsigned long size; | ||
63 | size_t record_size; | ||
64 | int dump_oops; | ||
65 | unsigned int count; | ||
66 | unsigned int max_count; | ||
67 | unsigned int read_count; | ||
68 | struct pstore_info pstore; | ||
69 | }; | ||
70 | |||
71 | static struct platform_device *dummy; | ||
72 | static struct ramoops_platform_data *dummy_data; | ||
73 | |||
74 | static int ramoops_pstore_open(struct pstore_info *psi) | ||
75 | { | ||
76 | struct ramoops_context *cxt = psi->data; | ||
77 | |||
78 | cxt->read_count = 0; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | ||
83 | struct timespec *time, | ||
84 | char **buf, | ||
85 | struct pstore_info *psi) | ||
86 | { | ||
87 | ssize_t size; | ||
88 | char *rambuf; | ||
89 | struct ramoops_context *cxt = psi->data; | ||
90 | |||
91 | if (cxt->read_count >= cxt->max_count) | ||
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 | |||
110 | static 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; | ||
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; | ||
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 | */ | ||
129 | if (reason != KMSG_DUMP_OOPS && | ||
130 | reason != KMSG_DUMP_PANIC) | ||
131 | return -EINVAL; | ||
132 | |||
133 | /* Skip Oopes when configured to do so. */ | ||
134 | if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) | ||
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; | ||
144 | |||
145 | buf = cxt->virt_addr + (cxt->count * cxt->record_size); | ||
146 | |||
147 | res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); | ||
148 | buf += res; | ||
149 | available -= res; | ||
150 | |||
151 | do_gettimeofday(×tamp); | ||
152 | res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); | ||
153 | buf += res; | ||
154 | available -= res; | ||
155 | |||
156 | if (size > available) | ||
157 | size = available; | ||
158 | |||
159 | memcpy(buf, cxt->pstore.buf, size); | ||
160 | memset(buf + size, '\0', available - size); | ||
161 | |||
162 | cxt->count = (cxt->count + 1) % cxt->max_count; | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static 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; | ||
180 | } | ||
181 | |||
182 | static 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 | |||
193 | static int __init ramoops_probe(struct platform_device *pdev) | ||
194 | { | ||
195 | struct ramoops_platform_data *pdata = pdev->dev.platform_data; | ||
196 | struct ramoops_context *cxt = &oops_cxt; | ||
197 | int err = -EINVAL; | ||
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 | |||
205 | if (!pdata->mem_size || !pdata->record_size) { | ||
206 | pr_err("The memory size and the record size must be " | ||
207 | "non-zero\n"); | ||
208 | goto fail_out; | ||
209 | } | ||
210 | |||
211 | pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); | ||
212 | pdata->record_size = rounddown_pow_of_two(pdata->record_size); | ||
213 | |||
214 | /* Check for the minimum memory size */ | ||
215 | if (pdata->mem_size < MIN_MEM_SIZE && | ||
216 | pdata->record_size < MIN_MEM_SIZE) { | ||
217 | pr_err("memory size too small, minimum is %lu\n", | ||
218 | MIN_MEM_SIZE); | ||
219 | goto fail_out; | ||
220 | } | ||
221 | |||
222 | if (pdata->mem_size < pdata->record_size) { | ||
223 | pr_err("The memory size must be larger than the " | ||
224 | "records size\n"); | ||
225 | goto fail_out; | ||
226 | } | ||
227 | |||
228 | cxt->max_count = pdata->mem_size / pdata->record_size; | ||
229 | cxt->count = 0; | ||
230 | cxt->size = pdata->mem_size; | ||
231 | cxt->phys_addr = pdata->mem_address; | ||
232 | cxt->record_size = pdata->record_size; | ||
233 | cxt->dump_oops = pdata->dump_oops; | ||
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 | |||
244 | if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { | ||
245 | pr_err("request mem region (0x%lx@0x%llx) failed\n", | ||
246 | cxt->size, (unsigned long long)cxt->phys_addr); | ||
247 | err = -EINVAL; | ||
248 | goto fail_buf; | ||
249 | } | ||
250 | |||
251 | cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); | ||
252 | if (!cxt->virt_addr) { | ||
253 | pr_err("ioremap failed\n"); | ||
254 | goto fail_mem_region; | ||
255 | } | ||
256 | |||
257 | err = pstore_register(&cxt->pstore); | ||
258 | if (err) { | ||
259 | pr_err("registering with pstore failed\n"); | ||
260 | goto fail_iounmap; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Update the module parameter variables as well so they are visible | ||
265 | * through /sys/module/ramoops/parameters/ | ||
266 | */ | ||
267 | mem_size = pdata->mem_size; | ||
268 | mem_address = pdata->mem_address; | ||
269 | record_size = pdata->record_size; | ||
270 | dump_oops = pdata->dump_oops; | ||
271 | |||
272 | pr_info("attached 0x%lx@0x%llx (%ux0x%zx)\n", | ||
273 | cxt->size, (unsigned long long)cxt->phys_addr, | ||
274 | cxt->max_count, cxt->record_size); | ||
275 | |||
276 | return 0; | ||
277 | |||
278 | fail_iounmap: | ||
279 | iounmap(cxt->virt_addr); | ||
280 | fail_mem_region: | ||
281 | release_mem_region(cxt->phys_addr, cxt->size); | ||
282 | fail_buf: | ||
283 | kfree(cxt->pstore.buf); | ||
284 | fail_clear: | ||
285 | cxt->pstore.bufsize = 0; | ||
286 | cxt->max_count = 0; | ||
287 | fail_out: | ||
288 | return err; | ||
289 | } | ||
290 | |||
291 | static int __exit ramoops_remove(struct platform_device *pdev) | ||
292 | { | ||
293 | #if 0 | ||
294 | /* TODO(kees): We cannot unload ramoops since pstore doesn't support | ||
295 | * unregistering yet. | ||
296 | */ | ||
297 | struct ramoops_context *cxt = &oops_cxt; | ||
298 | |||
299 | iounmap(cxt->virt_addr); | ||
300 | release_mem_region(cxt->phys_addr, cxt->size); | ||
301 | cxt->max_count = 0; | ||
302 | |||
303 | /* TODO(kees): When pstore supports unregistering, call it here. */ | ||
304 | kfree(cxt->pstore.buf); | ||
305 | cxt->pstore.bufsize = 0; | ||
306 | |||
307 | return 0; | ||
308 | #endif | ||
309 | return -EBUSY; | ||
310 | } | ||
311 | |||
312 | static struct platform_driver ramoops_driver = { | ||
313 | .remove = __exit_p(ramoops_remove), | ||
314 | .driver = { | ||
315 | .name = "ramoops", | ||
316 | .owner = THIS_MODULE, | ||
317 | }, | ||
318 | }; | ||
319 | |||
320 | static int __init ramoops_init(void) | ||
321 | { | ||
322 | int ret; | ||
323 | ret = platform_driver_probe(&ramoops_driver, ramoops_probe); | ||
324 | if (ret == -ENODEV) { | ||
325 | /* | ||
326 | * If we didn't find a platform device, we use module parameters | ||
327 | * building platform data on the fly. | ||
328 | */ | ||
329 | pr_info("platform device not found, using module parameters\n"); | ||
330 | dummy_data = kzalloc(sizeof(struct ramoops_platform_data), | ||
331 | GFP_KERNEL); | ||
332 | if (!dummy_data) | ||
333 | return -ENOMEM; | ||
334 | dummy_data->mem_size = mem_size; | ||
335 | dummy_data->mem_address = mem_address; | ||
336 | dummy_data->record_size = record_size; | ||
337 | dummy_data->dump_oops = dump_oops; | ||
338 | dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, | ||
339 | NULL, 0, dummy_data, | ||
340 | sizeof(struct ramoops_platform_data)); | ||
341 | |||
342 | if (IS_ERR(dummy)) | ||
343 | ret = PTR_ERR(dummy); | ||
344 | else | ||
345 | ret = 0; | ||
346 | } | ||
347 | |||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static void __exit ramoops_exit(void) | ||
352 | { | ||
353 | platform_driver_unregister(&ramoops_driver); | ||
354 | kfree(dummy_data); | ||
355 | } | ||
356 | |||
357 | module_init(ramoops_init); | ||
358 | module_exit(ramoops_exit); | ||
359 | |||
360 | MODULE_LICENSE("GPL"); | ||
361 | MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>"); | ||
362 | MODULE_DESCRIPTION("RAM Oops/Panic logger/driver"); | ||