Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/char/ramoops.c
15109 views
1
/*
2
* RAM Oops/Panic logger
3
*
4
* Copyright (C) 2010 Marco Stornelli <[email protected]>
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
#include <linux/platform_device.h>
29
#include <linux/ramoops.h>
30
31
#define RAMOOPS_KERNMSG_HDR "===="
32
33
#define RECORD_SIZE 4096UL
34
35
static ulong mem_address;
36
module_param(mem_address, ulong, 0400);
37
MODULE_PARM_DESC(mem_address,
38
"start of reserved RAM used to store oops/panic logs");
39
40
static ulong mem_size;
41
module_param(mem_size, ulong, 0400);
42
MODULE_PARM_DESC(mem_size,
43
"size of reserved RAM used to store oops/panic logs");
44
45
static int dump_oops = 1;
46
module_param(dump_oops, int, 0600);
47
MODULE_PARM_DESC(dump_oops,
48
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
49
50
static struct ramoops_context {
51
struct kmsg_dumper dump;
52
void *virt_addr;
53
phys_addr_t phys_addr;
54
unsigned long size;
55
int count;
56
int max_count;
57
} oops_cxt;
58
59
static void ramoops_do_dump(struct kmsg_dumper *dumper,
60
enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
61
const char *s2, unsigned long l2)
62
{
63
struct ramoops_context *cxt = container_of(dumper,
64
struct ramoops_context, dump);
65
unsigned long s1_start, s2_start;
66
unsigned long l1_cpy, l2_cpy;
67
int res, hdr_size;
68
char *buf, *buf_orig;
69
struct timeval timestamp;
70
71
if (reason != KMSG_DUMP_OOPS &&
72
reason != KMSG_DUMP_PANIC &&
73
reason != KMSG_DUMP_KEXEC)
74
return;
75
76
/* Only dump oopses if dump_oops is set */
77
if (reason == KMSG_DUMP_OOPS && !dump_oops)
78
return;
79
80
buf = cxt->virt_addr + (cxt->count * RECORD_SIZE);
81
buf_orig = buf;
82
83
memset(buf, '\0', RECORD_SIZE);
84
res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
85
buf += res;
86
do_gettimeofday(&timestamp);
87
res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec);
88
buf += res;
89
90
hdr_size = buf - buf_orig;
91
l2_cpy = min(l2, RECORD_SIZE - hdr_size);
92
l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy);
93
94
s2_start = l2 - l2_cpy;
95
s1_start = l1 - l1_cpy;
96
97
memcpy(buf, s1 + s1_start, l1_cpy);
98
memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy);
99
100
cxt->count = (cxt->count + 1) % cxt->max_count;
101
}
102
103
static int __init ramoops_probe(struct platform_device *pdev)
104
{
105
struct ramoops_platform_data *pdata = pdev->dev.platform_data;
106
struct ramoops_context *cxt = &oops_cxt;
107
int err = -EINVAL;
108
109
if (pdata) {
110
mem_size = pdata->mem_size;
111
mem_address = pdata->mem_address;
112
}
113
114
if (!mem_size) {
115
printk(KERN_ERR "ramoops: invalid size specification");
116
goto fail3;
117
}
118
119
rounddown_pow_of_two(mem_size);
120
121
if (mem_size < RECORD_SIZE) {
122
printk(KERN_ERR "ramoops: size too small");
123
goto fail3;
124
}
125
126
cxt->max_count = mem_size / RECORD_SIZE;
127
cxt->count = 0;
128
cxt->size = mem_size;
129
cxt->phys_addr = mem_address;
130
131
if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
132
printk(KERN_ERR "ramoops: request mem region failed");
133
err = -EINVAL;
134
goto fail3;
135
}
136
137
cxt->virt_addr = ioremap(cxt->phys_addr, cxt->size);
138
if (!cxt->virt_addr) {
139
printk(KERN_ERR "ramoops: ioremap failed");
140
goto fail2;
141
}
142
143
cxt->dump.dump = ramoops_do_dump;
144
err = kmsg_dump_register(&cxt->dump);
145
if (err) {
146
printk(KERN_ERR "ramoops: registering kmsg dumper failed");
147
goto fail1;
148
}
149
150
return 0;
151
152
fail1:
153
iounmap(cxt->virt_addr);
154
fail2:
155
release_mem_region(cxt->phys_addr, cxt->size);
156
fail3:
157
return err;
158
}
159
160
static int __exit ramoops_remove(struct platform_device *pdev)
161
{
162
struct ramoops_context *cxt = &oops_cxt;
163
164
if (kmsg_dump_unregister(&cxt->dump) < 0)
165
printk(KERN_WARNING "ramoops: could not unregister kmsg_dumper");
166
167
iounmap(cxt->virt_addr);
168
release_mem_region(cxt->phys_addr, cxt->size);
169
return 0;
170
}
171
172
static struct platform_driver ramoops_driver = {
173
.remove = __exit_p(ramoops_remove),
174
.driver = {
175
.name = "ramoops",
176
.owner = THIS_MODULE,
177
},
178
};
179
180
static int __init ramoops_init(void)
181
{
182
return platform_driver_probe(&ramoops_driver, ramoops_probe);
183
}
184
185
static void __exit ramoops_exit(void)
186
{
187
platform_driver_unregister(&ramoops_driver);
188
}
189
190
module_init(ramoops_init);
191
module_exit(ramoops_exit);
192
193
MODULE_LICENSE("GPL");
194
MODULE_AUTHOR("Marco Stornelli <[email protected]>");
195
MODULE_DESCRIPTION("RAM Oops/Panic logger/driver");
196
197