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>
This commit is contained in:
Mark Salyzyn 2015-01-16 16:01:10 -08:00 committed by Tony Luck
parent f44f96528a
commit 9d5438f462
9 changed files with 170 additions and 2 deletions

View File

@ -21,6 +21,16 @@ config PSTORE_CONSOLE
When the option is enabled, pstore will log all kernel When the option is enabled, pstore will log all kernel
messages, even if no oops or panic happened. messages, even if no oops or panic happened.
config PSTORE_PMSG
bool "Log user space messages"
depends on PSTORE
help
When the option is enabled, pstore will export a character
interface /dev/pmsg0 to log user space messages. On reboot
data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID].
If unsure, say N.
config PSTORE_FTRACE config PSTORE_FTRACE
bool "Persistent function tracer" bool "Persistent function tracer"
depends on PSTORE depends on PSTORE

View File

@ -7,5 +7,7 @@ obj-y += pstore.o
pstore-objs += inode.o platform.o pstore-objs += inode.o platform.o
obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o
obj-$(CONFIG_PSTORE_PMSG) += pmsg.o
ramoops-objs += ram.o ram_core.o ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o obj-$(CONFIG_PSTORE_RAM) += ramoops.o

View File

@ -361,6 +361,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", scnprintf(name, sizeof(name), "powerpc-common-%s-%lld",
psname, id); psname, id);
break; break;
case PSTORE_TYPE_PMSG:
scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id);
break;
case PSTORE_TYPE_UNKNOWN: case PSTORE_TYPE_UNKNOWN:
scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id);
break; break;

View File

@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void);
static inline void pstore_register_ftrace(void) {} static inline void pstore_register_ftrace(void) {}
#endif #endif
#ifdef CONFIG_PSTORE_PMSG
extern void pstore_register_pmsg(void);
#else
static inline void pstore_register_pmsg(void) {}
#endif
extern struct pstore_info *psinfo; extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int); extern void pstore_set_kmsg_bytes(int);

View File

