9
0
Fork 0

Merge branch 'for-next/pstore'

This commit is contained in:
Sascha Hauer 2016-01-11 13:11:07 +01:00
commit 70ef1cef5c
29 changed files with 2688 additions and 65 deletions

View File

@ -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.

View File

@ -21,6 +21,7 @@
#include <init.h>
#include <i2c/i2c.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <environment.h>
#include <mach/bbu.h>
#include <mach/imx6.h>
@ -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

View File

@ -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);

View File

@ -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)
@ -104,14 +106,48 @@ 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;
}
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;
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)
{
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);
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 +158,19 @@ __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 */
arm_barebox_size = barebox_size;
arm_head_bottom = arm_mem_barebox_image(membase, endmem,
arm_barebox_size);
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 +191,17 @@ __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);
arm_head_bottom = mem;
}
}
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

View File

@ -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);

View File

@ -94,25 +94,56 @@ 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)
{
endmem -= STACK_SIZE;
endmem -= SZ_32K; /* ttb */
endmem -= SZ_128K; /* early malloc */
endmem -= SZ_1M; /* place for barebox image */
return endmem - STACK_SIZE;
}
/*
* round down to make translating the objdump easier
*/
endmem &= ~(SZ_1M - 1);
static inline unsigned long arm_mem_ttb(unsigned long membase,
unsigned long endmem)
{
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_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_ramoops(membase, endmem);
if (IS_ENABLED(CONFIG_RELOCATABLE)) {
endmem -= size;
endmem &= ~(SZ_1M - 1);
}
return endmem;
}

View File

@ -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);

View File

@ -89,4 +89,6 @@ config FS_SMHFS
located on a debugging host connected to the target running
Barebox
source fs/pstore/Kconfig
endmenu

View File

@ -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/

86
fs/pstore/Kconfig Normal file
View File

@ -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

9
fs/pstore/Makefile Normal file
View File

@ -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

280
fs/pstore/fs.c Normal file
View File

@ -0,0 +1,280 @@
/*
* Persistent Storage Barebox filesystem layer
* Copyright © 2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <driver.h>
#include <fs.h>
#include <errno.h>
#include <fcntl.h>
#include <fs.h>
#include <malloc.h>
#include <init.h>
#include <linux/stat.h>
#include <linux/err.h>
#include <linux/pstore.h>
#include <libbb.h>
#include <rtc.h>
#include <libfile.h>
#include <linux/pstore.h>
#include "internal.h"
struct list_head allpstore = LIST_HEAD_INIT(allpstore);
struct pstore_private {
char name[PSTORE_NAMELEN];
struct list_head list;
struct pstore_info *psi;
enum pstore_type_id type;
u64 id;
int count;
ssize_t size;
ssize_t pos;
char data[];
};
/*
* Make a regular file in the root directory of our file system.
* Load it up with "size" bytes of data from "buf".
* Set the mtime & ctime to the date that this record was originally stored.
*/
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
char *data, bool compressed, size_t size,
struct pstore_info *psi)
{
struct pstore_private *private, *pos;
list_for_each_entry(pos, &allpstore, list) {
if (pos->type == type && pos->id == id && pos->psi == psi)
return -EEXIST;
}
private = xzalloc(sizeof(*private) + size);
if (!private)
return -ENOMEM;
private->type = type;
private->id = id;
private->count = count;
private->psi = psi;
switch (type) {
case PSTORE_TYPE_DMESG:
scnprintf(private->name, sizeof(private->name),
"dmesg-%s-%lld%s", psname, id,
compressed ? ".enc.z" : "");
break;
case PSTORE_TYPE_CONSOLE:
scnprintf(private->name, sizeof(private->name),
"console-%s-%lld", psname, id);
break;
case PSTORE_TYPE_FTRACE:
scnprintf(private->name, sizeof(private->name),
"ftrace-%s-%lld", psname, id);
break;
case PSTORE_TYPE_MCE:
scnprintf(private->name, sizeof(private->name),
"mce-%s-%lld", psname, id);
break;
case PSTORE_TYPE_PPC_RTAS:
scnprintf(private->name, sizeof(private->name),
"rtas-%s-%lld", psname, id);
break;
case PSTORE_TYPE_PPC_OF:
scnprintf(private->name, sizeof(private->name),
"powerpc-ofw-%s-%lld", psname, id);
break;
case PSTORE_TYPE_PPC_COMMON:
scnprintf(private->name, sizeof(private->name),
"powerpc-common-%s-%lld", psname, id);
break;
case PSTORE_TYPE_PMSG:
scnprintf(private->name, sizeof(private->name),
"pmsg-%s-%lld", psname, id);
break;
case PSTORE_TYPE_UNKNOWN:
scnprintf(private->name, sizeof(private->name),
"unknown-%s-%lld", psname, id);
break;
default:
scnprintf(private->name, sizeof(private->name),
"type%d-%s-%lld", type, psname, id);
break;
}
memcpy(private->data, data, size);
private->size = size;
list_add(&private->list, &allpstore);
return 0;
}
static struct pstore_private *pstore_get_by_name(struct list_head *head,
const char *name)
{
struct pstore_private *d;
if (!name)
return NULL;
list_for_each_entry(d, head, list) {
if (strcmp(d->name, name) == 0)
return d;
}
return NULL;
}
static int pstore_open(struct device_d *dev, FILE *file, const char *filename)
{
struct list_head *head = dev->priv;
struct pstore_private *d;
if (filename[0] == '/')
filename++;
d = pstore_get_by_name(head, filename);
if (!d)
return -EINVAL;
file->size = d->size;
file->priv = d;
d->pos = 0;
return 0;
}
static int pstore_close(struct device_d *dev, FILE *file)
{
return 0;
}
static int pstore_read(struct device_d *dev, FILE *file, void *buf,
size_t insize)
{
struct pstore_private *d = file->priv;
memcpy(buf, &d->data[d->pos], insize);
d->pos += insize;
return insize;
}
static loff_t pstore_lseek(struct device_d *dev, FILE *file, loff_t pos)
{
struct pstore_private *d = file->priv;
d->pos = pos;
return pos;
}
static DIR *pstore_opendir(struct device_d *dev, const char *pathname)
{
DIR *dir;
dir = xzalloc(sizeof(DIR));
if (list_empty(&allpstore))
return dir;
dir->priv = list_first_entry(&allpstore, struct pstore_private, list);
return dir;
}
static struct dirent *pstore_readdir(struct device_d *dev, DIR *dir)
{
struct pstore_private *d = dir->priv;
if (!d || &d->list == &allpstore)
return NULL;
strcpy(dir->d.d_name, d->name);
dir->priv = list_entry(d->list.next, struct pstore_private, list);
return &dir->d;
}
static int pstore_closedir(struct device_d *dev, DIR *dir)
{
free(dir);
return 0;
}
static int pstore_stat(struct device_d *dev, const char *filename,
struct stat *s)
{
struct pstore_private *d;
if (filename[0] == '/')
filename++;
d = pstore_get_by_name(&allpstore, filename);
if (!d)
return -EINVAL;
s->st_size = d->size;
s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
return 0;
}
static void pstore_remove(struct device_d *dev)
{
struct pstore_private *d, *tmp;
list_for_each_entry_safe(d, tmp, &allpstore, list) {
free(d);
}
}
static int pstore_probe(struct device_d *dev)
{
struct list_head *priv = &allpstore;
dev->priv = priv;
dev_dbg(dev, "mounted pstore\n");
return 0;
}
static struct fs_driver_d pstore_driver = {
.open = pstore_open,
.close = pstore_close,
.read = pstore_read,
.lseek = pstore_lseek,
.opendir = pstore_opendir,
.readdir = pstore_readdir,
.closedir = pstore_closedir,
.stat = pstore_stat,
.flags = FS_DRIVER_NO_DEV,
.type = filetype_uimage,
.drv = {
.probe = pstore_probe,
.remove = pstore_remove,
.name = "pstore",
}
};
static int pstore_init(void)
{
return register_fs_driver(&pstore_driver);
}
coredevice_initcall(pstore_init);

