Commit 9d5438f4 authored by Mark Salyzyn's avatar Mark Salyzyn Committed by Tony Luck

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: default avatarMark Salyzyn <salyzyn@android.com>
Acked-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent f44f9652
...@@ -21,6 +21,16 @@ config PSTORE_CONSOLE ...@@ -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
......
...@@ -7,5 +7,7 @@ obj-y += pstore.o ...@@ -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
...@@ -361,6 +361,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -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;
......
...@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void); ...@@ -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);
......
...@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi) ...@@ -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) {
......
/*
* 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;
}
...@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE; ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -523,6 +550,8 @@ static int ramoops_probe(struct platform_device *pdev)
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) ...@@ -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
......
...@@ -39,6 +39,7 @@ enum pstore_type_id { ...@@ -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
}; };
......
...@@ -81,6 +81,7 @@ struct ramoops_platform_data { ...@@ -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;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment