From 2ab52a7b67c63b1619bba681afbfec965c3df702 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:24 +0100 Subject: arm: boards: karo-tx6x remove definition of DIV_ROUND_UP DIV_ROUND_UP is defined in include/linux/kernel.h. Use that instead. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- arch/arm/boards/karo-tx6x/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boards/karo-tx6x/board.c b/arch/arm/boards/karo-tx6x/board.c index 6d9dd9a505..a921541bfc 100644 --- a/arch/arm/boards/karo-tx6x/board.c +++ b/arch/arm/boards/karo-tx6x/board.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,6 @@ #define ETH_PHY_RST IMX_GPIO_NR(7, 6) #define ETH_PHY_PWR IMX_GPIO_NR(3, 20) #define ETH_PHY_INT IMX_GPIO_NR(7, 1) -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define DIV_ROUND(n,d) (((n) + ((d)/2)) / (d)) #define LTC3676_BUCK1 0x01 -- cgit v1.2.3 From 6f42da2fc8aee833f4eeb5ad9cde6e7c79cd10d9 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:25 +0100 Subject: log2: Add missing __rounddown_pow_of_two() Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- include/linux/log2.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/log2.h b/include/linux/log2.h index d9913f06bd..36519e3aa3 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -63,6 +63,15 @@ unsigned long __roundup_pow_of_two(unsigned long n) return 1UL << fls(n - 1); } +/* + * round down to nearest power of two + */ +static inline __attribute__((const)) +unsigned long __rounddown_pow_of_two(unsigned long n) +{ + return 1UL << (fls_long(n) - 1); +} + /** * ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value * @n - parameter -- cgit v1.2.3 From 45885266f98577f71cca528538a8512a40228d03 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:26 +0100 Subject: printk: Add missing include/declaration There are functions or structs used that do need these. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- include/printk.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/printk.h b/include/printk.h index a27ad514cf..822f64c61f 100644 --- a/include/printk.h +++ b/include/printk.h @@ -1,6 +1,8 @@ #ifndef __PRINTK_H #define __PRINTK_H +#include + #define MSG_EMERG 0 /* system is unusable */ #define MSG_ALERT 1 /* action must be taken immediately */ #define MSG_CRIT 2 /* critical conditions */ @@ -20,6 +22,7 @@ #endif /* debugging and troubleshooting/diagnostic helpers. */ +struct device_d; #ifndef CONFIG_CONSOLE_NONE int dev_printf(int level, const struct device_d *dev, const char *format, ...) -- cgit v1.2.3 From 647fba8ee386bbbcd430979e41ab4bdda4273673 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:27 +0100 Subject: vsprintf: Add scnprintf from kernel Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- include/stdio.h | 1 + lib/vsprintf.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/stdio.h b/include/stdio.h index f190911762..d0817bd071 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -13,6 +13,7 @@ void serial_printf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2 int sprintf(char *buf, const char *fmt, ...) __attribute__ ((format(__printf__, 2, 3))); int snprintf(char *buf, size_t size, const char *fmt, ...) __attribute__ ((format(__printf__, 3, 4))); +int scnprintf(char *buf, size_t size, const char *fmt, ...) __attribute__ ((format(__printf__, 3, 4))); int vsprintf(char *buf, const char *fmt, va_list args); char *asprintf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); char *vasprintf(const char *fmt, va_list ap); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 800ded7939..9b8e8cf5f8 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -595,6 +595,30 @@ int snprintf(char *buf, size_t size, const char *fmt, ...) } EXPORT_SYMBOL(snprintf); +/** + * scnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters written into @buf not including + * the trailing '\0'. If @size is == 0 the function returns 0. + */ + +int scnprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vscnprintf(buf, size, fmt, args); + va_end(args); + + return i; +} +EXPORT_SYMBOL(scnprintf); + /* Simplified asprintf. */ char *vasprintf(const char *fmt, va_list ap) { -- cgit v1.2.3 From 899b3822d2be2eb07622fd1946614675a9bb5109 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:28 +0100 Subject: lib: Import reed solomon code from kernel reed solomon code is used by RAMOOPS to check and fix data stored in volatile memory. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- include/linux/rslib.h | 103 +++++++++++ lib/Kconfig | 3 + lib/Makefile | 1 + lib/reed_solomon/Makefile | 6 + lib/reed_solomon/decode_rs.c | 271 +++++++++++++++++++++++++++++ lib/reed_solomon/encode_rs.c | 54 ++++++ lib/reed_solomon/reed_solomon.c | 369 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 807 insertions(+) create mode 100644 include/linux/rslib.h create mode 100644 lib/reed_solomon/Makefile create mode 100644 lib/reed_solomon/decode_rs.c create mode 100644 lib/reed_solomon/encode_rs.c create mode 100644 lib/reed_solomon/reed_solomon.c diff --git a/include/linux/rslib.h b/include/linux/rslib.h new file mode 100644 index 0000000000..b5e3ffd0de --- /dev/null +++ b/include/linux/rslib.h @@ -0,0 +1,103 @@ +/* + * include/linux/rslib.h + * + * Overview: + * Generic Reed Solomon encoder / decoder library + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * RS code lifted from reed solomon library written by Phil Karn + * Copyright 2002 Phil Karn, KA9Q + * + * $Id: rslib.h,v 1.4 2005/11/07 11:14:52 gleixner Exp $ + * + * 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. + */ + +#ifndef _RSLIB_H_ +#define _RSLIB_H_ + +#include + +/** + * struct rs_control - rs control structure + * + * @mm: Bits per symbol + * @nn: Symbols per block (= (1<mm = number of bits per symbol + * rs->nn = (2^rs->mm) - 1 + * + * Simple arithmetic modulo would return a wrong result for values + * >= 3 * rs->nn +*/ +static inline int rs_modnn(struct rs_control *rs, int x) +{ + while (x >= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index fbf9f0f348..46ec58c62e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -22,6 +22,9 @@ config XZ_DECOMPRESS bool "include xz uncompression support" select UNCOMPRESS +config REED_SOLOMON + bool + config GENERIC_FIND_NEXT_BIT def_bool n diff --git a/lib/Makefile b/lib/Makefile index abb34cfbdb..6ffa3e0104 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -56,3 +56,4 @@ obj-y += gcd.o obj-y += hexdump.o obj-$(CONFIG_FONTS) += fonts/ obj-$(CONFIG_BAREBOX_LOGO) += logo/ +obj-y += reed_solomon/ diff --git a/lib/reed_solomon/Makefile b/lib/reed_solomon/Makefile new file mode 100644 index 0000000000..c3d7136827 --- /dev/null +++ b/lib/reed_solomon/Makefile @@ -0,0 +1,6 @@ +# +# This is a modified version of reed solomon lib, +# + +obj-$(CONFIG_REED_SOLOMON) += reed_solomon.o + diff --git a/lib/reed_solomon/decode_rs.c b/lib/reed_solomon/decode_rs.c new file mode 100644 index 0000000000..0ec3f257ff --- /dev/null +++ b/lib/reed_solomon/decode_rs.c @@ -0,0 +1,271 @@ +/* + * lib/reed_solomon/decode_rs.c + * + * Overview: + * Generic Reed Solomon encoder / decoder library + * + * Copyright 2002, Phil Karn, KA9Q + * May be used under the terms of the GNU General Public License (GPL) + * + * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de) + * + * $Id: decode_rs.c,v 1.7 2005/11/07 11:14:59 gleixner Exp $ + * + */ + +/* Generic data width independent code which is included by the + * wrappers. + */ +{ + int deg_lambda, el, deg_omega; + int i, j, r, k, pad; + int nn = rs->nn; + int nroots = rs->nroots; + int fcr = rs->fcr; + int prim = rs->prim; + int iprim = rs->iprim; + uint16_t *alpha_to = rs->alpha_to; + uint16_t *index_of = rs->index_of; + uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error; + /* Err+Eras Locator poly and syndrome poly The maximum value + * of nroots is 8. So the necessary stack size will be about + * 220 bytes max. + */ + uint16_t lambda[nroots + 1], syn[nroots]; + uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1]; + uint16_t root[nroots], reg[nroots + 1], loc[nroots]; + int count = 0; + uint16_t msk = (uint16_t) rs->nn; + + /* Check length parameter for validity */ + pad = nn - nroots - len; + BUG_ON(pad < 0 || pad >= nn); + + /* Does the caller provide the syndrome ? */ + if (s != NULL) + goto decode; + + /* form the syndromes; i.e., evaluate data(x) at roots of + * g(x) */ + for (i = 0; i < nroots; i++) + syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk; + + for (j = 1; j < len; j++) { + for (i = 0; i < nroots; i++) { + if (syn[i] == 0) { + syn[i] = (((uint16_t) data[j]) ^ + invmsk) & msk; + } else { + syn[i] = ((((uint16_t) data[j]) ^ + invmsk) & msk) ^ + alpha_to[rs_modnn(rs, index_of[syn[i]] + + (fcr + i) * prim)]; + } + } + } + + for (j = 0; j < nroots; j++) { + for (i = 0; i < nroots; i++) { + if (syn[i] == 0) { + syn[i] = ((uint16_t) par[j]) & msk; + } else { + syn[i] = (((uint16_t) par[j]) & msk) ^ + alpha_to[rs_modnn(rs, index_of[syn[i]] + + (fcr+i)*prim)]; + } + } + } + s = syn; + + /* Convert syndromes to index form, checking for nonzero condition */ + syn_error = 0; + for (i = 0; i < nroots; i++) { + syn_error |= s[i]; + s[i] = index_of[s[i]]; + } + + if (!syn_error) { + /* if syndrome is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + count = 0; + goto finish; + } + + decode: + memset(&lambda[1], 0, nroots * sizeof(lambda[0])); + lambda[0] = 1; + + if (no_eras > 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = alpha_to[rs_modnn(rs, + prim * (nn - 1 - eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i])); + for (j = i + 1; j > 0; j--) { + tmp = index_of[lambda[j - 1]]; + if (tmp != nn) { + lambda[j] ^= + alpha_to[rs_modnn(rs, u + tmp)]; + } + } + } + } + + for (i = 0; i < nroots + 1; i++) + b[i] = index_of[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= nroots) { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++) { + if ((lambda[i] != 0) && (s[r - i - 1] != nn)) { + discr_r ^= + alpha_to[rs_modnn(rs, + index_of[lambda[i]] + + s[r - i - 1])]; + } + } + discr_r = index_of[discr_r]; /* Index form */ + if (discr_r == nn) { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove (&b[1], b, nroots * sizeof (b[0])); + b[0] = nn; + } else { + /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0; i < nroots; i++) { + if (b[i] != nn) { + t[i + 1] = lambda[i + 1] ^ + alpha_to[rs_modnn(rs, discr_r + + b[i])]; + } else + t[i + 1] = lambda[i + 1]; + } + if (2 * el <= r + no_eras - 1) { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= nroots; i++) { + b[i] = (lambda[i] == 0) ? nn : + rs_modnn(rs, index_of[lambda[i]] + - discr_r + nn); + } + } else { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1], b, nroots * sizeof(b[0])); + b[0] = nn; + } + memcpy(lambda, t, (nroots + 1) * sizeof(t[0])); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for (i = 0; i < nroots + 1; i++) { + lambda[i] = index_of[lambda[i]]; + if (lambda[i] != nn) + deg_lambda = i; + } + /* Find roots of error+erasure locator polynomial by Chien search */ + memcpy(®[1], &lambda[1], nroots * sizeof(reg[0])); + count = 0; /* Number of roots of lambda(x) */ + for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) { + q = 1; /* lambda[0] is always 0 */ + for (j = deg_lambda; j > 0; j--) { + if (reg[j] != nn) { + reg[j] = rs_modnn(rs, reg[j] + j); + q ^= alpha_to[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if (++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -EBADMSG; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**nroots). in index form. Also find deg(omega). + */ + deg_omega = deg_lambda - 1; + for (i = 0; i <= deg_omega; i++) { + tmp = 0; + for (j = i; j >= 0; j--) { + if ((s[i - j] != nn) && (lambda[j] != nn)) + tmp ^= + alpha_to[rs_modnn(rs, s[i - j] + lambda[j])]; + } + omega[i] = index_of[tmp]; + } + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count - 1; j >= 0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != nn) + num1 ^= alpha_to[rs_modnn(rs, omega[i] + + i * root[j])]; + } + num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative + * lambda_pr of lambda[i] */ + for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) { + if (lambda[i + 1] != nn) { + den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + + i * root[j])]; + } + } + /* Apply error to data */ + if (num1 != 0 && loc[j] >= pad) { + uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] + + index_of[num2] + + nn - index_of[den])]; + /* Store the error correction pattern, if a + * correction buffer is available */ + if (corr) { + corr[j] = cor; + } else { + /* If a data buffer is given and the + * error is inside the message, + * correct it */ + if (data && (loc[j] < (nn - nroots))) + data[loc[j] - pad] ^= cor; + } + } + } + +finish: + if (eras_pos != NULL) { + for (i = 0; i < count; i++) + eras_pos[i] = loc[i] - pad; + } + return count; + +} diff --git a/lib/reed_solomon/encode_rs.c b/lib/reed_solomon/encode_rs.c new file mode 100644 index 0000000000..0b5b1a6728 --- /dev/null +++ b/lib/reed_solomon/encode_rs.c @@ -0,0 +1,54 @@ +/* + * lib/reed_solomon/encode_rs.c + * + * Overview: + * Generic Reed Solomon encoder / decoder library + * + * Copyright 2002, Phil Karn, KA9Q + * May be used under the terms of the GNU General Public License (GPL) + * + * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de) + * + * $Id: encode_rs.c,v 1.5 2005/11/07 11:14:59 gleixner Exp $ + * + */ + +/* Generic data width independent code which is included by the + * wrappers. + * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par) + */ +{ + int i, j, pad; + int nn = rs->nn; + int nroots = rs->nroots; + uint16_t *alpha_to = rs->alpha_to; + uint16_t *index_of = rs->index_of; + uint16_t *genpoly = rs->genpoly; + uint16_t fb; + uint16_t msk = (uint16_t) rs->nn; + + /* Check length parameter for validity */ + pad = nn - nroots - len; + if (pad < 0 || pad >= nn) + return -ERANGE; + + for (i = 0; i < len; i++) { + fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]]; + /* feedback term is non-zero */ + if (fb != nn) { + for (j = 1; j < nroots; j++) { + par[j] ^= alpha_to[rs_modnn(rs, fb + + genpoly[nroots - j])]; + } + } + /* Shift */ + memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1)); + if (fb != nn) { + par[nroots - 1] = alpha_to[rs_modnn(rs, + fb + genpoly[0])]; + } else { + par[nroots - 1] = 0; + } + } + return 0; +} diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c new file mode 100644 index 0000000000..51c67c3c8d --- /dev/null +++ b/lib/reed_solomon/reed_solomon.c @@ -0,0 +1,369 @@ +/* + * lib/reed_solomon/reed_solomon.c + * + * Overview: + * Generic Reed Solomon encoder / decoder library + * + * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * Reed Solomon code lifted from reed solomon library written by Phil Karn + * Copyright 2002 Phil Karn, KA9Q + * + * $Id: rslib.c,v 1.7 2005/11/07 11:14:59 gleixner Exp $ + * + * 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. + * + * Description: + * + * The generic Reed Solomon library provides runtime configurable + * encoding / decoding of RS codes. + * Each user must call init_rs to get a pointer to a rs_control + * structure for the given rs parameters. This structure is either + * generated or a already available matching control structure is used. + * If a structure is generated then the polynomial arrays for + * fast encoding / decoding are built. This can take some time so + * make sure not to call this function from a time critical path. + * Usually a module / driver should initialize the necessary + * rs_control structure on module / driver init and release it + * on exit. + * The encoding puts the calculated syndrome into a given syndrome + * buffer. + * The decoding is a two step process. The first step calculates + * the syndrome over the received (data + syndrome) and calls the + * second stage, which does the decoding / error correction itself. + * Many hw encoders provide a syndrome calculation over the received + * data + syndrome and can call the second stage directly. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* This list holds all currently allocated rs control structures */ +static LIST_HEAD (rslist); + +/** + * rs_init - Initialize a Reed-Solomon codec + * @symsize: symbol size, bits (1-8) + * @gfpoly: Field generator polynomial coefficients + * @gffunc: Field generator function + * @fcr: first root of RS code generator polynomial, index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + * + * Allocate a control structure and the polynom arrays for faster + * en/decoding. Fill the arrays according to the given parameters. + */ +static struct rs_control *rs_init(int symsize, int gfpoly, int (*gffunc)(int), + int fcr, int prim, int nroots) +{ + struct rs_control *rs; + int i, j, sr, root, iprim; + + /* Allocate the control structure */ + rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL); + if (rs == NULL) + return NULL; + + INIT_LIST_HEAD(&rs->list); + + rs->mm = symsize; + rs->nn = (1 << symsize) - 1; + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + rs->gfpoly = gfpoly; + rs->gffunc = gffunc; + + /* Allocate the arrays */ + rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL); + if (rs->alpha_to == NULL) + goto errrs; + + rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL); + if (rs->index_of == NULL) + goto erralp; + + rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL); + if(rs->genpoly == NULL) + goto erridx; + + /* Generate Galois field lookup tables */ + rs->index_of[0] = rs->nn; /* log(zero) = -inf */ + rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */ + if (gfpoly) { + sr = 1; + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if (sr & (1 << symsize)) + sr ^= gfpoly; + sr &= rs->nn; + } + } else { + sr = gffunc(0); + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr = gffunc(sr); + } + } + /* If it's not primitive, exit */ + if(sr != rs->alpha_to[0]) + goto errpol; + + /* Find prim-th root of 1, used in decoding */ + for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn); + /* prim-th root of 1, index form */ + rs->iprim = iprim / prim; + + /* Form RS code generator polynomial from its roots */ + rs->genpoly[0] = 1; + for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) { + rs->genpoly[i + 1] = 1; + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--) { + if (rs->genpoly[j] != 0) { + rs->genpoly[j] = rs->genpoly[j -1] ^ + rs->alpha_to[rs_modnn(rs, + rs->index_of[rs->genpoly[j]] + root)]; + } else + rs->genpoly[j] = rs->genpoly[j - 1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = + rs->alpha_to[rs_modnn(rs, + rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + return rs; + + /* Error exit */ +errpol: + kfree(rs->genpoly); +erridx: + kfree(rs->index_of); +erralp: + kfree(rs->alpha_to); +errrs: + kfree(rs); + return NULL; +} + + +/** + * free_rs - Free the rs control structure, if it is no longer used + * @rs: the control structure which is not longer used by the + * caller + */ +void free_rs(struct rs_control *rs) +{ + rs->users--; + if(!rs->users) { + list_del(&rs->list); + kfree(rs->alpha_to); + kfree(rs->index_of); + kfree(rs->genpoly); + kfree(rs); + } +} + +/** + * init_rs_internal - Find a matching or allocate a new rs control structure + * @symsize: the symbol size (number of bits) + * @gfpoly: the extended Galois field generator polynomial coefficients, + * with the 0th coefficient in the low order bit. The polynomial + * must be primitive; + * @gffunc: pointer to function to generate the next field element, + * or the multiplicative identity element if given 0. Used + * instead of gfpoly if gfpoly is 0 + * @fcr: the first consecutive root of the rs code generator polynomial + * in index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + */ +static struct rs_control *init_rs_internal(int symsize, int gfpoly, + int (*gffunc)(int), int fcr, + int prim, int nroots) +{ + struct list_head *tmp; + struct rs_control *rs; + + /* Sanity checks */ + if (symsize < 1) + return NULL; + if (fcr < 0 || fcr >= (1<= (1<= (1<mm) + continue; + if (gfpoly != rs->gfpoly) + continue; + if (gffunc != rs->gffunc) + continue; + if (fcr != rs->fcr) + continue; + if (prim != rs->prim) + continue; + if (nroots != rs->nroots) + continue; + /* We have a matching one already */ + rs->users++; + goto out; + } + + /* Create a new one */ + rs = rs_init(symsize, gfpoly, gffunc, fcr, prim, nroots); + if (rs) { + rs->users = 1; + list_add(&rs->list, &rslist); + } +out: + return rs; +} + +/** + * init_rs - Find a matching or allocate a new rs control structure + * @symsize: the symbol size (number of bits) + * @gfpoly: the extended Galois field generator polynomial coefficients, + * with the 0th coefficient in the low order bit. The polynomial + * must be primitive; + * @fcr: the first consecutive root of the rs code generator polynomial + * in index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + */ +struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, + int nroots) +{ + return init_rs_internal(symsize, gfpoly, NULL, fcr, prim, nroots); +} + +/** + * init_rs_non_canonical - Find a matching or allocate a new rs control + * structure, for fields with non-canonical + * representation + * @symsize: the symbol size (number of bits) + * @gffunc: pointer to function to generate the next field element, + * or the multiplicative identity element if given 0. Used + * instead of gfpoly if gfpoly is 0 + * @fcr: the first consecutive root of the rs code generator polynomial + * in index form + * @prim: primitive element to generate polynomial roots + * @nroots: RS code generator polynomial degree (number of roots) + */ +struct rs_control *init_rs_non_canonical(int symsize, int (*gffunc)(int), + int fcr, int prim, int nroots) +{ + return init_rs_internal(symsize, 0, gffunc, fcr, prim, nroots); +} + +/** + * encode_rs8 - Calculate the parity for data values (8bit data width) + * @rs: the rs control structure + * @data: data field of a given type + * @len: data length + * @par: parity data, must be initialized by caller (usually all 0) + * @invmsk: invert data mask (will be xored on data) + * + * The parity uses a uint16_t data type to enable + * symbol size > 8. The calling code must take care of encoding of the + * syndrome result for storage itself. + */ +int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, + uint16_t invmsk) +{ +#include "encode_rs.c" +} +EXPORT_SYMBOL_GPL(encode_rs8); + +/** + * decode_rs8 - Decode codeword (8bit data width) + * @rs: the rs control structure + * @data: data field of a given type + * @par: received parity data field + * @len: data length + * @s: syndrome data field (if NULL, syndrome is calculated) + * @no_eras: number of erasures + * @eras_pos: position of erasures, can be NULL + * @invmsk: invert data mask (will be xored on data, not on parity!) + * @corr: buffer to store correction bitmask on eras_pos + * + * The syndrome and parity uses a uint16_t data type to enable + * symbol size > 8. The calling code must take care of decoding of the + * syndrome result and the received parity before calling this code. + * Returns the number of corrected bits or -EBADMSG for uncorrectable errors. + */ +int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, + uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, + uint16_t *corr) +{ +#include "decode_rs.c" +} +EXPORT_SYMBOL_GPL(decode_rs8); + +/** + * encode_rs16 - Calculate the parity for data values (16bit data width) + * @rs: the rs control structure + * @data: data field of a given type + * @len: data length + * @par: parity data, must be initialized by caller (usually all 0) + * @invmsk: invert data mask (will be xored on data, not on parity!) + * + * Each field in the data array contains up to symbol size bits of valid data. + */ +int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, + uint16_t invmsk) +{ +#include "encode_rs.c" +} +EXPORT_SYMBOL_GPL(encode_rs16); + +/** + * decode_rs16 - Decode codeword (16bit data width) + * @rs: the rs control structure + * @data: data field of a given type + * @par: received parity data field + * @len: data length + * @s: syndrome data field (if NULL, syndrome is calculated) + * @no_eras: number of erasures + * @eras_pos: position of erasures, can be NULL + * @invmsk: invert data mask (will be xored on data, not on parity!) + * @corr: buffer to store correction bitmask on eras_pos + * + * Each field in the data array contains up to symbol size bits of valid data. + * Returns the number of corrected bits or -EBADMSG for uncorrectable errors. + */ +int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len, + uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, + uint16_t *corr) +{ +#include "decode_rs.c" +} +EXPORT_SYMBOL_GPL(decode_rs16); + +EXPORT_SYMBOL_GPL(init_rs); +EXPORT_SYMBOL_GPL(init_rs_non_canonical); +EXPORT_SYMBOL_GPL(free_rs); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Reed Solomon encoder/decoder"); +MODULE_AUTHOR("Phil Karn, Thomas Gleixner"); + -- cgit v1.2.3 From 65071bd0910ef109c86b9645c570a6ceed7de534 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:29 +0100 Subject: arm: Clarify memory layout calculation The memory calculations used are all hardcoded into three different files, start-pbl.c, uncompress.c and start.c. To make this more readable and reliable, this patch gathers these information in barebox-arm.h with static inline functions for the calculation of the memory offsets. This patch also adds proper handling of different barebox/board data sizes. Currently only 1MB+Alignment of RAM is reserved for Barebox and board data. This could be too small for bigger devicetrees and barebox. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- arch/arm/cpu/start-pbl.c | 14 ++++------ arch/arm/cpu/start.c | 48 ++++++++++++++++++--------------- arch/arm/cpu/uncompress.c | 31 ++++++++++----------- arch/arm/include/asm/barebox-arm.h | 55 +++++++++++++++++++++++++------------- 4 files changed, 83 insertions(+), 65 deletions(-) diff --git a/arch/arm/cpu/start-pbl.c b/arch/arm/cpu/start-pbl.c index 2075ffeee7..f723edc613 100644 --- a/arch/arm/cpu/start-pbl.c +++ b/arch/arm/cpu/start-pbl.c @@ -54,8 +54,6 @@ __noreturn void barebox_single_pbl_start(unsigned long membase, uint32_t endmem = membase + memsize; unsigned long barebox_base; - endmem -= STACK_SIZE; /* stack */ - if (IS_ENABLED(CONFIG_PBL_RELOCATABLE)) relocate_to_current_adr(); @@ -67,7 +65,7 @@ __noreturn void barebox_single_pbl_start(unsigned long membase, pg_len = pg_end - pg_start; if (IS_ENABLED(CONFIG_RELOCATABLE)) - barebox_base = arm_barebox_image_place(membase + memsize); + barebox_base = arm_mem_barebox_image(membase, endmem, pg_len); else barebox_base = TEXT_BASE; @@ -83,14 +81,12 @@ __noreturn void barebox_single_pbl_start(unsigned long membase, setup_c(); if (IS_ENABLED(CONFIG_MMU_EARLY)) { - endmem &= ~0x3fff; - endmem -= SZ_16K; /* ttb */ - mmu_early_enable(membase, memsize, endmem); + unsigned long ttb = arm_mem_ttb(membase, endmem); + mmu_early_enable(membase, memsize, ttb); } - endmem -= SZ_128K; /* early malloc */ - free_mem_ptr = endmem; - free_mem_end_ptr = free_mem_ptr + SZ_128K; + free_mem_ptr = arm_mem_early_malloc(membase, endmem); + free_mem_end_ptr = arm_mem_early_malloc_end(membase, endmem); pbl_barebox_uncompress((void*)barebox_base, (void *)pg_start, pg_len); diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index c054f3c440..e06c4c8f37 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -104,14 +104,31 @@ void *barebox_arm_boot_dtb(void) return barebox_boarddata; } +static inline unsigned long arm_mem_boarddata(unsigned long membase, + unsigned long endmem, + unsigned long size) +{ + unsigned long mem; + + mem = arm_mem_barebox_image(membase, endmem, barebox_image_size); + mem -= ALIGN(size, 64); + + return mem; +} + __noreturn void barebox_non_pbl_start(unsigned long membase, unsigned long memsize, void *boarddata) { unsigned long endmem = membase + memsize; unsigned long malloc_start, malloc_end; + unsigned long barebox_size = barebox_image_size + + ((unsigned long)&__bss_stop - (unsigned long)&__bss_start); + unsigned long arm_head_bottom; if (IS_ENABLED(CONFIG_RELOCATABLE)) { - unsigned long barebox_base = arm_barebox_image_place(endmem); + unsigned long barebox_base = arm_mem_barebox_image(membase, + endmem, + barebox_size); relocate_to_adr(barebox_base); } @@ -122,19 +139,16 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); arm_stack_top = endmem; - endmem -= STACK_SIZE; /* Stack */ if (IS_ENABLED(CONFIG_MMU_EARLY)) { - - endmem &= ~0x3fff; - endmem -= SZ_16K; /* ttb */ + unsigned long ttb = arm_mem_ttb(membase, endmem); if (IS_ENABLED(CONFIG_PBL_IMAGE)) { arm_set_cache_functions(); } else { - pr_debug("enabling MMU, ttb @ 0x%08lx\n", endmem); + pr_debug("enabling MMU, ttb @ 0x%08lx\n", ttb); arm_early_mmu_cache_invalidate(); - mmu_early_enable(membase, memsize, endmem); + mmu_early_enable(membase, memsize, ttb); } } @@ -155,24 +169,16 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, } if (totalsize) { - endmem -= ALIGN(totalsize, 64); + unsigned long mem = arm_mem_boarddata(membase, endmem, + totalsize); pr_debug("found %s in boarddata, copying to 0x%lu\n", - name, endmem); - barebox_boarddata = memcpy((void *)endmem, - boarddata, totalsize); + name, mem); + barebox_boarddata = memcpy((void *)mem, boarddata, + totalsize); } } - if ((unsigned long)_text > membase + memsize || - (unsigned long)_text < membase) - /* - * barebox is either outside SDRAM or in another - * memory bank, so we can use the whole bank for - * malloc. - */ - malloc_end = endmem; - else - malloc_end = (unsigned long)_text; + malloc_end = arm_head_bottom; /* * Maximum malloc space is the Kconfig value if given diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c index dbf6b1e3f8..5bcce6b9e3 100644 --- a/arch/arm/cpu/uncompress.c +++ b/arch/arm/cpu/uncompress.c @@ -52,8 +52,6 @@ void __noreturn barebox_multi_pbl_start(unsigned long membase, void *pg_start; unsigned long pc = get_pc(); - endmem -= STACK_SIZE; /* stack */ - image_end = (void *)ld_var(__image_end) - get_runtime_offset(); if (IS_ENABLED(CONFIG_PBL_RELOCATABLE)) { @@ -68,8 +66,16 @@ void __noreturn barebox_multi_pbl_start(unsigned long membase, relocate_to_adr(membase); } + /* + * image_end is the first location after the executable. It contains + * the size of the appended compressed binary followed by the binary. + */ + pg_start = image_end + 1; + pg_len = *(image_end); + if (IS_ENABLED(CONFIG_RELOCATABLE)) - barebox_base = arm_barebox_image_place(membase + memsize); + barebox_base = arm_mem_barebox_image(membase, endmem, + pg_len); else barebox_base = TEXT_BASE; @@ -78,22 +84,13 @@ void __noreturn barebox_multi_pbl_start(unsigned long membase, pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); if (IS_ENABLED(CONFIG_MMU_EARLY)) { - endmem &= ~0x3fff; - endmem -= SZ_16K; /* ttb */ - pr_debug("enabling MMU, ttb @ 0x%08x\n", endmem); - mmu_early_enable(membase, memsize, endmem); + unsigned long ttb = arm_mem_ttb(membase, endmem); + pr_debug("enabling MMU, ttb @ 0x%08lx\n", ttb); + mmu_early_enable(membase, memsize, ttb); } - endmem -= SZ_128K; /* early malloc */ - free_mem_ptr = endmem; - free_mem_end_ptr = free_mem_ptr + SZ_128K; - - /* - * image_end is the first location after the executable. It contains - * the size of the appended compressed binary followed by the binary. - */ - pg_start = image_end + 1; - pg_len = *(image_end); + free_mem_ptr = arm_mem_early_malloc(membase, endmem); + free_mem_end_ptr = arm_mem_early_malloc_end(membase, endmem); pr_debug("uncompressing barebox binary at 0x%p (size 0x%08x) to 0x%08lx\n", pg_start, pg_len, barebox_base); diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h index 76e356413a..8d52882981 100644 --- a/arch/arm/include/asm/barebox-arm.h +++ b/arch/arm/include/asm/barebox-arm.h @@ -94,25 +94,44 @@ static inline void arm_fixup_vectors(void) void *barebox_arm_boot_dtb(void); -/* - * For relocatable binaries find a suitable start address for the - * relocated binary. Beginning at the memory end substract the reserved - * space and round down a bit at the end. This is used by the pbl to - * extract the image to a suitable place so that the uncompressed image - * does not have to copy itself to another place. Also it's used by - * the uncompressed image to relocate itself to the same place. - */ -static inline unsigned long arm_barebox_image_place(unsigned long endmem) +static inline unsigned long arm_mem_stack(unsigned long membase, + unsigned long endmem) +{ + return endmem - STACK_SIZE; +} + +static inline unsigned long arm_mem_ttb(unsigned long membase, + unsigned long endmem) { - endmem -= STACK_SIZE; - endmem -= SZ_32K; /* ttb */ - endmem -= SZ_128K; /* early malloc */ - endmem -= SZ_1M; /* place for barebox image */ - - /* - * round down to make translating the objdump easier - */ - endmem &= ~(SZ_1M - 1); + endmem = arm_mem_stack(membase, endmem); + endmem &= ~(SZ_16K - 1); + endmem -= SZ_16K; + + return endmem; +} + +static inline unsigned long arm_mem_early_malloc(unsigned long membase, + unsigned long endmem) +{ + return arm_mem_ttb(membase, endmem) - SZ_128K; +} + +static inline unsigned long arm_mem_early_malloc_end(unsigned long membase, + unsigned long endmem) +{ + return arm_mem_ttb(membase, endmem); +} + +static inline unsigned long arm_mem_barebox_image(unsigned long membase, + unsigned long endmem, + unsigned long size) +{ + endmem = arm_mem_ttb(membase, endmem); + + if (IS_ENABLED(CONFIG_RELOCATABLE)) { + endmem -= size; + endmem &= ~(SZ_1M - 1); + } return endmem; } -- cgit v1.2.3 From 9280600522a5ec6c3618683cd5ca9f7973bffbe3 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:30 +0100 Subject: arm: start: Add visible sdram region for barebox board data This helps to understand and find problems with the memory layout of barebox. It adds another entry for the board data that barebox allocated. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- arch/arm/cpu/start.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index e06c4c8f37..4ad64b61e8 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -34,6 +34,8 @@ #include "mmu-early.h" unsigned long arm_stack_top; +static unsigned long arm_head_bottom; +static unsigned long arm_barebox_size; static void *barebox_boarddata; static bool blob_is_fdt(const void *blob) @@ -116,6 +118,18 @@ static inline unsigned long arm_mem_boarddata(unsigned long membase, return mem; } +static int barebox_memory_areas_init(void) +{ + unsigned long start = arm_head_bottom; + unsigned long size = arm_mem_barebox_image(0, arm_stack_top, + arm_barebox_size) - + arm_head_bottom; + request_sdram_region("board data", start, size); + + return 0; +} +device_initcall(barebox_memory_areas_init); + __noreturn void barebox_non_pbl_start(unsigned long membase, unsigned long memsize, void *boarddata) { @@ -123,7 +137,6 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, unsigned long malloc_start, malloc_end; unsigned long barebox_size = barebox_image_size + ((unsigned long)&__bss_stop - (unsigned long)&__bss_start); - unsigned long arm_head_bottom; if (IS_ENABLED(CONFIG_RELOCATABLE)) { unsigned long barebox_base = arm_mem_barebox_image(membase, @@ -139,6 +152,9 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize); arm_stack_top = endmem; + arm_barebox_size = barebox_size; + arm_head_bottom = arm_mem_barebox_image(membase, endmem, + arm_barebox_size); if (IS_ENABLED(CONFIG_MMU_EARLY)) { unsigned long ttb = arm_mem_ttb(membase, endmem); @@ -175,6 +191,7 @@ __noreturn void barebox_non_pbl_start(unsigned long membase, name, mem); barebox_boarddata = memcpy((void *)mem, boarddata, totalsize); + arm_head_bottom = mem; } } -- cgit v1.2.3 From 6c42710ae03bd642f68220dea4dac60b6cbd6d23 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:31 +0100 Subject: arm: Add RAMOOPS memory area RAMOOPS is a driver that uses a reserved static memory region to store the data from the last panic or boot. This helps to debug crashes at the next boot while preserving the boot messages. To guarantee a memory area that is not altered by barebox or the kernel, this area is located at the end of the RAM right after barebox and before the STACK. This ensures that changing barebox sizes do not interfere with RAMOOPS. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- arch/arm/cpu/start.c | 6 ++++++ arch/arm/include/asm/barebox-arm.h | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index 4ad64b61e8..d03d1edfaf 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -118,6 +118,12 @@ static inline unsigned long arm_mem_boarddata(unsigned long membase, return mem; } +unsigned long arm_mem_ramoops_get(void) +{ + return arm_mem_ramoops(0, arm_stack_top); +} +EXPORT_SYMBOL_GPL(arm_mem_ramoops_get); + static int barebox_memory_areas_init(void) { unsigned long start = arm_head_bottom; diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h index 8d52882981..67133265d1 100644 --- a/arch/arm/include/asm/barebox-arm.h +++ b/arch/arm/include/asm/barebox-arm.h @@ -122,11 +122,23 @@ static inline unsigned long arm_mem_early_malloc_end(unsigned long membase, return arm_mem_ttb(membase, endmem); } +static inline unsigned long arm_mem_ramoops(unsigned long membase, + unsigned long endmem) +{ + endmem = arm_mem_ttb(membase, endmem); +#ifdef CONFIG_FS_PSTORE_RAMOOPS + endmem -= CONFIG_FS_PSTORE_RAMOOPS_SIZE; + endmem &= ~(SZ_4K - 1); /* Align to 4K */ +#endif + + return endmem; +} + static inline unsigned long arm_mem_barebox_image(unsigned long membase, unsigned long endmem, unsigned long size) { - endmem = arm_mem_ttb(membase, endmem); + endmem = arm_mem_ramoops(membase, endmem); if (IS_ENABLED(CONFIG_RELOCATABLE)) { endmem -= size; -- cgit v1.2.3 From 160d2ed0ba18b5d849ecc98dfde25e350928e7b8 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Tue, 8 Dec 2015 10:39:32 +0100 Subject: fs: Add pstore filesystem 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 Signed-off-by: Sascha Hauer --- 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 4a303b297a..093a23ba08 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 9217bc81ea..5413a9295c 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 46932057c1..2f952038d1 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 0000000000..0c6f136920 --- /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 0000000000..c4043e1a8f --- /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 0000000000..0e05d48ea0 --- /dev/null +++ b/fs/pstore/fs.c @@ -0,0 +1,280 @@ +/* + * Persistent Storage Barebox filesystem layer + * Copyright © 2015 Pengutronix, Markus Pargmann + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000000..0a8df1f4e2 --- /dev/null +++ b/fs/pstore/internal.h @@ -0,0 +1,19 @@ +#ifndef __PSTORE_INTERNAL_H__ +#define __PSTORE_INTERNAL_H__ + +#include +#include +#include + +#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 0000000000..dc2611f732 --- /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 + * + * 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 . + */ + +#define pr_fmt(fmt) "pstore: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000..dc31ed16f9 --- /dev/null +++ b/fs/pstore/ram.c @@ -0,0 +1,507 @@ +/* + * RAM Oops/Panic logger + * + * Copyright (C) 2010 Marco Stornelli + * Copyright (C) 2011 Kees Cook + * + * 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 . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000..d68d80900b --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 0000000000..a925e14397 --- /dev/null +++ b/include/linux/pstore.h @@ -0,0 +1,90 @@ +/* + * Persistent Storage - pstore.h + * + * Copyright (C) 2010 Intel Corporation + * + * 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 . + */ +#ifndef _LINUX_PSTORE_H +#define _LINUX_PSTORE_H + +#include +#include +#include + +/* 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 0000000000..5ef823a57b --- /dev/null +++ b/include/linux/pstore_ram.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Marco Stornelli + * Copyright (C) 2011 Kees Cook + * 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 +#include +#include + +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 -- cgit v1.2.3 From 9e617362fc300d31a4e7f6c5b72781b58c03f78a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 10 Dec 2015 16:37:34 +0100 Subject: Documentation: Document pstore/RAMOOPS Add some documentation about behaviour and setup of pstore/RAMOOPS. Signed-off-by: Markus Pargmann Signed-off-by: Sascha Hauer --- Documentation/filesystems/pstore.rst | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/filesystems/pstore.rst diff --git a/Documentation/filesystems/pstore.rst b/Documentation/filesystems/pstore.rst new file mode 100644 index 0000000000..74acd87ca0 --- /dev/null +++ b/Documentation/filesystems/pstore.rst @@ -0,0 +1,76 @@ +.. index:: pstore (filesystem) + +pstore filesystem with RAM backend (RAMOOPS) +============================================ + +Barebox supports the pstore filesystem known from the kernel. The main backend +implementation is RAM. All other backends are currently not implemented by +Barebox. + +pstore is a filesystem to store kernel log or kernel panic messages. These +messages are stored by the kernel in a specified RAM area which is never +overwritten by any user. This data can be accessed after a reboot through +/pstore in Barebox or the kernel. The pstore filesystem is automatically mounted +at boot:: + none on / type ramfs + none on /dev type devfs + none on /pstore type pstore + +pstore may add additional warnings during boot due to wrong ECCs (no data +written):: + + persistent_ram: found existing invalid buffer, size 791282217, start 1116786789 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 791282281, start 1133564005 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 791347753, start 1133564005 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 791347753, start 1133572197 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 774505001, start 1133564005 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 791282281, start 1133564005 + persistent_ram: uncorrectable error in header + persistent_ram: found existing invalid buffer, size 791282217, start 1133564005 + pstore: Registered ramoops as persistent store backend + ramoops: attached 0x200000@0x1fdf4000, ecc: 16/0 + +To use pstore/RAMOOPS both Barebox and Kernel have to be compiled with pstore +and RAM backend support. The kernel receives the parameters describing the +layout over the kernel command line. These parameters are automatically +generated by Barebox. You can change these parameters in Barebox menuconfig. The +RAMOOPS parameters for the Kernel are stored in the variable +global.linux.bootargs.ramoops:: + +To see where the RAMOOPS area is located, you can execute iomem in Barebox. The +RAMOOPS area is listed as 'persistent ram':: + + 0x10000000 - 0x1fffffff (size 0x10000000) ram0 + 0x17e7c0c0 - 0x1fcf817f (size 0x07e7c0c0) malloc space + 0x1fcf8180 - 0x1fcfffff (size 0x00007e80) board data + 0x1fd00000 - 0x1fd6eeff (size 0x0006ef00) barebox + 0x1fd6ef00 - 0x1fd88dff (size 0x00019f00) barebox data + 0x1fd88e00 - 0x1fd8c3db (size 0x000035dc) bss + 0x1fdf4000 - 0x1fe13fff (size 0x00020000) persistent ram + 0x1fe14000 - 0x1fe33fff (size 0x00020000) persistent ram + 0x1fe34000 - 0x1fe53fff (size 0x00020000) persistent ram + 0x1fe54000 - 0x1fe73fff (size 0x00020000) persistent ram + 0x1fe74000 - 0x1fe93fff (size 0x00020000) persistent ram + 0x1fe94000 - 0x1feb3fff (size 0x00020000) persistent ram + 0x1feb4000 - 0x1fed3fff (size 0x00020000) persistent ram + 0x1fed4000 - 0x1fef3fff (size 0x00020000) persistent ram + 0x1fef4000 - 0x1ff13fff (size 0x00020000) persistent ram + 0x1ff14000 - 0x1ff33fff (size 0x00020000) persistent ram + 0x1ff34000 - 0x1ff53fff (size 0x00020000) persistent ram + 0x1ff54000 - 0x1ff73fff (size 0x00020000) persistent ram + 0x1ff74000 - 0x1ff93fff (size 0x00020000) persistent ram + 0x1ff94000 - 0x1ffb3fff (size 0x00020000) persistent ram + 0x1ffb4000 - 0x1ffd3fff (size 0x00020000) persistent ram + 0x1ffd4000 - 0x1fff3fff (size 0x00020000) persistent ram + 0x1fff4000 - 0x1fff7fff (size 0x00004000) ttb + 0x1fff8000 - 0x1fffffff (size 0x00008000) stack + +All pstore files that could be found are added to the /pstore directory. This is +a read-only filesystem. If you disable the Kconfig option FS_PSTORE_RAMOOPS_RO, +the RAMOOPS area is reset and its ECC recalculated. But that does not allow any +writes from Barebox into that area. -- cgit v1.2.3