19
fs/pstore/internal.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __PSTORE_INTERNAL_H__
#define __PSTORE_INTERNAL_H__
#include <linux/types.h>
#include <linux/time.h>
#include <linux/pstore.h>
#define PSTORE_NAMELEN 64
extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
int count, char *data, bool compressed,
size_t size, struct pstore_info *psi);
extern int pstore_is_mounted(void);
#endif

138
fs/pstore/platform.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Persistent Storage - platform driver interface parts.
*
* Copyright (C) 2007-2008 Google, Inc.
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "pstore: " fmt
#include <linux/types.h>
#include <linux/pstore.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <malloc.h>
#include <printk.h>
#include <module.h>
#include "internal.h"
struct pstore_info *psinfo;
static char *backend;
/* How much of the console log to snapshot */
static unsigned long kmsg_bytes = 10240;
void pstore_set_kmsg_bytes(int bytes)
{
kmsg_bytes = bytes;
}
static int pstore_write_compat(enum pstore_type_id type,
enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count,
bool compressed, size_t size,
struct pstore_info *psi)
{
return psi->write_buf(type, reason, id, part, psinfo->buf, compressed,
size, psi);
}
/*
* platform specific persistent storage driver registers with
* us here. If pstore is already mounted, call the platform
* read function right away to populate the file system. If not
* then the pstore mount code will call us later to fill out
* the file system.
*
* Register with kmsg_dump to save last part of console log on panic.
*/
int pstore_register(struct pstore_info *psi)
{
if (backend && strcmp(backend, psi->name))
return -EPERM;
spin_lock(&pstore_lock);
if (psinfo) {
spin_unlock(&pstore_lock);
return -EBUSY;
}
if (!psi->write)
psi->write = pstore_write_compat;
psinfo = psi;
mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock);
pstore_get_records(0);
pr_info("Registered %s as persistent store backend\n", psi->name);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
/*
* Read all the records from the persistent store. Create
* files in our filesystem. Don't warn about -EEXIST errors
* when we are re-scanning the backing store looking to add new
* error records.
*/
void pstore_get_records(int quiet)
{
struct pstore_info *psi = psinfo;
char *buf = NULL;
ssize_t size;
u64 id;
int count;
enum pstore_type_id type;
int failed = 0, rc;
bool compressed;
int unzipped_len = -1;
if (!psi)
return;
mutex_lock(&psi->read_mutex);
if (psi->open && psi->open(psi))
goto out;
while ((size = psi->read(&id, &type, &count, &buf, &compressed,
psi)) > 0) {
if (compressed && (type == PSTORE_TYPE_DMESG)) {
pr_err("barebox does not have ramoops compression support\n");
continue;
}
rc = pstore_mkfile(type, psi->name, id, count, buf,
compressed, (size_t)size, psi);
if (unzipped_len < 0) {
/* Free buffer other than big oops */
kfree(buf);
buf = NULL;
} else
unzipped_len = -1;
if (rc && (rc != -EEXIST || !quiet))
failed++;
}
if (psi->close)
psi->close(psi);
out:
mutex_unlock(&psi->read_mutex);
if (failed)
pr_warn("failed to load %d record(s) from '%s'\n",
failed, psi->name);
}

