Blame view

drivers/char/ramoops.c 6.45 KB
56d611a04   Marco Stornelli   char drivers: RAM...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * RAM Oops/Panic logger
   *
   * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   *
   */
0169256e4   Marco Stornelli   ramoops: add new ...
21
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
56d611a04   Marco Stornelli   char drivers: RAM...
22
  #include <linux/kernel.h>
83c1b3179   James Bottomley   ramoops: fix comp...
23
  #include <linux/err.h>
56d611a04   Marco Stornelli   char drivers: RAM...
24
25
26
  #include <linux/module.h>
  #include <linux/kmsg_dump.h>
  #include <linux/time.h>
c05aa8fbc   Paul Gortmaker   drivers/char: Fix...
27
  #include <linux/err.h>
56d611a04   Marco Stornelli   char drivers: RAM...
28
29
  #include <linux/io.h>
  #include <linux/ioport.h>
c3b92ce9e   Kyungmin Park   ramoops: use the ...
30
  #include <linux/platform_device.h>
13aefd729   Marco Stornelli   ramoops: use modu...
31
  #include <linux/slab.h>
c3b92ce9e   Kyungmin Park   ramoops: use the ...
32
  #include <linux/ramoops.h>
56d611a04   Marco Stornelli   char drivers: RAM...
33
34
  
  #define RAMOOPS_KERNMSG_HDR "===="
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
35
  #define MIN_MEM_SIZE 4096UL
56d611a04   Marco Stornelli   char drivers: RAM...
36

3e5c4fadb   Sergiu Iordache   ramoops: make rec...
37
38
39
40
  static ulong record_size = MIN_MEM_SIZE;
  module_param(record_size, ulong, 0400);
  MODULE_PARM_DESC(record_size,
  		"size of each dump done on oops/panic");
56d611a04   Marco Stornelli   char drivers: RAM...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  
  static ulong mem_address;
  module_param(mem_address, ulong, 0400);
  MODULE_PARM_DESC(mem_address,
  		"start of reserved RAM used to store oops/panic logs");
  
  static ulong mem_size;
  module_param(mem_size, ulong, 0400);
  MODULE_PARM_DESC(mem_size,
  		"size of reserved RAM used to store oops/panic logs");
  
  static int dump_oops = 1;
  module_param(dump_oops, int, 0600);
  MODULE_PARM_DESC(dump_oops,
  		"set to 1 to dump oopses, 0 to only dump panics (default 1)");
  
  static struct ramoops_context {
  	struct kmsg_dumper dump;
  	void *virt_addr;
  	phys_addr_t phys_addr;
  	unsigned long size;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
62
  	unsigned long record_size;
6b4d2a273   Sergiu Iordache   ramoops: move dum...
63
  	int dump_oops;
56d611a04   Marco Stornelli   char drivers: RAM...
64
65
66
  	int count;
  	int max_count;
  } oops_cxt;
13aefd729   Marco Stornelli   ramoops: use modu...
67
68
  static struct platform_device *dummy;
  static struct ramoops_platform_data *dummy_data;
56d611a04   Marco Stornelli   char drivers: RAM...
69
70
71
72
73
74
75
76
  static void ramoops_do_dump(struct kmsg_dumper *dumper,
  		enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
  		const char *s2, unsigned long l2)
  {
  	struct ramoops_context *cxt = container_of(dumper,
  			struct ramoops_context, dump);
  	unsigned long s1_start, s2_start;
  	unsigned long l1_cpy, l2_cpy;
1873bb811   Ahmed S. Darwish   RAMOOPS: Don't ov...
77
78
  	int res, hdr_size;
  	char *buf, *buf_orig;
56d611a04   Marco Stornelli   char drivers: RAM...
79
  	struct timeval timestamp;
fc2d557c7   Seiji Aguchi   kmsg_dump: constr...
80
  	if (reason != KMSG_DUMP_OOPS &&
a3dd33230   WANG Cong   kexec: remove KMS...
81
  	    reason != KMSG_DUMP_PANIC)
fc2d557c7   Seiji Aguchi   kmsg_dump: constr...
82
  		return;
56d611a04   Marco Stornelli   char drivers: RAM...
83
  	/* Only dump oopses if dump_oops is set */
6b4d2a273   Sergiu Iordache   ramoops: move dum...
84
  	if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
56d611a04   Marco Stornelli   char drivers: RAM...
85
  		return;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
86
  	buf = cxt->virt_addr + (cxt->count * cxt->record_size);
1873bb811   Ahmed S. Darwish   RAMOOPS: Don't ov...
87
  	buf_orig = buf;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
88
  	memset(buf, '\0', cxt->record_size);
56d611a04   Marco Stornelli   char drivers: RAM...
89
90
91
92
93
94
  	res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
  	buf += res;
  	do_gettimeofday(&timestamp);
  	res = sprintf(buf, "%lu.%lu
  ", (long)timestamp.tv_sec, (long)timestamp.tv_usec);
  	buf += res;
1873bb811   Ahmed S. Darwish   RAMOOPS: Don't ov...
95
  	hdr_size = buf - buf_orig;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
96
97
  	l2_cpy = min(l2, cxt->record_size - hdr_size);
  	l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy);
56d611a04   Marco Stornelli   char drivers: RAM...
98
99
100
101
102
103
104
105
106
  
  	s2_start = l2 - l2_cpy;
  	s1_start = l1 - l1_cpy;
  
  	memcpy(buf, s1 + s1_start, l1_cpy);
  	memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy);
  
  	cxt->count = (cxt->count + 1) % cxt->max_count;
  }
