[Crash-utility] [PATCH v3 6/6] x86_64: Fix the misusage of PGDIR_SHIFT

Dou Liyang douly.fnst at cn.fujitsu.com
Mon Jan 29 10:51:43 UTC 2018


Due to the backwards compatibility, we should setup machdep->machspec->pgdir_shift
correctly in x86_64_init() for all versions of linux. this patch makes the x86_64
default page level as 4-level paging table and sets the 4-level paging table for
each Linux versions, and add a new function named x86_64_upgd_offset_legacy()
for old linuxes to get the user page global directory entry.

Signed-off-by: Dou Liyang <douly.fnst at cn.fujitsu.com>
---
 defs.h   | 15 +++++++--------
 sadump.c |  4 ++--
 x86_64.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/defs.h b/defs.h
index 5804206..ff5494c 100644
--- a/defs.h
+++ b/defs.h
@@ -3332,19 +3332,18 @@ struct arm64_stackframe {
 #define VTOP(X)               x86_64_VTOP((ulong)(X))
 #define IS_VMALLOC_ADDR(X)    x86_64_IS_VMALLOC_ADDR((ulong)(X))
 
-/* origin level page */
-#define PGDIR_SHIFT     30
+/*
+ * the default page table level for x86_64:
+ *    4 level page tables
+ */
+#define PGDIR_SHIFT     39
 #define PTRS_PER_PGD    512
+#define PUD_SHIFT       30
+#define PTRS_PER_PUD    512
 #define PMD_SHIFT       21
 #define PTRS_PER_PMD    512
 #define PTRS_PER_PTE    512
 
-/* 4 level page */
-#define PGDIR_SHIFT_4LEVEL    39
-#define PTRS_PER_PGD_4LEVEL  512
-#define PUD_SHIFT       30
-#define PTRS_PER_PUD    512
-
 /* 5 level page */
 #define PGDIR_SHIFT_5LEVEL    48
 #define PTRS_PER_PGD_5LEVEL  512
diff --git a/sadump.c b/sadump.c
index 17cfd93..d19b40a 100644
--- a/sadump.c
+++ b/sadump.c
@@ -2059,8 +2059,8 @@ sadump_calc_kaslr_offset(ulong *kaslr_offset)
 	vt->kernel_pgd[0] = pgd;
 	machdep->last_pgd_read = vt->kernel_pgd[0];
 	machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6;
-	machdep->machspec->pgdir_shift = PGDIR_SHIFT_4LEVEL;
-	machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD_4LEVEL;
+	machdep->machspec->pgdir_shift = PGDIR_SHIFT;
+	machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
 	if (!readmem(pgd, PHYSADDR, machdep->pgd, PAGESIZE(),
 			"pgd", RETURN_ON_ERROR))
 		goto quit;
diff --git a/x86_64.c b/x86_64.c
index d3d5ab9..a6b1f50 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -79,6 +79,7 @@ static int x86_64_is_kvaddr(ulong);
 static int x86_64_is_uvaddr(ulong, struct task_context *);
 static ulong *x86_64_kpgd_offset(ulong, int, int);
 static ulong x86_64_upgd_offset(struct task_context *, ulong, int, int);
+static ulong x86_64_upgd_offset_legacy(struct task_context *, ulong, int, int);
 static ulong x86_64_p4d_offset(ulong, ulong, int, int);
 static ulong x86_64_pud_offset(ulong, ulong, int, int);
 static ulong x86_64_pmd_offset(ulong, ulong, int, int);
@@ -260,8 +261,8 @@ x86_64_init(int when)
 
 			machdep->uvtop = x86_64_uvtop_level4;
 			machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_2_6;
-			machdep->machspec->pgdir_shift = PGDIR_SHIFT_4LEVEL;
-			machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD_4LEVEL;
+			machdep->machspec->pgdir_shift = PGDIR_SHIFT;
+			machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
 			break;
 
                 case VM_XEN:
@@ -273,6 +274,8 @@ x86_64_init(int when)
                         machdep->machspec->modules_vaddr = MODULES_VADDR_XEN;
                         machdep->machspec->modules_end = MODULES_END_XEN;
 			machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN;
+			machdep->machspec->pgdir_shift = PGDIR_SHIFT;
+			machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
                         break;
 
 		case VM_XEN_RHEL4:
@@ -284,6 +287,8 @@ x86_64_init(int when)
                         machdep->machspec->modules_vaddr = MODULES_VADDR_XEN_RHEL4;
                         machdep->machspec->modules_end = MODULES_END_XEN_RHEL4;
 			machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN;
+			machdep->machspec->pgdir_shift = PGDIR_SHIFT;
+			machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
 			break;
 
 		case VM_5LEVEL:
@@ -627,20 +632,14 @@ x86_64_init(int when)
 				case VM_XEN: 
 				case VM_2_6_11:
                         		machdep->uvtop = x86_64_uvtop_level4_xen_wpt;
-					machdep->machspec->pgdir_shift = PGDIR_SHIFT_4LEVEL;
-					machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD_4LEVEL;
 					break;
 				case VM_XEN_RHEL4:
                         		machdep->uvtop = x86_64_uvtop_level4_rhel4_xen_wpt;
-					machdep->machspec->pgdir_shift = PGDIR_SHIFT;
-					machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD;
 					break;
 				}
 				machdep->machspec->physical_mask_shift = __PHYSICAL_MASK_SHIFT_XEN;
 			} else {
 				machdep->uvtop = x86_64_uvtop_level4;
-				machdep->machspec->pgdir_shift = PGDIR_SHIFT_4LEVEL;
-				machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD_4LEVEL;
 			}
                         MEMBER_OFFSET_INIT(vcpu_guest_context_user_regs,
                                 "vcpu_guest_context", "user_regs");
@@ -1617,6 +1616,43 @@ x86_64_kpgd_offset(ulong kvaddr, int verbose, int IS_XEN)
 	return pgd;
 }
 
+/*
+ * In x86 64 bit system, Linux uses the 4-level page table as the default both
+ * in Kernel page tables and user page tables.
+ *
+ * But in some old versions(pre-2.6.11), the 3-level page table is used for
+ * user page tables.
+ *
+ * So reuse the PUD and find the user pgd entry for this older version Linux..
+ * pgd = pgd_offset(mm, address);
+ */
+static ulong
+x86_64_upgd_offset_legacy(struct task_context *tc, ulong uvaddr, int verbose, int IS_XEN)
+{
+	ulong *pud;
+	ulong pud_paddr;
+	ulong pud_pte;
+
+        if (task_mm(tc->task, TRUE))
+                pud = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd));
+        else
+                readmem(tc->mm_struct + OFFSET(mm_struct_pgd), KVADDR, &pud,
+                        sizeof(long), "mm_struct pgd", FAULT_ON_ERROR);
+
+        pud_paddr = x86_64_VTOP((ulong)pud);
+        FILL_PUD(pud_paddr, PHYSADDR, PAGESIZE());
+	pud = ((ulong *)pud_paddr) + pud_index(uvaddr);
+	pud_pte = ULONG(machdep->pud + PAGEOFFSET(pud));
+        if (verbose) {
+		if(IS_XEN)
+			fprintf(fp, "   PGD: %lx => %lx [machine]\n", (ulong)pud, pud_pte);
+		else
+			fprintf(fp, "   PGD: %lx => %lx\n", (ulong)pud, pud_pte);
+        }
+
+	return pud_pte;
+}
+
 /*
  * Find the user pgd entry..
  * pgd = pgd_offset(mm, address);
@@ -1997,7 +2033,7 @@ x86_64_uvtop_level4_rhel4_xen_wpt(struct task_context *tc, ulong uvaddr, physadd
 	if (IS_KVADDR(uvaddr))
 		return x86_64_kvtop(tc, uvaddr, paddr, verbose);
 
-	pgd_pte = x86_64_upgd_offset(tc, uvaddr, verbose, TRUE);
+	pgd_pte = x86_64_upgd_offset_legacy(tc, uvaddr, verbose, TRUE);
 	if (!(pgd_pte & _PAGE_PRESENT))
 		goto no_upage;
 
@@ -2094,7 +2130,7 @@ x86_64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verbo
 	/*
 	 *  pgd = pgd_offset(mm, address);
 	 */
-	pgd_pte = x86_64_upgd_offset(tc, uvaddr, verbose, FALSE);
+	pgd_pte = x86_64_upgd_offset_legacy(tc, uvaddr, verbose, FALSE);
 	if (!(pgd_pte & _PAGE_PRESENT))
 		goto no_upage;
 
-- 
2.14.3






More information about the Crash-utility mailing list