507
fs/pstore/ram.c Normal file
View File

@ -0,0 +1,507 @@
/*
* RAM Oops/Panic logger
*
* Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
* Copyright (C) 2011 Kees Cook <keescook@chromium.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/pstore.h>
#include <linux/time.h>
#include <linux/ioport.h>
#include <linux/compiler.h>
#include <linux/pstore_ram.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/log2.h>
#include <malloc.h>
#include <printk.h>
#include <stdio.h>
#include <globalvar.h>
#include <init.h>
#include <common.h>
#define RAMOOPS_KERNMSG_HDR "===="
#define MIN_MEM_SIZE 4096UL
static const ulong record_size = CONFIG_FS_PSTORE_RAMOOPS_RECORD_SIZE;
static const ulong ramoops_console_size = CONFIG_FS_PSTORE_RAMOOPS_CONSOLE_SIZE;
static const ulong ramoops_ftrace_size = CONFIG_FS_PSTORE_RAMOOPS_FTRACE_SIZE;
static const ulong ramoops_pmsg_size = CONFIG_FS_PSTORE_RAMOOPS_PMSG_SIZE;
static const ulong mem_size = CONFIG_FS_PSTORE_RAMOOPS_SIZE;
static const int dump_oops = 1;
static const int ramoops_ecc = CONFIG_FS_PSTORE_ECC_SIZE;
struct ramoops_context {
struct persistent_ram_zone **przs;
struct persistent_ram_zone *cprz;
struct persistent_ram_zone *fprz;
struct persistent_ram_zone *mprz;
phys_addr_t phys_addr;
unsigned long size;
unsigned int memtype;
size_t record_size;
size_t console_size;
size_t ftrace_size;
size_t pmsg_size;
int dump_oops;
struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt;
unsigned int dump_write_cnt;
/* _read_cnt need clear on ramoops_pstore_open */
unsigned int dump_read_cnt;
unsigned int console_read_cnt;
unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
struct pstore_info pstore;
};
static struct ramoops_platform_data *dummy_data;
static int ramoops_pstore_open(struct pstore_info *psi)
{
struct ramoops_context *cxt = psi->data;
cxt->dump_read_cnt = 0;
cxt->console_read_cnt = 0;
cxt->ftrace_read_cnt = 0;
cxt->pmsg_read_cnt = 0;
return 0;
}
static struct persistent_ram_zone *
ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
u64 *id,
enum pstore_type_id *typep, enum pstore_type_id type,
bool update)
{
struct persistent_ram_zone *prz;
int i = (*c)++;
if (i >= max)
return NULL;
prz = przs[i];
if (!prz)
return NULL;
/* Update old/shadowed buffer. */
if (update)
persistent_ram_save_old(prz);
if (!persistent_ram_old_size(prz))
return NULL;
*typep = type;
*id = i;
return prz;
}
static bool prz_ok(struct persistent_ram_zone *prz)
{
return !!prz && !!(persistent_ram_old_size(prz) +
persistent_ram_ecc_string(prz, NULL, 0));
}
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, char **buf, bool *compressed,
struct pstore_info *psi)
{
ssize_t size;
ssize_t ecc_notice_size;
struct ramoops_context *cxt = psi->data;
struct persistent_ram_zone *prz;
prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, id, type,
PSTORE_TYPE_DMESG, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
1, id, type, PSTORE_TYPE_CONSOLE, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
1, id, type, PSTORE_TYPE_FTRACE, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, id, type, PSTORE_TYPE_PMSG, 0);
if (!prz_ok(prz))
return 0;
if (!persistent_ram_old(prz))
return 0;
size = persistent_ram_old_size(prz);
/* ECC correction notice */
ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
*buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL);
if (*buf == NULL)
return -ENOMEM;
memcpy(*buf, (char *)persistent_ram_old(prz), size);
persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
return size + ecc_notice_size;
}
static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
enum kmsg_dump_reason reason,
u64 *id, unsigned int part,
const char *buf,
bool compressed, size_t size,
struct pstore_info *psi)
{
struct ramoops_context *cxt = psi->data;
struct persistent_ram_zone *prz;
if (type == PSTORE_TYPE_CONSOLE) {
if (!cxt->cprz)
return -ENOMEM;
persistent_ram_write(cxt->cprz, buf, size);
return 0;
} else if (type == PSTORE_TYPE_FTRACE) {
if (!cxt->fprz)
return -ENOMEM;
persistent_ram_write(cxt->fprz, buf, size);
return 0;
} else if (type == PSTORE_TYPE_PMSG) {
if (!cxt->mprz)
return -ENOMEM;
persistent_ram_write(cxt->mprz, buf, size);
return 0;
}
if (type != PSTORE_TYPE_DMESG)
return -EINVAL;
/* Explicitly only take the first part of any new crash.
* If our buffer is larger than kmsg_bytes, this can never happen,
* and if our buffer is smaller than kmsg_bytes, we don't want the
* report split across multiple records.
*/
if (part != 1)
return -ENOSPC;
if (!cxt->przs)
return -ENOSPC;
prz = cxt->przs[cxt->dump_write_cnt];
persistent_ram_write(prz, buf, size);
cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt;
return 0;
}
static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
struct pstore_info *psi)
{
struct ramoops_context *cxt = psi->data;
struct persistent_ram_zone *prz;
switch (type) {
case PSTORE_TYPE_DMESG:
if (id >= cxt->max_dump_cnt)
return -EINVAL;
prz = cxt->przs[id];
break;
case PSTORE_TYPE_CONSOLE:
prz = cxt->cprz;
break;
case PSTORE_TYPE_FTRACE:
prz = cxt->fprz;
break;
case PSTORE_TYPE_PMSG:
prz = cxt->mprz;
break;
default:
return -EINVAL;
}
persistent_ram_free_old(prz);
persistent_ram_zap(prz);
return 0;
}
static struct ramoops_context oops_cxt = {
.pstore = {
.name = "ramoops",
.open = ramoops_pstore_open,
.read = ramoops_pstore_read,
.write_buf = ramoops_pstore_write_buf,
.erase = ramoops_pstore_erase,
},
};
static void ramoops_free_przs(struct ramoops_context *cxt)
{
int i;
cxt->max_dump_cnt = 0;
if (!cxt->przs)
return;
for (i = 0; !IS_ERR_OR_NULL(cxt->przs[i]); i++)
persistent_ram_free(cxt->przs[i]);
kfree(cxt->przs);
}
static int ramoops_init_przs(struct ramoops_context *cxt, phys_addr_t *paddr,
size_t dump_mem_sz)
{
int err = -ENOMEM;
int i;
if (!cxt->record_size)
return 0;
if (*paddr + dump_mem_sz - cxt->phys_addr > cxt->size) {
pr_err("no room for dumps\n");
return -ENOMEM;
}
cxt->max_dump_cnt = dump_mem_sz / cxt->record_size;
if (!cxt->max_dump_cnt)
return -ENOMEM;
cxt->przs = kzalloc(sizeof(*cxt->przs) * cxt->max_dump_cnt,
GFP_KERNEL);
if (!cxt->przs) {
pr_err("failed to initialize a prz array for dumps\n");
goto fail_prz;
}
for (i = 0; i < cxt->max_dump_cnt; i++) {
size_t sz = cxt->record_size;
cxt->przs[i] = persistent_ram_new(*paddr, sz, 0,
&cxt->ecc_info,
cxt->memtype);
if (IS_ERR(cxt->przs[i])) {
err = PTR_ERR(cxt->przs[i]);
pr_err("failed to request mem region (0x%zx@0x%llx): %d\n",
sz, (unsigned long long)*paddr, err);
goto fail_prz;
}
*paddr += sz;
}
return 0;
fail_prz:
ramoops_free_przs(cxt);
return err;
}
static int ramoops_init_prz(struct ramoops_context *cxt,
struct persistent_ram_zone **prz,
phys_addr_t *paddr, size_t sz, u32 sig)
{
if (!sz)
return 0;
if (*paddr + sz - cxt->phys_addr > cxt->size) {
pr_err("no room for mem region (0x%zx@0x%llx) in (0x%lx@0x%llx)\n",
sz, (unsigned long long)*paddr,
cxt->size, (unsigned long long)cxt->phys_addr);
return -ENOMEM;
}
*prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info,
cxt->memtype);
if (IS_ERR(*prz)) {
int err = PTR_ERR(*prz);
pr_err("failed to request mem region (0x%zx@0x%llx): %d\n",
sz, (unsigned long long)*paddr, err);
return err;
}
persistent_ram_zap(*prz);
*paddr += sz;
return 0;
}
static int ramoops_probe(struct ramoops_platform_data *pdata)
{
struct ramoops_context *cxt = &oops_cxt;
size_t dump_mem_sz;
phys_addr_t paddr;
int err = -EINVAL;
char kernelargs[512];
/* Only a single ramoops area allowed at a time, so fail extra
* probes.
*/
if (cxt->max_dump_cnt)
goto fail_out;
if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
!pdata->ftrace_size && !pdata->pmsg_size)) {
pr_err("The memory size and the record/console size must be "
"non-zero\n");
goto fail_out;
}
if (pdata->record_size && !is_power_of_2(pdata->record_size))
pdata->record_size = rounddown_pow_of_two(pdata->record_size);
if (pdata->console_size && !is_power_of_2(pdata->console_size))
pdata->console_size = rounddown_pow_of_two(pdata->console_size);
if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size))
pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size);
cxt->size = pdata->mem_size;
cxt->phys_addr = pdata->mem_address;
cxt->memtype = pdata->mem_type;
cxt->record_size = pdata->record_size;
cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops;
cxt->ecc_info = pdata->ecc_info;
paddr = cxt->phys_addr;
dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
- cxt->pmsg_size;
err = ramoops_init_przs(cxt, &paddr, dump_mem_sz);
if (err)
goto fail_out;
err = ramoops_init_prz(cxt, &cxt->cprz, &paddr,
cxt->console_size, 0);
if (err)
goto fail_init_cprz;
err = ramoops_init_prz(cxt, &cxt->fprz, &paddr, cxt->ftrace_size, 0);
if (err)
goto fail_init_fprz;
err = ramoops_init_prz(cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0);
if (err)
goto fail_init_mprz;
cxt->pstore.data = cxt;
/*
* Console can handle any buffer size, so prefer LOG_LINE_MAX. If we
* have to handle dumps, we must have at least record_size buffer. And
* for ftrace, bufsize is irrelevant (if bufsize is 0, buf will be
* ZERO_SIZE_PTR).
*/
if (cxt->console_size)
cxt->pstore.bufsize = 1024; /* LOG_LINE_MAX */
cxt->pstore.bufsize = max(cxt->record_size, cxt->pstore.bufsize);
cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL);
spin_lock_init(&cxt->pstore.buf_lock);
if (!cxt->pstore.buf) {
pr_err("cannot allocate pstore buffer\n");
err = -ENOMEM;
goto fail_clear;
}
err = pstore_register(&cxt->pstore);
if (err) {
pr_err("registering with pstore failed\n");
goto fail_buf;
}
pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n",
cxt->size, (unsigned long long)cxt->phys_addr,
cxt->ecc_info.ecc_size, cxt->ecc_info.block_size);
scnprintf(kernelargs, sizeof(kernelargs),
"ramoops.record_size=0x%x "
"ramoops.console_size=0x%x "
"ramoops.ftrace_size=0x%x "
"ramoops.pmsg_size=0x%x "
"ramoops.mem_address=0x%llx "
"ramoops.mem_size=0x%lx "
"ramoops.ecc=%d",
cxt->record_size,
cxt->console_size,
cxt->ftrace_size,
cxt->pmsg_size,
(unsigned long long)cxt->phys_addr,
mem_size,
ramoops_ecc);
globalvar_add_simple("linux.bootargs.ramoops", kernelargs);
of_add_reserve_entry(cxt->phys_addr, cxt->phys_addr + mem_size);
return 0;
fail_buf:
kfree(cxt->pstore.buf);
fail_clear:
cxt->pstore.bufsize = 0;
kfree(cxt->mprz);
fail_init_mprz:
kfree(cxt->fprz);
fail_init_fprz:
kfree(cxt->cprz);
fail_init_cprz:
ramoops_free_przs(cxt);
fail_out:
return err;
}
unsigned long arm_mem_ramoops_get(void);
static void ramoops_register_dummy(void)
{
dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL);
if (!dummy_data) {
pr_info("could not allocate pdata\n");
return;
}
dummy_data->mem_size = mem_size;
dummy_data->mem_address = arm_mem_ramoops_get();
dummy_data->mem_type = 0;
dummy_data->record_size = record_size;
dummy_data->console_size = ramoops_console_size;
dummy_data->ftrace_size = ramoops_ftrace_size;
dummy_data->pmsg_size = ramoops_pmsg_size;
dummy_data->dump_oops = dump_oops;
/*
* For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
* (using 1 byte for ECC isn't much of use anyway).
*/
dummy_data->ecc_info.ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc;
ramoops_probe(dummy_data);
}
static int __init ramoops_init(void)
{
ramoops_register_dummy();
return 0;
}
postcore_initcall(ramoops_init);

