From: Petr Tesarik <ptesarik@suse.cz>
Subject: [x86] Add correct handling of regions allocated with the remap allocator
References: bnc#738742
Patch-mainline: no

For NUMA x86, the pgdat is remapped into the node's physical memory. Since
that physical memory may not be reachable through the identity mapping, a
small part of the identity mapping is used.

This special case has never been handled properly by crash.

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 defs.h |    4 ++
 x86.c  |  126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 122 insertions(+), 8 deletions(-)

--- a/defs.h
+++ b/defs.h
@@ -4253,6 +4253,10 @@ struct machine_specific {
 	ulonglong last_pmd_read_PAE;
 	ulonglong last_ptbl_read_PAE;
 	ulong page_protnone;
+	int max_numnodes;
+	ulong *remap_start_vaddr;
+	ulong *remap_end_vaddr;
+	ulong *remap_start_pfn;
 };
 
 struct syment *x86_is_entry_tramp_address(ulong, ulong *); 
--- a/x86.c
+++ b/x86.c
@@ -996,6 +996,7 @@ static int x86_uvtop_xen_wpt(struct task
 static int x86_kvtop_xen_wpt(struct task_context *, ulong, physaddr_t *, int);
 static int x86_uvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int);
 static int x86_kvtop_xen_wpt_PAE(struct task_context *, ulong, physaddr_t *, int);
+static int x86_kvtop_remap(ulong, physaddr_t *);
 static ulong x86_get_task_pgd(ulong);
 static ulong x86_processor_speed(void);
 static ulong x86_get_pc(struct bt_info *);
@@ -1006,6 +1007,7 @@ static uint64_t x86_memory_size(void);
 static ulong x86_vmalloc_start(void);
 static ulong *read_idt_table(int);
 static void eframe_init(void);
+static int remap_init(void);
 #define READ_IDT_INIT     1
 #define READ_IDT_RUNTIME  2
 static char *extract_idt_function(ulong *, char *, ulong *);
@@ -2014,6 +2016,9 @@ x86_init(int when)
 				   "pr_reg");
 		STRUCT_SIZE_INIT(percpu_data, "percpu_data");
 
+		if (!remap_init())
+			machdep->machspec->max_numnodes = -1;
+
 		break;
 
 	case POST_INIT:
