[1/4] core/stack: backtrace unwind basic OPAL call details

Message ID 20180408064939.18879-2-npiggin@gmail.com
State Accepted
Headers show
Series
  • next round of OPAL debugging improvements
Related show

Commit Message

Nicholas Piggin April 8, 2018, 6:49 a.m.
Put OPAL callers' r1 into the stack back chain, and then use that to
unwind back to the OPAL entry frame (as opposed to boot entry, which
has a 0 back chain).

From there, dump the OPAL call token and the caller's r1. A backtrace
looks like this:

  CPU 0000 Backtrace:
   S: 0000000031c03ba0 R: 000000003001a548   ._abort+0x4c
   S: 0000000031c03c20 R: 000000003001baac   .opal_run_pollers+0x3c
   S: 0000000031c03ca0 R: 000000003001bcbc   .opal_poll_events+0xc4
   S: 0000000031c03d20 R: 00000000300051dc   opal_entry+0x12c
   --- OPAL call entry token: 0xa caller R1: 0xc0000000006d3b90 ---

This is pretty basic for the moment, but it does give you the bottom
of the Linux stack. It will allow some interesting improvements in
future.

First, with the eframe, all the call's parameters can be printed out
as well.  The ___backtrace / ___print_backtrace API needs to be
reworked in order to support this, but it's otherwise very simple
(see opal_trace_entry()).

Second, it will allow Linux's stack to be passed back to Linux via
a debugging opal call. This will allow Linux's BUG() or xmon to
also print the Linux back trace in case of a NMI or MCE or watchdog
lockup that hits in OPAL.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 asm/head.S      |  6 +++++-
 core/stack.c    | 33 ++++++++++++++++++++++++++-------
 include/stack.h | 25 ++++++++++++++++++++++---
 3 files changed, 53 insertions(+), 11 deletions(-)

Patch

diff --git a/asm/head.S b/asm/head.S
index 8049d785..ad306252 100644
--- a/asm/head.S
+++ b/asm/head.S
@@ -359,7 +359,10 @@  boot_entry:
 	/* Get ready for C code: get a stack */
 2:	GET_STACK(%r1,%r31)
 
-	/* Clear up initial frame */
+	/* Clear up initial frame.
+	 * Zero back chain indicates stack entry from boot,
+	 * non-zero indicates entry from OS (see backtrace code).
+	 */
 	li	%r3,0
 	std	%r3,0(%r1)
 	std	%r3,8(%r1)
@@ -965,6 +968,7 @@  opal_entry:
 	stdu	%r12,-STACK_FRAMESIZE(%r12)
 
 	/* Save caller r1, establish new r1 */
+	std	%r1,0(%r12)
 	std	%r1,STACK_GPR1(%r12)
 	mr	%r1,%r12
 
diff --git a/core/stack.c b/core/stack.c
index e04a4ddb..10118e42 100644
--- a/core/stack.c
+++ b/core/stack.c
@@ -29,11 +29,14 @@  static struct bt_entry bt_buf[STACK_BUF_ENTRIES];
 extern uint32_t _stext, _etext;
 
 /* Dumps backtrace to buffer */
-void __nomcount __backtrace(struct bt_entry *entries, unsigned int *count)
+void __nomcount ___backtrace(struct bt_entry *entries, unsigned int *count,
+				unsigned long r1,
+				unsigned long *token, unsigned long *r1_caller)
 {
 	unsigned int room = *count;
-	unsigned long *fp = __builtin_frame_address(0);
+	unsigned long *fp = (unsigned long *)r1;
 	unsigned long top_adj = top_of_ram;
+	struct stack_frame *eframe = (struct stack_frame *)fp;
 
 	/* Assume one stack for early backtraces */
 	if (top_of_ram == SKIBOOT_BASE + SKIBOOT_SIZE)
@@ -44,17 +47,26 @@  void __nomcount __backtrace(struct bt_entry *entries, unsigned int *count)
 		fp = (unsigned long *)fp[0];
 		if (!fp || (unsigned long)fp > top_adj)
 			break;
+		eframe = (struct stack_frame *)fp;
 		entries->sp = (unsigned long)fp;
 		entries->pc = fp[2];
 		entries++;
 		*count = (*count) + 1;
 		room--;
 	}