426
fs/pstore/ram_core.c Normal file
View File

@ -0,0 +1,426 @@
/*
* Copyright (C) 2012 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "persistent_ram: " fmt
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/rslib.h>
#include <linux/pstore_ram.h>
#include <linux/string.h>
#include <linux/rslib.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <common.h>
struct persistent_ram_buffer {
uint32_t sig;
resource_size_t start;
resource_size_t size;
uint8_t data[0];
};
#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
static inline size_t buffer_size(struct persistent_ram_zone *prz)
{
return prz->buffer->size;
}
static inline size_t buffer_start(struct persistent_ram_zone *prz)
{
return prz->buffer->start;
}
/* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{
int old;
int new;
old = prz->buffer->start;
new = old + a;
while (unlikely(new >= prz->buffer_size))
new -= prz->buffer_size;
prz->buffer->start = new;
return old;
}
/* increase the size counter until it hits the max size */
static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
{
size_t old;
size_t new;
old = prz->buffer->size;
if (old == prz->buffer_size)
return;
new = old + a;
if (new > prz->buffer_size)
new = prz->buffer_size;
prz->buffer->size = new;
}
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
uint8_t *data, size_t len, uint8_t *ecc)
{
int i;
uint16_t par[prz->ecc_info.ecc_size];
/* Initialize the parity buffer */
memset(par, 0, sizeof(par));
encode_rs8(prz->rs_decoder, data, len, par, 0);
for (i = 0; i < prz->ecc_info.ecc_size; i++)
ecc[i] = par[i];
}
static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
void *data, size_t len, uint8_t *ecc)
{
int i;
uint16_t par[prz->ecc_info.ecc_size];
for (i = 0; i < prz->ecc_info.ecc_size; i++)
par[i] = ecc[i];
return decode_rs8(prz->rs_decoder, data, par, len,
NULL, 0, NULL, 0, NULL);
}
static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
unsigned int start, unsigned int count)
{
struct persistent_ram_buffer *buffer = prz->buffer;
uint8_t *buffer_end = buffer->data + prz->buffer_size;
uint8_t *block;
uint8_t *par;
int ecc_block_size = prz->ecc_info.block_size;
int ecc_size = prz->ecc_info.ecc_size;
int size = ecc_block_size;
if (!ecc_size)
return;
block = buffer->data + (start & ~(ecc_block_size - 1));
par = prz->par_buffer + (start / ecc_block_size) * ecc_size;
do {
if (block + ecc_block_size > buffer_end)
size = buffer_end - block;
persistent_ram_encode_rs8(prz, block, size, par);
block += ecc_block_size;
par += ecc_size;
} while (block < buffer->data + start + count);
}
static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
{
struct persistent_ram_buffer *buffer = prz->buffer;
if (!prz->ecc_info.ecc_size)
return;
persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
prz->par_header);
}
static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
{
struct persistent_ram_buffer *buffer = prz->buffer;
uint8_t *block;
uint8_t *par;
if (!prz->ecc_info.ecc_size)
return;
block = buffer->data;
par = prz->par_buffer;
while (block < buffer->data + buffer_size(prz)) {
int numerr;
int size = prz->ecc_info.block_size;
if (block + size > buffer->data + prz->buffer_size)
size = buffer->data + prz->buffer_size - block;
numerr = persistent_ram_decode_rs8(prz, block, size, par);
if (numerr > 0) {
pr_debug("error in block %p, %d\n", block, numerr);
prz->corrected_bytes += numerr;
} else if (numerr < 0) {
pr_debug("uncorrectable error in block %p\n", block);
prz->bad_blocks++;
}
block += prz->ecc_info.block_size;
par += prz->ecc_info.ecc_size;
}
}
static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
struct persistent_ram_ecc_info *ecc_info)
{
int numerr;
struct persistent_ram_buffer *buffer = prz->buffer;
int ecc_blocks;
size_t ecc_total;
if (!ecc_info || !ecc_info->ecc_size)
return 0;
prz->ecc_info.block_size = ecc_info->block_size ?: 128;
prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16;
prz->ecc_info.symsize = ecc_info->symsize ?: 8;
prz->ecc_info.poly = ecc_info->poly ?: 0x11d;
ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size,
prz->ecc_info.block_size +
prz->ecc_info.ecc_size);
ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size;
if (ecc_total >= prz->buffer_size) {
pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
__func__, prz->ecc_info.ecc_size,
ecc_total, prz->buffer_size);
return -EINVAL;
}
prz->buffer_size -= ecc_total;
prz->par_buffer = buffer->data + prz->buffer_size;
prz->par_header = prz->par_buffer +
ecc_blocks * prz->ecc_info.ecc_size;
/*
* first consecutive root is 0
* primitive element to generate roots = 1
*/
prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly,
0, 1, prz->ecc_info.ecc_size);
if (prz->rs_decoder == NULL) {
pr_info("init_rs failed\n");
return -EINVAL;
}
prz->corrected_bytes = 0;
prz->bad_blocks = 0;
numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
prz->par_header);
if (numerr > 0) {
pr_info("error in header, %d\n", numerr);
prz->corrected_bytes += numerr;
} else if (numerr < 0) {
pr_info("uncorrectable error in header\n");
prz->bad_blocks++;
}
return 0;
}
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
char *str, size_t len)
{
ssize_t ret;
if (!prz->ecc_info.ecc_size)
return 0;
if (prz->corrected_bytes || prz->bad_blocks)
ret = snprintf(str, len, ""
"\n%d Corrected bytes, %d unrecoverable blocks\n",
prz->corrected_bytes, prz->bad_blocks);
else
ret = snprintf(str, len, "\nNo errors detected\n");
return ret;
}
static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
const void *s, unsigned int start, unsigned int count)
{
struct persistent_ram_buffer *buffer = prz->buffer;
memcpy(buffer->data + start, s, count);
persistent_ram_update_ecc(prz, start, count);
}
void persistent_ram_save_old(struct persistent_ram_zone *prz)
{
struct persistent_ram_buffer *buffer = prz->buffer;
size_t size = buffer_size(prz);
size_t start = buffer_start(prz);
if (!size)
return;
if (!prz->old_log) {
persistent_ram_ecc_old(prz);
prz->old_log = kmalloc(size, GFP_KERNEL);
}
if (!prz->old_log) {
pr_err("failed to allocate buffer\n");
return;
}
prz->old_log_size = size;
memcpy(prz->old_log, &buffer->data[start], size - start);
memcpy(prz->old_log + size - start, &buffer->data[0], start);
}
int notrace persistent_ram_write(struct persistent_ram_zone *prz,
const void *s, unsigned int count)
{
int rem;
int c = count;
size_t start;
if (unlikely(c > prz->buffer_size)) {
s += c - prz->buffer_size;
c = prz->buffer_size;
}
buffer_size_add(prz, c);
start = buffer_start_add(prz, c);
rem = prz->buffer_size - start;
if (unlikely(rem < c)) {
persistent_ram_update(prz, s, start, rem);
s += rem;
c -= rem;
start = 0;
}
persistent_ram_update(prz, s, start, c);
persistent_ram_update_header_ecc(prz);
return count;
}
size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
{
return prz->old_log_size;
}
void *persistent_ram_old(struct persistent_ram_zone *prz)
{
return prz->old_log;
}
void persistent_ram_free_old(struct persistent_ram_zone *prz)
{
kfree(prz->old_log);
prz->old_log = NULL;
prz->old_log_size = 0;
}
#ifdef CONFIG_FS_PSTORE_RAMOOPS_RO
void persistent_ram_zap(struct persistent_ram_zone *prz)
{
}
#else
void persistent_ram_zap(struct persistent_ram_zone *prz)
{
prz->buffer->start = 0;
prz->buffer->size = 0;
persistent_ram_update_header_ecc(prz);
}
#endif /* CONFIG_PSTORE_RAMOOPS_RO */
static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
struct persistent_ram_zone *prz, int memtype)
{
prz->res = request_sdram_region("persistent ram", start, size);
if (!prz->res)
return -ENOMEM;
prz->paddr = start;
prz->size = size;
prz->buffer = (void *)start;
prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
return 0;
}
static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
struct persistent_ram_ecc_info *ecc_info)
{
int ret;
ret = persistent_ram_init_ecc(prz, ecc_info);
if (ret)
return ret;
sig ^= PERSISTENT_RAM_SIG;
if (prz->buffer->sig == sig) {
if (buffer_size(prz) > prz->buffer_size ||
buffer_start(prz) > buffer_size(prz))
pr_info("found existing invalid buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
else {
pr_debug("found existing buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
persistent_ram_save_old(prz);
return 0;
}
} else {
pr_debug("no valid data in buffer (sig = 0x%08x)\n",
prz->buffer->sig);
}
prz->buffer->sig = sig;
persistent_ram_zap(prz);
return 0;
}
void persistent_ram_free(struct persistent_ram_zone *prz)
{
if (!prz)
return;
if (prz->res) {
release_sdram_region(prz->res);
prz->res = NULL;
}
persistent_ram_free_old(prz);
kfree(prz);
}
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype)
{
struct persistent_ram_zone *prz;
int ret = -ENOMEM;
prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
if (!prz) {
pr_err("failed to allocate persistent ram zone\n");
goto err;
}
ret = persistent_ram_buffer_map(start, size, prz, memtype);
if (ret)
goto err;
ret = persistent_ram_post_init(prz, sig, ecc_info);
if (ret)
goto err;
return prz;
err:
persistent_ram_free(prz);
return ERR_PTR(ret);
}

View File

@ -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

90
include/linux/pstore.h Normal file
View File

@ -0,0 +1,90 @@
/*
* Persistent Storage - pstore.h
*
* Copyright (C) 2010 Intel Corporation <tony.luck@intel.com>
*
* This code is the generic layer to export data records from platform
* level persistent storage via a file system.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _LINUX_PSTORE_H
#define _LINUX_PSTORE_H
#include <linux/time.h>
#include <linux/types.h>
#include <asm-generic/errno.h>
/* types */
enum pstore_type_id {
PSTORE_TYPE_DMESG = 0,
PSTORE_TYPE_MCE = 1,
PSTORE_TYPE_CONSOLE = 2,
PSTORE_TYPE_FTRACE = 3,
/* PPC64 partition types */
PSTORE_TYPE_PPC_RTAS = 4,
PSTORE_TYPE_PPC_OF = 5,
PSTORE_TYPE_PPC_COMMON = 6,
PSTORE_TYPE_PMSG = 7,
PSTORE_TYPE_UNKNOWN = 255
};
enum kmsg_dump_reason {
KMSG_DUMP_UNDEF,
};
struct module;
struct pstore_info {
struct module *owner;
char *name;
char *buf;
size_t bufsize;
int flags;
int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
int *count, char **buf, bool *compressed,
struct pstore_info *psi);
int (*write)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi);
int (*write_buf)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, const char *buf, bool compressed,
size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id,
int count, struct pstore_info *psi);
void *data;
};
#define PSTORE_FLAGS_FRAGILE 1
#ifdef CONFIG_FS_PSTORE
extern int pstore_register(struct pstore_info *);
extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
#else
static inline int
pstore_register(struct pstore_info *psi)
{
return -ENODEV;
}
static inline bool
pstore_cannot_block_path(enum kmsg_dump_reason reason)
{
return false;
}
#endif
#endif /*_LINUX_PSTORE_H*/

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
* Copyright (C) 2011 Kees Cook <keescook@chromium.org>
* Copyright (C) 2011 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __LINUX_PSTORE_RAM_H__
#define __LINUX_PSTORE_RAM_H__
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/types.h>
struct persistent_ram_buffer;
struct rs_control;
struct persistent_ram_ecc_info {
int block_size;
int ecc_size;
int symsize;
int poly;
};
struct persistent_ram_zone {
phys_addr_t paddr;
size_t size;
struct persistent_ram_buffer *buffer;
size_t buffer_size;
struct resource *res;
/* ECC correction */
char *par_buffer;
char *par_header;
struct rs_control *rs_decoder;
int corrected_bytes;
int bad_blocks;
struct persistent_ram_ecc_info ecc_info;
char *old_log;
size_t old_log_size;
};
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
u32 sig, struct persistent_ram_ecc_info *ecc_info,
unsigned int memtype);
void persistent_ram_free(struct persistent_ram_zone *prz);
void persistent_ram_zap(struct persistent_ram_zone *prz);
int persistent_ram_write(struct persistent_ram_zone *prz, const void *s,
unsigned int count);
void persistent_ram_save_old(struct persistent_ram_zone *prz);
size_t persistent_ram_old_size(struct persistent_ram_zone *prz);
void *persistent_ram_old(struct persistent_ram_zone *prz);
void persistent_ram_free_old(struct persistent_ram_zone *prz);
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
char *str, size_t len);
/*
* Ramoops platform data
* @mem_size memory size for ramoops
* @mem_address physical memory address to contain ramoops
*/
struct ramoops_platform_data {
unsigned long mem_size;
unsigned long mem_address;
unsigned int mem_type;
unsigned long record_size;
unsigned long console_size;
unsigned long ftrace_size;
unsigned long pmsg_size;
int dump_oops;
struct persistent_ram_ecc_info ecc_info;
};
#endif

