ram.c 10.0 KB
Newer Older
1 2 3 4
/*
 * RAM Oops/Panic logger
 *
 * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
K
Kees Cook 已提交
5
 * Copyright (C) 2011 Kees Cook <keescook@chromium.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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
 *
 */

23 24
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

25
#include <linux/kernel.h>
26
#include <linux/err.h>
27
#include <linux/module.h>
K
Kees Cook 已提交
28
#include <linux/pstore.h>
29 30 31
#include <linux/time.h>
#include <linux/io.h>
#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 40 41 42
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");
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

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)");

A
Anton Vorontsov 已提交
59 60 61 62 63
static int ramoops_ecc;
module_param_named(ecc, ramoops_ecc, int, 0600);
MODULE_PARM_DESC(ramoops_ecc,
		"set to 1 to enable ECC support");

K
Kees Cook 已提交
64
struct ramoops_context {
65
	struct persistent_ram_zone **przs;
66 67
	phys_addr_t phys_addr;
	unsigned long size;
K
Kees Cook 已提交
68
	size_t record_size;
69
	int dump_oops;
A
Anton Vorontsov 已提交
70
	bool ecc;
71 72 73
	unsigned int max_dump_cnt;
	unsigned int dump_write_cnt;
	unsigned int dump_read_cnt;
K
Kees Cook 已提交
74 75
	struct pstore_info pstore;
};
76

77 78 79
static struct platform_device *dummy;
static struct ramoops_platform_data *dummy_data;

K
Kees Cook 已提交
80 81 82 83
static int ramoops_pstore_open(struct pstore_info *psi)
{
	struct ramoops_context *cxt = psi->data;

84
	cxt->dump_read_cnt = 0;
K
Kees Cook 已提交
85 86 87 88 89 90 91
	return 0;
}

static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
				   struct timespec *time,
				   char **buf,
				   struct pstore_info *psi)
92
{
K
Kees Cook 已提交
93 94
	ssize_t size;
	struct ramoops_context *cxt = psi->data;
95
	struct persistent_ram_zone *prz;
K
Kees Cook 已提交
96

97
	if (cxt->dump_read_cnt >= cxt->max_dump_cnt)
K
Kees Cook 已提交
98
		return -EINVAL;
99

100
	*id = cxt->dump_read_cnt++;
101 102
	prz = cxt->przs[*id];

K
Kees Cook 已提交
103 104 105 106 107 108
	/* Only supports dmesg output so far. */
	*type = PSTORE_TYPE_DMESG;
	/* TODO(kees): Bogus time for the moment. */
	time->tv_sec = 0;
	time->tv_nsec = 0;

109 110
	/* Update old/shadowed buffer. */
	persistent_ram_save_old(prz);
111
	size = persistent_ram_old_size(prz);
K
Kees Cook 已提交
112 113 114
	*buf = kmalloc(size, GFP_KERNEL);
	if (*buf == NULL)
		return -ENOMEM;
115
	memcpy(*buf, persistent_ram_old(prz), size);
K
Kees Cook 已提交
116 117 118 119

	return size;
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
{
	char *hdr;
	struct timeval timestamp;
	size_t len;

	do_gettimeofday(&timestamp);
	hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
		(long)timestamp.tv_sec, (long)timestamp.tv_usec);
	WARN_ON_ONCE(!hdr);
	len = hdr ? strlen(hdr) : 0;
	persistent_ram_write(prz, hdr, len);
	kfree(hdr);

	return len;
}