@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi)
if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
pstore_register_console(); pstore_register_console();
pstore_register_ftrace(); pstore_register_ftrace();
pstore_register_pmsg();
} }
if (pstore_update_ms >= 0) { if (pstore_update_ms >= 0) {

114
fs/pstore/pmsg.c Normal file
View File

@ -0,0 +1,114 @@
/*
* Copyright 2014 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "internal.h"
static DEFINE_MUTEX(pmsg_lock);
#define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE)
static ssize_t write_pmsg(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
size_t i, buffer_size;
char *buffer;
if (!count)
return 0;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
buffer_size = count;
if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE)
buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE;
buffer = vmalloc(buffer_size);
mutex_lock(&pmsg_lock);
for (i = 0; i < count; ) {
size_t c = min(count - i, buffer_size);
u64 id;
long ret;
ret = __copy_from_user(buffer, buf + i, c);
if (unlikely(ret != 0)) {
mutex_unlock(&pmsg_lock);
vfree(buffer);
return -EFAULT;
}
psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c,
psinfo);
i += c;
}
mutex_unlock(&pmsg_lock);
vfree(buffer);
return count;
}
static const struct file_operations pmsg_fops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.write = write_pmsg,
};
static struct class *pmsg_class;
static int pmsg_major;
#define PMSG_NAME "pmsg"
#undef pr_fmt
#define pr_fmt(fmt) PMSG_NAME ": " fmt
static char *pmsg_devnode(struct device *dev, umode_t *mode)
{
if (mode)
*mode = 0220;
return NULL;
}
void pstore_register_pmsg(void)
{
struct device *pmsg_device;
pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops);
if (pmsg_major < 0) {
pr_err("register_chrdev failed\n");
goto err;
}
pmsg_class = class_create(THIS_MODULE, PMSG_NAME);
if (IS_ERR(pmsg_class)) {
pr_err("device class file already in use\n");
goto err_class;
}
pmsg_class->devnode = pmsg_devnode;
pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0),
NULL, "%s%d", PMSG_NAME, 0);
if (IS_ERR(pmsg_device)) {
pr_err("failed to create device\n");
goto err_device;
}
return;
err_device:
class_destroy(pmsg_class);
err_class:
unregister_chrdev(pmsg_major, PMSG_NAME);
err:
return;
}

View File

@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); MODULE_PARM_DESC(ftrace_size, "size of ftrace log");
static ulong ramoops_pmsg_size = MIN_MEM_SIZE;
module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400);
MODULE_PARM_DESC(pmsg_size, "size of user space message log");
static ulong mem_address; static ulong mem_address;
module_param(mem_address, ulong, 0400); module_param(mem_address, ulong, 0400);
MODULE_PARM_DESC(mem_address, MODULE_PARM_DESC(mem_address,
@ -82,12 +86,14 @@ struct ramoops_context {
struct persistent_ram_zone **przs; struct persistent_ram_zone **przs;
struct persistent_ram_zone *cprz; struct persistent_ram_zone *cprz;
struct persistent_ram_zone *fprz; struct persistent_ram_zone *fprz;
struct persistent_ram_zone *mprz;
phys_addr_t phys_addr; phys_addr_t phys_addr;
unsigned long size; unsigned long size;
unsigned int memtype; unsigned int memtype;
size_t record_size; size_t record_size;
size_t console_size; size_t console_size;
size_t ftrace_size; size_t ftrace_size;
size_t pmsg_size;
int dump_oops; int dump_oops;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt; unsigned int max_dump_cnt;
@ -96,6 +102,7 @@ struct ramoops_context {
unsigned int dump_read_cnt; unsigned int dump_read_cnt;
unsigned int console_read_cnt; unsigned int console_read_cnt;
unsigned int ftrace_read_cnt; unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
struct pstore_info pstore; struct pstore_info pstore;
}; };
@ -109,6 +116,7 @@ static int ramoops_pstore_open(struct pstore_info *psi)
cxt->dump_read_cnt = 0; cxt->dump_read_cnt = 0;
cxt->console_read_cnt = 0; cxt->console_read_cnt = 0;
cxt->ftrace_read_cnt = 0; cxt->ftrace_read_cnt = 0;
cxt->pmsg_read_cnt = 0;
return 0; return 0;
} }
@ -190,6 +198,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (!prz_ok(prz)) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
1, id, type, PSTORE_TYPE_FTRACE, 0); 1, id, type, PSTORE_TYPE_FTRACE, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, id, type, PSTORE_TYPE_PMSG, 0);
if (!prz_ok(prz)) if (!prz_ok(prz))
return 0; return 0;
@ -258,6 +269,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
return -ENOMEM; return -ENOMEM;
persistent_ram_write(cxt->fprz, buf, size); persistent_ram_write(cxt->fprz, buf, size);
return 0; return 0;
} else if (type == PSTORE_TYPE_PMSG) {
if (!cxt->mprz)
return -ENOMEM;
persistent_ram_write(cxt->mprz, buf, size);
return 0;
} }
if (type != PSTORE_TYPE_DMESG) if (type != PSTORE_TYPE_DMESG)
@ -315,6 +331,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
case PSTORE_TYPE_FTRACE: case PSTORE_TYPE_FTRACE:
prz = cxt->fprz; prz = cxt->fprz;
break; break;
case PSTORE_TYPE_PMSG:
prz = cxt->mprz;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -441,7 +460,7 @@ static int ramoops_probe(struct platform_device *pdev)
goto fail_out; goto fail_out;
if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
!pdata->ftrace_size)) { !pdata->ftrace_size && !pdata->pmsg_size)) {
pr_err("The memory size and the record/console size must be " pr_err("The memory size and the record/console size must be "
"non-zero\n"); "non-zero\n");
goto fail_out; goto fail_out;
@ -453,6 +472,8 @@ static int ramoops_probe(struct platform_device *pdev)
pdata->console_size = rounddown_pow_of_two(pdata->console_size); pdata->console_size = rounddown_pow_of_two(pdata->console_size);
if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size))
pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size);
cxt->size = pdata->mem_size; cxt->size = pdata->mem_size;
cxt->phys_addr = pdata->mem_address; cxt->phys_addr = pdata->mem_address;
@ -460,12 +481,14 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->record_size = pdata->record_size; cxt->record_size = pdata->record_size;
cxt->console_size = pdata->console_size; cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size; cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops; cxt->dump_oops = pdata->dump_oops;
cxt->ecc_info = pdata->ecc_info; cxt->ecc_info = pdata->ecc_info;
paddr = cxt->phys_addr; paddr = cxt->phys_addr;
dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
- cxt->pmsg_size;
err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
if (err) if (err)
goto fail_out; goto fail_out;
@ -480,6 +503,10 @@ static int ramoops_probe(struct platform_device *pdev)
if (err) if (err)
goto fail_init_fprz; goto fail_init_fprz;
err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0);
if (err)
goto fail_init_mprz;
cxt->pstore.data = cxt; cxt->pstore.data = cxt;
/* /*
* Console can handle any buffer size, so prefer LOG_LINE_MAX. If we * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we
@ -523,6 +550,8 @@ fail_buf:
kfree(cxt->pstore.buf); kfree(cxt->pstore.buf);
fail_clear: fail_clear:
cxt->pstore.bufsize = 0; cxt->pstore.bufsize = 0;
kfree(cxt->mprz);
fail_init_mprz:
kfree(cxt->fprz); kfree(cxt->fprz);
fail_init_fprz: fail_init_fprz:
kfree(cxt->cprz); kfree(cxt->cprz);
@ -580,6 +609,7 @@ static void ramoops_register_dummy(void)
dummy_data->record_size = record_size; dummy_data->record_size = record_size;
dummy_data->console_size = ramoops_console_size; dummy_data->console_size = ramoops_console_size;
dummy_data->ftrace_size = ramoops_ftrace_size; dummy_data->ftrace_size = ramoops_ftrace_size;
dummy_data->pmsg_size = ramoops_pmsg_size;
dummy_data->dump_oops = dump_oops; dummy_data->dump_oops = dump_oops;
/* /*
* For backwards compatibility ramoops.ecc=1 means 16 bytes ECC * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC

View File

@ -39,6 +39,7 @@ enum pstore_type_id {
PSTORE_TYPE_PPC_RTAS = 4, PSTORE_TYPE_PPC_RTAS = 4,
PSTORE_TYPE_PPC_OF = 5, PSTORE_TYPE_PPC_OF = 5,
PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PPC_COMMON = 6,
PSTORE_TYPE_PMSG = 7,
PSTORE_TYPE_UNKNOWN = 255 PSTORE_TYPE_UNKNOWN = 255
}; };

View File

@ -81,6 +81,7 @@ struct ramoops_platform_data {
unsigned long record_size; unsigned long record_size;
unsigned long console_size; unsigned long console_size;
unsigned long ftrace_size; unsigned long ftrace_size;
unsigned long pmsg_size;
int dump_oops; int dump_oops;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
}; };