@@ -2084,6 +2089,74 @@ eframe_init(void)
 }
 
 /*
+ *  Locate regions remapped by the remap allocator
+ */
+static int
+remap_init(void)
+{
+	ulong start_vaddr, end_vaddr, start_pfn;
+	int max_numnodes;
+	struct machine_specific *ms;
+	struct syment *sp;
+
+	if (! (sp = symbol_search("node_remap_start_vaddr")) )
+		return FALSE;
+	start_vaddr = sp->value;
+
+	if (! (sp = symbol_search("node_remap_end_vaddr")) )
+		return FALSE;
+	end_vaddr = sp->value;
+
+	if (! (sp = symbol_search("node_remap_start_pfn")) )
+		return FALSE;
+	start_pfn = sp->value;
+
+	max_numnodes = get_array_length("node_remap_start_pfn", NULL,
+					sizeof(ulong));
+	if (max_numnodes < 1)
+		max_numnodes = 1;
+
+	ms = machdep->machspec;
+	ms->remap_start_vaddr = calloc(3 * max_numnodes, sizeof(ulong));
+	if (!ms->remap_start_vaddr)
+		error(FATAL, "cannot malloc remap array");
+	ms->remap_end_vaddr = ms->remap_start_vaddr + max_numnodes;
+	ms->remap_start_pfn = ms->remap_end_vaddr + max_numnodes;
+
+	readmem(start_vaddr, KVADDR, ms->remap_start_vaddr,
+		max_numnodes * sizeof(ulong), "node_remap_start_vaddr",
+		FAULT_ON_ERROR);
+	readmem(end_vaddr, KVADDR, ms->remap_end_vaddr,
+		max_numnodes * sizeof(ulong), "node_remap_end_vaddr",
+		FAULT_ON_ERROR);
+	readmem(start_pfn, KVADDR, ms->remap_start_pfn,
+		max_numnodes * sizeof(ulong), "node_remap_end_vaddr",
+		FAULT_ON_ERROR);
+	ms->max_numnodes = max_numnodes;
+
+	return TRUE;
+}
+
+static int
+x86_kvtop_remap(ulong kvaddr, physaddr_t *paddr)
+{
+	struct machine_specific *ms;
+	int i;
+
+	ms = machdep->machspec;
+
+	for (i = 0; i < ms->max_numnodes; ++i) {
+		if (kvaddr >= ms->remap_start_vaddr[i] &&
+		    kvaddr < ms->remap_end_vaddr[i]) {
+			*paddr = PTOB(ms->remap_start_pfn[i]) +
+				kvaddr - ms->remap_start_vaddr[i];
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+/*
  *  Needs to be done this way because of potential 4G/4G split.
  */
 static int 
@@ -2768,12 +2841,13 @@ x86_kvtop(struct task_context *tc, ulong
 		}
 		pgd = (ulong *)symbol_value("idle_pg_table_l2");
 	} else {
-		if (!vt->vmalloc_start) {
+		if (x86_kvtop_remap(kvaddr, paddr)) {
+			if (!verbose)
+				return TRUE;
+		} else if (!vt->vmalloc_start) {
 			*paddr = VTOP(kvaddr);
 			return TRUE;
-		}
-
-		if (!IS_VMALLOC_ADDR(kvaddr)) { 
+		} else if (!IS_VMALLOC_ADDR(kvaddr)) { 
 			*paddr = VTOP(kvaddr);
 			if (!verbose)
 				return TRUE;
@@ -3023,12 +3097,13 @@ x86_kvtop_PAE(struct task_context *tc, u
 		else
 			pgd = (ulonglong *)symbol_value("idle_pg_table");
 	} else {
-		if (!vt->vmalloc_start) {
+		if (x86_kvtop_remap(kvaddr, paddr)) {
+			if (!verbose)
+				return TRUE;
+		} else if (!vt->vmalloc_start) {
 			*paddr = VTOP(kvaddr);
 			return TRUE;
-		}
-
-		if (!IS_VMALLOC_ADDR(kvaddr)) { 
+		} else if (!IS_VMALLOC_ADDR(kvaddr)) { 
 			*paddr = VTOP(kvaddr);
 			if (!verbose)
 				return TRUE;
@@ -3354,6 +3429,8 @@ x86_dump_machdep_table(ulong arg)
         int others;
 	ulong xen_wpt;
 	char buf[BUFSIZE];
+	struct machine_specific *ms;
+	int i, j, max_numnodes;
 
 	switch (arg) {
 	default:
@@ -3489,6 +3566,39 @@ x86_dump_machdep_table(ulong arg)
 		machdep->machspec->last_ptbl_read_PAE);
 	fprintf(fp, "                 page_protnone: %lx\n",
 		machdep->machspec->page_protnone);
+
+	ms = machdep->machspec;
+	max_numnodes = ms->max_numnodes;
+	fprintf(fp, "                  MAX_NUMNODES: ");
+	if (max_numnodes < 0) {
+		fprintf(fp, "(unused)\n");
+	} else {
+		fprintf(fp, "%ld\n", max_numnodes);
+
+		fprintf(fp, "             remap_start_vaddr:");
+		for (i = 0; i < max_numnodes; ++i) {
+			if ((i % 8) == 0)
+				fprintf(fp, "\n        ");
+			fprintf(fp, "%08lx ", ms->remap_start_vaddr[i]);
+		}
+		fprintf(fp, "\n");
+
+		fprintf(fp, "               remap_end_vaddr:");
+		for (i = 0; i < max_numnodes; ++i) {
+			if ((i % 8) == 0)
+				fprintf(fp, "\n        ");
+			fprintf(fp, "%08lx ", ms->remap_end_vaddr[i]);
+		}
+		fprintf(fp, "\n");
+
+		fprintf(fp, "               remap_start_pfn:");
+		for (i = 0; i < max_numnodes; ++i) {
+			if ((i % 8) == 0)
+				fprintf(fp, "\n        ");
+			fprintf(fp, "%08lx ", ms->remap_start_pfn[i]);
+		}
+		fprintf(fp, "\n");
+	}
 }
 
 /*