103
include/linux/rslib.h Normal file
View File

@ -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 <linux/list.h>
/**
* struct rs_control - rs control structure
*
* @mm: Bits per symbol
* @nn: Symbols per block (= (1<<mm)-1)
* @alpha_to: log lookup table
* @index_of: Antilog lookup table
* @genpoly: Generator polynomial
* @nroots: Number of generator roots = number of parity symbols
* @fcr: First consecutive root, index form
* @prim: Primitive element, index form
* @iprim: prim-th root of 1, index form
* @gfpoly: The primitive generator polynominal
* @gffunc: Function to generate the field, if non-canonical representation
* @users: Users of this structure
* @list: List entry for the rs control list
*/
struct rs_control {
int mm;
int nn;
uint16_t *alpha_to;
uint16_t *index_of;
uint16_t *genpoly;
int nroots;
int fcr;
int prim;
int iprim;
int gfpoly;
int (*gffunc)(int);
int users;
struct list_head list;
};
#ifdef CONFIG_REED_SOLOMON
/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit */
int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
uint16_t invmsk);
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);
/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit */
int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
uint16_t invmsk);
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);
#endif /* CONFIG_REED_SOLOMON */
/* Create or get a matching rs control structure */
struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim,
int nroots);
struct rs_control *init_rs_non_canonical(int symsize, int (*func)(int),
int fcr, int prim, int nroots);
/* Release a rs control structure */
void free_rs(struct rs_control *rs);
/** modulo replacement for galois field arithmetics
*
* @rs: the rs control structure
* @x: the value to reduce
*
* where
* rs->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

View File

@ -1,6 +1,8 @@
#ifndef __PRINTK_H
#define __PRINTK_H
#include <linux/list.h>
#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, ...)

View File

@ -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);

View File

@ -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

View File

@ -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/

View File

@ -0,0 +1,6 @@
#
# This is a modified version of reed solomon lib,
#
obj-$(CONFIG_REED_SOLOMON) += reed_solomon.o

View File

@ -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(&reg[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;
}

View File

@ -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;
}

View File

@ -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 <linux/kernel.h>
#include <linux/rslib.h>
#include <malloc.h>
#include <module.h>
#include <linux/string.h>
#include <stdio.h>
#include <asm-generic/errno.h>
/* 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<<symsize))
return NULL;
if (prim <= 0 || prim >= (1<<symsize))
return NULL;
if (nroots < 0 || nroots >= (1<<symsize))
return NULL;
/* Walk through the list and look for a matching entry */
list_for_each(tmp, &rslist) {
rs = list_entry(tmp, struct rs_control, list);
if (symsize != rs->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");

View File

@ -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)
{