diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/Kconfig | 7 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/ramoops.c | 162 |
3 files changed, 170 insertions, 0 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index e21175be25d0..f09fc0e2062d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
@@ -1121,5 +1121,12 @@ config DEVPORT | |||
1121 | 1121 | ||
1122 | source "drivers/s390/char/Kconfig" | 1122 | source "drivers/s390/char/Kconfig" |
1123 | 1123 | ||
1124 | config RAMOOPS | ||
1125 | tristate "Log panic/oops to a RAM buffer" | ||
1126 | default n | ||
1127 | help | ||
1128 | This enables panic and oops messages to be logged to a circular | ||
1129 | buffer in RAM where it can be read back at some later point. | ||
1130 | |||
1124 | endmenu | 1131 | endmenu |
1125 | 1132 | ||
diff --git a/drivers/char/Makefile b/drivers/char/Makefile index d39be4cf1f5d..88d6eac69754 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile | |||
@@ -108,6 +108,7 @@ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o | |||
108 | obj-$(CONFIG_TCG_TPM) += tpm/ | 108 | obj-$(CONFIG_TCG_TPM) += tpm/ |
109 | 109 | ||
110 | obj-$(CONFIG_PS3_FLASH) += ps3flash.o | 110 | obj-$(CONFIG_PS3_FLASH) += ps3flash.o |
111 | obj-$(CONFIG_RAMOOPS) += ramoops.o | ||
111 | 112 | ||
112 | obj-$(CONFIG_JS_RTC) += js-rtc.o | 113 | obj-$(CONFIG_JS_RTC) += js-rtc.o |
113 | js-rtc-y = rtc.o | 114 | js-rtc-y = rtc.o |
diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c new file mode 100644 index 000000000000..74f00b5ffa36 --- /dev/null +++ b/drivers/char/ramoops.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * RAM Oops/Panic logger | ||
3 | * | ||
4 | * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/kmsg_dump.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/ioport.h> | ||
28 | |||
29 | #define RAMOOPS_KERNMSG_HDR "====" | ||
30 | #define RAMOOPS_HEADER_SIZE (5 + sizeof(struct timeval)) | ||
31 | |||
32 | #define RECORD_SIZE 4096 | ||
33 | |||
34 | static ulong mem_address; | ||
35 | module_param(mem_address, ulong, 0400); | ||
36 | MODULE_PARM_DESC(mem_address, | ||
37 | "start of reserved RAM used to store oops/panic logs"); | ||
38 | |||
39 | static ulong mem_size; | ||
40 | module_param(mem_size, ulong, 0400); | ||
41 | MODULE_PARM_DESC(mem_size, | ||
42 | "size of reserved RAM used to store oops/panic logs"); | ||
43 | |||
44 | static int dump_oops = 1; | ||
45 | module_param(dump_oops, int, 0600); | ||
46 | MODULE_PARM_DESC(dump_oops, | ||
47 | "set to 1 to dump oopses, 0 to only dump panics (default 1)"); | ||
48 | |||
49 | static struct ramoops_context { | ||
50 | struct kmsg_dumper dump; | ||
51 | void *virt_addr; | ||
52 | phys_addr_t phys_addr; | ||
53 | unsigned long size; | ||
54 | int count; | ||
55 | int max_count; | ||
56 | } oops_cxt; | ||
57 | |||
58 | static void ramoops_do_dump(struct kmsg_dumper *dumper, | ||
59 | enum kmsg_dump_reason reason, const char *s1, unsigned long l1, | ||
60 | const char *s2, unsigned long l2) | ||
61 | { | ||
62 | struct ramoops_context *cxt = container_of(dumper, | ||
63 | struct ramoops_context, dump); | ||
64 | unsigned long s1_start, s2_start; | ||
65 | unsigned long l1_cpy, l2_cpy; | ||
66 | int res; | ||
67 | char *buf; | ||
68 | struct timeval timestamp; | ||
69 | |||
70 | /* Only dump oopses if dump_oops is set */ | ||
71 | if (reason == KMSG_DUMP_OOPS && !dump_oops) | ||
72 | return; | ||
73 | |||
74 | buf = (char *)(cxt->virt_addr + (cxt->count * RECORD_SIZE)); | ||
75 | memset(buf, '\0', RECORD_SIZE); | ||
76 | res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); | ||
77 | buf += res; | ||
78 | do_gettimeofday(×tamp); | ||
79 | res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); | ||
80 | buf += res; | ||
81 | |||
82 | l2_cpy = min(l2, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE)); | ||
83 | l1_cpy = min(l1, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE) - l2_cpy); | ||
84 | |||
85 | s2_start = l2 - l2_cpy; | ||
86 | s1_start = l1 - l1_cpy; | ||
87 | |||
88 | memcpy(buf, s1 + s1_start, l1_cpy); | ||
89 | memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy); | ||
90 | |||
91 | cxt->count = (cxt->count + 1) % cxt->max_count; | ||
92 | } | ||
93 | |||
94 | static int __init ramoops_init(void) | ||
95 | { | ||
96 | struct ramoops_context *cxt = &oops_cxt; | ||
97 | int err = -EINVAL; | ||
98 | |||
99 | if (!mem_size) { | ||
100 | printk(KERN_ERR "ramoops: invalid size specification"); | ||
101 | goto fail3; | ||
102 | } | ||
103 | |||
104 | rounddown_pow_of_two(mem_size); | ||
105 | |||
106 | if (mem_size < RECORD_SIZE) { | ||
107 | printk(KERN_ERR "ramoops: size too small"); | ||
108 | goto fail3; | ||
109 | } | ||
110 | |||
111 | cxt->max_count = mem_size / RECORD_SIZE; | ||
112 | cxt->count = 0; | ||
113 | cxt->size = mem_size; | ||
114 | cxt->phys_addr = mem_address; | ||
115 | |||
116 | if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) { | ||
117 | printk(KERN_ERR "ramoops: request mem region failed"); | ||
118 | err = -EINVAL; | ||
119 | goto fail3; | ||
120 | } | ||
121 | |||
122 | cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size); | ||
123 | if (!cxt->virt_addr) { | ||
124 | printk(KERN_ERR "ramoops: ioremap failed"); | ||
125 | goto fail2; | ||
126 | } | ||
127 | |||
128 | cxt->dump.dump = ramoops_do_dump; | ||
129 | err = kmsg_dump_register(&cxt->dump); | ||
130 | if (err) { | ||
131 | printk(KERN_ERR "ramoops: registering kmsg dumper failed"); | ||
132 | goto fail1; | ||
133 | } | ||
134 | |||
135 | return 0; | ||
136 | |||
137 | fail1: | ||
138 | iounmap(cxt->virt_addr); | ||
139 | fail2: | ||
140 | release_mem_region(cxt->phys_addr, cxt->size); | ||
141 | fail3: | ||
142 | return err; | ||
143 | } | ||
144 | |||
145 | static void __exit ramoops_exit(void) | ||
146 | { | ||
147 | struct ramoops_context *cxt = &oops_cxt; | ||
148 | |||
149 | if (kmsg_dump_unregister(&cxt->dump) < 0) | ||
150 | printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper"); | ||
151 | |||
152 | iounmap(cxt->virt_addr); | ||
153 | release_mem_region(cxt->phys_addr, cxt->size); | ||
154 | } | ||
155 | |||
156 | |||
157 | module_init(ramoops_init); | ||
158 | module_exit(ramoops_exit); | ||
159 | |||
160 | MODULE_LICENSE("GPL"); | ||
161 | MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>"); | ||
162 | MODULE_DESCRIPTION("RAM Oops/Panic logger/driver"); | ||