+
+	*r1_caller = eframe->gpr[1];
+
+	if (fp)
+		*token = eframe->gpr[0];
+	else
+		*token = -1UL;
 }
 
-void __print_backtrace(unsigned int pir,
-		       struct bt_entry *entries, unsigned int count,
-		       char *out_buf, unsigned int *len, bool symbols)
+void ___print_backtrace(unsigned int pir, struct bt_entry *entries,
+			      unsigned int count, unsigned long token,
+			      unsigned long r1_caller, char *out_buf,
+			      unsigned int *len, bool symbols)
 {
 	static char bt_text_buf[4096];
 	int i, l = 0, max;
@@ -89,6 +101,10 @@  void __print_backtrace(unsigned int pir,
 		l += snprintf(buf + l, max - l, "\n");
 		entries++;
 	}
+	if (token <= OPAL_LAST)
+		l += snprintf(buf + l, max - l, " --- OPAL call token: 0x%lx caller R1: 0x%016lx ---\n", token, r1_caller);
+	else if (token == -1UL)
+		l += snprintf(buf + l, max - l, " --- OPAL boot ---\n");
 	if (!out_buf)
 		write(stdout->fd, bt_text_buf, l);
 	buf[l++] = 0;
@@ -107,11 +123,14 @@  struct lock bt_lock = LOCK_UNLOCKED;
 void backtrace(void)
 {
 	unsigned int ents = STACK_BUF_ENTRIES;
+	unsigned long token, r1_caller;
 
 	lock(&bt_lock);
 
-	__backtrace(bt_buf, &ents);
-	__print_backtrace(mfspr(SPR_PIR), bt_buf, ents, NULL, NULL, true);
+	___backtrace(bt_buf, &ents, (unsigned long)__builtin_frame_address(0),
+			&token, &r1_caller);
+	___print_backtrace(mfspr(SPR_PIR), bt_buf, ents, token, r1_caller,
+			NULL, NULL, true);
 
 	unlock(&bt_lock);
 }
diff --git a/include/stack.h b/include/stack.h
index df08ac13..4d3e504d 100644
--- a/include/stack.h
+++ b/include/stack.h
@@ -49,6 +49,7 @@ 
 #ifndef __ASSEMBLY__
 
 #include <stdint.h>
+#include <opal-api.h>
 
 /* This is the struct used to save GPRs etc.. on OPAL entry
  * and from some exceptions. It is not always entirely populated
@@ -108,13 +109,31 @@  struct bt_entry {
 extern void *boot_stack_top;
 
 /* Create a backtrace */
-extern void __backtrace(struct bt_entry *entries, unsigned int *count);
+void ___backtrace(struct bt_entry *entries, unsigned int *count,
+				unsigned long r1,
+				unsigned long *token, unsigned long *r1_caller);
+static inline void __backtrace(struct bt_entry *entries, unsigned int *count)
+{
+	unsigned long token, r1_caller;
+
+	___backtrace(entries, count,
+			(unsigned long)__builtin_frame_address(0),
+			&token, &r1_caller);
+}
 
 /* Convert a backtrace to ASCII */
-extern void __print_backtrace(unsigned int pir, struct bt_entry *entries,
-			      unsigned int count, char *out_buf,
+extern void ___print_backtrace(unsigned int pir, struct bt_entry *entries,
+			      unsigned int count, unsigned long token,
+			      unsigned long r1_caller, char *out_buf,
 			      unsigned int *len, bool symbols);
 
+static inline void __print_backtrace(unsigned int pir, struct bt_entry *entries,
+			      unsigned int count, char *out_buf,
+			      unsigned int *len, bool symbols)
+{
+	___print_backtrace(pir, entries, count, OPAL_LAST + 1, 0, out_buf, len, symbols);
+}
+
 /* For use by debug code, create and print backtrace, uses a static buffer */
 extern void backtrace(void);