K
Kees Cook 已提交
137 138 139 140 141 142 143
static int ramoops_pstore_write(enum pstore_type_id type,
				enum kmsg_dump_reason reason,
				u64 *id,
				unsigned int part,
				size_t size, struct pstore_info *psi)
{
	struct ramoops_context *cxt = psi->data;
144
	struct persistent_ram_zone *prz = cxt->przs[cxt->dump_write_cnt];
145
	size_t hlen;
K
Kees Cook 已提交
146 147 148 149

	/* Currently ramoops is designed to only store dmesg dumps. */
	if (type != PSTORE_TYPE_DMESG)
		return -EINVAL;
150

K
Kees Cook 已提交
151 152 153
	/* Out of the various dmesg dump types, ramoops is currently designed
	 * to only store crash logs, rather than storing general kernel logs.
	 */
154
	if (reason != KMSG_DUMP_OOPS &&
W
WANG Cong 已提交
155
	    reason != KMSG_DUMP_PANIC)
K
Kees Cook 已提交
156
		return -EINVAL;
157

K
Kees Cook 已提交
158
	/* Skip Oopes when configured to do so. */
159
	if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
K
Kees Cook 已提交
160 161 162 163 164 165 166 167 168
		return -EINVAL;

	/* Explicitly only take the first part of any new crash.
	 * If our buffer is larger than kmsg_bytes, this can never happen,
	 * and if our buffer is smaller than kmsg_bytes, we don't want the
	 * report split across multiple records.
	 */
	if (part != 1)
		return -ENOSPC;
169

170 171 172 173
	hlen = ramoops_write_kmsg_hdr(prz);
	if (size + hlen > prz->buffer_size)
		size = prz->buffer_size - hlen;
	persistent_ram_write(prz, cxt->pstore.buf, size);
174

175
	cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt;
K
Kees Cook 已提交
176 177 178 179 180 181 182 183 184

	return 0;
}

static int ramoops_pstore_erase(enum pstore_type_id type, u64 id,
				struct pstore_info *psi)
{
	struct ramoops_context *cxt = psi->data;

185
	if (id >= cxt->max_dump_cnt)
K
Kees Cook 已提交
186 187
		return -EINVAL;

188
	persistent_ram_free_old(cxt->przs[id]);
189
	persistent_ram_zap(cxt->przs[id]);
K
Kees Cook 已提交
190 191

	return 0;
192 193
}

K
Kees Cook 已提交
194 195 196 197 198 199 200 201 202 203 204
static struct ramoops_context oops_cxt = {
	.pstore = {
		.owner	= THIS_MODULE,
		.name	= "ramoops",
		.open	= ramoops_pstore_open,
		.read	= ramoops_pstore_read,
		.write	= ramoops_pstore_write,
		.erase	= ramoops_pstore_erase,
	},
};

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
static void ramoops_free_przs(struct ramoops_context *cxt)
{
	int i;

	if (!cxt->przs)
		return;

	for (i = 0; cxt->przs[i]; i++)
		persistent_ram_free(cxt->przs[i]);
	kfree(cxt->przs);
}

static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
			      phys_addr_t *paddr, size_t dump_mem_sz)
{
	int err = -ENOMEM;
	int i;

	if (!cxt->record_size)
		return 0;

	cxt->max_dump_cnt = dump_mem_sz / cxt->record_size;
	if (!cxt->max_dump_cnt)
		return -ENOMEM;

	cxt->przs = kzalloc(sizeof(*cxt->przs) * cxt->max_dump_cnt,
			     GFP_KERNEL);
	if (!cxt->przs) {
		dev_err(dev, "failed to initialize a prz array for dumps\n");
		return -ENOMEM;
	}

	for (i = 0; i < cxt->max_dump_cnt; i++) {
		size_t sz = cxt->record_size;

		cxt->przs[i] = persistent_ram_new(*paddr, sz, cxt->ecc);
		if (IS_ERR(cxt->przs[i])) {
			err = PTR_ERR(cxt->przs[i]);
			dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n",
				sz, (unsigned long long)*paddr, err);
			goto fail_prz;
		}
		*paddr += sz;
	}

	return 0;
fail_prz:
	ramoops_free_przs(cxt);
	return err;
}

