Message ID | 20200226094211.20392-2-frederic.danis@collabora.com |
---|---|
State | Superseded |
Headers | show |
Series | Add command to display or save Linux PStore dumps | expand |
On 2/26/20 10:42 AM, Fr?d?ric Danis wrote: > This patch adds a new pstore command allowing to display or save ramoops > logs (oops, panic, console, ftrace and user) generated by a previous > kernel crash. > PStore parameters can be set in U-Boot configuration file, or at run-time > using "pstore set" command. Records size should be the same as the ones > used by kernel, and should be a power of 2. > This command allows: > - to display uncompressed logs > - to save compressed or uncompressed logs, compressed logs are saved as a > compressed stream, it may need some work to be able to decompress it, > e.g. adding a fake header: > "printf "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00" | > cat - dmesg-ramoops-0.enc.z | gzip -dc" > - ECC part is not used to check memory corruption > - only 1st FTrace log is displayed or saved > > Signed-off-by: Fr?d?ric Danis <frederic.danis at collabora.com> > --- > cmd/Kconfig | 63 ++++++ > cmd/Makefile | 1 + > cmd/pstore.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++ > doc/index.rst | 7 + > doc/pstore.rst | 68 +++++++ > 5 files changed, 644 insertions(+) > create mode 100644 cmd/pstore.c > create mode 100644 doc/pstore.rst > > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 6403bc45a5..7e78343c3d 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -1736,6 +1736,69 @@ config CMD_QFW > feature is to allow easy loading of files passed to qemu-system > via -kernel / -initrd > > +config CMD_PSTORE > + bool "pstore" > + help > + This provides access to Linux PStore. The main feature is to allow to > + display or save PStore records. Linux PStore can have many backends. I would suggest to mention 'rampoops' explicitely and point to the documentation that you have added. > + Please, use 'if CMD_PSTORE' to indent all options depending on CMD_PSTORE'. > +config CMD_PSTORE_ADDR > + hex "Mem Address" Please, use whole words. hex "Memory address" > + depends on CMD_PSTORE > + default "0x0" > + help > + Base addr used for PStore ramoops memory, should be identical to > + ramoops.mem_address parameter used by kernel > + > +config CMD_PSTORE_SIZE Linux calls the parameter mem_size. So shouldn't we call our parameter CMD_PSTORE_MEM_SIZE. > + hex "Mem size" hex "Memory size" > + depends on CMD_PSTORE > + default "0x0" A value of zero will lead to an error in Linux "The memory size and the record/console size must be non-zero". Please, provide a reasonable default. > + help > + Size of PStore ramoops memory, should be identical to ramoops.mem_size > + parameter used by kernel Please, describe the constraints on ramoops.mem_size. Does it have to be larger than the some of the following parameters? > + > +config CMD_PSTORE_RECORD_SIZE > + hex "Dump record size" > + depends on CMD_PSTORE > + default "0x1000" > + help > + Size of each dump done on oops/panic, should be identical to > + ramoops.record_size parameter used by kernel > + > +config CMD_PSTORE_CONSOLE_SIZE > + hex "Kernel console log size" > + depends on CMD_PSTORE > + default "0x1000" > + help > + Size of kernel console log, should be identical to > + ramoops.console_size parameter used by kernel Please, mention that this value must be non-zero. > + > +config CMD_PSTORE_FTRACE_SIZE > + hex "FTrace log size" > + depends on CMD_PSTORE > + default "0x1000" > + help > + Size of ftrace log, should be identical to ramoops.ftrace_size > + parameter used by kernel > + > +config CMD_PSTORE_PMSG_SIZE > + hex "User space message log size" > + depends on CMD_PSTORE > + default "0x1000" > + help > + Size of user space message log, should be identical to > + ramoops.pmsg_size parameter used by kernel > + > +config CMD_PSTORE_ECC_SIZE > + int "ECC size" > + depends on CMD_PSTORE > + default "0" > + help > + if non-zero, the option enables ECC support and specifies ECC buffer > + size in bytes (1 is a special value, means 16 bytes ECC), should be > + identical to ramoops.ramoops_ecc parameter used by kernel > + > source "cmd/mvebu/Kconfig" > > config CMD_TERMINAL > diff --git a/cmd/Makefile b/cmd/Makefile > index f1dd513a4b..06d7ad7375 100644 > --- a/cmd/Makefile > +++ b/cmd/Makefile > @@ -110,6 +110,7 @@ obj-$(CONFIG_CMD_PCI) += pci.o > endif > obj-$(CONFIG_CMD_PINMUX) += pinmux.o > obj-$(CONFIG_CMD_PMC) += pmc.o > +obj-$(CONFIG_CMD_PSTORE) += pstore.o > obj-$(CONFIG_CMD_PXE) += pxe.o pxe_utils.o > obj-$(CONFIG_CMD_WOL) += wol.o > obj-$(CONFIG_CMD_QFW) += qfw.o > diff --git a/cmd/pstore.c b/cmd/pstore.c > new file mode 100644 > index 0000000000..a14b8522ce > --- /dev/null > +++ b/cmd/pstore.c > @@ -0,0 +1,505 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright ? 2019 Collabora Ltd > + */ > + > +#include <config.h> > +#include <common.h> > +#include <fs.h> > +#include <mapmem.h> > +#include <memalign.h> > +#include <part.h> > + > +struct persistent_ram_buffer { > + u32 sig; > + u32 start; > + u32 size; > + u8 data[0]; > +}; > + > +#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ > +#define RAMOOPS_KERNMSG_HDR "====" > + > +#define PSTORE_TYPE_DMESG 0 > +#define PSTORE_TYPE_CONSOLE 2 > +#define PSTORE_TYPE_FTRACE 3 > +#define PSTORE_TYPE_PMSG 7 > +#define PSTORE_TYPE_ALL 255 > + > +static phys_addr_t pstore_addr = CONFIG_CMD_PSTORE_ADDR; > +static phys_size_t pstore_length = CONFIG_CMD_PSTORE_SIZE; > +static unsigned int pstore_record_size = CONFIG_CMD_PSTORE_RECORD_SIZE; > +static unsigned int pstore_console_size = CONFIG_CMD_PSTORE_CONSOLE_SIZE; > +static unsigned int pstore_ftrace_size = CONFIG_CMD_PSTORE_FTRACE_SIZE; > +static unsigned int pstore_pmsg_size = CONFIG_CMD_PSTORE_PMSG_SIZE; > +static unsigned int pstore_ecc_size = CONFIG_CMD_PSTORE_ECC_SIZE; > +static unsigned int buffer_size; > + > + /** > + * pstore_read_kmsg_hdr() - Check kernel header and get compression flag if > + * available. > + * @buffer: Kernel messages buffer. > + * @compressed: Returns TRUE if kernel buffer is compressed, else FALSE. > + * > + * Check if buffer starts with a kernel header of the form: > + * ====<secs>.<nsecs>[-<compression>]\n > + * If <compression> is equal to 'C' then the buffer is compressed, else iter > + * should be 'D'. > + * > + * Return: Length of kernel header. > + */ > +static int pstore_read_kmsg_hdr(char *buffer, bool *compressed) > +{ > + char *ptr = buffer; > + *compressed = false; > + > + if (strncmp(RAMOOPS_KERNMSG_HDR, ptr, strlen(RAMOOPS_KERNMSG_HDR)) != 0) > + return 0; > + > + ptr += strlen(RAMOOPS_KERNMSG_HDR); > + > + ptr = strchr(ptr, '\n'); > + if (!ptr) > + return 0; > + > + if (ptr[-2] == '-' && ptr[-1] == 'C') > + *compressed = true; > + > + return ptr - buffer + 1; > +} > + > +/** > + * pstore_get_buffer() - Get unwrapped record buffer > + * @sig: Signature to check > + * @buffer: Buffer containing wrapped record > + * @size: wrapped record size > + * @dest: Buffer used to store unwrapped record > + * > + * The record starts with <signature><start><size> header. > + * The signature is 'DBGC' for all records except for Ftrace's record(s) wich > + * use LINUX_VERSION_CODE ^ 'DBGC'. > + * Use 0 for @sig to prevent checking signature. > + * Start and size are 4 bytes long. > + * > + * Return: record's length > + */ > +static u32 pstore_get_buffer(u32 sig, phys_addr_t buffer, u32 size, char *dest) > +{ > + struct persistent_ram_buffer *prb = > + (struct persistent_ram_buffer *)map_sysmem(buffer, size); > + u32 dest_size; > + > + if (sig == 0 || prb->sig == sig) { > + if (prb->size == 0) { > + debug("found existing empty buffer\n"); Please, use log() as described in doc/README.log. This allows the user to control verbosity if CONFIG_LOG=y. > + return 0; > + } > + > + if (prb->size > size) { > + debug("found existing invalid buffer, size %u, start %u\n", > + prb->size, prb->start); > + return 0; > + } > + } else { > + debug("no valid data in buffer (sig = 0x%08x)\n", prb->sig); > + return 0; > + } > + > + debug("found existing buffer, size %u, start %u\n", > + prb->size, prb->start); > + > + memcpy(dest, &prb->data[prb->start], prb->size - prb->start); > + memcpy(dest + prb->size - prb->start, &prb->data[0], prb->start); > + > + dest_size = prb->size; > + unmap_sysmem(prb); > + > + return dest_size; > +} > + > +/** > + * pstore_init_buffer_size() - Init buffer size to largest record size > + * > + * Records, console, FTrace and user logs can use different buffer sizes. > + * This function allows to retrieve the biggest one. > + */ > +static void pstore_init_buffer_size(void) > +{ > + if (pstore_record_size > buffer_size) > + buffer_size = pstore_record_size; > + > + if (pstore_console_size > buffer_size) > + buffer_size = pstore_console_size; > + > + if (pstore_ftrace_size > buffer_size) > + buffer_size = pstore_ftrace_size; > + > + if (pstore_pmsg_size > buffer_size) > + buffer_size = pstore_pmsg_size; > +} > + > +/** > + * pstore_set() - Initialize PStore settings from command line arguments > + * @cmdtp: Command data struct pointer > + * @flag: Command flag > + * @argc: Command-line argument count > + * @argv: Array of command-line arguments > + * > + * Set pstore reserved memory info, starting at 'addr' for 'len' bytes. > + * Default length for records is 4K. > + * Mandatory arguments: > + * - addr: ramoops starting address > + * - len: ramoops total length > + * Optional arguments: > + * - record-size: size of one panic or oops record ('dump' type) > + * - console-size: size of the kernel logs record > + * - ftrace-size: size of the ftrace record(s), this can be a single record or > + * divided in parts based on number of CPUs > + * - pmsg-size: size of the user space logs record > + * - ecc-size: enables/disables ECC support and specifies ECC buffer size in > + * bytes (0 disables it, 1 is a special value, means 16 bytes ECC) > + * > + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative > + * on error. > + */ > +static int pstore_set(cmd_tbl_t *cmdtp, int flag, int argc, > + char * const argv[]) > +{ > + if (argc < 3) > + return CMD_RET_USAGE; > + > + /* Address is specified since argc > 2 > + */ > + pstore_addr = simple_strtoul(argv[1], NULL, 16); > + > + /* Length is specified since argc > 2 > + */ > + pstore_length = simple_strtoul(argv[2], NULL, 16); > + > + if (argc > 3) > + pstore_record_size = simple_strtoul(argv[3], NULL, 16); > + > + if (argc > 4) > + pstore_console_size = simple_strtoul(argv[4], NULL, 16); > + > + if (argc > 5) > + pstore_ftrace_size = simple_strtoul(argv[5], NULL, 16); > + > + if (argc > 6) > + pstore_pmsg_size = simple_strtoul(argv[6], NULL, 16); > + > + if (argc > 7) > + pstore_ecc_size = simple_strtoul(argv[7], NULL, 16); > + > + if (pstore_length < (pstore_record_size + pstore_console_size > + + pstore_ftrace_size + pstore_pmsg_size)) { > + printf("pstore <len> should be larger than the sum of all records sizes\n"); > + pstore_length = 0; > + } > + > + debug("pstore set done: start 0x%08llx - length 0x%llx\n", > + (unsigned long long)pstore_addr, > + (unsigned long long)pstore_length); > + > + return 0; > +} > + > +/** > + * pstore_print_buffer() - Print buffer > + * @type: buffer type > + * @buffer: buffer to print > + * @size: buffer size > + * > + * Print buffer type and content > + */ > +static void pstore_print_buffer(char *type, char *buffer, u32 size) > +{ > + u32 i = 0; > + > + printf("**** %s\n", type); > + while (i < size && buffer[i] != 0) { > + putc(buffer[i]); > + i++; > + } > +} > + > +/** > + * pstore_display() - Display existing records in pstore reserved memory > + * @cmdtp: Command data struct pointer > + * @flag: Command flag > + * @argc: Command-line argument count > + * @argv: Array of command-line arguments > + * > + * A 'record-type' can be given to only display records of this kind. > + * If no 'record-type' is given, all valid records are dispayed. > + * 'record-type' can be one of 'dump', 'console', 'ftrace' or 'user'. For 'dump' > + * and 'ftrace' types, a 'nb' can be given to only display one record. > + * > + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative > + * on error. > + */ > +static int pstore_display(cmd_tbl_t *cmdtp, int flag, int argc, > + char * const argv[]) > +{ > + int type = PSTORE_TYPE_ALL; > + phys_addr_t ptr; > + char *buffer; > + u32 size; > + int header_len = 0; > + bool compressed; > + > + if (argc > 1) { > + if (!strcmp(argv[1], "dump")) > + type = PSTORE_TYPE_DMESG; > + else if (!strcmp(argv[1], "console")) > + type = PSTORE_TYPE_CONSOLE; > + else if (!strcmp(argv[1], "ftrace")) > + type = PSTORE_TYPE_FTRACE; > + else if (!strcmp(argv[1], "user")) > + type = PSTORE_TYPE_PMSG; > + else > + return CMD_RET_USAGE; > + } > + > + if (pstore_length == 0) { > + printf("Please set PStore configuration\n"); > + return CMD_RET_USAGE; > + } > + > + if (buffer_size == 0) > + pstore_init_buffer_size(); > + > + buffer = malloc_cache_aligned(buffer_size); > + > + if (type == PSTORE_TYPE_DMESG || type == PSTORE_TYPE_ALL) { > + ptr = pstore_addr; > + phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size > + - pstore_ftrace_size - pstore_console_size; > + > + if (argc > 2) { > + ptr += simple_strtoul(argv[2], NULL, 10) > + * pstore_record_size; > + ptr_end = ptr + pstore_record_size; > + } > + > + while (ptr < ptr_end) { > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, > + pstore_record_size, buffer); > + ptr += pstore_record_size; > + > + if (size == 0) > + continue; > + > + header_len = pstore_read_kmsg_hdr(buffer, &compressed); > + if (header_len == 0) { > + debug("no valid kernel header\n"); > + continue; > + } > + > + if (compressed) { > + printf("Compressed buffer, display not available\n"); > + continue; > + } > + > + pstore_print_buffer("Dump", buffer + header_len, > + size - header_len); > + } > + } > + > + if (type == PSTORE_TYPE_CONSOLE || type == PSTORE_TYPE_ALL) { > + ptr = pstore_addr + pstore_length - pstore_pmsg_size > + - pstore_ftrace_size - pstore_console_size; > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, > + pstore_console_size, buffer); > + if (size != 0) > + pstore_print_buffer("Console", buffer, size); > + } > + > + if (type == PSTORE_TYPE_FTRACE || type == PSTORE_TYPE_ALL) { > + ptr = pstore_addr + pstore_length - pstore_pmsg_size > + - pstore_ftrace_size; > + /* The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' > + * signature, pass 0 to pstore_get_buffer to prevent > + * checking it > + */ > + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); > + if (size != 0) > + pstore_print_buffer("FTrace", buffer, size); > + } > + > + if (type == PSTORE_TYPE_PMSG || type == PSTORE_TYPE_ALL) { > + ptr = pstore_addr + pstore_length - pstore_pmsg_size; > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, > + pstore_pmsg_size, buffer); > + if (size != 0) > + pstore_print_buffer("User", buffer, size); > + } > + > + free(buffer); > + > + return 0; > +} > + > +/** > + * pstore_save() - Save existing records from pstore reserved memory > + * @cmdtp: Command data struct pointer > + * @flag: Command flag > + * @argc: Command-line argument count > + * @argv: Array of command-line arguments > + * > + * the records are saved under 'directory path', which should already exist, > + * to partition 'part' on device type 'interface' instance 'dev' > + * Filenames are automatically generated, depending on record type, like in > + * /sys/fs/pstore under Linux > + * > + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative > + * on error. > + */ > +static int pstore_save(cmd_tbl_t *cmdtp, int flag, int argc, > + char * const argv[]) > +{ > + phys_addr_t ptr, ptr_end; > + char *buffer; > + char *save_argv[6]; > + char addr[19], length[19]; > + char path[256]; > + u32 size; > + unsigned int index; > + int header_len = 0; > + bool compressed; > + > + if (argc < 4) > + return CMD_RET_USAGE; > + > + if (pstore_length == 0) { > + printf("Please set PStore configuration\n"); > + return CMD_RET_USAGE; > + } > + > + if (buffer_size == 0) > + pstore_init_buffer_size(); > + > + buffer = malloc_cache_aligned(buffer_size); > + sprintf(addr, "0x%p", buffer); > + > + save_argv[0] = argv[0]; > + save_argv[1] = argv[1]; > + save_argv[2] = argv[2]; > + save_argv[3] = addr; > + save_argv[4] = path; > + save_argv[5] = length; > + > + /* Save all Dump records */ > + ptr = pstore_addr; > + ptr_end = ptr + pstore_length - pstore_pmsg_size - pstore_ftrace_size > + - pstore_console_size; > + index = 0; > + while (ptr < ptr_end) { > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, > + pstore_record_size, buffer); > + ptr += pstore_record_size; > + > + if (size == 0) > + continue; > + > + header_len = pstore_read_kmsg_hdr(buffer, &compressed); > + if (header_len == 0) { > + debug("no valid kernel header\n"); > + continue; > + } > + > + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer + header_len)); > + sprintf(length, "0x%X", size - header_len); > + sprintf(path, "%s/dmesg-ramoops-%u%s", argv[3], index, > + compressed ? ".enc.z" : ""); > + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); > + index++; > + } > + > + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer)); > + > + /* Save Console record */ > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_console_size, > + buffer); > + if (size != 0) { > + sprintf(length, "0x%X", size); > + sprintf(path, "%s/console-ramoops-0", argv[3]); > + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); > + } > + ptr += pstore_console_size; > + > + /* Save FTrace record(s) > + * The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' signature, > + * pass 0 to pstore_get_buffer to prevent checking it > + */ > + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); > + if (size != 0) { > + sprintf(length, "0x%X", size); > + sprintf(path, "%s/ftrace-ramoops-0", argv[3]); > + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); > + } > + ptr += pstore_ftrace_size; > + > + /* Save Console record */ > + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_pmsg_size, > + buffer); > + if (size != 0) { > + sprintf(length, "0x%X", size); > + sprintf(path, "%s/pmsg-ramoops-0", argv[3]); > + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); > + } > + > + free(buffer); > + > + return 0; > +} > + > +static cmd_tbl_t cmd_pstore_sub[] = { > + U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""), > + U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""), > + U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""), > +}; > + > +static int do_pstore(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) > +{ > + cmd_tbl_t *c; > + > + if (argc < 2) > + return CMD_RET_USAGE; > + > + /* Strip off leading argument */ > + argc--; > + argv++; > + > + c = find_cmd_tbl(argv[0], cmd_pstore_sub, ARRAY_SIZE(cmd_pstore_sub)); > + > + if (!c) > + return CMD_RET_USAGE; > + > + return c->cmd(cmdtp, flag, argc, argv); > +} > + > +U_BOOT_CMD(pstore, 10, 0, do_pstore, > + "Manage Linux Persistent Storage", > + "set <addr> <len> [record-size] [console-size] [ftrace-size] [pmsg_size] [ecc-size]\n" > + "- Set pstore reserved memory info, starting at 'addr' for 'len' bytes.\n" > + " Default length for records is 4K.\n" > + " 'record-size' is the size of one panic or oops record ('dump' type).\n" > + " 'console-size' is the size of the kernel logs record.\n" > + " 'ftrace-size' is the size of the ftrace record(s), this can be a single\n" > + " record or divided in parts based on number of CPUs.\n" > + " 'pmsg-size' is the size of the user space logs record.\n" > + " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n" > + " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n" > + "pstore display [record-type] [nb]\n" > + "- Display existing records in pstore reserved memory. A 'record-type' can\n" > + " be given to only display records of this kind. 'record-type' can be one\n" > + " of 'dump', 'console', 'ftrace' or 'user'. For 'dump' and 'ftrace' types,\n" > + " a 'nb' can be given to only display one record.\n" > + "pstore save <interface> <dev[:part]> <directory-path>\n" > + "- Save existing records in pstore reserved memory under 'directory path'\n" > + " to partition 'part' on device type 'interface' instance 'dev'.\n" > + " Filenames are automatically generated, depending on record type, like\n" > + " in /sys/fs/pstore under Linux.\n" > + " The 'directory-path' should already exist.\n" > +); > diff --git a/doc/index.rst b/doc/index.rst > index cd98be6cc5..c556cdb607 100644 > --- a/doc/index.rst > +++ b/doc/index.rst > @@ -98,6 +98,13 @@ Android-specific features available in U-Boot. > > android/index > > +Command line > +------------ > +.. toctree:: > + :maxdepth: 2 > + > + pstore.rst > + > Indices and tables > ================== > > diff --git a/doc/pstore.rst b/doc/pstore.rst > new file mode 100644 > index 0000000000..401ba34373 > --- /dev/null > +++ b/doc/pstore.rst > @@ -0,0 +1,68 @@ > +.. SPDX-License-Identifier: GPL-2.0+ > + > +PStore command > +============== > + > +Design > +------ > + > +Linux PStore and Ramoops modules allow to use memory to pass data from the dying Please, mention Linux config option PSTORE_RAM here. > +breath of a crashing kernel to its successor. This command allows to read those > +records from U-Boot command line. > + > +Ramoops is an oops/panic logger that writes its logs to RAM before the system > +crashes. It works by logging oopses and panics in a circular buffer. Ramoops > +needs a system with persistent RAM so that the content of that area can survive > +after a restart. > + > +Ramoops uses a predefined memory area to store the dump. > + > +Ramoops parameters can be passed as kernel parameters or through Device Tree, Please, add the node in image_setup_libfdt() as described in Linux's Documentation/device-tree/bindings/reserved-memory/admin-guide/ramoops.rst. > +i.e.:: > + ramoops.mem_address=0x30000000 ramoops.mem_size=0x100000 ramoops.record_size=0x2000 ramoops.console_size=0x2000 memmap=0x100000$0x30000000 Using the command line seems to be rather error prone. > + > +The same values should be set in U-Boot to be able to retrieve the records. > +This values can be set at build time in U-Boot configuration file, or at runtime. > + > +The PStore configuration parameters are: > + > +======================= ======= > + Name Default > +======================= ======= > +CMD_PSTORE_ADDR 0x0 > +CMD_PSTORE_SIZE 0x0 > +CMD_PSTORE_RECORD_SIZE 0x1000 > +CMD_PSTORE_CONSOLE_SIZE 0x1000 > +CMD_PSTORE_FTRACE_SIZE 0x1000 > +CMD_PSTORE_PMSG_SIZE 0x1000 > +CMD_PSTORE_ECC_SIZE 0 > +======================= ======= Please, describe the requirements on CMD_PSTORE_SIZE. I would assume that is has to be larger than the sum of the other sizes. > + > +Records sizes should be a power of 2. Please, mention: The memory size and the record/console size must be non-zero. Best regards Heinrich > + > +Usage > +----- > + > +Generate kernel crash > +~~~~~~~~~~~~~~~~~~~~~ > + > +For test purpose, you can generate a kernel crash by setting reboot timeout to > +10 seconds and trigger a panic:: > + $ sudo sh -c "echo 1 > /proc/sys/kernel/sysrq" > + $ sudo sh -c "echo 10 > /proc/sys/kernel/panic" > + $ sudo sh -c "echo c > /proc/sysrq-trigger" > + > +Retrieve logs in U-Boot > +~~~~~~~~~~~~~~~~~~~~~~~ > + > +First of all, unless PStore parameters as been set during U-Boot configuration > +and match kernel ramoops parameters, it needs to be set using 'pstore set', e.g.:: > + => pstore set 0x30000000 0x100000 0x2000 0x2000 > + > +Then all available dumps can be displayed > +using:: > + => pstore display > + > +Or saved to an existing directory in an Ext2 or Ext4 partition, e.g. on root > +directory of 1st partition of the 2nd MMC:: > + => pstore save mmc 1:1 / >
Hi Heinrich, On 17/03/2020 20:57, Heinrich Schuchardt wrote: > On 2/26/20 10:42 AM, Fr?d?ric Danis wrote: <snip> >> +Ramoops is an oops/panic logger that writes its logs to RAM before >> the system >> +crashes. It works by logging oopses and panics in a circular buffer. >> Ramoops >> +needs a system with persistent RAM so that the content of that area >> can survive >> +after a restart. >> + >> +Ramoops uses a predefined memory area to store the dump. >> + >> +Ramoops parameters can be passed as kernel parameters or through >> Device Tree, > > Please, add the node in image_setup_libfdt() as described in Linux's > Documentation/device-tree/bindings/reserved-memory/admin-guide/ramoops.rst. > I'm not sure to understand what you expect here. Can you please give me more info about it? > >> +i.e.:: >> + ramoops.mem_address=0x30000000 ramoops.mem_size=0x100000 >> ramoops.record_size=0x2000 ramoops.console_size=0x2000 >> memmap=0x100000$0x30000000 > > Using the command line seems to be rather error prone. > Best regards, Fr?d?ric Danis
diff --git a/cmd/Kconfig b/cmd/Kconfig index 6403bc45a5..7e78343c3d 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1736,6 +1736,69 @@ config CMD_QFW feature is to allow easy loading of files passed to qemu-system via -kernel / -initrd +config CMD_PSTORE + bool "pstore" + help + This provides access to Linux PStore. The main feature is to allow to + display or save PStore records. + +config CMD_PSTORE_ADDR + hex "Mem Address" + depends on CMD_PSTORE + default "0x0" + help + Base addr used for PStore ramoops memory, should be identical to + ramoops.mem_address parameter used by kernel + +config CMD_PSTORE_SIZE + hex "Mem size" + depends on CMD_PSTORE + default "0x0" + help + Size of PStore ramoops memory, should be identical to ramoops.mem_size + parameter used by kernel + +config CMD_PSTORE_RECORD_SIZE + hex "Dump record size" + depends on CMD_PSTORE + default "0x1000" + help + Size of each dump done on oops/panic, should be identical to + ramoops.record_size parameter used by kernel + +config CMD_PSTORE_CONSOLE_SIZE + hex "Kernel console log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of kernel console log, should be identical to + ramoops.console_size parameter used by kernel + +config CMD_PSTORE_FTRACE_SIZE + hex "FTrace log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of ftrace log, should be identical to ramoops.ftrace_size + parameter used by kernel + +config CMD_PSTORE_PMSG_SIZE + hex "User space message log size" + depends on CMD_PSTORE + default "0x1000" + help + Size of user space message log, should be identical to + ramoops.pmsg_size parameter used by kernel + +config CMD_PSTORE_ECC_SIZE + int "ECC size" + depends on CMD_PSTORE + default "0" + help + if non-zero, the option enables ECC support and specifies ECC buffer + size in bytes (1 is a special value, means 16 bytes ECC), should be + identical to ramoops.ramoops_ecc parameter used by kernel + source "cmd/mvebu/Kconfig" config CMD_TERMINAL diff --git a/cmd/Makefile b/cmd/Makefile index f1dd513a4b..06d7ad7375 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_CMD_PCI) += pci.o endif obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PMC) += pmc.o +obj-$(CONFIG_CMD_PSTORE) += pstore.o obj-$(CONFIG_CMD_PXE) += pxe.o pxe_utils.o obj-$(CONFIG_CMD_WOL) += wol.o obj-$(CONFIG_CMD_QFW) += qfw.o diff --git a/cmd/pstore.c b/cmd/pstore.c new file mode 100644 index 0000000000..a14b8522ce --- /dev/null +++ b/cmd/pstore.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright ? 2019 Collabora Ltd + */ + +#include <config.h> +#include <common.h> +#include <fs.h> +#include <mapmem.h> +#include <memalign.h> +#include <part.h> + +struct persistent_ram_buffer { + u32 sig; + u32 start; + u32 size; + u8 data[0]; +}; + +#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ +#define RAMOOPS_KERNMSG_HDR "====" + +#define PSTORE_TYPE_DMESG 0 +#define PSTORE_TYPE_CONSOLE 2 +#define PSTORE_TYPE_FTRACE 3 +#define PSTORE_TYPE_PMSG 7 +#define PSTORE_TYPE_ALL 255 + +static phys_addr_t pstore_addr = CONFIG_CMD_PSTORE_ADDR; +static phys_size_t pstore_length = CONFIG_CMD_PSTORE_SIZE; +static unsigned int pstore_record_size = CONFIG_CMD_PSTORE_RECORD_SIZE; +static unsigned int pstore_console_size = CONFIG_CMD_PSTORE_CONSOLE_SIZE; +static unsigned int pstore_ftrace_size = CONFIG_CMD_PSTORE_FTRACE_SIZE; +static unsigned int pstore_pmsg_size = CONFIG_CMD_PSTORE_PMSG_SIZE; +static unsigned int pstore_ecc_size = CONFIG_CMD_PSTORE_ECC_SIZE; +static unsigned int buffer_size; + + /** + * pstore_read_kmsg_hdr() - Check kernel header and get compression flag if + * available. + * @buffer: Kernel messages buffer. + * @compressed: Returns TRUE if kernel buffer is compressed, else FALSE. + * + * Check if buffer starts with a kernel header of the form: + * ====<secs>.<nsecs>[-<compression>]\n + * If <compression> is equal to 'C' then the buffer is compressed, else iter + * should be 'D'. + * + * Return: Length of kernel header. + */ +static int pstore_read_kmsg_hdr(char *buffer, bool *compressed) +{ + char *ptr = buffer; + *compressed = false; + + if (strncmp(RAMOOPS_KERNMSG_HDR, ptr, strlen(RAMOOPS_KERNMSG_HDR)) != 0) + return 0; + + ptr += strlen(RAMOOPS_KERNMSG_HDR); + + ptr = strchr(ptr, '\n'); + if (!ptr) + return 0; + + if (ptr[-2] == '-' && ptr[-1] == 'C') + *compressed = true; + + return ptr - buffer + 1; +} + +/** + * pstore_get_buffer() - Get unwrapped record buffer + * @sig: Signature to check + * @buffer: Buffer containing wrapped record + * @size: wrapped record size + * @dest: Buffer used to store unwrapped record + * + * The record starts with <signature><start><size> header. + * The signature is 'DBGC' for all records except for Ftrace's record(s) wich + * use LINUX_VERSION_CODE ^ 'DBGC'. + * Use 0 for @sig to prevent checking signature. + * Start and size are 4 bytes long. + * + * Return: record's length + */ +static u32 pstore_get_buffer(u32 sig, phys_addr_t buffer, u32 size, char *dest) +{ + struct persistent_ram_buffer *prb = + (struct persistent_ram_buffer *)map_sysmem(buffer, size); + u32 dest_size; + + if (sig == 0 || prb->sig == sig) { + if (prb->size == 0) { + debug("found existing empty buffer\n"); + return 0; + } + + if (prb->size > size) { + debug("found existing invalid buffer, size %u, start %u\n", + prb->size, prb->start); + return 0; + } + } else { + debug("no valid data in buffer (sig = 0x%08x)\n", prb->sig); + return 0; + } + + debug("found existing buffer, size %u, start %u\n", + prb->size, prb->start); + + memcpy(dest, &prb->data[prb->start], prb->size - prb->start); + memcpy(dest + prb->size - prb->start, &prb->data[0], prb->start); + + dest_size = prb->size; + unmap_sysmem(prb); + + return dest_size; +} + +/** + * pstore_init_buffer_size() - Init buffer size to largest record size + * + * Records, console, FTrace and user logs can use different buffer sizes. + * This function allows to retrieve the biggest one. + */ +static void pstore_init_buffer_size(void) +{ + if (pstore_record_size > buffer_size) + buffer_size = pstore_record_size; + + if (pstore_console_size > buffer_size) + buffer_size = pstore_console_size; + + if (pstore_ftrace_size > buffer_size) + buffer_size = pstore_ftrace_size; + + if (pstore_pmsg_size > buffer_size) + buffer_size = pstore_pmsg_size; +} + +/** + * pstore_set() - Initialize PStore settings from command line arguments + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Set pstore reserved memory info, starting at 'addr' for 'len' bytes. + * Default length for records is 4K. + * Mandatory arguments: + * - addr: ramoops starting address + * - len: ramoops total length + * Optional arguments: + * - record-size: size of one panic or oops record ('dump' type) + * - console-size: size of the kernel logs record + * - ftrace-size: size of the ftrace record(s), this can be a single record or + * divided in parts based on number of CPUs + * - pmsg-size: size of the user space logs record + * - ecc-size: enables/disables ECC support and specifies ECC buffer size in + * bytes (0 disables it, 1 is a special value, means 16 bytes ECC) + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_set(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + if (argc < 3) + return CMD_RET_USAGE; + + /* Address is specified since argc > 2 + */ + pstore_addr = simple_strtoul(argv[1], NULL, 16); + + /* Length is specified since argc > 2 + */ + pstore_length = simple_strtoul(argv[2], NULL, 16); + + if (argc > 3) + pstore_record_size = simple_strtoul(argv[3], NULL, 16); + + if (argc > 4) + pstore_console_size = simple_strtoul(argv[4], NULL, 16); + + if (argc > 5) + pstore_ftrace_size = simple_strtoul(argv[5], NULL, 16); + + if (argc > 6) + pstore_pmsg_size = simple_strtoul(argv[6], NULL, 16); + + if (argc > 7) + pstore_ecc_size = simple_strtoul(argv[7], NULL, 16); + + if (pstore_length < (pstore_record_size + pstore_console_size + + pstore_ftrace_size + pstore_pmsg_size)) { + printf("pstore <len> should be larger than the sum of all records sizes\n"); + pstore_length = 0; + } + + debug("pstore set done: start 0x%08llx - length 0x%llx\n", + (unsigned long long)pstore_addr, + (unsigned long long)pstore_length); + + return 0; +} + +/** + * pstore_print_buffer() - Print buffer + * @type: buffer type + * @buffer: buffer to print + * @size: buffer size + * + * Print buffer type and content + */ +static void pstore_print_buffer(char *type, char *buffer, u32 size) +{ + u32 i = 0; + + printf("**** %s\n", type); + while (i < size && buffer[i] != 0) { + putc(buffer[i]); + i++; + } +} + +/** + * pstore_display() - Display existing records in pstore reserved memory + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * A 'record-type' can be given to only display records of this kind. + * If no 'record-type' is given, all valid records are dispayed. + * 'record-type' can be one of 'dump', 'console', 'ftrace' or 'user'. For 'dump' + * and 'ftrace' types, a 'nb' can be given to only display one record. + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_display(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int type = PSTORE_TYPE_ALL; + phys_addr_t ptr; + char *buffer; + u32 size; + int header_len = 0; + bool compressed; + + if (argc > 1) { + if (!strcmp(argv[1], "dump")) + type = PSTORE_TYPE_DMESG; + else if (!strcmp(argv[1], "console")) + type = PSTORE_TYPE_CONSOLE; + else if (!strcmp(argv[1], "ftrace")) + type = PSTORE_TYPE_FTRACE; + else if (!strcmp(argv[1], "user")) + type = PSTORE_TYPE_PMSG; + else + return CMD_RET_USAGE; + } + + if (pstore_length == 0) { + printf("Please set PStore configuration\n"); + return CMD_RET_USAGE; + } + + if (buffer_size == 0) + pstore_init_buffer_size(); + + buffer = malloc_cache_aligned(buffer_size); + + if (type == PSTORE_TYPE_DMESG || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr; + phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size - pstore_console_size; + + if (argc > 2) { + ptr += simple_strtoul(argv[2], NULL, 10) + * pstore_record_size; + ptr_end = ptr + pstore_record_size; + } + + while (ptr < ptr_end) { + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_record_size, buffer); + ptr += pstore_record_size; + + if (size == 0) + continue; + + header_len = pstore_read_kmsg_hdr(buffer, &compressed); + if (header_len == 0) { + debug("no valid kernel header\n"); + continue; + } + + if (compressed) { + printf("Compressed buffer, display not available\n"); + continue; + } + + pstore_print_buffer("Dump", buffer + header_len, + size - header_len); + } + } + + if (type == PSTORE_TYPE_CONSOLE || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size - pstore_console_size; + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_console_size, buffer); + if (size != 0) + pstore_print_buffer("Console", buffer, size); + } + + if (type == PSTORE_TYPE_FTRACE || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size + - pstore_ftrace_size; + /* The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' + * signature, pass 0 to pstore_get_buffer to prevent + * checking it + */ + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); + if (size != 0) + pstore_print_buffer("FTrace", buffer, size); + } + + if (type == PSTORE_TYPE_PMSG || type == PSTORE_TYPE_ALL) { + ptr = pstore_addr + pstore_length - pstore_pmsg_size; + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_pmsg_size, buffer); + if (size != 0) + pstore_print_buffer("User", buffer, size); + } + + free(buffer); + + return 0; +} + +/** + * pstore_save() - Save existing records from pstore reserved memory + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * the records are saved under 'directory path', which should already exist, + * to partition 'part' on device type 'interface' instance 'dev' + * Filenames are automatically generated, depending on record type, like in + * /sys/fs/pstore under Linux + * + * Return: zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ +static int pstore_save(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + phys_addr_t ptr, ptr_end; + char *buffer; + char *save_argv[6]; + char addr[19], length[19]; + char path[256]; + u32 size; + unsigned int index; + int header_len = 0; + bool compressed; + + if (argc < 4) + return CMD_RET_USAGE; + + if (pstore_length == 0) { + printf("Please set PStore configuration\n"); + return CMD_RET_USAGE; + } + + if (buffer_size == 0) + pstore_init_buffer_size(); + + buffer = malloc_cache_aligned(buffer_size); + sprintf(addr, "0x%p", buffer); + + save_argv[0] = argv[0]; + save_argv[1] = argv[1]; + save_argv[2] = argv[2]; + save_argv[3] = addr; + save_argv[4] = path; + save_argv[5] = length; + + /* Save all Dump records */ + ptr = pstore_addr; + ptr_end = ptr + pstore_length - pstore_pmsg_size - pstore_ftrace_size + - pstore_console_size; + index = 0; + while (ptr < ptr_end) { + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, + pstore_record_size, buffer); + ptr += pstore_record_size; + + if (size == 0) + continue; + + header_len = pstore_read_kmsg_hdr(buffer, &compressed); + if (header_len == 0) { + debug("no valid kernel header\n"); + continue; + } + + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer + header_len)); + sprintf(length, "0x%X", size - header_len); + sprintf(path, "%s/dmesg-ramoops-%u%s", argv[3], index, + compressed ? ".enc.z" : ""); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + index++; + } + + sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer)); + + /* Save Console record */ + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_console_size, + buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/console-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + ptr += pstore_console_size; + + /* Save FTrace record(s) + * The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' signature, + * pass 0 to pstore_get_buffer to prevent checking it + */ + size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/ftrace-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + ptr += pstore_ftrace_size; + + /* Save Console record */ + size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_pmsg_size, + buffer); + if (size != 0) { + sprintf(length, "0x%X", size); + sprintf(path, "%s/pmsg-ramoops-0", argv[3]); + do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY); + } + + free(buffer); + + return 0; +} + +static cmd_tbl_t cmd_pstore_sub[] = { + U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""), + U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""), + U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""), +}; + +static int do_pstore(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + /* Strip off leading argument */ + argc--; + argv++; + + c = find_cmd_tbl(argv[0], cmd_pstore_sub, ARRAY_SIZE(cmd_pstore_sub)); + + if (!c) + return CMD_RET_USAGE; + + return c->cmd(cmdtp, flag, argc, argv); +} + +U_BOOT_CMD(pstore, 10, 0, do_pstore, + "Manage Linux Persistent Storage", + "set <addr> <len> [record-size] [console-size] [ftrace-size] [pmsg_size] [ecc-size]\n" + "- Set pstore reserved memory info, starting at 'addr' for 'len' bytes.\n" + " Default length for records is 4K.\n" + " 'record-size' is the size of one panic or oops record ('dump' type).\n" + " 'console-size' is the size of the kernel logs record.\n" + " 'ftrace-size' is the size of the ftrace record(s), this can be a single\n" + " record or divided in parts based on number of CPUs.\n" + " 'pmsg-size' is the size of the user space logs record.\n" + " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n" + " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n" + "pstore display [record-type] [nb]\n" + "- Display existing records in pstore reserved memory. A 'record-type' can\n" + " be given to only display records of this kind. 'record-type' can be one\n" + " of 'dump', 'console', 'ftrace' or 'user'. For 'dump' and 'ftrace' types,\n" + " a 'nb' can be given to only display one record.\n" + "pstore save <interface> <dev[:part]> <directory-path>\n" + "- Save existing records in pstore reserved memory under 'directory path'\n" + " to partition 'part' on device type 'interface' instance 'dev'.\n" + " Filenames are automatically generated, depending on record type, like\n" + " in /sys/fs/pstore under Linux.\n" + " The 'directory-path' should already exist.\n" +); diff --git a/doc/index.rst b/doc/index.rst index cd98be6cc5..c556cdb607 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -98,6 +98,13 @@ Android-specific features available in U-Boot. android/index +Command line +------------ +.. toctree:: + :maxdepth: 2 + + pstore.rst + Indices and tables ================== diff --git a/doc/pstore.rst b/doc/pstore.rst new file mode 100644 index 0000000000..401ba34373 --- /dev/null +++ b/doc/pstore.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +PStore command +============== + +Design +------ + +Linux PStore and Ramoops modules allow to use memory to pass data from the dying +breath of a crashing kernel to its successor. This command allows to read those +records from U-Boot command line. + +Ramoops is an oops/panic logger that writes its logs to RAM before the system +crashes. It works by logging oopses and panics in a circular buffer. Ramoops +needs a system with persistent RAM so that the content of that area can survive +after a restart. + +Ramoops uses a predefined memory area to store the dump. + +Ramoops parameters can be passed as kernel parameters or through Device Tree, +i.e.:: + ramoops.mem_address=0x30000000 ramoops.mem_size=0x100000 ramoops.record_size=0x2000 ramoops.console_size=0x2000 memmap=0x100000$0x30000000 + +The same values should be set in U-Boot to be able to retrieve the records. +This values can be set at build time in U-Boot configuration file, or at runtime. + +The PStore configuration parameters are: + +======================= ======= + Name Default +======================= ======= +CMD_PSTORE_ADDR 0x0 +CMD_PSTORE_SIZE 0x0 +CMD_PSTORE_RECORD_SIZE 0x1000 +CMD_PSTORE_CONSOLE_SIZE 0x1000 +CMD_PSTORE_FTRACE_SIZE 0x1000 +CMD_PSTORE_PMSG_SIZE 0x1000 +CMD_PSTORE_ECC_SIZE 0 +======================= ======= + +Records sizes should be a power of 2. + +Usage +----- + +Generate kernel crash +~~~~~~~~~~~~~~~~~~~~~ + +For test purpose, you can generate a kernel crash by setting reboot timeout to +10 seconds and trigger a panic:: + $ sudo sh -c "echo 1 > /proc/sys/kernel/sysrq" + $ sudo sh -c "echo 10 > /proc/sys/kernel/panic" + $ sudo sh -c "echo c > /proc/sysrq-trigger" + +Retrieve logs in U-Boot +~~~~~~~~~~~~~~~~~~~~~~~ + +First of all, unless PStore parameters as been set during U-Boot configuration +and match kernel ramoops parameters, it needs to be set using 'pstore set', e.g.:: + => pstore set 0x30000000 0x100000 0x2000 0x2000 + +Then all available dumps can be displayed +using:: + => pstore display + +Or saved to an existing directory in an Ext2 or Ext4 partition, e.g. on root +directory of 1st partition of the 2nd MMC:: + => pstore save mmc 1:1 /
This patch adds a new pstore command allowing to display or save ramoops logs (oops, panic, console, ftrace and user) generated by a previous kernel crash. PStore parameters can be set in U-Boot configuration file, or at run-time using "pstore set" command. Records size should be the same as the ones used by kernel, and should be a power of 2. This command allows: - to display uncompressed logs - to save compressed or uncompressed logs, compressed logs are saved as a compressed stream, it may need some work to be able to decompress it, e.g. adding a fake header: "printf "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00" | cat - dmesg-ramoops-0.enc.z | gzip -dc" - ECC part is not used to check memory corruption - only 1st FTrace log is displayed or saved Signed-off-by: Fr?d?ric Danis <frederic.danis at collabora.com> --- cmd/Kconfig | 63 ++++++ cmd/Makefile | 1 + cmd/pstore.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst | 7 + doc/pstore.rst | 68 +++++++ 5 files changed, 644 insertions(+) create mode 100644 cmd/pstore.c create mode 100644 doc/pstore.rst