[PATCH v2 1/2] cmd: Add command to display or save Linux PStore dumps

Heinrich Schuchardt xypron.debian at gmx.de
Tue Mar 17 20:57:01 CET 2020


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



More information about the U-Boot mailing list