c3b92ce9e   Kyungmin Park   ramoops: use the ...
107
  static int __init ramoops_probe(struct platform_device *pdev)
56d611a04   Marco Stornelli   char drivers: RAM...
108
  {
c3b92ce9e   Kyungmin Park   ramoops: use the ...
109
  	struct ramoops_platform_data *pdata = pdev->dev.platform_data;
56d611a04   Marco Stornelli   char drivers: RAM...
110
111
  	struct ramoops_context *cxt = &oops_cxt;
  	int err = -EINVAL;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
112
113
114
115
  	if (!pdata->mem_size || !pdata->record_size) {
  		pr_err("The memory size and the record size must be "
  			"non-zero
  ");
56d611a04   Marco Stornelli   char drivers: RAM...
116
117
  		goto fail3;
  	}
fdb595075   Marco Stornelli   ramoops: fix use ...
118
119
  	pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
  	pdata->record_size = rounddown_pow_of_two(pdata->record_size);
56d611a04   Marco Stornelli   char drivers: RAM...
120

3e5c4fadb   Sergiu Iordache   ramoops: make rec...
121
122
123
124
125
  	/* Check for the minimum memory size */
  	if (pdata->mem_size < MIN_MEM_SIZE &&
  			pdata->record_size < MIN_MEM_SIZE) {
  		pr_err("memory size too small, minium is %lu
  ", MIN_MEM_SIZE);
56d611a04   Marco Stornelli   char drivers: RAM...
126
127
  		goto fail3;
  	}
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
128
129
130
131
132
133
134
135
  	if (pdata->mem_size < pdata->record_size) {
  		pr_err("The memory size must be larger than the "
  			"records size
  ");
  		goto fail3;
  	}
  
  	cxt->max_count = pdata->mem_size / pdata->record_size;
56d611a04   Marco Stornelli   char drivers: RAM...
136
  	cxt->count = 0;
13aefd729   Marco Stornelli   ramoops: use modu...
137
138
  	cxt->size = pdata->mem_size;
  	cxt->phys_addr = pdata->mem_address;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
139
  	cxt->record_size = pdata->record_size;
6b4d2a273   Sergiu Iordache   ramoops: move dum...
140
  	cxt->dump_oops = pdata->dump_oops;
56d611a04   Marco Stornelli   char drivers: RAM...
141
142
  
  	if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
0169256e4   Marco Stornelli   ramoops: add new ...
143
144
  		pr_err("request mem region failed
  ");
56d611a04   Marco Stornelli   char drivers: RAM...
145
146
147
148
149
150
  		err = -EINVAL;
  		goto fail3;
  	}
  
  	cxt->virt_addr = ioremap(cxt->phys_addr,  cxt->size);
  	if (!cxt->virt_addr) {
0169256e4   Marco Stornelli   ramoops: add new ...
151
152
  		pr_err("ioremap failed
  ");
56d611a04   Marco Stornelli   char drivers: RAM...
153
154
155
156
157
158
  		goto fail2;
  	}
  
  	cxt->dump.dump = ramoops_do_dump;
  	err = kmsg_dump_register(&cxt->dump);
  	if (err) {
0169256e4   Marco Stornelli   ramoops: add new ...
159
160
  		pr_err("registering kmsg dumper failed
  ");
56d611a04   Marco Stornelli   char drivers: RAM...
161
162
  		goto fail1;
  	}
c755201eb   Kees Cook   ramoops: update p...
163
164
165
166
167
168
169
170
  	/*
  	 * Update the module parameter variables as well so they are visible
  	 * through /sys/module/ramoops/parameters/
  	 */
  	mem_size = pdata->mem_size;
  	mem_address = pdata->mem_address;
  	record_size = pdata->record_size;
  	dump_oops = pdata->dump_oops;
56d611a04   Marco Stornelli   char drivers: RAM...
171
172
173
174
175
176
177
178
179
  	return 0;
  
  fail1:
  	iounmap(cxt->virt_addr);
  fail2:
  	release_mem_region(cxt->phys_addr, cxt->size);
  fail3:
  	return err;
  }
c3b92ce9e   Kyungmin Park   ramoops: use the ...
180
  static int __exit ramoops_remove(struct platform_device *pdev)
56d611a04   Marco Stornelli   char drivers: RAM...
181
182
183
184
  {
  	struct ramoops_context *cxt = &oops_cxt;
  
  	if (kmsg_dump_unregister(&cxt->dump) < 0)
0169256e4   Marco Stornelli   ramoops: add new ...
185
186
  		pr_warn("could not unregister kmsg_dumper
  ");
56d611a04   Marco Stornelli   char drivers: RAM...
187
188
189
  
  	iounmap(cxt->virt_addr);
  	release_mem_region(cxt->phys_addr, cxt->size);
c3b92ce9e   Kyungmin Park   ramoops: use the ...
190
  	return 0;
56d611a04   Marco Stornelli   char drivers: RAM...
191
  }
c3b92ce9e   Kyungmin Park   ramoops: use the ...
192
193
194
195
196
197
198
199
200
201
  static struct platform_driver ramoops_driver = {
  	.remove		= __exit_p(ramoops_remove),
  	.driver		= {
  		.name	= "ramoops",
  		.owner	= THIS_MODULE,
  	},
  };
  
  static int __init ramoops_init(void)
  {
13aefd729   Marco Stornelli   ramoops: use modu...
202
203
204
205
206
207
208
  	int ret;
  	ret = platform_driver_probe(&ramoops_driver, ramoops_probe);
  	if (ret == -ENODEV) {
  		/*
  		 * If we didn't find a platform device, we use module parameters
  		 * building platform data on the fly.
  		 */
0169256e4   Marco Stornelli   ramoops: add new ...
209
210
  		pr_info("platform device not found, using module parameters
  ");
13aefd729   Marco Stornelli   ramoops: use modu...
211
212
213
214
215
216
  		dummy_data = kzalloc(sizeof(struct ramoops_platform_data),
  				     GFP_KERNEL);
  		if (!dummy_data)
  			return -ENOMEM;
  		dummy_data->mem_size = mem_size;
  		dummy_data->mem_address = mem_address;
3e5c4fadb   Sergiu Iordache   ramoops: make rec...
217
  		dummy_data->record_size = record_size;
6b4d2a273   Sergiu Iordache   ramoops: move dum...
218
  		dummy_data->dump_oops = dump_oops;
13aefd729   Marco Stornelli   ramoops: use modu...
219
220
221
222
223
224
225
226
227
228
229
  		dummy = platform_create_bundle(&ramoops_driver, ramoops_probe,
  			NULL, 0, dummy_data,
  			sizeof(struct ramoops_platform_data));
  
  		if (IS_ERR(dummy))
  			ret = PTR_ERR(dummy);
  		else
  			ret = 0;
  	}
  
  	return ret;
c3b92ce9e   Kyungmin Park   ramoops: use the ...
230
231
232
233
234
  }
  
  static void __exit ramoops_exit(void)
  {
  	platform_driver_unregister(&ramoops_driver);
13aefd729   Marco Stornelli   ramoops: use modu...
235
  	kfree(dummy_data);
c3b92ce9e   Kyungmin Park   ramoops: use the ...
236
  }
56d611a04   Marco Stornelli   char drivers: RAM...
237
238
239
240
241
242
243
  
  module_init(ramoops_init);
  module_exit(ramoops_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>");
  MODULE_DESCRIPTION("RAM Oops/Panic logger/driver");