diff mbox

: Implementation of opalmsg command in crash-utility

Message ID 1486454698-15164-1-git-send-email-ankit@linux.vnet.ibm.com
State Not Applicable
Headers show

Commit Message

Ankit Kumar Feb. 7, 2017, 8:04 a.m. UTC
This patch creates 'opalmsg' command which can be used to dump opal console log
in crash. Currently it uses hard-coded start address and size of opal console
log buffer.
As this command is specific to POWER, it is coded as such.

Here is the sample output of 'opalmsg' command:
crash> opalmsg
[   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-mukesh-dirty-df9a248 starting...
[   65.219065872,5] initial console log level: memory 7, driver 5
[   65.219068917,6] CPU: P8 generation processor(max 8 threads/core)
[   65.219071681,7] CPU: Boot CPU PIR is 0x0060 PVR is 0x004d0200
[   65.219074685,7] CPU: Initial max PIR set to 0x1fff
[   65.219602559,5] OPAL table: 0x300c7440 .. 0x300c78d0, branch table: 0x30002000
[   65.219607955,5] FDT: Parsing fdt @0xff00000
[   65.225380389,5] XSCOM: chip 0x8 at 0x3fc4000000000 [P8 DD2.0]
[   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31
[  491.377710151,7] PHB#0022: LINK: Link is up
[  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host
[  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done

Log for dump collected on non OPAL ppc machine:
crash> opalmsg
Dump was not captured on an OPAL based machine

crash> help
btop           help           opalmsg        set            vm

On Non POWER machine 'opalmsg' command won't be available as opalmsg is
specific to POWER.
crash> opalmsg
crash: command not found: opalmsg

crash> help opalmsg

NAME
  opalmsg - dump opal console log buffer

SYNOPSIS
  opalmsg

DESCRIPTION
  The opalmsg command retrieves, formats and dumps the OPAL console ring
  buffer to help understand the state of OPAL firmware when the dump was
  taken

EXAMPLES
crash> opalmsg
[   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-df9a248 starting...
[   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31
[  491.377710151,7] PHB#0022: LINK: Link is up
[  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host
[  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done

Signed-off-by: Ankit Kumar <ankit@linux.vnet.ibm.com>
---

 defs.h        |  2 ++
 global_data.c |  3 ++
 help.c        | 25 ++++++++++++++++
 memory.c      | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 121 insertions(+)

Comments

Dave Anderson Feb. 7, 2017, 2:41 p.m. UTC | #1
----- Original Message -----
> This patch creates 'opalmsg' command which can be used to dump opal console log
> in crash. Currently it uses hard-coded start address and size of opal console log buffer.
> As this command is specific to POWER, it is coded as such.

Hi Ankit,

There's no way I'm going to add such a subsystem- and machine-specific command to the standard
set of generic commands.  The base command set is almost never extended, where, for example, 
the "ipcs" and "tree" commands were added in 2012, and are the only ones added since the 
crash utility was originally introduced.  

Here are a couple suggestions:

 (1) Make it an extension module, which can be hosted on the extensions page here:

       http://people.redhat.com/anderson/extensions.html

 (2) Make it an option to the "mach" command, which does advertise several machine-specific
     options:

       crash> help mach

       NAME
         mach - machine specific data

       SYNOPSIS
         mach [-m | -c -[xd]]

       DESCRIPTION
         This command displays data specific to a machine type.

           -m  Display the physical memory map (x86, x86_64 and ia64 only).
           -c  Display each cpu's cpuinfo structure (x86, x86_64 and ia64 only).
               Display each cpu's x8664_pda structure (x86_64 only),
               Display the hwrpb_struct, and each cpu's percpu_struct (alpha only).
           -x  override default output format with hexadecimal format.
           -d  override default output format with decimal format.

       EXAMPLES
       ...

Your choice, but at least "mach -o" or whatever would allow it to be contained
in the crash binary.  Sound reasonable?

Dave



 

> 
> Here is the sample output of 'opalmsg' command:
> crash> opalmsg
> [   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-mukesh-dirty-df9a248
> starting...
> [   65.219065872,5] initial console log level: memory 7, driver 5
> [   65.219068917,6] CPU: P8 generation processor(max 8 threads/core)
> [   65.219071681,7] CPU: Boot CPU PIR is 0x0060 PVR is 0x004d0200
> [   65.219074685,7] CPU: Initial max PIR set to 0x1fff
> [   65.219602559,5] OPAL table: 0x300c7440 .. 0x300c78d0, branch table:
> 0x30002000
> [   65.219607955,5] FDT: Parsing fdt @0xff00000
> [   65.225380389,5] XSCOM: chip 0x8 at 0x3fc4000000000 [P8 DD2.0]
> [   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31
> [  491.377710151,7] PHB#0022: LINK: Link is up
> [  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host
> [  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done
> 
> Log for dump collected on non OPAL ppc machine:
> crash> opalmsg
> Dump was not captured on an OPAL based machine
> 
> crash> help
> btop           help           opalmsg        set            vm
> 
> On Non POWER machine 'opalmsg' command won't be available as opalmsg is
> specific to POWER.
> crash> opalmsg
> crash: command not found: opalmsg
> 
> crash> help opalmsg
> 
> NAME
>   opalmsg - dump opal console log buffer
> 
> SYNOPSIS
>   opalmsg
> 
> DESCRIPTION
>   The opalmsg command retrieves, formats and dumps the OPAL console ring
>   buffer to help understand the state of OPAL firmware when the dump was
>   taken
> 
> EXAMPLES
> crash> opalmsg
> [   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-df9a248 starting...
> [   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31
> [  491.377710151,7] PHB#0022: LINK: Link is up
> [  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host
> [  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done
> 
> Signed-off-by: Ankit Kumar <ankit@linux.vnet.ibm.com>
> ---
> 
>  defs.h        |  2 ++
>  global_data.c |  3 ++
>  help.c        | 25 ++++++++++++++++
>  memory.c      | 91
>  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 121 insertions(+)
> 
> diff --git a/defs.h b/defs.h
> index 68ed4d4..c81154a 100644
> --- a/defs.h
> +++ b/defs.h
> @@ -4623,6 +4623,7 @@ void cmd_template(void);     /* tools.c */
>  void cmd_alias(void);        /* cmdline.c */
>  void cmd_repeat(void);       /* cmdline.c */
>  void cmd_rd(void);           /* memory.c */
> +void cmd_opalmsg(void);     /* memory.c */
>  void cmd_wr(void);           /* memory.c */
>  void cmd_ptov(void);         /* memory.c */
>  void cmd_vtop(void);         /* memory.c */
> @@ -5189,6 +5190,7 @@ extern char *help_ptob[];
>  extern char *help_ptov[];
>  extern char *help_quit[];
>  extern char *help_rd[];
> +extern char *help_opalmsg[];
>  extern char *help_repeat[];
>  extern char *help_runq[];
>  extern char *help_ipcs[];
> diff --git a/global_data.c b/global_data.c
> index 998aaae..6bf3406 100644
> --- a/global_data.c
> +++ b/global_data.c
> @@ -102,6 +102,9 @@ struct command_table_entry linux_command_table[] = {
>          {"q",       cmd_quit,    help_quit,    MINIMAL},
>          {"tree",    cmd_tree,    help_tree,    REFRESH_TASK_TABLE},
>          {"rd",      cmd_rd,      help_rd,      MINIMAL},
> +#if defined(PPC) || defined(PPC64)
> +	{"opalmsg", cmd_opalmsg, help_opalmsg, 0},
> +#endif
>  	{"repeat",  cmd_repeat,  help_repeat,  0},
>  	{"runq",    cmd_runq,    help_runq,    REFRESH_TASK_TABLE},
>          {"search",  cmd_search,  help_search,  0},
> diff --git a/help.c b/help.c
> index 35cd941..75d771e 100644
> --- a/help.c
> +++ b/help.c
> @@ -1717,6 +1717,30 @@ char *help_rd[] = {
>  NULL
>  };
>  
> +char *help_opalmsg[] = {
> +"opalmsg",
> +"dump opal console log buffer",
> +"",
> +"  The opalmsg command retrieves, formats and dumps the OPAL console ring",
> +"  buffer to help understand the state of OPAL firmware when the dump was",
> +"  taken",
> +"\nEXAMPLES",
> +"crash> opalmsg",
> +"[   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-df9a248
> starting...",
> +"[   65.219065872,5] initial console log level: memory 7, driver 5",
> +"[   65.219068917,6] CPU: P8 generation processor(max 8 threads/core)",
> +"[   65.219071681,7] CPU: Boot CPU PIR is 0x0060 PVR is 0x004d0200",
> +"[   65.219074685,7] CPU: Initial max PIR set to 0x1fff",
> +"[   65.219602559,5] OPAL table: 0x300c7440 .. 0x300c78d0, branch table:
> 0x30002000",
> +"[   65.219607955,5] FDT: Parsing fdt @0xff00000",
> +"[   65.225380389,5] XSCOM: chip 0x8 at 0x3fc4000000000 [P8 DD2.0]",
> +"[   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31",
> +"[  491.377710151,7] PHB#0022: LINK: Link is up",
> +"[  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to
> host",
> +"[  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done",
> +NULL
> +};
> +
>  char *help_wr[] = {
>  "wr",
>  "write memory",
> @@ -3778,6 +3802,7 @@ char *help_alias[] = {
>  "    builtin  for      foreach",
>  "    builtin  size     *",
>  "    builtin  dmesg    log",
> +"    builtin  opalmsg    opalmsg",
>  "    builtin  lsmod    mod",
>  "    builtin  last     ps -l",
>  " ",
> diff --git a/memory.c b/memory.c
> index 774d090..e7b8495 100644
> --- a/memory.c
> +++ b/memory.c
> @@ -1419,6 +1419,97 @@ struct memloc {                  /* common holder of
> read memory */
>          uint64_t limit64;
>  };
>  
> +/*
> + * Definitions derived from OPAL. These need to track corresponding values
> in
> + * https://github.com/open-power/skiboot/blob/master/include/mem-map.h
> + */
> +#define SKIBOOT_CONSOLE_DUMP_START	0x31000000
> +#define SKIBOOT_CONSOLE_DUMP_SIZE	0x40000
> +#define SKIBOOT_BASE			0x30000000
> +
> +void
> +cmd_opalmsg(void)
> +{
> +	int i, a;
> +	size_t typesz, sz;
> +	void *location;
> +	char readtype[20];
> +	char *addrtype;
> +	struct memloc mem;
> +	int displayed, per_line;
> +	int lost;
> +	ulong error_handle;
> +	struct opal {
> +		unsigned long long base;
> +		unsigned long long entry;
> +	} opal;
> +	long count = SKIBOOT_CONSOLE_DUMP_SIZE;
> +	ulonglong addr = SKIBOOT_CONSOLE_DUMP_START;
> +
> +	if (CRASHDEBUG(4))
> +		fprintf(fp, "<addr: %llx count: %ld (%s)>\n",
> +			addr, count, "PHYSADDR");
> +
> +	/*
> +	 * Are we on an OPAL platform?
> +	 * struct opal of BSS section and hence default value will be ZERO(0)
> +	 * opal_init() in the kernel initializes this structure based on
> +	 * the platform. Use it as a key to determine whether the dump
> +	 * was taken on an OPAL based system or not.
> +	 */
> +	if (symbol_exists("opal")) {
> +		get_symbol_data("opal", sizeof(struct opal), &opal);
> +		if (opal.base != SKIBOOT_BASE) {
> +			fprintf(fp, "Dump was not captured on an OPAL based machine");
> +			return;
> +		}
> +	} else {
> +		fprintf(fp, "Dump was not captured on an OPAL based machine");
> +		return;
> +	}
> +
> +	BZERO(&mem, sizeof(struct memloc));
> +	lost = typesz = per_line = 0;
> +	location = NULL;
> +
> +	/* ASCII */
> +	typesz = SIZEOF_8BIT;
> +	location = &mem.u8;
> +	sprintf(readtype, "ascii");
> +	per_line = 256;
> +	displayed = 0;
> +
> +	error_handle = FAULT_ON_ERROR;
> +
> +	for (i = a = 0; i < count; i++) {
> +		if (!readmem(addr, PHYSADDR, location, typesz,
> +			readtype, error_handle)) {
> +			addr += typesz;
> +			lost += 1;
> +			continue;
> +		}
> +
> +		if (isprint(mem.u8)) {
> +			if ((a % per_line) == 0) {
> +				if (displayed && i)
> +					fprintf(fp, "\n");
> +			}
> +			fprintf(fp, "%c", mem.u8);
> +			displayed++;
> +			a++;
> +		} else {
> +			if (count == ASCII_UNLIMITED)
> +				return;
> +			a = 0;
> +		}
> +
> +		addr += typesz;
> +	}
> +
> +	if (lost != count)
> +		fprintf(fp, "\n");
> +}
> +
>  static void
>  display_memory(ulonglong addr, long count, ulong flag, int memtype, void
>  *opt)
>  {
> --
> 2.7.4
> 
> --
> Crash-utility mailing list
> Crash-utility@redhat.com
> https://www.redhat.com/mailman/listinfo/crash-utility
>
Ankit Kumar Feb. 11, 2017, 7:12 p.m. UTC | #2
Hi Dave,


On Tuesday 07 February 2017 08:11 PM, Dave Anderson wrote:
>
> ----- Original Message -----
>> This patch creates 'opalmsg' command which can be used to dump opal console log
>> in crash. Currently it uses hard-coded start address and size of opal console log buffer.
>> As this command is specific to POWER, it is coded as such.
> Hi Ankit,
>
> There's no way I'm going to add such a subsystem- and machine-specific command to the standard
> set of generic commands.  The base command set is almost never extended, where, for example,
> the "ipcs" and "tree" commands were added in 2012, and are the only ones added since the
> crash utility was originally introduced.
>
> Here are a couple suggestions:
>
>   (1) Make it an extension module, which can be hosted on the extensions page here:
>
>         http://people.redhat.com/anderson/extensions.html
>
>   (2) Make it an option to the "mach" command, which does advertise several machine-specific
>       options:
>
>         crash> help mach
>
>         NAME
>           mach - machine specific data
>
>         SYNOPSIS
>           mach [-m | -c -[xd]]
>
>         DESCRIPTION
>           This command displays data specific to a machine type.
>
>             -m  Display the physical memory map (x86, x86_64 and ia64 only).
>             -c  Display each cpu's cpuinfo structure (x86, x86_64 and ia64 only).
>                 Display each cpu's x8664_pda structure (x86_64 only),
>                 Display the hwrpb_struct, and each cpu's percpu_struct (alpha only).
>             -x  override default output format with hexadecimal format.
>             -d  override default output format with decimal format.
>
>         EXAMPLES
>         ...
>
> Your choice, but at least "mach -o" or whatever would allow it to be contained
> in the crash binary.  Sound reasonable?
>
> Dave
>

Thank you so much for your quick reply and nice suggestion.
Will rework and send the patch soon.

~Ankit
Stewart Smith Feb. 16, 2017, 7:02 a.m. UTC | #3
Ankit Kumar <ankit@linux.vnet.ibm.com> writes:
> +/*
> + * Definitions derived from OPAL. These need to track corresponding values in
> + * https://github.com/open-power/skiboot/blob/master/include/mem-map.h
> + */
> +#define SKIBOOT_CONSOLE_DUMP_START	0x31000000
> +#define SKIBOOT_CONSOLE_DUMP_SIZE	0x40000

Where did you get this size from?

INMEM_CON_LEN in skiboot is 0x100000

Even if booting a kdump kernel, you're going to have a device tree with
the appropriate nodes present to *programatically* find out where the
console is. You should certainly do that.

We've never said that the skiboot memory map is ABI, so it *may* change.
Ankit Kumar Feb. 17, 2017, 1:24 p.m. UTC | #4
Hi Stewart,

Thank you so much for taking time and reviewing.


On Thursday 16 February 2017 12:32 PM, Stewart Smith wrote:
> Ankit Kumar <ankit@linux.vnet.ibm.com> writes:
>> +/*
>> + * Definitions derived from OPAL. These need to track corresponding values in
>> + * https://github.com/open-power/skiboot/blob/master/include/mem-map.h
>> + */
>> +#define SKIBOOT_CONSOLE_DUMP_START	0x31000000
>> +#define SKIBOOT_CONSOLE_DUMP_SIZE	0x40000
> Where did you get this size from?
>
> INMEM_CON_LEN in skiboot is 0x100000

Sorry about that.
Will send a patch correcting this.

~Ankit

> Even if booting a kdump kernel, you're going to have a device tree with
> the appropriate nodes present to *programatically* find out where the
> console is. You should certainly do that.


Currently, INMEM_CON_START(console start address) and 
INMEM_CON_LEN(size) is not part of kernel symbol table.

In crash-utility , I didn't find a way to read device-tree node from 
collected vmcore file and Hence, I hard-coded these values.

I am looking on it. Will correct this, once I get the way to read 
device-tree entry in crash-utility from collected vmcore dump.

~Ankit
Stewart Smith Feb. 20, 2017, 1:56 a.m. UTC | #5
Ankit Kumar <ankit@linux.vnet.ibm.com> writes:
> Hi Stewart,
>
> Thank you so much for taking time and reviewing.
>
>
> On Thursday 16 February 2017 12:32 PM, Stewart Smith wrote:
>> Ankit Kumar <ankit@linux.vnet.ibm.com> writes:
>>> +/*
>>> + * Definitions derived from OPAL. These need to track corresponding values in
>>> + * https://github.com/open-power/skiboot/blob/master/include/mem-map.h
>>> + */
>>> +#define SKIBOOT_CONSOLE_DUMP_START	0x31000000
>>> +#define SKIBOOT_CONSOLE_DUMP_SIZE	0x40000
>> Where did you get this size from?
>>
>> INMEM_CON_LEN in skiboot is 0x100000
>
> Sorry about that.
> Will send a patch correcting this.
>
> ~Ankit
>
>> Even if booting a kdump kernel, you're going to have a device tree with
>> the appropriate nodes present to *programatically* find out where the
>> console is. You should certainly do that.
>
>
> Currently, INMEM_CON_START(console start address) and 
> INMEM_CON_LEN(size) is not part of kernel symbol table.
>
> In crash-utility , I didn't find a way to read device-tree node from 
> collected vmcore file and Hence, I hard-coded these values.
>
> I am looking on it. Will correct this, once I get the way to read 
> device-tree entry in crash-utility from collected vmcore dump.

In ./arch/powerpc/platforms/powernv/opal-msglog.c
there's this:
/* OPAL in-memory console. Defined in OPAL source at core/console.c */
struct memcons {
        __be64 magic;
#define MEMCONS_MAGIC   0x6630696567726173L
        __be64 obuf_phys;
        __be64 ibuf_phys;
        __be32 obuf_size;
        __be32 ibuf_size;
        __be32 out_pos;
#define MEMCONS_OUT_POS_WRAP    0x80000000u
#define MEMCONS_OUT_POS_MASK    0x00ffffffu
        __be32 in_prod;
        __be32 in_cons;
};

static struct memcons *opal_memcons = NULL;

Of course.. since opal_memcons ends up pointing into memory owned by
OPAL, it may not be that useful.

Although... if that memory isn't being captured in a crash dump, it
maybe should be :)
Ankit Kumar Feb. 20, 2017, 4:31 a.m. UTC | #6
Hi Stewart,



On Monday 20 February 2017 07:26 AM, Stewart Smith wrote:
> Currently, INMEM_CON_START(console start address) and
>> INMEM_CON_LEN(size) is not part of kernel symbol table.
>>
>> In crash-utility , I didn't find a way to read device-tree node from
>> collected vmcore file and Hence, I hard-coded these values.
>>
>> I am looking on it. Will correct this, once I get the way to read
>> device-tree entry in crash-utility from collected vmcore dump.
> In ./arch/powerpc/platforms/powernv/opal-msglog.c
> there's this:
> /* OPAL in-memory console. Defined in OPAL source at core/console.c */
> struct memcons {
>          __be64 magic;
> #define MEMCONS_MAGIC   0x6630696567726173L
>          __be64 obuf_phys;
>          __be64 ibuf_phys;
>          __be32 obuf_size;
>          __be32 ibuf_size;
>          __be32 out_pos;
> #define MEMCONS_OUT_POS_WRAP    0x80000000u
> #define MEMCONS_OUT_POS_MASK    0x00ffffffu
>          __be32 in_prod;
>          __be32 in_cons;
> };
>
> static struct memcons *opal_memcons = NULL;
>
> Of course.. since opal_memcons ends up pointing into memory owned by
> OPAL, it may not be that useful.
>
> Although... if that memory isn't being captured in a crash dump, it
> maybe should be :)
>


crash-utility provides an interface to read kernel symbol but As "struct 
opal_memcons" is static and hence, is not part of kernel symbol table 
(kallsyms).
crash-utility doesn't provide an way to read device-tree from vmcore 
file(As fas as I analyzed).
We will replace hard-coded value , once we find a way to read 
device-tree from collected vmcore file in crash-utility(or any other 
alternative).


~Ankit
diff mbox

Patch

diff --git a/defs.h b/defs.h
index 68ed4d4..c81154a 100644
--- a/defs.h
+++ b/defs.h
@@ -4623,6 +4623,7 @@  void cmd_template(void);     /* tools.c */
 void cmd_alias(void);        /* cmdline.c */
 void cmd_repeat(void);       /* cmdline.c */
 void cmd_rd(void);           /* memory.c */
+void cmd_opalmsg(void);     /* memory.c */
 void cmd_wr(void);           /* memory.c */
 void cmd_ptov(void);         /* memory.c */
 void cmd_vtop(void);         /* memory.c */
@@ -5189,6 +5190,7 @@  extern char *help_ptob[];
 extern char *help_ptov[];
 extern char *help_quit[];
 extern char *help_rd[];
+extern char *help_opalmsg[];
 extern char *help_repeat[];
 extern char *help_runq[];
 extern char *help_ipcs[];
diff --git a/global_data.c b/global_data.c
index 998aaae..6bf3406 100644
--- a/global_data.c
+++ b/global_data.c
@@ -102,6 +102,9 @@  struct command_table_entry linux_command_table[] = {
         {"q",       cmd_quit,    help_quit,    MINIMAL},
         {"tree",    cmd_tree,    help_tree,    REFRESH_TASK_TABLE},
         {"rd",      cmd_rd,      help_rd,      MINIMAL},
+#if defined(PPC) || defined(PPC64)
+	{"opalmsg", cmd_opalmsg, help_opalmsg, 0},
+#endif
 	{"repeat",  cmd_repeat,  help_repeat,  0},
 	{"runq",    cmd_runq,    help_runq,    REFRESH_TASK_TABLE},
         {"search",  cmd_search,  help_search,  0},
diff --git a/help.c b/help.c
index 35cd941..75d771e 100644
--- a/help.c
+++ b/help.c
@@ -1717,6 +1717,30 @@  char *help_rd[] = {
 NULL               
 };
 
+char *help_opalmsg[] = {
+"opalmsg",
+"dump opal console log buffer",
+"",
+"  The opalmsg command retrieves, formats and dumps the OPAL console ring",
+"  buffer to help understand the state of OPAL firmware when the dump was",
+"  taken",
+"\nEXAMPLES",
+"crash> opalmsg",
+"[   65.219056911,5] SkiBoot skiboot-5.4.0-218-ge0225cc-df9a248 starting...",
+"[   65.219065872,5] initial console log level: memory 7, driver 5",
+"[   65.219068917,6] CPU: P8 generation processor(max 8 threads/core)",
+"[   65.219071681,7] CPU: Boot CPU PIR is 0x0060 PVR is 0x004d0200",
+"[   65.219074685,7] CPU: Initial max PIR set to 0x1fff",
+"[   65.219602559,5] OPAL table: 0x300c7440 .. 0x300c78d0, branch table: 0x30002000",
+"[   65.219607955,5] FDT: Parsing fdt @0xff00000",
+"[   65.225380389,5] XSCOM: chip 0x8 at 0x3fc4000000000 [P8 DD2.0]",
+"[   65.225387919,6] XSTOP: XSCOM addr = 0x2010c82, FIR bit = 31",
+"[  491.377710151,7] PHB#0022: LINK: Link is up",
+"[  494.026291523,7] BT: seq 0x25 netfn 0x0a cmd 0x48: Message sent to host",
+"[  494.027636927,7] BT: seq 0x25 netfn 0x0a cmd 0x48: IPMI MSG done",
+NULL
+};
+
 char *help_wr[] = {
 "wr",
 "write memory",
@@ -3778,6 +3802,7 @@  char *help_alias[] = {
 "    builtin  for      foreach", 
 "    builtin  size     *",
 "    builtin  dmesg    log",
+"    builtin  opalmsg    opalmsg",
 "    builtin  lsmod    mod",
 "    builtin  last     ps -l",
 " ",
diff --git a/memory.c b/memory.c
index 774d090..e7b8495 100644
--- a/memory.c
+++ b/memory.c
@@ -1419,6 +1419,97 @@  struct memloc {                  /* common holder of read memory */
         uint64_t limit64;
 };
 
+/*
+ * Definitions derived from OPAL. These need to track corresponding values in
+ * https://github.com/open-power/skiboot/blob/master/include/mem-map.h
+ */
+#define SKIBOOT_CONSOLE_DUMP_START	0x31000000
+#define SKIBOOT_CONSOLE_DUMP_SIZE	0x40000
+#define SKIBOOT_BASE			0x30000000
+
+void
+cmd_opalmsg(void)
+{
+	int i, a;
+	size_t typesz, sz;
+	void *location;
+	char readtype[20];
+	char *addrtype;
+	struct memloc mem;
+	int displayed, per_line;
+	int lost;
+	ulong error_handle;
+	struct opal {
+		unsigned long long base;
+		unsigned long long entry;
+	} opal;
+	long count = SKIBOOT_CONSOLE_DUMP_SIZE;
+	ulonglong addr = SKIBOOT_CONSOLE_DUMP_START;
+
+	if (CRASHDEBUG(4))
+		fprintf(fp, "<addr: %llx count: %ld (%s)>\n",
+			addr, count, "PHYSADDR");
+
+	/*
+	 * Are we on an OPAL platform?
+	 * struct opal of BSS section and hence default value will be ZERO(0)
+	 * opal_init() in the kernel initializes this structure based on
+	 * the platform. Use it as a key to determine whether the dump
+	 * was taken on an OPAL based system or not.
+	 */
+	if (symbol_exists("opal")) {
+		get_symbol_data("opal", sizeof(struct opal), &opal);
+		if (opal.base != SKIBOOT_BASE) {
+			fprintf(fp, "Dump was not captured on an OPAL based machine");
+			return;
+		}
+	} else {
+		fprintf(fp, "Dump was not captured on an OPAL based machine");
+		return;
+	}
+
+	BZERO(&mem, sizeof(struct memloc));
+	lost = typesz = per_line = 0;
+	location = NULL;
+
+	/* ASCII */
+	typesz = SIZEOF_8BIT;
+	location = &mem.u8;
+	sprintf(readtype, "ascii");
+	per_line = 256;
+	displayed = 0;
+
+	error_handle = FAULT_ON_ERROR;
+
+	for (i = a = 0; i < count; i++) {
+		if (!readmem(addr, PHYSADDR, location, typesz,
+			readtype, error_handle)) {
+			addr += typesz;
+			lost += 1;
+			continue;
+		}
+
+		if (isprint(mem.u8)) {
+			if ((a % per_line) == 0) {
+				if (displayed && i)
+					fprintf(fp, "\n");
+			}
+			fprintf(fp, "%c", mem.u8);
+			displayed++;
+			a++;
+		} else {
+			if (count == ASCII_UNLIMITED)
+				return;
+			a = 0;
+		}
+
+		addr += typesz;
+	}
+
+	if (lost != count)
+		fprintf(fp, "\n");
+}
+
 static void
 display_memory(ulonglong addr, long count, ulong flag, int memtype, void *opt)
 {