From e4cc9e7faf4517d049d2c997603015d08fe2c9e4 Mon Sep 17 00:00:00 2001
From: Dave Anderson <anderson@redhat.com>
Date: Mon, 15 Dec 2014 15:23:52 -0500
Subject: [PATCH] Fix for the X86_64 "bt" and "mach" commands when running
 against kernels that have the following Linux 3.18 commit, which removes the
 special per-cpu exception stack for handling stack segment faults:

  commit 6f442be2fb22be02cafa606f1769fa1e6f894441
  x86_64, traps: Stop using IST for #SS

Without this patch, backtraces that originate on any of the other 4
per-cpu exception stacks will be mis-labeled at the transition point
back to the previous stack.  For example, backtraces that that
originate on the NMI stack will indicate that they are coming from
the "DOUBLEFAULT" stack.  The patch examines all idt_table entries
during initialization, looking for gate descriptors that have
non-zero index values, and when found, pulls out out the handler
function address; from that information, the exception stack name
string array is properly initialized rather than being hard-coded.
This fix also properly labels the exception stack names on x86_64
CONFIG_PREEMPT_RT realtime kernels, which only utilize 3 exception
stacks instead of the traditional 5 (now 4 with this kernel commit),
instead of just showing "RT".  Also, without the patch, the "mach"
command will mis-label the stack names when it displays the base
addresses of each per-cpu exception stack.
(anderson@redhat.com)
---
 defs.h   |    5 +-
 x86_64.c |  113 ++++++++++++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 86 insertions(+), 32 deletions(-)

--- a/defs.h
+++ b/defs.h
@@ -5181,14 +5181,15 @@ struct x86_64_pt_regs_offsets {
 };
 
 #define MAX_EXCEPTION_STACKS 7
-#define NMI_STACK 2    /* ebase[] index to NMI exception stack */
-#define DEBUG_STACK 3  /* ebase[] index to DEBUG exception stack */
+#define NMI_STACK (machdep->machspec->stkinfo.NMI_stack_index)
 
 struct x86_64_stkinfo {
 	ulong ebase[NR_CPUS][MAX_EXCEPTION_STACKS];
 	int esize[MAX_EXCEPTION_STACKS];
 	ulong ibase[NR_CPUS];
 	int isize;
+	int NMI_stack_index;
+	char *exception_stacks[MAX_EXCEPTION_STACKS];
 };
 
 struct machine_specific {
--- a/x86_64.c
+++ b/x86_64.c
@@ -117,6 +117,7 @@ static void GART_init(void);
 
 struct machine_specific x86_64_machine_specific = { 0 };
 
+static void x86_64_exception_stacks_init(void);
 /*
  *  Do all necessary machine-specific setup here.  This is called several
  *  times during initialization.
@@ -798,6 +799,14 @@ x86_64_dump_machdep_table(ulong arg)
 		ms->stkinfo.esize[5], 
 		ms->stkinfo.esize[6], 
 		machdep->flags & NO_TSS ? " (NO TSS) " : " ");
+
+	fprintf(fp, "                           NMI_stack_index: %d\n", 
+		ms->stkinfo.NMI_stack_index);
+        fprintf(fp, "                           exception_stacks:\n");
+        for (i = 0; i < MAX_EXCEPTION_STACKS; i++)
+		fprintf(fp, "                             [%d]: %s\n", i, 
+			ms->stkinfo.exception_stacks[i]);
+
 	fprintf(fp, "                           ebase[%s][%d]:",
 		arg ? "NR_CPUS" : "cpus", MAX_EXCEPTION_STACKS);
 	cpus = arg ? NR_CPUS : kt->cpus;
@@ -1059,17 +1068,6 @@ x86_64_per_cpu_init(void)
 	verify_spinlock();
 }
 
-static char *
-x86_64_exception_stacks[MAX_EXCEPTION_STACKS] = {
-	"STACKFAULT",
-	"DOUBLEFAULT",
-	"NMI",
-	"DEBUG",
-	"MCE",
-	"(unknown)",
-	"(unknown)"
-};
-
 /*
  *  Gather the ist addresses for each CPU.
  */
@@ -1086,6 +1084,8 @@ x86_64_ist_init(void)
 	tss_sp = per_cpu_symbol_search("per_cpu__init_tss");
 	ist_sp = per_cpu_symbol_search("per_cpu__orig_ist");
 
+	x86_64_exception_stacks_init();
+
 	if (!tss_sp && symbol_exists("init_tss")) {
 		init_tss = symbol_value("init_tss");
 	
@@ -1135,7 +1135,7 @@ x86_64_ist_init(void)
 					if (ms->stkinfo.ebase[c][i] != estacks[i])
 						error(WARNING, 
 						    "cpu %d %s stack: init_tss: %lx orig_ist: %lx\n", c,  
-							x86_64_exception_stacks[i],
+							ms->stkinfo.exception_stacks[i],
 							ms->stkinfo.ebase[c][i], estacks[i]);
 					ms->stkinfo.ebase[c][i] = estacks[i];
 				}
@@ -1165,22 +1165,12 @@ x86_64_ist_init(void)
                                 break;
 			cnt++;
 			if ((THIS_KERNEL_VERSION >= LINUX(2,6,18)) &&
-			    (i == DEBUG_STACK))
+			    STREQ(ms->stkinfo.exception_stacks[i], "DEBUG"))
 				ms->stkinfo.esize[i] = esize*2;
 			else
 				ms->stkinfo.esize[i] = esize;
 			ms->stkinfo.ebase[c][i] -= ms->stkinfo.esize[i];
 		}
-		/*
-		 * RT kernel only uses 3 exception stacks for the 5 types.
-		 */
-		if ((c == 0) && (cnt == 3)) {
-			x86_64_exception_stacks[0] = "RT";
-			x86_64_exception_stacks[1] = "RT";
-			x86_64_exception_stacks[2] = "RT";
-			x86_64_exception_stacks[3] = "(unknown)";
-			x86_64_exception_stacks[4] = "(unknown)";
-		}
 	}
 
 	/*
@@ -2351,7 +2341,7 @@ x86_64_eframe_search(struct bt_info *bt)
                                 	break;
                                 bt->hp->esp = ms->stkinfo.ebase[c][i];
                                 fprintf(fp, "CPU %d %s EXCEPTION STACK:",
-					c, x86_64_exception_stacks[i]);
+					c, ms->stkinfo.exception_stacks[i]);
 
 				if (hide_offline_cpu(c)) {
 					fprintf(fp, " [OFFLINE]\n\n");
@@ -3079,7 +3069,7 @@ in_exception_stack:
 
 		if (!BT_REFERENCE_CHECK(bt))
 			fprintf(fp, "--- <%s exception stack> ---\n",
-				x86_64_exception_stacks[estack_index]);
+				ms->stkinfo.exception_stacks[estack_index]);
 
 		/*
 		 * Find the CPU-saved, or handler-saved registers
@@ -3128,7 +3118,7 @@ in_exception_stack:
 				fprintf(ofp, 
      				    "    [ %s exception stack recursion: "
 				    "prior stack location overwritten ]\n",
-					x86_64_exception_stacks[estack_index]);
+					ms->stkinfo.exception_stacks[estack_index]);
 				return;
 			}
 
@@ -4546,12 +4536,12 @@ skip_stage:
 		bt->stacktop = ms->stkinfo.ebase[bt->tc->processor][estack] +
                 	ms->stkinfo.esize[estack];
 		console("x86_64_get_dumpfile_stack_frame: searching %s estack at %lx\n", 
-			x86_64_exception_stacks[estack], bt->stackbase);
+			ms->stkinfo.exception_stacks[estack], bt->stackbase);
 		if (!(bt->stackbase)) 
 			goto skip_stage;
 		bt->stackbuf = ms->irqstack;
 		alter_stackbuf(bt);
-		in_nmi_stack = STREQ(x86_64_exception_stacks[estack], "NMI");
+		in_nmi_stack = STREQ(ms->stkinfo.exception_stacks[estack], "NMI");
 		goto next_stack;
 
 	}
@@ -4778,6 +4768,69 @@ x86_64_display_idt_table(void)
 	FREEBUF(idt_table_buf);
 }
 
+static void
+x86_64_exception_stacks_init(void)
+{
+        char *idt_table_buf;
+        char buf[BUFSIZE];
+	int i;
+        ulong *ip, ist;
+	long size;
+	struct machine_specific *ms;
+
+	ms = machdep->machspec;
+
+	ms->stkinfo.NMI_stack_index = -1;
+	for (i = 0; i < MAX_EXCEPTION_STACKS; i++)
+		ms->stkinfo.exception_stacks[i] = "(unknown)";
+
+	if (!kernel_symbol_exists("idt_table"))
+		return;
+
+        if (INVALID_SIZE(gate_struct))
+                size = 16;
+	else
+		size = SIZE(gate_struct);
+
+        idt_table_buf = GETBUF(size * 256);
+        readmem(symbol_value("idt_table"), KVADDR, idt_table_buf,
+                size * 256, "idt_table", FAULT_ON_ERROR);
+        ip = (ulong *)idt_table_buf;
+
+	if (CRASHDEBUG(1))
+		fprintf(fp, "exception IST:\n");
+
+	for (i = 0; i < 256; i++, ip += 2) {
+		ist = ((*ip) >> 32) & 0x7;
+		if (ist) {
+                        x86_64_extract_idt_function(ip, buf, NULL);
+			if (CRASHDEBUG(1))
+				fprintf(fp, "  %ld: %s\n", ist, buf);
+			if (strstr(buf, "nmi")) {
+				ms->stkinfo.NMI_stack_index = ist-1; 
+				ms->stkinfo.exception_stacks[ist-1] = "NMI";
+			}
+			if (strstr(buf, "debug"))
+				ms->stkinfo.exception_stacks[ist-1] = "DEBUG";
+			if (strstr(buf, "stack"))
+				ms->stkinfo.exception_stacks[ist-1] = "STACKFAULT";
+			if (strstr(buf, "double"))
+				ms->stkinfo.exception_stacks[ist-1] = "DOUBLEFAULT";
+			if (strstr(buf, "machine"))
+				ms->stkinfo.exception_stacks[ist-1] = "MCE";
+		}
+	}
+
+	if (CRASHDEBUG(1)) {
+		fprintf(fp, "exception stacks:\n");
+		for (i = 0; i < MAX_EXCEPTION_STACKS; i++) 
+			fprintf(fp, "  [%d]: %s\n", i, ms->stkinfo.exception_stacks[i]);
+	}
+
+	FREEBUF(idt_table_buf);
+}
+
+
 /*
  *  Extract the function name out of the IDT entry.
  */
@@ -5109,9 +5162,9 @@ x86_64_display_machine_stats(void)
 		if (machdep->machspec->stkinfo.ebase[0][i] == 0)
 			break;
 		fprintf(fp, "%11s STACK SIZE: %d\n",
-			x86_64_exception_stacks[i],
+			machdep->machspec->stkinfo.exception_stacks[i],
 			machdep->machspec->stkinfo.esize[i]);
-		sprintf(buf, "%s STACKS:\n", x86_64_exception_stacks[i]);
+		sprintf(buf, "%s STACKS:\n", machdep->machspec->stkinfo.exception_stacks[i]);
 		fprintf(fp, "%24s", buf);
 		for (c = 0; c < kt->cpus; c++) {
 			if (machdep->machspec->stkinfo.ebase[c][i] == 0)
