pstore is a persistent storage filesystem used for RAMOOPS. It is used to store console logs, panics, ftrace and other information in case of a crash/panic/oops/reboot. pstore is implemented for barebox as a read-only filesystem at the moment. It may be extended later on. The idea is to provide a way to extract essential data from the last running kernel. Most of the code is copied from the kernel. However this is only a lightweight implementation without real write support yet. Signed-off-by: Markus Pargmann <mpa@xxxxxxxxxxxxxx> --- Notes: Changes in v2: - Moved the config symbol from the pstore Makefile to the upper level Makefile common/startup.c | 5 + fs/Kconfig | 2 + fs/Makefile | 1 + fs/pstore/Kconfig | 86 ++++++++ fs/pstore/Makefile | 9 + fs/pstore/fs.c | 280 +++++++++++++++++++++++++ fs/pstore/internal.h | 19 ++ fs/pstore/platform.c | 138 ++++++++++++ fs/pstore/ram.c | 507 +++++++++++++++++++++++++++++++++++++++++++++ fs/pstore/ram_core.c | 426 +++++++++++++++++++++++++++++++++++++ include/linux/pstore.h | 90 ++++++++ include/linux/pstore_ram.h | 87 ++++++++ 12 files changed, 1650 insertions(+) create mode 100644 fs/pstore/Kconfig create mode 100644 fs/pstore/Makefile create mode 100644 fs/pstore/fs.c create mode 100644 fs/pstore/internal.h create mode 100644 fs/pstore/platform.c create mode 100644 fs/pstore/ram.c create mode 100644 fs/pstore/ram_core.c create mode 100644 include/linux/pstore.h create mode 100644 include/linux/pstore_ram.h diff --git a/common/startup.c b/common/startup.c index 4a303b297aaf..093a23ba08c9 100644 --- a/common/startup.c +++ b/common/startup.c @@ -60,6 +60,11 @@ static int mount_root(void) mount("none", "efivarfs", "/efivars", NULL); } + if (IS_ENABLED(CONFIG_FS_PSTORE)) { + mkdir("/pstore", 0); + mount("none", "pstore", "/pstore", NULL); + } + return 0; } fs_initcall(mount_root); diff --git a/fs/Kconfig b/fs/Kconfig index 9217bc81ea1e..5413a9295ccc 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -89,4 +89,6 @@ config FS_SMHFS located on a debugging host connected to the target running Barebox +source fs/pstore/Kconfig + endmenu diff --git a/fs/Makefile b/fs/Makefile index 46932057c1b7..2f952038d1b2 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_FS_UIMAGEFS) += uimagefs.o obj-$(CONFIG_FS_EFI) += efi.o obj-$(CONFIG_FS_EFIVARFS) += efivarfs.o obj-$(CONFIG_FS_SMHFS) += smhfs.o +obj-$(CONFIG_FS_PSTORE) += pstore/ diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig new file mode 100644 index 000000000000..0c6f136920bd --- /dev/null +++ b/fs/pstore/Kconfig @@ -0,0 +1,86 @@ +menuconfig FS_PSTORE + bool + prompt "pstore fs support" + help + Support for persistent storage to read data from the last crash, like + panic, dmesg and ftrace. + +if FS_PSTORE + +config FS_PSTORE_RAMOOPS + bool + depends on RELOCATABLE + depends on ARM + select REED_SOLOMON + prompt "pstore RAM backend" + help + Here the data is stored in a specific memory area that is neither + overwritten by barebox nor the kernel. + +if FS_PSTORE_RAMOOPS + +config FS_PSTORE_RAMOOPS_RO + bool + default y + prompt "pstore RAM backend read only" + help + This prevents the data from being erased by reinitializing the ramoops + area with new empty ECCs. Select this if you want to see the same + ramoops in the kernel. + +config FS_PSTORE_RAMOOPS_SIZE + int + prompt "Size of the RAMOOPS area" + default 2097152 + help + Size of the RAMOOPS area that is reserved. This is passed to the + kernel as well as argument. Default is 2MiB. + +config FS_PSTORE_RAMOOPS_CONSOLE_SIZE + int + prompt "Size of the console area" + default 131072 # 2^17 + range 4096 1073741824 # Random upper limitation of 1GiB + help + Size of the RAMOOPS console area that is reserved. This is passed to + the kernel as well as argument. It should be a power of 2. + +config FS_PSTORE_RAMOOPS_FTRACE_SIZE + int + prompt "Size of the ftrace area" + default 131072 # 2^17 + range 4096 1073741824 # Random upper limitation of 1GiB + help + Size of the RAMOOPS ftrace area that is reserved. This is passed to + the kernel as well as argument. It should be a power of 2. + +config FS_PSTORE_RAMOOPS_PMSG_SIZE + int + prompt "Size of the userspace message area" + default 131072 # 2^17 + range 4096 1073741824 # Random upper limitation of 1GiB + help + Size of the RAMOOPS pmsg area that is reserved. This is passed to + the kernel as well as argument. It should be a power of 2. + +config FS_PSTORE_RAMOOPS_RECORD_SIZE + int + prompt "Size of each oops area" + default 131072 # 2^17 + range 4096 1073741824 # Random upper limitation of 1GiB + help + Size of each RAMOOPS oops area. There are multiple oops/panic areas + to store individual oops/panic messages. This is the size of each of + these areas. It should be a power of 2. + +config FS_PSTORE_ECC_SIZE + int + prompt "ECC size" + default 16 + help + ECC size used. This is the amount of bytes for reed solomon codes + that is used. 0 disables ECC. + +endif + +endif diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile new file mode 100644 index 000000000000..c4043e1a8fb2 --- /dev/null +++ b/fs/pstore/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the linux pstorefs routines. +# + +obj-y += fs.o platform.o + + +ramoops-objs += ram.o ram_core.o +obj-$(CONFIG_FS_PSTORE_RAMOOPS) += ramoops.o diff --git a/fs/pstore/fs.c b/fs/pstore/fs.c new file mode 100644 index 000000000000..0e05d48ea041 --- /dev/null +++ b/fs/pstore/fs.c @@ -0,0 +1,280 @@ +/* + * Persistent Storage Barebox filesystem layer + * Copyright © 2015 Pengutronix, Markus Pargmann <mpa@xxxxxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <common.h> +#include <driver.h> +#include <fs.h> +#include <errno.h> +#include <fcntl.h> +#include <fs.h> +#include <malloc.h> +#include <init.h> +#include <linux/stat.h> +#include <linux/err.h> +#include <linux/pstore.h> +#include <libbb.h> +#include <rtc.h> +#include <libfile.h> +#include <linux/pstore.h> +#include "internal.h" + +struct list_head allpstore = LIST_HEAD_INIT(allpstore); + +struct pstore_private { + char name[PSTORE_NAMELEN]; + struct list_head list; + struct pstore_info *psi; + enum pstore_type_id type; + u64 id; + int count; + ssize_t size; + ssize_t pos; + char data[]; +}; + +/* + * Make a regular file in the root directory of our file system. + * Load it up with "size" bytes of data from "buf". + * Set the mtime & ctime to the date that this record was originally stored. + */ +int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, + char *data, bool compressed, size_t size, + struct pstore_info *psi) +{ + struct pstore_private *private, *pos; + + list_for_each_entry(pos, &allpstore, list) { + if (pos->type == type && pos->id == id && pos->psi == psi) + return -EEXIST; + } + + private = xzalloc(sizeof(*private) + size); + if (!private) + return -ENOMEM; + private->type = type; + private->id = id; + private->count = count; + private->psi = psi; + + switch (type) { + case PSTORE_TYPE_DMESG: + scnprintf(private->name, sizeof(private->name), + "dmesg-%s-%lld%s", psname, id, + compressed ? ".enc.z" : ""); + break; + case PSTORE_TYPE_CONSOLE: + scnprintf(private->name, sizeof(private->name), + "console-%s-%lld", psname, id); + break; + case PSTORE_TYPE_FTRACE: + scnprintf(private->name, sizeof(private->name), + "ftrace-%s-%lld", psname, id); + break; + case PSTORE_TYPE_MCE: + scnprintf(private->name, sizeof(private->name), + "mce-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PPC_RTAS: + scnprintf(private->name, sizeof(private->name), + "rtas-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PPC_OF: + scnprintf(private->name, sizeof(private->name), + "powerpc-ofw-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PPC_COMMON: + scnprintf(private->name, sizeof(private->name), + "powerpc-common-%s-%lld", psname, id); + break; + case PSTORE_TYPE_PMSG: + scnprintf(private->name, sizeof(private->name), + "pmsg-%s-%lld", psname, id); + break; + case PSTORE_TYPE_UNKNOWN: + scnprintf(private->name, sizeof(private->name), + "unknown-%s-%lld", psname, id); + break; + default: + scnprintf(private->name, sizeof(private->name), + "type%d-%s-%lld", type, psname, id); + break; + } + + memcpy(private->data, data, size); + private->size = size; + + list_add(&private->list, &allpstore); + + return 0; +} + +static struct pstore_private *pstore_get_by_name(struct list_head *head, + const char *name) +{ + struct pstore_private *d; + + if (!name) + return NULL; + + list_for_each_entry(d, head, list) { + if (strcmp(d->name, name) == 0) + return d; + } + + return NULL; +} + +static int pstore_open(struct device_d *dev, FILE *file, const char *filename) +{ + struct list_head *head = dev->priv; + struct pstore_private *d; + + if (filename[0] == '/') + filename++; + + d = pstore_get_by_name(head, filename); + if (!d) + return -EINVAL; + + file->size = d->size; + file->priv = d; + d->pos = 0; + + return 0; +} + +static int pstore_close(struct device_d *dev, FILE *file) +{ + return 0; +} + +static int pstore_read(struct device_d *dev, FILE *file, void *buf, + size_t insize) +{ + struct pstore_private *d = file->priv; + + memcpy(buf, &d->data[d->pos], insize); + d->pos += insize; + + return insize; +} + +static loff_t pstore_lseek(struct device_d *dev, FILE *file, loff_t pos) +{ + struct pstore_private *d = file->priv; + + d->pos = pos; + + return pos; +} + +static DIR *pstore_opendir(struct device_d *dev, const char *pathname) +{ + DIR *dir; + + dir = xzalloc(sizeof(DIR)); + + if (list_empty(&allpstore)) + return dir; + + dir->priv = list_first_entry(&allpstore, struct pstore_private, list); + + return dir; +} + +static struct dirent *pstore_readdir(struct device_d *dev, DIR *dir) +{ + struct pstore_private *d = dir->priv; + + if (!d || &d->list == &allpstore) + return NULL; + + strcpy(dir->d.d_name, d->name); + dir->priv = list_entry(d->list.next, struct pstore_private, list); + + return &dir->d; +} + +static int pstore_closedir(struct device_d *dev, DIR *dir) +{ + free(dir); + + return 0; +} + +static int pstore_stat(struct device_d *dev, const char *filename, + struct stat *s) +{ + struct pstore_private *d; + + if (filename[0] == '/') + filename++; + + d = pstore_get_by_name(&allpstore, filename); + if (!d) + return -EINVAL; + + s->st_size = d->size; + s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; + + return 0; +} + +static void pstore_remove(struct device_d *dev) +{ + struct pstore_private *d, *tmp; + + list_for_each_entry_safe(d, tmp, &allpstore, list) { + free(d); + } +} + +static int pstore_probe(struct device_d *dev) +{ + struct list_head *priv = &allpstore; + + dev->priv = priv; + + dev_dbg(dev, "mounted pstore\n"); + + return 0; +} + +static struct fs_driver_d pstore_driver = { + .open = pstore_open, + .close = pstore_close, + .read = pstore_read, + .lseek = pstore_lseek, + .opendir = pstore_opendir, + .readdir = pstore_readdir, + .closedir = pstore_closedir, + .stat = pstore_stat, + .flags = FS_DRIVER_NO_DEV, + .type = filetype_uimage, + .drv = { + .probe = pstore_probe, + .remove = pstore_remove, + .name = "pstore", + } +}; + +static int pstore_init(void) +{ + return register_fs_driver(&pstore_driver); +} +coredevice_initcall(pstore_init); diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h new file mode 100644 index 000000000000..0a8df1f4e296 --- /dev/null +++ b/fs/pstore/internal.h @@ -0,0 +1,19 @@ +#ifndef __PSTORE_INTERNAL_H__ +#define __PSTORE_INTERNAL_H__ + +#include <linux/types.h> +#include <linux/time.h> +#include <linux/pstore.h> + +#define PSTORE_NAMELEN 64 + +extern struct pstore_info *psinfo; + +extern void pstore_set_kmsg_bytes(int); +extern void pstore_get_records(int); +extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, + int count, char *data, bool compressed, + size_t size, struct pstore_info *psi); +extern int pstore_is_mounted(void); + +#endif diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c new file mode 100644 index 000000000000..dc2611f7328c --- /dev/null +++ b/fs/pstore/platform.c @@ -0,0 +1,138 @@ +/* + * Persistent Storage - platform driver interface parts. + * + * Copyright (C) 2007-2008 Google, Inc. + * Copyright (C) 2010 Intel Corporation <tony.luck@xxxxxxxxx> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) "pstore: " fmt + +#include <linux/types.h> +#include <linux/pstore.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <malloc.h> +#include <printk.h> +#include <module.h> + +#include "internal.h" + +struct pstore_info *psinfo; + +static char *backend; + +/* How much of the console log to snapshot */ +static unsigned long kmsg_bytes = 10240; + +void pstore_set_kmsg_bytes(int bytes) +{ + kmsg_bytes = bytes; +} + +static int pstore_write_compat(enum pstore_type_id type, + enum kmsg_dump_reason reason, + u64 *id, unsigned int part, int count, + bool compressed, size_t size, + struct pstore_info *psi) +{ + return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, + size, psi); +} + +/* + * platform specific persistent storage driver registers with + * us here. If pstore is already mounted, call the platform + * read function right away to populate the file system. If not + * then the pstore mount code will call us later to fill out + * the file system. + * + * Register with kmsg_dump to save last part of console log on panic. + */ +int pstore_register(struct pstore_info *psi) +{ + if (backend && strcmp(backend, psi->name)) + return -EPERM; + + spin_lock(&pstore_lock); + if (psinfo) { + spin_unlock(&pstore_lock); + return -EBUSY; + } + + if (!psi->write) + psi->write = pstore_write_compat; + psinfo = psi; + mutex_init(&psinfo->read_mutex); + spin_unlock(&pstore_lock); + + pstore_get_records(0); + + pr_info("Registered %s as persistent store backend\n", psi->name); + + return 0; +} +EXPORT_SYMBOL_GPL(pstore_register); + +/* + * Read all the records from the persistent store. Create + * files in our filesystem. Don't warn about -EEXIST errors + * when we are re-scanning the backing store looking to add new + * error records. + */ +void pstore_get_records(int quiet) +{ + struct pstore_info *psi = psinfo; + char *buf = NULL; + ssize_t size; + u64 id; + int count; + enum pstore_type_id type; + int failed = 0, rc; + bool compressed; + int unzipped_len = -1; + + if (!psi) + return; + + mutex_lock(&psi->read_mutex); + if (psi->open && psi->open(psi)) + goto out; + + while ((size = psi->read(&id, &type, &count, &buf, &compressed, + psi)) > 0) { + if (compressed && (type == PSTORE_TYPE_DMESG)) { + pr_err("barebox does not have ramoops compression support\n"); + continue; + } + rc = pstore_mkfile(type, psi->name, id, count, buf, + compressed, (size_t)size, psi); + if (unzipped_len < 0) { + /* Free buffer other than big oops */ + kfree(buf); + buf = NULL; + } else + unzipped_len = -1; + if (rc && (rc != -EEXIST || !quiet)) + failed++; + } + if (psi->close) + psi->close(psi); +out: + mutex_unlock(&psi->read_mutex); + + if (failed) + pr_warn("failed to load %d record(s) from '%s'\n", + failed, psi->name); +} diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c new file mode 100644 index 000000000000..dc31ed16f923 --- /dev/null +++ b/fs/pstore/ram.c @@ -0,0 +1,507 @@ +/* + * RAM Oops/Panic logger + * + * Copyright (C) 2010 Marco Stornelli <marco.stornelli@xxxxxxxxx> + * Copyright (C) 2011 Kees Cook <keescook@xxxxxxxxxxxx> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/pstore.h> +#include <linux/time.h> +#include <linux/ioport.h> +#include <linux/compiler.h> +#include <linux/pstore_ram.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/log2.h> +#include <malloc.h> +#include <printk.h> +#include <stdio.h> +#include <globalvar.h> +#include <init.h> +#include <common.h> + +#define RAMOOPS_KERNMSG_HDR "====" +#define MIN_MEM_SIZE 4096UL + +static const ulong record_size = CONFIG_FS_PSTORE_RAMOOPS_RECORD_SIZE; + +static const ulong ramoops_console_size = CONFIG_FS_PSTORE_RAMOOPS_CONSOLE_SIZE; + +static const ulong ramoops_ftrace_size = CONFIG_FS_PSTORE_RAMOOPS_FTRACE_SIZE; + +static const ulong ramoops_pmsg_size = CONFIG_FS_PSTORE_RAMOOPS_PMSG_SIZE; + +static const ulong mem_size = CONFIG_FS_PSTORE_RAMOOPS_SIZE; + +static const int dump_oops = 1; + +static const int ramoops_ecc = CONFIG_FS_PSTORE_ECC_SIZE; + +struct ramoops_context { + struct persistent_ram_zone **przs; + struct persistent_ram_zone *cprz; + struct persistent_ram_zone *fprz; + struct persistent_ram_zone *mprz; + phys_addr_t phys_addr; + unsigned long size; + unsigned int memtype; + size_t record_size; + size_t console_size; + size_t ftrace_size; + size_t pmsg_size; + int dump_oops; + struct persistent_ram_ecc_info ecc_info; + unsigned int max_dump_cnt; + unsigned int dump_write_cnt; + /* _read_cnt need clear on ramoops_pstore_open */ + unsigned int dump_read_cnt; + unsigned int console_read_cnt; + unsigned int ftrace_read_cnt; + unsigned int pmsg_read_cnt; + struct pstore_info pstore; +}; + +static struct ramoops_platform_data *dummy_data; + +static int ramoops_pstore_open(struct pstore_info *psi) +{ + struct ramoops_context *cxt = psi->data; + + cxt->dump_read_cnt = 0; + cxt->console_read_cnt = 0; + cxt->ftrace_read_cnt = 0; + cxt->pmsg_read_cnt = 0; + return 0; +} + +static struct persistent_ram_zone * +ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, + u64 *id, + enum pstore_type_id *typep, enum pstore_type_id type, + bool update) +{ + struct persistent_ram_zone *prz; + int i = (*c)++; + + if (i >= max) + return NULL; + + prz = przs[i]; + if (!prz) + return NULL; + + /* Update old/shadowed buffer. */ + if (update) + persistent_ram_save_old(prz); + + if (!persistent_ram_old_size(prz)) + return NULL; + + *typep = type; + *id = i; + + return prz; +} + +static bool prz_ok(struct persistent_ram_zone *prz) +{ + return !!prz && !!(persistent_ram_old_size(prz) + + persistent_ram_ecc_string(prz, NULL, 0)); +} + +static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, + int *count, char **buf, bool *compressed, + struct pstore_info *psi) +{ + ssize_t size; + ssize_t ecc_notice_size; + struct ramoops_context *cxt = psi->data; + struct persistent_ram_zone *prz; + + prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, + cxt->max_dump_cnt, id, type, + PSTORE_TYPE_DMESG, 0); + if (!prz_ok(prz)) + prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, + 1, id, type, PSTORE_TYPE_CONSOLE, 0); + if (!prz_ok(prz)) + prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, + 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)) + return 0; + + if (!persistent_ram_old(prz)) + return 0; + + size = persistent_ram_old_size(prz); + + /* ECC correction notice */ + ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); + + *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL); + if (*buf == NULL) + return -ENOMEM; + + memcpy(*buf, (char *)persistent_ram_old(prz), size); + persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1); + + return size + ecc_notice_size; +} + +static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, + enum kmsg_dump_reason reason, + u64 *id, unsigned int part, + const char *buf, + bool compressed, size_t size, + struct pstore_info *psi) +{ + struct ramoops_context *cxt = psi->data; + struct persistent_ram_zone *prz; + + if (type == PSTORE_TYPE_CONSOLE) { + if (!cxt->cprz) + return -ENOMEM; + persistent_ram_write(cxt->cprz, buf, size); + return 0; + } else if (type == PSTORE_TYPE_FTRACE) { + if (!cxt->fprz) + return -ENOMEM; + persistent_ram_write(cxt->fprz, buf, size); + 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) + 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; + + if (!cxt->przs) + return -ENOSPC; + + prz = cxt->przs[cxt->dump_write_cnt]; + + persistent_ram_write(prz, buf, size); + + cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; + + return 0; +} + +static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, + struct pstore_info *psi) +{ + struct ramoops_context *cxt = psi->data; + struct persistent_ram_zone *prz; + + switch (type) { + case PSTORE_TYPE_DMESG: + if (id >= cxt->max_dump_cnt) + return -EINVAL; + prz = cxt->przs[id]; + break; + case PSTORE_TYPE_CONSOLE: + prz = cxt->cprz; + break; + case PSTORE_TYPE_FTRACE: + prz = cxt->fprz; + break; + case PSTORE_TYPE_PMSG: + prz = cxt->mprz; + break; + default: + return -EINVAL; + } + + persistent_ram_free_old(prz); + persistent_ram_zap(prz); + + return 0; +} + +static struct ramoops_context oops_cxt = { + .pstore = { + .name = "ramoops", + .open = ramoops_pstore_open, + .read = ramoops_pstore_read, + .write_buf = ramoops_pstore_write_buf, + .erase = ramoops_pstore_erase, + }, +}; + +static void ramoops_free_przs(struct ramoops_context *cxt) +{ + int i; + + cxt->max_dump_cnt = 0; + if (!cxt->przs) + return; + + for (i = 0; !IS_ERR_OR_NULL(cxt->przs[i]); i++) + persistent_ram_free(cxt->przs[i]); + kfree(cxt->przs); +} + +static int ramoops_init_przs(struct ramoops_context *cxt, phys_addr_t *paddr, + size_t dump_mem_sz) +{ + int err = -ENOMEM; + int i; + + if (!cxt->record_size) + return 0; + + if (*paddr + dump_mem_sz - cxt->phys_addr > cxt->size) { + pr_err("no room for dumps\n"); + return -ENOMEM; + } + + 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) { + pr_err("failed to initialize a prz array for dumps\n"); + goto fail_prz; + } + + for (i = 0; i < cxt->max_dump_cnt; i++) { + size_t sz = cxt->record_size; + + cxt->przs[i] = persistent_ram_new(*paddr, sz, 0, + &cxt->ecc_info, + cxt->memtype); + if (IS_ERR(cxt->przs[i])) { + err = PTR_ERR(cxt->przs[i]); + pr_err("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; +} + +static int ramoops_init_prz(struct ramoops_context *cxt, + struct persistent_ram_zone **prz, + phys_addr_t *paddr, size_t sz, u32 sig) +{ + if (!sz) + return 0; + + if (*paddr + sz - cxt->phys_addr > cxt->size) { + pr_err("no room for mem region (0x%zx@0x%llx) in (0x%lx@0x%llx)\n", + sz, (unsigned long long)*paddr, + cxt->size, (unsigned long long)cxt->phys_addr); + return -ENOMEM; + } + + *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, + cxt->memtype); + if (IS_ERR(*prz)) { + int err = PTR_ERR(*prz); + + pr_err("failed to request mem region (0x%zx@0x%llx): %d\n", + sz, (unsigned long long)*paddr, err); + return err; + } + + persistent_ram_zap(*prz); + + *paddr += sz; + + return 0; +} + +static int ramoops_probe(struct ramoops_platform_data *pdata) +{ + struct ramoops_context *cxt = &oops_cxt; + size_t dump_mem_sz; + phys_addr_t paddr; + int err = -EINVAL; + char kernelargs[512]; + + /* Only a single ramoops area allowed at a time, so fail extra + * probes. + */ + if (cxt->max_dump_cnt) + goto fail_out; + + if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && + !pdata->ftrace_size && !pdata->pmsg_size)) { + pr_err("The memory size and the record/console size must be " + "non-zero\n"); + goto fail_out; + } + + if (pdata->record_size && !is_power_of_2(pdata->record_size)) + pdata->record_size = rounddown_pow_of_two(pdata->record_size); + if (pdata->console_size && !is_power_of_2(pdata->console_size)) + pdata->console_size = rounddown_pow_of_two(pdata->console_size); + if (pdata->ftrace_size && !is_power_of_2(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->phys_addr = pdata->mem_address; + cxt->memtype = pdata->mem_type; + cxt->record_size = pdata->record_size; + cxt->console_size = pdata->console_size; + cxt->ftrace_size = pdata->ftrace_size; + cxt->pmsg_size = pdata->pmsg_size; + cxt->dump_oops = pdata->dump_oops; + cxt->ecc_info = pdata->ecc_info; + + paddr = cxt->phys_addr; + + dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size + - cxt->pmsg_size; + err = ramoops_init_przs(cxt, &paddr, dump_mem_sz); + if (err) + goto fail_out; + + err = ramoops_init_prz(cxt, &cxt->cprz, &paddr, + cxt->console_size, 0); + if (err) + goto fail_init_cprz; + + err = ramoops_init_prz(cxt, &cxt->fprz, &paddr, cxt->ftrace_size, 0); + if (err) + goto fail_init_fprz; + + err = ramoops_init_prz(cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0); + if (err) + goto fail_init_mprz; + + cxt->pstore.data = cxt; + /* + * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we + * have to handle dumps, we must have at least record_size buffer. And + * for ftrace, bufsize is irrelevant (if bufsize is 0, buf will be + * ZERO_SIZE_PTR). + */ + if (cxt->console_size) + cxt->pstore.bufsize = 1024; /* LOG_LINE_MAX */ + cxt->pstore.bufsize = max(cxt->record_size, cxt->pstore.bufsize); + 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"); + err = -ENOMEM; + goto fail_clear; + } + + err = pstore_register(&cxt->pstore); + if (err) { + pr_err("registering with pstore failed\n"); + goto fail_buf; + } + + pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", + cxt->size, (unsigned long long)cxt->phys_addr, + cxt->ecc_info.ecc_size, cxt->ecc_info.block_size); + + scnprintf(kernelargs, sizeof(kernelargs), + "ramoops.record_size=0x%x " + "ramoops.console_size=0x%x " + "ramoops.ftrace_size=0x%x " + "ramoops.pmsg_size=0x%x " + "ramoops.mem_address=0x%llx " + "ramoops.mem_size=0x%lx " + "ramoops.ecc=%d", + cxt->record_size, + cxt->console_size, + cxt->ftrace_size, + cxt->pmsg_size, + (unsigned long long)cxt->phys_addr, + mem_size, + ramoops_ecc); + globalvar_add_simple("linux.bootargs.ramoops", kernelargs); + + of_add_reserve_entry(cxt->phys_addr, cxt->phys_addr + mem_size); + + return 0; + +fail_buf: + kfree(cxt->pstore.buf); +fail_clear: + cxt->pstore.bufsize = 0; + kfree(cxt->mprz); +fail_init_mprz: + kfree(cxt->fprz); +fail_init_fprz: + kfree(cxt->cprz); +fail_init_cprz: + ramoops_free_przs(cxt); +fail_out: + return err; +} +unsigned long arm_mem_ramoops_get(void); + +static void ramoops_register_dummy(void) +{ + dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL); + if (!dummy_data) { + pr_info("could not allocate pdata\n"); + return; + } + + dummy_data->mem_size = mem_size; + dummy_data->mem_address = arm_mem_ramoops_get(); + dummy_data->mem_type = 0; + dummy_data->record_size = record_size; + dummy_data->console_size = ramoops_console_size; + dummy_data->ftrace_size = ramoops_ftrace_size; + dummy_data->pmsg_size = ramoops_pmsg_size; + dummy_data->dump_oops = dump_oops; + /* + * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC + * (using 1 byte for ECC isn't much of use anyway). + */ + dummy_data->ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc; + + ramoops_probe(dummy_data); +} + +static int __init ramoops_init(void) +{ + ramoops_register_dummy(); + return 0; +} +postcore_initcall(ramoops_init); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c new file mode 100644 index 000000000000..d68d80900b50 --- /dev/null +++ b/fs/pstore/ram_core.c @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2012 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. + * + */ + +#define pr_fmt(fmt) "persistent_ram: " fmt + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/rslib.h> +#include <linux/pstore_ram.h> +#include <linux/string.h> +#include <linux/rslib.h> +#include <stdio.h> +#include <malloc.h> +#include <memory.h> +#include <common.h> + +struct persistent_ram_buffer { + uint32_t sig; + resource_size_t start; + resource_size_t size; + uint8_t data[0]; +}; + +#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ + +static inline size_t buffer_size(struct persistent_ram_zone *prz) +{ + return prz->buffer->size; +} + +static inline size_t buffer_start(struct persistent_ram_zone *prz) +{ + return prz->buffer->start; +} + +/* increase and wrap the start pointer, returning the old value */ +static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) +{ + int old; + int new; + + old = prz->buffer->start; + new = old + a; + while (unlikely(new >= prz->buffer_size)) + new -= prz->buffer_size; + prz->buffer->start = new; + + return old; +} + +/* increase the size counter until it hits the max size */ +static void buffer_size_add(struct persistent_ram_zone *prz, size_t a) +{ + size_t old; + size_t new; + + old = prz->buffer->size; + if (old == prz->buffer_size) + return; + + new = old + a; + if (new > prz->buffer_size) + new = prz->buffer_size; + prz->buffer->size = new; +} + +static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, + uint8_t *data, size_t len, uint8_t *ecc) +{ + int i; + uint16_t par[prz->ecc_info.ecc_size]; + + /* Initialize the parity buffer */ + memset(par, 0, sizeof(par)); + encode_rs8(prz->rs_decoder, data, len, par, 0); + for (i = 0; i < prz->ecc_info.ecc_size; i++) + ecc[i] = par[i]; +} + +static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz, + void *data, size_t len, uint8_t *ecc) +{ + int i; + uint16_t par[prz->ecc_info.ecc_size]; + + for (i = 0; i < prz->ecc_info.ecc_size; i++) + par[i] = ecc[i]; + return decode_rs8(prz->rs_decoder, data, par, len, + NULL, 0, NULL, 0, NULL); +} + +static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz, + unsigned int start, unsigned int count) +{ + struct persistent_ram_buffer *buffer = prz->buffer; + uint8_t *buffer_end = buffer->data + prz->buffer_size; + uint8_t *block; + uint8_t *par; + int ecc_block_size = prz->ecc_info.block_size; + int ecc_size = prz->ecc_info.ecc_size; + int size = ecc_block_size; + + if (!ecc_size) + return; + + block = buffer->data + (start & ~(ecc_block_size - 1)); + par = prz->par_buffer + (start / ecc_block_size) * ecc_size; + + do { + if (block + ecc_block_size > buffer_end) + size = buffer_end - block; + persistent_ram_encode_rs8(prz, block, size, par); + block += ecc_block_size; + par += ecc_size; + } while (block < buffer->data + start + count); +} + +static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz) +{ + struct persistent_ram_buffer *buffer = prz->buffer; + + if (!prz->ecc_info.ecc_size) + return; + + persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer), + prz->par_header); +} + +static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) +{ + struct persistent_ram_buffer *buffer = prz->buffer; + uint8_t *block; + uint8_t *par; + + if (!prz->ecc_info.ecc_size) + return; + + block = buffer->data; + par = prz->par_buffer; + while (block < buffer->data + buffer_size(prz)) { + int numerr; + int size = prz->ecc_info.block_size; + if (block + size > buffer->data + prz->buffer_size) + size = buffer->data + prz->buffer_size - block; + numerr = persistent_ram_decode_rs8(prz, block, size, par); + if (numerr > 0) { + pr_debug("error in block %p, %d\n", block, numerr); + prz->corrected_bytes += numerr; + } else if (numerr < 0) { + pr_debug("uncorrectable error in block %p\n", block); + prz->bad_blocks++; + } + block += prz->ecc_info.block_size; + par += prz->ecc_info.ecc_size; + } +} + +static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, + struct persistent_ram_ecc_info *ecc_info) +{ + int numerr; + struct persistent_ram_buffer *buffer = prz->buffer; + int ecc_blocks; + size_t ecc_total; + + if (!ecc_info || !ecc_info->ecc_size) + return 0; + + prz->ecc_info.block_size = ecc_info->block_size ?: 128; + prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16; + prz->ecc_info.symsize = ecc_info->symsize ?: 8; + prz->ecc_info.poly = ecc_info->poly ?: 0x11d; + + ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size, + prz->ecc_info.block_size + + prz->ecc_info.ecc_size); + ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size; + if (ecc_total >= prz->buffer_size) { + pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n", + __func__, prz->ecc_info.ecc_size, + ecc_total, prz->buffer_size); + return -EINVAL; + } + + prz->buffer_size -= ecc_total; + prz->par_buffer = buffer->data + prz->buffer_size; + prz->par_header = prz->par_buffer + + ecc_blocks * prz->ecc_info.ecc_size; + + /* + * first consecutive root is 0 + * primitive element to generate roots = 1 + */ + prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly, + 0, 1, prz->ecc_info.ecc_size); + if (prz->rs_decoder == NULL) { + pr_info("init_rs failed\n"); + return -EINVAL; + } + + prz->corrected_bytes = 0; + prz->bad_blocks = 0; + + numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer), + prz->par_header); + if (numerr > 0) { + pr_info("error in header, %d\n", numerr); + prz->corrected_bytes += numerr; + } else if (numerr < 0) { + pr_info("uncorrectable error in header\n"); + prz->bad_blocks++; + } + + return 0; +} + +ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, + char *str, size_t len) +{ + ssize_t ret; + + if (!prz->ecc_info.ecc_size) + return 0; + + if (prz->corrected_bytes || prz->bad_blocks) + ret = snprintf(str, len, "" + "\n%d Corrected bytes, %d unrecoverable blocks\n", + prz->corrected_bytes, prz->bad_blocks); + else + ret = snprintf(str, len, "\nNo errors detected\n"); + + return ret; +} + +static void notrace persistent_ram_update(struct persistent_ram_zone *prz, + const void *s, unsigned int start, unsigned int count) +{ + struct persistent_ram_buffer *buffer = prz->buffer; + memcpy(buffer->data + start, s, count); + persistent_ram_update_ecc(prz, start, count); +} + +void persistent_ram_save_old(struct persistent_ram_zone *prz) +{ + struct persistent_ram_buffer *buffer = prz->buffer; + size_t size = buffer_size(prz); + size_t start = buffer_start(prz); + + if (!size) + return; + + if (!prz->old_log) { + persistent_ram_ecc_old(prz); + prz->old_log = kmalloc(size, GFP_KERNEL); + } + if (!prz->old_log) { + pr_err("failed to allocate buffer\n"); + return; + } + + prz->old_log_size = size; + memcpy(prz->old_log, &buffer->data[start], size - start); + memcpy(prz->old_log + size - start, &buffer->data[0], start); +} + +int notrace persistent_ram_write(struct persistent_ram_zone *prz, + const void *s, unsigned int count) +{ + int rem; + int c = count; + size_t start; + + if (unlikely(c > prz->buffer_size)) { + s += c - prz->buffer_size; + c = prz->buffer_size; + } + + buffer_size_add(prz, c); + + start = buffer_start_add(prz, c); + + rem = prz->buffer_size - start; + if (unlikely(rem < c)) { + persistent_ram_update(prz, s, start, rem); + s += rem; + c -= rem; + start = 0; + } + persistent_ram_update(prz, s, start, c); + + persistent_ram_update_header_ecc(prz); + + return count; +} + +size_t persistent_ram_old_size(struct persistent_ram_zone *prz) +{ + return prz->old_log_size; +} + +void *persistent_ram_old(struct persistent_ram_zone *prz) +{ + return prz->old_log; +} + +void persistent_ram_free_old(struct persistent_ram_zone *prz) +{ + kfree(prz->old_log); + prz->old_log = NULL; + prz->old_log_size = 0; +} + +#ifdef CONFIG_FS_PSTORE_RAMOOPS_RO +void persistent_ram_zap(struct persistent_ram_zone *prz) +{ +} +#else +void persistent_ram_zap(struct persistent_ram_zone *prz) +{ + prz->buffer->start = 0; + prz->buffer->size = 0; + persistent_ram_update_header_ecc(prz); +} +#endif /* CONFIG_PSTORE_RAMOOPS_RO */ + +static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, + struct persistent_ram_zone *prz, int memtype) +{ + prz->res = request_sdram_region("persistent ram", start, size); + if (!prz->res) + return -ENOMEM; + + prz->paddr = start; + prz->size = size; + + prz->buffer = (void *)start; + prz->buffer_size = size - sizeof(struct persistent_ram_buffer); + + return 0; +} + +static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig, + struct persistent_ram_ecc_info *ecc_info) +{ + int ret; + + ret = persistent_ram_init_ecc(prz, ecc_info); + if (ret) + return ret; + + sig ^= PERSISTENT_RAM_SIG; + + if (prz->buffer->sig == sig) { + if (buffer_size(prz) > prz->buffer_size || + buffer_start(prz) > buffer_size(prz)) + pr_info("found existing invalid buffer, size %zu, start %zu\n", + buffer_size(prz), buffer_start(prz)); + else { + pr_debug("found existing buffer, size %zu, start %zu\n", + buffer_size(prz), buffer_start(prz)); + persistent_ram_save_old(prz); + return 0; + } + } else { + pr_debug("no valid data in buffer (sig = 0x%08x)\n", + prz->buffer->sig); + } + + prz->buffer->sig = sig; + persistent_ram_zap(prz); + + return 0; +} + +void persistent_ram_free(struct persistent_ram_zone *prz) +{ + if (!prz) + return; + + if (prz->res) { + release_sdram_region(prz->res); + prz->res = NULL; + } + + persistent_ram_free_old(prz); + kfree(prz); +} + +struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, + u32 sig, struct persistent_ram_ecc_info *ecc_info, + unsigned int memtype) +{ + struct persistent_ram_zone *prz; + int ret = -ENOMEM; + + prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); + if (!prz) { + pr_err("failed to allocate persistent ram zone\n"); + goto err; + } + + ret = persistent_ram_buffer_map(start, size, prz, memtype); + if (ret) + goto err; + + ret = persistent_ram_post_init(prz, sig, ecc_info); + if (ret) + goto err; + + return prz; +err: + persistent_ram_free(prz); + return ERR_PTR(ret); +} diff --git a/include/linux/pstore.h b/include/linux/pstore.h new file mode 100644 index 000000000000..a925e143971c --- /dev/null +++ b/include/linux/pstore.h @@ -0,0 +1,90 @@ +/* + * Persistent Storage - pstore.h + * + * Copyright (C) 2010 Intel Corporation <tony.luck@xxxxxxxxx> + * + * This code is the generic layer to export data records from platform + * level persistent storage via a file system. + * + * 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef _LINUX_PSTORE_H +#define _LINUX_PSTORE_H + +#include <linux/time.h> +#include <linux/types.h> +#include <asm-generic/errno.h> + +/* types */ +enum pstore_type_id { + PSTORE_TYPE_DMESG = 0, + PSTORE_TYPE_MCE = 1, + PSTORE_TYPE_CONSOLE = 2, + PSTORE_TYPE_FTRACE = 3, + /* PPC64 partition types */ + PSTORE_TYPE_PPC_RTAS = 4, + PSTORE_TYPE_PPC_OF = 5, + PSTORE_TYPE_PPC_COMMON = 6, + PSTORE_TYPE_PMSG = 7, + PSTORE_TYPE_UNKNOWN = 255 +}; + +enum kmsg_dump_reason { + KMSG_DUMP_UNDEF, +}; + +struct module; + +struct pstore_info { + struct module *owner; + char *name; + char *buf; + size_t bufsize; + int flags; + int (*open)(struct pstore_info *psi); + int (*close)(struct pstore_info *psi); + ssize_t (*read)(u64 *id, enum pstore_type_id *type, + int *count, char **buf, bool *compressed, + struct pstore_info *psi); + int (*write)(enum pstore_type_id type, + enum kmsg_dump_reason reason, u64 *id, + unsigned int part, int count, bool compressed, + size_t size, struct pstore_info *psi); + int (*write_buf)(enum pstore_type_id type, + enum kmsg_dump_reason reason, u64 *id, + unsigned int part, const char *buf, bool compressed, + size_t size, struct pstore_info *psi); + int (*erase)(enum pstore_type_id type, u64 id, + int count, struct pstore_info *psi); + void *data; +}; + +#define PSTORE_FLAGS_FRAGILE 1 + +#ifdef CONFIG_FS_PSTORE +extern int pstore_register(struct pstore_info *); +extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason); +#else +static inline int +pstore_register(struct pstore_info *psi) +{ + return -ENODEV; +} +static inline bool +pstore_cannot_block_path(enum kmsg_dump_reason reason) +{ + return false; +} +#endif + +#endif /*_LINUX_PSTORE_H*/ diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h new file mode 100644 index 000000000000..5ef823a57be9 --- /dev/null +++ b/include/linux/pstore_ram.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Marco Stornelli <marco.stornelli@xxxxxxxxx> + * Copyright (C) 2011 Kees Cook <keescook@xxxxxxxxxxxx> + * Copyright (C) 2011 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. + * + */ + +#ifndef __LINUX_PSTORE_RAM_H__ +#define __LINUX_PSTORE_RAM_H__ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/types.h> + +struct persistent_ram_buffer; +struct rs_control; + +struct persistent_ram_ecc_info { + int block_size; + int ecc_size; + int symsize; + int poly; +}; + +struct persistent_ram_zone { + phys_addr_t paddr; + size_t size; + struct persistent_ram_buffer *buffer; + size_t buffer_size; + struct resource *res; + + /* ECC correction */ + char *par_buffer; + char *par_header; + struct rs_control *rs_decoder; + int corrected_bytes; + int bad_blocks; + struct persistent_ram_ecc_info ecc_info; + + char *old_log; + size_t old_log_size; +}; + +struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, + u32 sig, struct persistent_ram_ecc_info *ecc_info, + unsigned int memtype); +void persistent_ram_free(struct persistent_ram_zone *prz); +void persistent_ram_zap(struct persistent_ram_zone *prz); + +int persistent_ram_write(struct persistent_ram_zone *prz, const void *s, + unsigned int count); + +void persistent_ram_save_old(struct persistent_ram_zone *prz); +size_t persistent_ram_old_size(struct persistent_ram_zone *prz); +void *persistent_ram_old(struct persistent_ram_zone *prz); +void persistent_ram_free_old(struct persistent_ram_zone *prz); +ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, + char *str, size_t len); + +/* + * Ramoops platform data + * @mem_size memory size for ramoops + * @mem_address physical memory address to contain ramoops + */ + +struct ramoops_platform_data { + unsigned long mem_size; + unsigned long mem_address; + unsigned int mem_type; + unsigned long record_size; + unsigned long console_size; + unsigned long ftrace_size; + unsigned long pmsg_size; + int dump_oops; + struct persistent_ram_ecc_info ecc_info; +}; + +#endif -- 2.6.2 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox