Merge tag 'v4.7' into for-linus/pstore
authorKees Cook <keescook@chromium.org>
Mon, 25 Jul 2016 20:50:36 +0000 (13:50 -0700)
committerKees Cook <keescook@chromium.org>
Mon, 25 Jul 2016 20:50:36 +0000 (13:50 -0700)
Linux 4.7

Documentation/devicetree/bindings/misc/ramoops.txt [new file with mode: 0644]
Documentation/ramoops.txt
arch/powerpc/kernel/nvram_64.c
drivers/acpi/apei/erst.c
drivers/firmware/efi/efi-pstore.c
fs/pstore/Kconfig
fs/pstore/inode.c
fs/pstore/platform.c
fs/pstore/ram.c
include/linux/pstore.h

diff --git a/Documentation/devicetree/bindings/misc/ramoops.txt b/Documentation/devicetree/bindings/misc/ramoops.txt
new file mode 100644 (file)
index 0000000..cd02cec
--- /dev/null
@@ -0,0 +1,48 @@
+Ramoops oops/panic logger
+=========================
+
+ramoops provides persistent RAM storage for oops and panics, so they can be
+recovered after a reboot. It is a backend to pstore, so this node is named
+"ramoops" after the backend, rather than "pstore" which is the subsystem.
+
+Parts of this storage may be set aside for other persistent log buffers, such
+as kernel log messages, or for optional ECC error-correction data.  The total
+size of these optional buffers must fit in the reserved region.
+
+Any remaining space will be used for a circular buffer of oops and panic
+records.  These records have a configurable size, with a size of 0 indicating
+that they should be disabled.
+
+At least one of "record-size", "console-size", "ftrace-size", or "pmsg-size"
+must be set non-zero, but are otherwise optional as listed below.
+
+
+Required properties:
+
+- compatible: must be "ramoops"
+
+- memory-region: phandle to a region of memory that is preserved between
+  reboots
+
+
+Optional properties:
+
+- ecc-size: enables ECC support and specifies ECC buffer size in bytes
+  (defaults to 0: no ECC)
+
+- record-size: maximum size in bytes of each dump done on oops/panic
+  (defaults to 0: disabled)
+
+- console-size: size in bytes of log buffer reserved for kernel messages
+  (defaults to 0: disabled)
+
+- ftrace-size: size in bytes of log buffer reserved for function tracing and
+  profiling (defaults to 0: disabled)
+
+- pmsg-size: size in bytes of log buffer reserved for userspace messages
+  (defaults to 0: disabled)
+
+- unbuffered: if present, use unbuffered mappings to map the reserved region
+  (defaults to buffered mappings)
+
+- no-dump-oops: if present, only dump panics (defaults to panics and oops)
index 5d8675615e59c40c6564710a0a9b73ae060e2a00..9264bcab4099a1e2198eb0acaf79c95ac4c62cb5 100644 (file)
@@ -45,7 +45,7 @@ corrupt, but usually it is restorable.
 
 2. Setting the parameters
 
-Setting the ramoops parameters can be done in 2 different manners:
+Setting the ramoops parameters can be done in 3 different manners:
  1. Use the module parameters (which have the names of the variables described
  as before).
  For quick debugging, you can also reserve parts of memory during boot
@@ -54,7 +54,9 @@ Setting the ramoops parameters can be done in 2 different manners:
  kernel to use only the first 128 MB of memory, and place ECC-protected ramoops
  region at 128 MB boundary:
  "mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1"
- 2. Use a platform device and set the platform data. The parameters can then
+ 2. Use Device Tree bindings, as described in
+ Documentation/device-tree/bindings/misc/ramoops.txt.
+ 3. Use a platform device and set the platform data. The parameters can then
  be set through that platform data. An example of doing that is:
 
 #include <linux/pstore_ram.h>
index 856f9a7944cd91052582cd1fa5531c4bc6c9f9ea..64174bf95611456e1a05d8c09f875e84b3c7331b 100644 (file)
@@ -444,7 +444,8 @@ static int nvram_pstore_write(enum pstore_type_id type,
  */
 static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
                                int *count, struct timespec *time, char **buf,
-                               bool *compressed, struct pstore_info *psi)
+                               bool *compressed, ssize_t *ecc_notice_size,
+                               struct pstore_info *psi)
 {
        struct oops_log_info *oops_hdr;
        unsigned int err_type, id_no, size = 0;
@@ -545,6 +546,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
                        return -ENOMEM;
                kfree(buff);
 
+               *ecc_notice_size = 0;
                if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
                        *compressed = true;
                else
index 006c3894c6ea1a9b3fd344b28b49a88b851c5fc4..f096ab3cb54d6f8b67fa1c7dee4cf0d4cc5179e2 100644 (file)
@@ -927,7 +927,8 @@ static int erst_open_pstore(struct pstore_info *psi);
 static int erst_close_pstore(struct pstore_info *psi);
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
                           struct timespec *time, char **buf,
-                          bool *compressed, struct pstore_info *psi);
+                          bool *compressed, ssize_t *ecc_notice_size,
+                          struct pstore_info *psi);
 static int erst_writer(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);
@@ -987,7 +988,8 @@ static int erst_close_pstore(struct pstore_info *psi)
 
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
                           struct timespec *time, char **buf,
-                          bool *compressed, struct pstore_info *psi)
+                          bool *compressed, ssize_t *ecc_notice_size,
+                          struct pstore_info *psi)
 {
        int rc;
        ssize_t len = 0;
@@ -1033,6 +1035,7 @@ skip:
        memcpy(*buf, rcd->data, len - sizeof(*rcd));
        *id = record_id;
        *compressed = false;
+       *ecc_notice_size = 0;
        if (uuid_le_cmp(rcd->sec_hdr.section_type,
                        CPER_SECTION_TYPE_DMESG_Z) == 0) {
                *type = PSTORE_TYPE_DMESG;
index eac76a79a88015a4b5e8f2d20d0c56c9cc7c7de0..30a24d09ea6cf712da643aee9adaac010b0e9473 100644 (file)
@@ -34,6 +34,7 @@ struct pstore_read_data {
        int *count;
        struct timespec *timespec;
        bool *compressed;
+       ssize_t *ecc_notice_size;
        char **buf;
 };
 
@@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                        *cb_data->compressed = true;
                else
                        *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else if (sscanf(name, "dump-type%u-%u-%d-%lu",
                   cb_data->type, &part, &cnt, &time) == 4) {
                *cb_data->id = generic_id(time, part, cnt);
@@ -76,6 +78,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                cb_data->timespec->tv_sec = time;
                cb_data->timespec->tv_nsec = 0;
                *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else if (sscanf(name, "dump-type%u-%u-%lu",
                          cb_data->type, &part, &time) == 3) {
                /*
@@ -88,6 +91,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                cb_data->timespec->tv_sec = time;
                cb_data->timespec->tv_nsec = 0;
                *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else
                return 0;
 
@@ -210,6 +214,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
 static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
                               int *count, struct timespec *timespec,
                               char **buf, bool *compressed,
+                              ssize_t *ecc_notice_size,
                               struct pstore_info *psi)
 {
        struct pstore_read_data data;
@@ -220,6 +225,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
        data.count = count;
        data.timespec = timespec;
        data.compressed = compressed;
+       data.ecc_notice_size = ecc_notice_size;
        data.buf = buf;
 
        *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
@@ -393,6 +399,13 @@ static __init int efivars_pstore_init(void)
 
 static __exit void efivars_pstore_exit(void)
 {
+       if (!efi_pstore_info.bufsize)
+               return;
+
+       pstore_unregister(&efi_pstore_info);
+       kfree(efi_pstore_info.buf);
+       efi_pstore_info.buf = NULL;
+       efi_pstore_info.bufsize = 0;
 }
 
 module_init(efivars_pstore_init);
index 360ae43f590cccf24630f4b5676ebf5f00ebca0b..be40813eff52c6a1dda5844a45e4b5670672ec6f 100644 (file)
@@ -1,8 +1,6 @@
 config PSTORE
        tristate "Persistent store support"
        default n
-       select ZLIB_DEFLATE
-       select ZLIB_INFLATE
        help
           This option enables generic access to platform level
           persistent storage via "pstore" filesystem that can
@@ -14,6 +12,35 @@ config PSTORE
           If you don't have a platform persistent store driver,
           say N.
 
+choice
+        prompt "Choose compression algorithm"
+        depends on PSTORE
+        default PSTORE_ZLIB_COMPRESS
+        help
+          This option chooses compression algorithm.
+
+config PSTORE_ZLIB_COMPRESS
+        bool "ZLIB"
+        select ZLIB_DEFLATE
+        select ZLIB_INFLATE
+        help
+          This option enables ZLIB compression algorithm support.
+
+config PSTORE_LZO_COMPRESS
+        bool "LZO"
+        select LZO_COMPRESS
+        select LZO_DECOMPRESS
+        help
+          This option enables LZO compression algorithm support.
+
+config PSTORE_LZ4_COMPRESS
+        bool "LZ4"
+        select LZ4_COMPRESS
+        select LZ4_DECOMPRESS
+        help
+          This option enables LZ4 compression algorithm support.
+endchoice
+
 config PSTORE_CONSOLE
        bool "Log kernel console messages"
        depends on PSTORE
index 45d6110744cb77255a2705f19176240e9be65e6f..ec9ddef5ae755d87ccd3359e49289e532f6a2d0f 100644 (file)
@@ -178,7 +178,6 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
 }
 
 static const struct file_operations pstore_file_operations = {
-       .owner          = THIS_MODULE,
        .open           = pstore_file_open,
        .read           = pstore_file_read,
        .llseek         = pstore_file_llseek,
index 588461bb2dd482bc7906cb284c57b1e6ff92de87..16ecca5b72d815d92163def3f03bd9f26c4cc8b1 100644 (file)
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pstore.h>
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 #include <linux/zlib.h>
+#endif
+#ifdef CONFIG_PSTORE_LZO_COMPRESS
+#include <linux/lzo.h>
+#endif
+#ifdef CONFIG_PSTORE_LZ4_COMPRESS
+#include <linux/lz4.h>
+#endif
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
@@ -69,10 +77,23 @@ struct pstore_info *psinfo;
 static char *backend;
 
 /* Compression parameters */
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 #define COMPR_LEVEL 6
 #define WINDOW_BITS 12
 #define MEM_LEVEL 4
 static struct z_stream_s stream;
+#else
+static unsigned char *workspace;
+#endif
+
+struct pstore_zbackend {
+       int (*compress)(const void *in, void *out, size_t inlen, size_t outlen);
+       int (*decompress)(void *in, void *out, size_t inlen, size_t outlen);
+       void (*allocate)(void);
+       void (*free)(void);
+
+       const char *name;
+};
 
 static char *big_oops_buf;
 static size_t big_oops_buf_sz;
@@ -129,9 +150,9 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
 }
 EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
 
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 /* Derived from logfs_compress() */
-static int pstore_compress(const void *in, void *out, size_t inlen,
-                                                       size_t outlen)
+static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen)
 {
        int err, ret;
 
@@ -165,7 +186,7 @@ error:
 }
 
 /* Derived from logfs_uncompress */
-static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
+static int decompress_zlib(void *in, void *out, size_t inlen, size_t outlen)
 {
        int err, ret;
 
@@ -194,7 +215,7 @@ error:
        return ret;
 }
 
-static void allocate_buf_for_compression(void)
+static void allocate_zlib(void)
 {
        size_t size;
        size_t cmpr;
@@ -237,12 +258,190 @@ static void allocate_buf_for_compression(void)
 
 }
 
-static void free_buf_for_compression(void)
+static void free_zlib(void)
 {
        kfree(stream.workspace);
        stream.workspace = NULL;
        kfree(big_oops_buf);
        big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_zlib = {
+       .compress       = compress_zlib,
+       .decompress     = decompress_zlib,
+       .allocate       = allocate_zlib,
+       .free           = free_zlib,
+       .name           = "zlib",
+};
+#endif
+
+#ifdef CONFIG_PSTORE_LZO_COMPRESS
+static int compress_lzo(const void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lzo1x_1_compress(in, inlen, out, &outlen, workspace);
+       if (ret != LZO_E_OK) {
+               pr_err("lzo_compress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lzo1x_decompress_safe(in, inlen, out, &outlen);
+       if (ret != LZO_E_OK) {
+               pr_err("lzo_decompress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static void allocate_lzo(void)
+{
+       big_oops_buf_sz = lzo1x_worst_compress(psinfo->bufsize);
+       big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+       if (big_oops_buf) {
+               workspace = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+               if (!workspace) {
+                       pr_err("No memory for compression workspace; skipping compression\n");
+                       kfree(big_oops_buf);
+                       big_oops_buf = NULL;
+               }
+       } else {
+               pr_err("No memory for uncompressed data; skipping compression\n");
+               workspace = NULL;
+       }
+}
+
+static void free_lzo(void)
+{
+       kfree(workspace);
+       kfree(big_oops_buf);
+       big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_lzo = {
+       .compress       = compress_lzo,
+       .decompress     = decompress_lzo,
+       .allocate       = allocate_lzo,
+       .free           = free_lzo,
+       .name           = "lzo",
+};
+#endif
+
+#ifdef CONFIG_PSTORE_LZ4_COMPRESS
+static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lz4_compress(in, inlen, out, &outlen, workspace);
+       if (ret) {
+               pr_err("lz4_compress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen);
+       if (ret) {
+               pr_err("lz4_decompress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static void allocate_lz4(void)
+{
+       big_oops_buf_sz = lz4_compressbound(psinfo->bufsize);
+       big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+       if (big_oops_buf) {
+               workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
+               if (!workspace) {
+                       pr_err("No memory for compression workspace; skipping compression\n");
+                       kfree(big_oops_buf);
+                       big_oops_buf = NULL;
+               }
+       } else {
+               pr_err("No memory for uncompressed data; skipping compression\n");
+               workspace = NULL;
+       }
+}
+
+static void free_lz4(void)
+{
+       kfree(workspace);
+       kfree(big_oops_buf);
+       big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_lz4 = {
+       .compress       = compress_lz4,
+       .decompress     = decompress_lz4,
+       .allocate       = allocate_lz4,
+       .free           = free_lz4,
+       .name           = "lz4",
+};
+#endif
+
+static struct pstore_zbackend *zbackend =
+#if defined(CONFIG_PSTORE_ZLIB_COMPRESS)
+       &backend_zlib;
+#elif defined(CONFIG_PSTORE_LZO_COMPRESS)
+       &backend_lzo;
+#elif defined(CONFIG_PSTORE_LZ4_COMPRESS)
+       &backend_lz4;
+#else
+       NULL;
+#endif
+
+static int pstore_compress(const void *in, void *out,
+                          size_t inlen, size_t outlen)
+{
+       if (zbackend)
+               return zbackend->compress(in, out, inlen, outlen);
+       else
+               return -EIO;
+}
+
+static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
+{
+       if (zbackend)
+               return zbackend->decompress(in, out, inlen, outlen);
+       else
+               return -EIO;
+}
+
+static void allocate_buf_for_compression(void)
+{
+       if (zbackend) {
+               pr_info("using %s compression\n", zbackend->name);
+               zbackend->allocate();
+       } else {
+               pr_err("allocate compression buffer error!\n");
+       }
+}
+
+static void free_buf_for_compression(void)
+{
+       if (zbackend)
+               zbackend->free();
+       else
+               pr_err("free compression buffer error!\n");
 }
 
 /*
@@ -284,7 +483,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
        u64             id;
        unsigned int    part = 1;
        unsigned long   flags = 0;
-       int             is_locked = 0;
+       int             is_locked;
        int             ret;
 
        why = get_reason_str(reason);
@@ -295,8 +494,10 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                        pr_err("pstore dump routine blocked in %s path, may corrupt error record\n"
                                       , in_nmi() ? "NMI" : why);
                }
-       } else
+       } else {
                spin_lock_irqsave(&psinfo->buf_lock, flags);
+               is_locked = 1;
+       }
        oopscount++;
        while (total < kmsg_bytes) {
                char *dst;
@@ -304,19 +505,25 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                int hsize;
                int zipped_len = -1;
                size_t len;
-               bool compressed;
+               bool compressed = false;
                size_t total_len;
 
                if (big_oops_buf && is_locked) {
                        dst = big_oops_buf;
-                       hsize = sprintf(dst, "%s#%d Part%u\n", why,
-                                                       oopscount, part);
-                       size = big_oops_buf_sz - hsize;
+                       size = big_oops_buf_sz;
+               } else {
+                       dst = psinfo->buf;
+                       size = psinfo->bufsize;
+               }
 
-                       if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
-                                                               size, &len))
-                               break;
+               hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part);
+               size -= hsize;
+
+               if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
+                                         size, &len))
+                       break;
 
+               if (big_oops_buf && is_locked) {
                        zipped_len = pstore_compress(dst, psinfo->buf,
                                                hsize + len, psinfo->bufsize);
 
@@ -324,21 +531,9 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                                compressed = true;
                                total_len = zipped_len;
                        } else {
-                               compressed = false;
                                total_len = copy_kmsg_to_buffer(hsize, len);
                        }
                } else {
-                       dst = psinfo->buf;
-                       hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount,
-                                                                       part);
-                       size = psinfo->bufsize - hsize;
-                       dst += hsize;
-
-                       if (!kmsg_dump_get_buffer(dumper, true, dst,
-                                                               size, &len))
-                               break;
-
-                       compressed = false;
                        total_len = hsize + len;
                }
 
@@ -350,10 +545,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                total += total_len;
                part++;
        }
-       if (pstore_cannot_block_path(reason)) {
-               if (is_locked)
-                       spin_unlock_irqrestore(&psinfo->buf_lock, flags);
-       } else
+       if (is_locked)
                spin_unlock_irqrestore(&psinfo->buf_lock, flags);
 }
 
@@ -497,9 +689,11 @@ EXPORT_SYMBOL_GPL(pstore_register);
 
 void pstore_unregister(struct pstore_info *psi)
 {
-       pstore_unregister_pmsg();
-       pstore_unregister_ftrace();
-       pstore_unregister_console();
+       if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
+               pstore_unregister_pmsg();
+               pstore_unregister_ftrace();
+               pstore_unregister_console();
+       }
        pstore_unregister_kmsg();
 
        free_buf_for_compression();
@@ -527,6 +721,7 @@ void pstore_get_records(int quiet)
        int                     failed = 0, rc;
        bool                    compressed;
        int                     unzipped_len = -1;
+       ssize_t                 ecc_notice_size = 0;
 
        if (!psi)
                return;
@@ -536,7 +731,7 @@ void pstore_get_records(int quiet)
                goto out;
 
        while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
-                               psi)) > 0) {
+                                &ecc_notice_size, psi)) > 0) {
                if (compressed && (type == PSTORE_TYPE_DMESG)) {
                        if (big_oops_buf)
                                unzipped_len = pstore_decompress(buf,
@@ -544,6 +739,9 @@ void pstore_get_records(int quiet)
                                                        big_oops_buf_sz);
 
                        if (unzipped_len > 0) {
+                               if (ecc_notice_size)
+                                       memcpy(big_oops_buf + unzipped_len,
+                                              buf + size, ecc_notice_size);
                                kfree(buf);
                                buf = big_oops_buf;
                                size = unzipped_len;
@@ -555,7 +753,8 @@ void pstore_get_records(int quiet)
                        }
                }
                rc = pstore_mkfile(type, psi->name, id, count, buf,
-                                 compressed, (size_t)size, time, psi);
+                                  compressed, size + ecc_notice_size,
+                                  time, psi);
                if (unzipped_len < 0) {
                        /* Free buffer other than big oops */
                        kfree(buf);
index bd9812e83461238745a57e30a6d39a732282680f..47516a794011ad54cf30fa82aafb164a8d7bb46a 100644 (file)
@@ -34,6 +34,8 @@
 #include <linux/slab.h>
 #include <linux/compiler.h>
 #include <linux/pstore_ram.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
 
 #define RAMOOPS_KERNMSG_HDR "===="
 #define MIN_MEM_SIZE 4096UL
@@ -181,10 +183,10 @@ static bool prz_ok(struct persistent_ram_zone *prz)
 static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
                                   int *count, struct timespec *time,
                                   char **buf, bool *compressed,
+                                  ssize_t *ecc_notice_size,
                                   struct pstore_info *psi)
 {
        ssize_t size;
-       ssize_t ecc_notice_size;
        struct ramoops_context *cxt = psi->data;
        struct persistent_ram_zone *prz = NULL;
        int header_length = 0;
@@ -229,16 +231,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
        size = persistent_ram_old_size(prz) - header_length;
 
        /* ECC correction notice */
-       ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
+       *ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
 
-       *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL);
+       *buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL);
        if (*buf == NULL)
                return -ENOMEM;
 
        memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size);
-       persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
+       persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1);
 
-       return size + ecc_notice_size;
+       return size;
 }
 
 static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
@@ -458,15 +460,98 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt,
        return 0;
 }
 
+static int ramoops_parse_dt_size(struct platform_device *pdev,
+                                const char *propname, u32 *value)
+{
+       u32 val32 = 0;
+       int ret;
+
+       ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
+       if (ret < 0 && ret != -EINVAL) {
+               dev_err(&pdev->dev, "failed to parse property %s: %d\n",
+                       propname, ret);
+               return ret;
+       }
+
+       if (val32 > INT_MAX) {
+               dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
+               return -EOVERFLOW;
+       }
+
+       *value = val32;
+       return 0;
+}
+
+static int ramoops_parse_dt(struct platform_device *pdev,
+                           struct ramoops_platform_data *pdata)
+{
+       struct device_node *of_node = pdev->dev.of_node;
+       struct device_node *mem_region;
+       struct resource res;
+       u32 value;
+       int ret;
+
+       dev_dbg(&pdev->dev, "using Device Tree\n");
+
+       mem_region = of_parse_phandle(of_node, "memory-region", 0);
+       if (!mem_region) {
+               dev_err(&pdev->dev, "no memory-region phandle\n");
+               return -ENODEV;
+       }
+
+       ret = of_address_to_resource(mem_region, 0, &res);
+       of_node_put(mem_region);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "failed to translate memory-region to resource: %d\n",
+                       ret);
+               return ret;
+       }
+
+       pdata->mem_size = resource_size(&res);
+       pdata->mem_address = res.start;
+       pdata->mem_type = of_property_read_bool(of_node, "unbuffered");
+       pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops");
+
+#define parse_size(name, field) {                                      \
+               ret = ramoops_parse_dt_size(pdev, name, &value);        \
+               if (ret < 0)                                            \
+                       return ret;                                     \
+               field = value;                                          \
+       }
+
+       parse_size("record-size", pdata->record_size);
+       parse_size("console-size", pdata->console_size);
+       parse_size("ftrace-size", pdata->ftrace_size);
+       parse_size("pmsg-size", pdata->pmsg_size);
+       parse_size("ecc-size", pdata->ecc_info.ecc_size);
+
+#undef parse_size
+
+       return 0;
+}
+
 static int ramoops_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct ramoops_platform_data *pdata = pdev->dev.platform_data;
+       struct ramoops_platform_data *pdata = dev->platform_data;
        struct ramoops_context *cxt = &oops_cxt;
        size_t dump_mem_sz;
        phys_addr_t paddr;
        int err = -EINVAL;
 
+       if (dev_of_node(dev) && !pdata) {
+               pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+               if (!pdata) {
+                       err = -ENOMEM;
+                       goto fail_out;
+               }
+
+               err = ramoops_parse_dt(pdev, pdata);
+               if (err < 0)
+                       goto fail_out;
+       }
+
        /* Only a single ramoops area allowed at a time, so fail extra
         * probes.
         */
@@ -596,11 +681,17 @@ static int ramoops_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id dt_match[] = {
+       { .compatible = "ramoops" },
+       {}
+};
+
 static struct platform_driver ramoops_driver = {
        .probe          = ramoops_probe,
        .remove         = ramoops_remove,
        .driver         = {
-               .name   = "ramoops",
+               .name           = "ramoops",
+               .of_match_table = dt_match,
        },
 };
 
index 831479f8df8f1b70638d59edc3cde6fc6fdcc22f..899e95e844006d9b2eb7ff1ecc41b9e40cc076a6 100644 (file)
@@ -58,7 +58,8 @@ struct pstore_info {
        int             (*close)(struct pstore_info *psi);
        ssize_t         (*read)(u64 *id, enum pstore_type_id *type,
                        int *count, struct timespec *time, char **buf,
-                       bool *compressed, struct pstore_info *psi);
+                       bool *compressed, ssize_t *ecc_notice_size,
+                       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,