diff --git a/Documentation/filesystems/pstore.rst b/Documentation/filesystems/pstore.rst new file mode 100644 index 0000000..74acd87 --- /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. diff --git a/arch/arm/boards/karo-tx6x/board.c b/arch/arm/boards/karo-tx6x/board.c index 6d9dd9a..a921541 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 diff --git a/arch/arm/cpu/start-pbl.c b/arch/arm/cpu/start-pbl.c index 2075ffe..f723edc 100644 --- a/arch/arm/cpu/start-pbl.c +++ b/arch/arm/cpu/start-pbl.c @@ -54,8 +54,6 @@ 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 @@ 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 @@ 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 c054f3c..d03d1ed 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) @@ -104,14 +106,48 @@ 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 @@ 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 @@ } 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 diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c index dbf6b1e..5bcce6b 100644 --- a/arch/arm/cpu/uncompress.c +++ b/arch/arm/cpu/uncompress.c @@ -52,8 +52,6 @@ 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 @@ 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 @@ 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 76e3564..6713326 100644 --- a/arch/arm/include/asm/barebox-arm.h +++ b/arch/arm/include/asm/barebox-arm.h @@ -94,25 +94,56 @@ 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; } diff --git a/common/startup.c b/common/startup.c index 4a303b2..093a23b 100644 --- a/common/startup.c +++ b/common/startup.c @@ -60,6 +60,11 @@ 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 9217bc8..5413a92 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -89,4 +89,6 @@ 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 4693205..2f95203 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -14,3 +14,4 @@ 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 0000000..0c6f136 --- /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 0000000..c4043e1 --- /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 0000000..0e05d48 --- /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 0000000..0a8df1f --- /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 0000000..dc2611f --- /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 0000000..dc31ed1 --- /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 0000000..d68d809 --- /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/log2.h b/include/linux/log2.h index d9913f0..36519e3 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -63,6 +63,15 @@ 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 diff --git a/include/linux/pstore.h b/include/linux/pstore.h new file mode 100644 index 0000000..a925e14 --- /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 0000000..5ef823a --- /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 diff --git a/include/linux/rslib.h b/include/linux/rslib.h new file mode 100644 index 0000000..b5e3ffd --- /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/include/printk.h b/include/printk.h index a27ad51..822f64c 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, ...) diff --git a/include/stdio.h b/include/stdio.h index f190911..d0817bd 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -13,6 +13,7 @@ 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/Kconfig b/lib/Kconfig index fbf9f0f..46ec58c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -22,6 +22,9 @@ 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 abb34cf..6ffa3e0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -56,3 +56,4 @@ 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 0000000..c3d7136 --- /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 0000000..0ec3f25 --- /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 0000000..0b5b1a6 --- /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 0000000..51c67c3 --- /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"); + diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 800ded7..9b8e8cf 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -595,6 +595,30 @@ } 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) {