[Crash-utility] [PATCH 6/6] ppc64/book3s: support big endian Linux page tables

Hari Bathini hbathini at linux.vnet.ibm.com
Wed Sep 14 21:23:45 UTC 2016


Power ISA 3.0 defines a new MMU mode, known as Radix Tree Translation,
where the hardware can directly operate on the Linux page tables.
However the hardware requires that the page tables be in big endian
format. Starting with kernel v4.7, Linux page tables are big endian
for both radix and hash MMU modes in book3s 64. This patch makes the
corresponding changes here.

Signed-off-by: Hari Bathini <hbathini at linux.vnet.ibm.com>
---
 defs.h  |    2 ++
 ppc64.c |   21 +++++++++++++++++----
 tools.c |   16 ++++++++++++++++
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/defs.h b/defs.h
index a56fa65..0a15798 100644
--- a/defs.h
+++ b/defs.h
@@ -4821,6 +4821,7 @@ int calculate(char *, ulong *, ulonglong *, ulong);
 int endian_mismatch(char *, char, ulong);
 uint16_t swap16(uint16_t, int);
 uint32_t swap32(uint32_t, int);
+uint64_t swap64(uint64_t, int);
 ulong *get_cpumask_buf(void);
 int make_cpumask(char *, ulong *, int, int *);
 size_t strlcpy(char *, char *, size_t);
@@ -5742,6 +5743,7 @@ void ppc64_dump_machdep_table(ulong);
 #define VMEMMAP_AWARE   (0x4)
 #define BOOK3E          (0x8)
 #define PHYS_ENTRY_L4   (0x10)
+#define SWAP_ENTRY_L4   (0x20)
 
 #define REGION_SHIFT       (60UL)
 #define REGION_ID(addr)    (((unsigned long)(addr)) >> REGION_SHIFT)
diff --git a/ppc64.c b/ppc64.c
index 4e18513..83ebd16 100644
--- a/ppc64.c
+++ b/ppc64.c
@@ -15,7 +15,9 @@
  * GNU General Public License for more details.
  */
 #ifdef PPC64
+
 #include "defs.h"
+#include <endian.h>
 
 static int ppc64_kvtop(struct task_context *, ulong, physaddr_t *, int);
 static int ppc64_uvtop(struct task_context *, ulong, physaddr_t *, int);
@@ -379,6 +381,16 @@ ppc64_init(int when)
 				m->_page_present = 0x1UL << 63;
 				machdep->flags |= PHYS_ENTRY_L4;
 			}
+
+			if (THIS_KERNEL_VERSION >= LINUX(4,7,0)) {
+				/*
+				 * Starting with kernel v4.7 page table entries
+				 * are always big endian on BOOK3S. Set this
+				 * flag if kernel is not big endian.
+				 */
+				if (__BYTE_ORDER == __LITTLE_ENDIAN)
+					machdep->flags |= SWAP_ENTRY_L4;
+			}
 		}
 
 		if (!(machdep->flags & (VM_ORIG|VM_4_LEVEL))) {
@@ -854,13 +866,14 @@ ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose)
 	ulong pte;
 	uint  pdshift;
 	uint  hugepage_type = 0; /* 0: regular entry; 1: huge pte; 2: huge pd */
+	uint  swap = !!(machdep->flags & SWAP_ENTRY_L4);
 
 	if (verbose)
 		fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)level4);
 
 	pgdir = (ulong *)((ulong *)level4 + PGD_OFFSET_L4(vaddr));
 	FILL_PGD(PAGEBASE(level4), KVADDR, PAGESIZE());
-	pgd_pte = ULONG(machdep->pgd + PAGEOFFSET(pgdir));
+	pgd_pte = swap64(ULONG(machdep->pgd + PAGEOFFSET(pgdir)), swap);
 	if (verbose)
 		fprintf(fp, "  PGD: %lx => %lx\n", (ulong)pgdir, pgd_pte);
 	if (!pgd_pte)
@@ -878,7 +891,7 @@ ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose)
 		pgd_pte = pgd_page_vaddr_l4(pgd_pte);
 		page_upper = (ulong *)((ulong *)pgd_pte + PUD_OFFSET_L4(vaddr));
 		FILL_PUD(PAGEBASE(pgd_pte), KVADDR, PAGESIZE());
-		pud_pte = ULONG(machdep->pud + PAGEOFFSET(page_upper));
+		pud_pte = swap64(ULONG(machdep->pud + PAGEOFFSET(page_upper)), swap);
 
 		if (verbose)
 			fprintf(fp, "  PUD: %lx => %lx\n", (ulong)page_upper, pud_pte);
@@ -898,7 +911,7 @@ ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose)
 	pud_pte = pud_page_vaddr_l4(pud_pte);
 	page_middle = (ulong *)((ulong *)pud_pte + PMD_OFFSET_L4(vaddr));
 	FILL_PMD(PAGEBASE(pud_pte), KVADDR, PAGESIZE());
-	pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(page_middle));
+	pmd_pte = swap64(ULONG(machdep->pmd + PAGEOFFSET(page_middle)), swap);
 
 	if (verbose)
 		fprintf(fp, "  PMD: %lx => %lx\n", (ulong)page_middle, pmd_pte);
@@ -921,7 +934,7 @@ ppc64_vtop_level4(ulong vaddr, ulong *level4, physaddr_t *paddr, int verbose)
 			(ulong)page_table);
 
 	FILL_PTBL(PAGEBASE(pmd_pte), KVADDR, PAGESIZE());
-	pte = ULONG(machdep->ptbl + PAGEOFFSET(page_table));
+	pte = swap64(ULONG(machdep->ptbl + PAGEOFFSET(page_table)), swap);
 
 	if (verbose)
 		fprintf(fp, "  PTE: %lx => %lx\n", (ulong)page_table, pte);
diff --git a/tools.c b/tools.c
index 1321a9b..53c496f 100644
--- a/tools.c
+++ b/tools.c
@@ -5838,6 +5838,22 @@ swap32(uint32_t val, int swap)
 		return val;
 }
 
+uint64_t
+swap64(uint64_t val, int swap)
+{
+	if (swap)
+		return (((val & 0x00000000000000ffULL) << 56) |
+			((val & 0x000000000000ff00ULL) << 40) |
+			((val & 0x0000000000ff0000ULL) << 24) |
+			((val & 0x00000000ff000000ULL) <<  8) |
+			((val & 0x000000ff00000000ULL) >>  8) |
+			((val & 0x0000ff0000000000ULL) >> 24) |
+			((val & 0x00ff000000000000ULL) >> 40) |
+			((val & 0xff00000000000000ULL) >> 56));
+	else
+		return val;
+}
+
 /*
  *  Get a sufficiently large buffer for cpumask.
  *  You should call FREEBUF() on the result when you no longer need it.




More information about the Crash-utility mailing list