256
static int __init ramoops_probe(struct platform_device *pdev)
257
{
258
	struct device *dev = &pdev->dev;
259
	struct ramoops_platform_data *pdata = pdev->dev.platform_data;
260
	struct ramoops_context *cxt = &oops_cxt;
261 262
	size_t dump_mem_sz;
	phys_addr_t paddr;
263 264
	int err = -EINVAL;

K
Kees Cook 已提交
265 266 267
	/* Only a single ramoops area allowed at a time, so fail extra
	 * probes.
	 */
268
	if (cxt->max_dump_cnt)
K
Kees Cook 已提交
269 270
		goto fail_out;

271 272 273
	if (!pdata->mem_size || !pdata->record_size) {
		pr_err("The memory size and the record size must be "
			"non-zero\n");
K
Kees Cook 已提交
274
		goto fail_out;
275 276
	}

277 278
	pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
	pdata->record_size = rounddown_pow_of_two(pdata->record_size);
279

280
	cxt->dump_read_cnt = 0;
281 282
	cxt->size = pdata->mem_size;
	cxt->phys_addr = pdata->mem_address;
283
	cxt->record_size = pdata->record_size;
284
	cxt->dump_oops = pdata->dump_oops;
A
Anton Vorontsov 已提交
285
	cxt->ecc = pdata->ecc;
286

287
	paddr = cxt->phys_addr;
288

289 290 291 292 293 294
	dump_mem_sz = cxt->size;
	err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
	if (err) {
		pr_err("memory size too small, minimum is %lu\n",
			cxt->record_size);
		goto fail_count;
295 296
	}

K
Kees Cook 已提交
297
	cxt->pstore.data = cxt;
298
	cxt->pstore.bufsize = cxt->przs[0]->buffer_size;
K
Kees Cook 已提交
299 300 301 302 303 304 305 306
	cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL);
	spin_lock_init(&cxt->pstore.buf_lock);
	if (!cxt->pstore.buf) {
		pr_err("cannot allocate pstore buffer\n");
		goto fail_clear;
	}

	err = pstore_register(&cxt->pstore);
307
	if (err) {
K
Kees Cook 已提交
308
		pr_err("registering with pstore failed\n");
309
		goto fail_buf;
310 311
	}

312 313 314 315 316 317 318 319 320
	/*
	 * 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;

A
Anton Vorontsov 已提交
321
	pr_info("attached 0x%lx@0x%llx (%ux0x%zx), ecc: %s\n",
322
		cxt->size, (unsigned long long)cxt->phys_addr,
323
		cxt->max_dump_cnt, cxt->record_size,
A
Anton Vorontsov 已提交
324
		ramoops_ecc ? "on" : "off");
K
Kees Cook 已提交
325

326 327
	return 0;

K
Kees Cook 已提交
328 329 330 331
fail_buf:
	kfree(cxt->pstore.buf);
fail_clear:
	cxt->pstore.bufsize = 0;
332
	cxt->max_dump_cnt = 0;
333 334
fail_count:
	ramoops_free_przs(cxt);
K
Kees Cook 已提交
335
fail_out:
336 337 338
	return err;
}

339
static int __exit ramoops_remove(struct platform_device *pdev)
340
{
K
Kees Cook 已提交
341 342 343 344
#if 0
	/* TODO(kees): We cannot unload ramoops since pstore doesn't support
	 * unregistering yet.
	 */
345 346 347 348
	struct ramoops_context *cxt = &oops_cxt;

	iounmap(cxt->virt_addr);
	release_mem_region(cxt->phys_addr, cxt->size);
349
	cxt->max_dump_cnt = 0;
K
Kees Cook 已提交
350 351 352 353 354

	/* TODO(kees): When pstore supports unregistering, call it here. */
	kfree(cxt->pstore.buf);
	cxt->pstore.bufsize = 0;

355
	return 0;
K
Kees Cook 已提交
356 357
#endif
	return -EBUSY;
358 359
}

360 361 362 363 364 365 366 367 368 369
static struct platform_driver ramoops_driver = {
	.remove		= __exit_p(ramoops_remove),
	.driver		= {
		.name	= "ramoops",
		.owner	= THIS_MODULE,
	},
};

static int __init ramoops_init(void)
{
370 371 372 373 374 375 376
	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.
		 */
377
		pr_info("platform device not found, using module parameters\n");
378 379 380 381 382 383
		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;
384
		dummy_data->record_size = record_size;
385
		dummy_data->dump_oops = dump_oops;
A
Anton Vorontsov 已提交
386
		dummy_data->ecc = ramoops_ecc;
387 388 389 390 391 392 393 394 395 396 397
		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;
398 399 400 401 402
}

static void __exit ramoops_exit(void)
{
	platform_driver_unregister(&ramoops_driver);
403
	kfree(dummy_data);
404
}
405 406 407 408 409 410 411

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");