Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pstore: Add pmsg - user-space accessible pstore object

A secured user-space accessible pstore object. Writes
to /dev/pmsg0 are appended to the buffer, on reboot
the persistent contents are available in
/sys/fs/pstore/pmsg-ramoops-[ID].

One possible use is syslogd, or other daemon, can
write messages, then on reboot provides a means to
triage user-space activities leading up to a panic
as a companion to the pstore dmesg or console logs.

Signed-off-by: Mark Salyzyn <salyzyn@android.com>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>

authored by

Mark Salyzyn and committed by
Tony Luck
9d5438f4 f44f9652

+170 -2
+10
fs/pstore/Kconfig
··· 21 21 When the option is enabled, pstore will log all kernel 22 22 messages, even if no oops or panic happened. 23 23 24 + config PSTORE_PMSG 25 + bool "Log user space messages" 26 + depends on PSTORE 27 + help 28 + When the option is enabled, pstore will export a character 29 + interface /dev/pmsg0 to log user space messages. On reboot 30 + data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID]. 31 + 32 + If unsure, say N. 33 + 24 34 config PSTORE_FTRACE 25 35 bool "Persistent function tracer" 26 36 depends on PSTORE
+2
fs/pstore/Makefile
··· 7 7 pstore-objs += inode.o platform.o 8 8 obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o 9 9 10 + obj-$(CONFIG_PSTORE_PMSG) += pmsg.o 11 + 10 12 ramoops-objs += ram.o ram_core.o 11 13 obj-$(CONFIG_PSTORE_RAM) += ramoops.o
+3
fs/pstore/inode.c
··· 361 361 scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", 362 362 psname, id); 363 363 break; 364 + case PSTORE_TYPE_PMSG: 365 + scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); 366 + break; 364 367 case PSTORE_TYPE_UNKNOWN: 365 368 scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); 366 369 break;
+6
fs/pstore/internal.h
··· 45 45 static inline void pstore_register_ftrace(void) {} 46 46 #endif 47 47 48 + #ifdef CONFIG_PSTORE_PMSG 49 + extern void pstore_register_pmsg(void); 50 + #else 51 + static inline void pstore_register_pmsg(void) {} 52 + #endif 53 + 48 54 extern struct pstore_info *psinfo; 49 55 50 56 extern void pstore_set_kmsg_bytes(int);
+1
fs/pstore/platform.c
··· 447 447 if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { 448 448 pstore_register_console(); 449 449 pstore_register_ftrace(); 450 + pstore_register_pmsg(); 450 451 } 451 452 452 453 if (pstore_update_ms >= 0) {
+114
fs/pstore/pmsg.c
··· 1 + /* 2 + * Copyright 2014 Google, Inc. 3 + * 4 + * This software is licensed under the terms of the GNU General Public 5 + * License version 2, as published by the Free Software Foundation, and 6 + * may be copied, distributed, and modified under those terms. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #include <linux/cdev.h> 15 + #include <linux/device.h> 16 + #include <linux/fs.h> 17 + #include <linux/uaccess.h> 18 + #include <linux/vmalloc.h> 19 + #include "internal.h" 20 + 21 + static DEFINE_MUTEX(pmsg_lock); 22 + #define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE) 23 + 24 + static ssize_t write_pmsg(struct file *file, const char __user *buf, 25 + size_t count, loff_t *ppos) 26 + { 27 + size_t i, buffer_size; 28 + char *buffer; 29 + 30 + if (!count) 31 + return 0; 32 + 33 + if (!access_ok(VERIFY_READ, buf, count)) 34 + return -EFAULT; 35 + 36 + buffer_size = count; 37 + if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE) 38 + buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE; 39 + buffer = vmalloc(buffer_size); 40 + 41 + mutex_lock(&pmsg_lock); 42 + for (i = 0; i < count; ) { 43 + size_t c = min(count - i, buffer_size); 44 + u64 id; 45 + long ret; 46 + 47 + ret = __copy_from_user(buffer, buf + i, c); 48 + if (unlikely(ret != 0)) { 49 + mutex_unlock(&pmsg_lock); 50 + vfree(buffer); 51 + return -EFAULT; 52 + } 53 + psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c, 54 + psinfo); 55 + 56 + i += c; 57 + } 58 + 59 + mutex_unlock(&pmsg_lock); 60 + vfree(buffer); 61 + return count; 62 + } 63 + 64 + static const struct file_operations pmsg_fops = { 65 + .owner = THIS_MODULE, 66 + .llseek = noop_llseek, 67 + .write = write_pmsg, 68 + }; 69 + 70 + static struct class *pmsg_class; 71 + static int pmsg_major; 72 + #define PMSG_NAME "pmsg" 73 + #undef pr_fmt 74 + #define pr_fmt(fmt) PMSG_NAME ": " fmt 75 + 76 + static char *pmsg_devnode(struct device *dev, umode_t *mode) 77 + { 78 + if (mode) 79 + *mode = 0220; 80 + return NULL; 81 + } 82 + 83 + void pstore_register_pmsg(void) 84 + { 85 + struct device *pmsg_device; 86 + 87 + pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); 88 + if (pmsg_major < 0) { 89 + pr_err("register_chrdev failed\n"); 90 + goto err; 91 + } 92 + 93 + pmsg_class = class_create(THIS_MODULE, PMSG_NAME); 94 + if (IS_ERR(pmsg_class)) { 95 + pr_err("device class file already in use\n"); 96 + goto err_class; 97 + } 98 + pmsg_class->devnode = pmsg_devnode; 99 + 100 + pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), 101 + NULL, "%s%d", PMSG_NAME, 0); 102 + if (IS_ERR(pmsg_device)) { 103 + pr_err("failed to create device\n"); 104 + goto err_device; 105 + } 106 + return; 107 + 108 + err_device: 109 + class_destroy(pmsg_class); 110 + err_class: 111 + unregister_chrdev(pmsg_major, PMSG_NAME); 112 + err: 113 + return; 114 + }
+32 -2
fs/pstore/ram.c
··· 51 51 module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); 52 52 MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); 53 53 54 + static ulong ramoops_pmsg_size = MIN_MEM_SIZE; 55 + module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); 56 + MODULE_PARM_DESC(pmsg_size, "size of user space message log"); 57 + 54 58 static ulong mem_address; 55 59 module_param(mem_address, ulong, 0400); 56 60 MODULE_PARM_DESC(mem_address, ··· 86 82 struct persistent_ram_zone **przs; 87 83 struct persistent_ram_zone *cprz; 88 84 struct persistent_ram_zone *fprz; 85 + struct persistent_ram_zone *mprz; 89 86 phys_addr_t phys_addr; 90 87 unsigned long size; 91 88 unsigned int memtype; 92 89 size_t record_size; 93 90 size_t console_size; 94 91 size_t ftrace_size; 92 + size_t pmsg_size; 95 93 int dump_oops; 96 94 struct persistent_ram_ecc_info ecc_info; 97 95 unsigned int max_dump_cnt; ··· 102 96 unsigned int dump_read_cnt; 103 97 unsigned int console_read_cnt; 104 98 unsigned int ftrace_read_cnt; 99 + unsigned int pmsg_read_cnt; 105 100 struct pstore_info pstore; 106 101 }; 107 102 ··· 116 109 cxt->dump_read_cnt = 0; 117 110 cxt->console_read_cnt = 0; 118 111 cxt->ftrace_read_cnt = 0; 112 + cxt->pmsg_read_cnt = 0; 119 113 return 0; 120 114 } 121 115 ··· 199 191 prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, 200 192 1, id, type, PSTORE_TYPE_FTRACE, 0); 201 193 if (!prz_ok(prz)) 194 + prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, 195 + 1, id, type, PSTORE_TYPE_PMSG, 0); 196 + if (!prz_ok(prz)) 202 197 return 0; 203 198 204 199 if (!persistent_ram_old(prz)) ··· 269 258 return -ENOMEM; 270 259 persistent_ram_write(cxt->fprz, buf, size); 271 260 return 0; 261 + } else if (type == PSTORE_TYPE_PMSG) { 262 + if (!cxt->mprz) 263 + return -ENOMEM; 264 + persistent_ram_write(cxt->mprz, buf, size); 265 + return 0; 272 266 } 273 267 274 268 if (type != PSTORE_TYPE_DMESG) ··· 330 314 break; 331 315 case PSTORE_TYPE_FTRACE: 332 316 prz = cxt->fprz; 317 + break; 318 + case PSTORE_TYPE_PMSG: 319 + prz = cxt->mprz; 333 320 break; 334 321 default: 335 322 return -EINVAL; ··· 460 441 goto fail_out; 461 442 462 443 if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && 463 - !pdata->ftrace_size)) { 444 + !pdata->ftrace_size && !pdata->pmsg_size)) { 464 445 pr_err("The memory size and the record/console size must be " 465 446 "non-zero\n"); 466 447 goto fail_out; ··· 472 453 pdata->console_size = rounddown_pow_of_two(pdata->console_size); 473 454 if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) 474 455 pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); 456 + if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) 457 + pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); 475 458 476 459 cxt->size = pdata->mem_size; 477 460 cxt->phys_addr = pdata->mem_address; ··· 481 460 cxt->record_size = pdata->record_size; 482 461 cxt->console_size = pdata->console_size; 483 462 cxt->ftrace_size = pdata->ftrace_size; 463 + cxt->pmsg_size = pdata->pmsg_size; 484 464 cxt->dump_oops = pdata->dump_oops; 485 465 cxt->ecc_info = pdata->ecc_info; 486 466 487 467 paddr = cxt->phys_addr; 488 468 489 - dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; 469 + dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size 470 + - cxt->pmsg_size; 490 471 err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); 491 472 if (err) 492 473 goto fail_out; ··· 502 479 LINUX_VERSION_CODE); 503 480 if (err) 504 481 goto fail_init_fprz; 482 + 483 + err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0); 484 + if (err) 485 + goto fail_init_mprz; 505 486 506 487 cxt->pstore.data = cxt; 507 488 /* ··· 550 523 kfree(cxt->pstore.buf); 551 524 fail_clear: 552 525 cxt->pstore.bufsize = 0; 526 + kfree(cxt->mprz); 527 + fail_init_mprz: 553 528 kfree(cxt->fprz); 554 529 fail_init_fprz: 555 530 kfree(cxt->cprz); ··· 609 580 dummy_data->record_size = record_size; 610 581 dummy_data->console_size = ramoops_console_size; 611 582 dummy_data->ftrace_size = ramoops_ftrace_size; 583 + dummy_data->pmsg_size = ramoops_pmsg_size; 612 584 dummy_data->dump_oops = dump_oops; 613 585 /* 614 586 * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
+1
include/linux/pstore.h
··· 39 39 PSTORE_TYPE_PPC_RTAS = 4, 40 40 PSTORE_TYPE_PPC_OF = 5, 41 41 PSTORE_TYPE_PPC_COMMON = 6, 42 + PSTORE_TYPE_PMSG = 7, 42 43 PSTORE_TYPE_UNKNOWN = 255 43 44 }; 44 45
+1
include/linux/pstore_ram.h
··· 81 81 unsigned long record_size; 82 82 unsigned long console_size; 83 83 unsigned long ftrace_size; 84 + unsigned long pmsg_size; 84 85 int dump_oops; 85 86 struct persistent_ram_ecc_info ecc_info; 86 87 };