]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
powerpc: Move page table dump files in a dedicated subdirectory
authorChristophe Leroy <christophe.leroy@c-s.fr>
Mon, 18 Feb 2019 12:28:36 +0000 (12:28 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 22 Feb 2019 11:29:22 +0000 (22:29 +1100)
This patch moves the files related to page table dump in a
dedicated subdirectory.

The purpose is to clean a bit arch/powerpc/mm by regrouping
multiple files handling a dedicated function.

Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr>
[mpe: Shorten the file names while we're at it]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
19 files changed:
arch/powerpc/Kconfig.debug
arch/powerpc/mm/Makefile
arch/powerpc/mm/dump_bats.c [deleted file]
arch/powerpc/mm/dump_hashpagetable.c [deleted file]
arch/powerpc/mm/dump_linuxpagetables-8xx.c [deleted file]
arch/powerpc/mm/dump_linuxpagetables-book3s64.c [deleted file]
arch/powerpc/mm/dump_linuxpagetables-generic.c [deleted file]
arch/powerpc/mm/dump_linuxpagetables.c [deleted file]
arch/powerpc/mm/dump_linuxpagetables.h [deleted file]
arch/powerpc/mm/dump_sr.c [deleted file]
arch/powerpc/mm/ptdump/8xx.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/Makefile [new file with mode: 0644]
arch/powerpc/mm/ptdump/bats.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/book3s64.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/hashpagetable.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/ptdump.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/ptdump.h [new file with mode: 0644]
arch/powerpc/mm/ptdump/segment_regs.c [new file with mode: 0644]
arch/powerpc/mm/ptdump/shared.c [new file with mode: 0644]

index f4961fbcb48d5458a6400a83d0b173fbd6d474fa..4e00cb0a54646706f7485900847fdbacf9a5dec5 100644 (file)
@@ -361,10 +361,6 @@ config PPC_PTDUMP
 
          If you are unsure, say N.
 
-config PPC_HTDUMP
-       def_bool y
-       depends on PPC_PTDUMP && PPC_BOOK3S_64
-
 config PPC_FAST_ENDIAN_SWITCH
        bool "Deprecated fast endian-switch syscall"
         depends on DEBUG_KERNEL && PPC_BOOK3S_64
index f965fc33a8b77ce47714c3a57fc8ccd12deae11e..ee1efa3b3382689285ff4e8cce09025d1a280bc0 100644 (file)
@@ -45,13 +45,5 @@ obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
 obj-$(CONFIG_HIGHMEM)          += highmem.o
 obj-$(CONFIG_PPC_COPRO_BASE)   += copro_fault.o
 obj-$(CONFIG_SPAPR_TCE_IOMMU)  += mmu_context_iommu.o
-obj-$(CONFIG_PPC_PTDUMP)       += dump_linuxpagetables.o
-ifdef CONFIG_PPC_PTDUMP
-obj-$(CONFIG_4xx)              += dump_linuxpagetables-generic.o
-obj-$(CONFIG_PPC_8xx)          += dump_linuxpagetables-8xx.o
-obj-$(CONFIG_PPC_BOOK3E_MMU)   += dump_linuxpagetables-generic.o
-obj-$(CONFIG_PPC_BOOK3S_32)    += dump_linuxpagetables-generic.o dump_bats.o dump_sr.o
-obj-$(CONFIG_PPC_BOOK3S_64)    += dump_linuxpagetables-book3s64.o
-endif
-obj-$(CONFIG_PPC_HTDUMP)       += dump_hashpagetable.o
+obj-$(CONFIG_PPC_PTDUMP)       += ptdump/
 obj-$(CONFIG_PPC_MEM_KEYS)     += pkeys.o
diff --git a/arch/powerpc/mm/dump_bats.c b/arch/powerpc/mm/dump_bats.c
deleted file mode 100644 (file)
index a0d23e9..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright 2018, Christophe Leroy CS S.I.
- * <christophe.leroy@c-s.fr>
- *
- * This dumps the content of BATS
- */
-
-#include <asm/debugfs.h>
-#include <asm/pgtable.h>
-#include <asm/cpu_has_feature.h>
-
-static char *pp_601(int k, int pp)
-{
-       if (pp == 0)
-               return k ? "NA" : "RWX";
-       if (pp == 1)
-               return k ? "ROX" : "RWX";
-       if (pp == 2)
-               return k ? "RWX" : "RWX";
-       return k ? "ROX" : "ROX";
-}
-
-static void bat_show_601(struct seq_file *m, int idx, u32 lower, u32 upper)
-{
-       u32 blpi = upper & 0xfffe0000;
-       u32 k = (upper >> 2) & 3;
-       u32 pp = upper & 3;
-       phys_addr_t pbn = PHYS_BAT_ADDR(lower);
-       u32 bsm = lower & 0x3ff;
-       u32 size = (bsm + 1) << 17;
-
-       seq_printf(m, "%d: ", idx);
-       if (!(lower & 0x40)) {
-               seq_puts(m, "        -\n");
-               return;
-       }
-
-       seq_printf(m, "0x%08x-0x%08x ", blpi, blpi + size - 1);
-#ifdef CONFIG_PHYS_64BIT
-       seq_printf(m, "0x%016llx ", pbn);
-#else
-       seq_printf(m, "0x%08x ", pbn);
-#endif
-
-       seq_printf(m, "Kernel %s User %s", pp_601(k & 2, pp), pp_601(k & 1, pp));
-
-       if (lower & _PAGE_WRITETHRU)
-               seq_puts(m, "write through ");
-       if (lower & _PAGE_NO_CACHE)
-               seq_puts(m, "no cache ");
-       if (lower & _PAGE_COHERENT)
-               seq_puts(m, "coherent ");
-       seq_puts(m, "\n");
-}
-
-#define BAT_SHOW_601(_m, _n, _l, _u) bat_show_601(_m, _n, mfspr(_l), mfspr(_u))
-
-static int bats_show_601(struct seq_file *m, void *v)
-{
-       seq_puts(m, "---[ Block Address Translation ]---\n");
-
-       BAT_SHOW_601(m, 0, SPRN_IBAT0L, SPRN_IBAT0U);
-       BAT_SHOW_601(m, 1, SPRN_IBAT1L, SPRN_IBAT1U);
-       BAT_SHOW_601(m, 2, SPRN_IBAT2L, SPRN_IBAT2U);
-       BAT_SHOW_601(m, 3, SPRN_IBAT3L, SPRN_IBAT3U);
-
-       return 0;
-}
-
-static void bat_show_603(struct seq_file *m, int idx, u32 lower, u32 upper, bool is_d)
-{
-       u32 bepi = upper & 0xfffe0000;
-       u32 bl = (upper >> 2) & 0x7ff;
-       u32 k = upper & 3;
-       phys_addr_t brpn = PHYS_BAT_ADDR(lower);
-       u32 size = (bl + 1) << 17;
-
-       seq_printf(m, "%d: ", idx);
-       if (k == 0) {
-               seq_puts(m, "        -\n");
-               return;
-       }
-
-       seq_printf(m, "0x%08x-0x%08x ", bepi, bepi + size - 1);
-#ifdef CONFIG_PHYS_64BIT
-       seq_printf(m, "0x%016llx ", brpn);
-#else
-       seq_printf(m, "0x%08x ", brpn);
-#endif
-
-       if (k == 1)
-               seq_puts(m, "User ");
-       else if (k == 2)
-               seq_puts(m, "Kernel ");
-       else
-               seq_puts(m, "Kernel/User ");
-
-       if (lower & BPP_RX)
-               seq_puts(m, is_d ? "RO " : "EXEC ");
-       else if (lower & BPP_RW)
-               seq_puts(m, is_d ? "RW " : "EXEC ");
-       else
-               seq_puts(m, is_d ? "NA " : "NX   ");
-
-       if (lower & _PAGE_WRITETHRU)
-               seq_puts(m, "write through ");
-       if (lower & _PAGE_NO_CACHE)
-               seq_puts(m, "no cache ");
-       if (lower & _PAGE_COHERENT)
-               seq_puts(m, "coherent ");
-       if (lower & _PAGE_GUARDED)
-               seq_puts(m, "guarded ");
-       seq_puts(m, "\n");
-}
-
-#define BAT_SHOW_603(_m, _n, _l, _u, _d) bat_show_603(_m, _n, mfspr(_l), mfspr(_u), _d)
-
-static int bats_show_603(struct seq_file *m, void *v)
-{
-       seq_puts(m, "---[ Instruction Block Address Translation ]---\n");
-
-       BAT_SHOW_603(m, 0, SPRN_IBAT0L, SPRN_IBAT0U, false);
-       BAT_SHOW_603(m, 1, SPRN_IBAT1L, SPRN_IBAT1U, false);
-       BAT_SHOW_603(m, 2, SPRN_IBAT2L, SPRN_IBAT2U, false);
-       BAT_SHOW_603(m, 3, SPRN_IBAT3L, SPRN_IBAT3U, false);
-       if (mmu_has_feature(MMU_FTR_USE_HIGH_BATS)) {
-               BAT_SHOW_603(m, 4, SPRN_IBAT4L, SPRN_IBAT4U, false);
-               BAT_SHOW_603(m, 5, SPRN_IBAT5L, SPRN_IBAT5U, false);
-               BAT_SHOW_603(m, 6, SPRN_IBAT6L, SPRN_IBAT6U, false);
-               BAT_SHOW_603(m, 7, SPRN_IBAT7L, SPRN_IBAT7U, false);
-       }
-
-       seq_puts(m, "\n---[ Data Block Address Translation ]---\n");
-
-       BAT_SHOW_603(m, 0, SPRN_DBAT0L, SPRN_DBAT0U, true);
-       BAT_SHOW_603(m, 1, SPRN_DBAT1L, SPRN_DBAT1U, true);
-       BAT_SHOW_603(m, 2, SPRN_DBAT2L, SPRN_DBAT2U, true);
-       BAT_SHOW_603(m, 3, SPRN_DBAT3L, SPRN_DBAT3U, true);
-       if (mmu_has_feature(MMU_FTR_USE_HIGH_BATS)) {
-               BAT_SHOW_603(m, 4, SPRN_DBAT4L, SPRN_DBAT4U, true);
-               BAT_SHOW_603(m, 5, SPRN_DBAT5L, SPRN_DBAT5U, true);
-               BAT_SHOW_603(m, 6, SPRN_DBAT6L, SPRN_DBAT6U, true);
-               BAT_SHOW_603(m, 7, SPRN_DBAT7L, SPRN_DBAT7U, true);
-       }
-
-       return 0;
-}
-
-static int bats_open(struct inode *inode, struct file *file)
-{
-       if (cpu_has_feature(CPU_FTR_601))
-               return single_open(file, bats_show_601, NULL);
-
-       return single_open(file, bats_show_603, NULL);
-}
-
-static const struct file_operations bats_fops = {
-       .open           = bats_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int __init bats_init(void)
-{
-       struct dentry *debugfs_file;
-
-       debugfs_file = debugfs_create_file("block_address_translation", 0400,
-                                          powerpc_debugfs_root, NULL, &bats_fops);
-       return debugfs_file ? 0 : -ENOMEM;
-}
-device_initcall(bats_init);
diff --git a/arch/powerpc/mm/dump_hashpagetable.c b/arch/powerpc/mm/dump_hashpagetable.c
deleted file mode 100644 (file)
index 8692946..0000000
+++ /dev/null
@@ -1,550 +0,0 @@
-/*
- * Copyright 2016, Rashmica Gupta, IBM Corp.
- *
- * This traverses the kernel virtual memory and dumps the pages that are in
- * the hash pagetable, along with their flags to
- * /sys/kernel/debug/kernel_hash_pagetable.
- *
- * If radix is enabled then there is no hash page table and so no debugfs file
- * is generated.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
-#include <linux/debugfs.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <asm/pgtable.h>
-#include <linux/const.h>
-#include <asm/page.h>
-#include <asm/pgalloc.h>
-#include <asm/plpar_wrappers.h>
-#include <linux/memblock.h>
-#include <asm/firmware.h>
-
-struct pg_state {
-       struct seq_file *seq;
-       const struct addr_marker *marker;
-       unsigned long start_address;
-       unsigned int level;
-       u64 current_flags;
-};
-
-struct addr_marker {
-       unsigned long start_address;
-       const char *name;
-};
-
-static struct addr_marker address_markers[] = {
-       { 0,    "Start of kernel VM" },
-       { 0,    "vmalloc() Area" },
-       { 0,    "vmalloc() End" },
-       { 0,    "isa I/O start" },
-       { 0,    "isa I/O end" },
-       { 0,    "phb I/O start" },
-       { 0,    "phb I/O end" },
-       { 0,    "I/O remap start" },
-       { 0,    "I/O remap end" },
-       { 0,    "vmemmap start" },
-       { -1,   NULL },
-};
-
-struct flag_info {
-       u64             mask;
-       u64             val;
-       const char      *set;
-       const char      *clear;
-       bool            is_val;
-       int             shift;
-};
-
-static const struct flag_info v_flag_array[] = {
-       {
-               .mask   = SLB_VSID_B,
-               .val    = SLB_VSID_B_256M,
-               .set    = "ssize: 256M",
-               .clear  = "ssize: 1T  ",
-       }, {
-               .mask   = HPTE_V_SECONDARY,
-               .val    = HPTE_V_SECONDARY,
-               .set    = "secondary",
-               .clear  = "primary  ",
-       }, {
-               .mask   = HPTE_V_VALID,
-               .val    = HPTE_V_VALID,
-               .set    = "valid  ",
-               .clear  = "invalid",
-       }, {
-               .mask   = HPTE_V_BOLTED,
-               .val    = HPTE_V_BOLTED,
-               .set    = "bolted",
-               .clear  = "",
-       }
-};
-
-static const struct flag_info r_flag_array[] = {
-       {
-               .mask   = HPTE_R_PP0 | HPTE_R_PP,
-               .val    = PP_RWXX,
-               .set    = "prot:RW--",
-       }, {
-               .mask   = HPTE_R_PP0 | HPTE_R_PP,
-               .val    = PP_RWRX,
-               .set    = "prot:RWR-",
-       }, {
-               .mask   = HPTE_R_PP0 | HPTE_R_PP,
-               .val    = PP_RWRW,
-               .set    = "prot:RWRW",
-       }, {
-               .mask   = HPTE_R_PP0 | HPTE_R_PP,
-               .val    = PP_RXRX,
-               .set    = "prot:R-R-",
-       }, {
-               .mask   = HPTE_R_PP0 | HPTE_R_PP,
-               .val    = PP_RXXX,
-               .set    = "prot:R---",
-       }, {
-               .mask   = HPTE_R_KEY_HI | HPTE_R_KEY_LO,
-               .val    = HPTE_R_KEY_HI | HPTE_R_KEY_LO,
-               .set    = "key",
-               .clear  = "",
-               .is_val = true,
-       }, {
-               .mask   = HPTE_R_R,
-               .val    = HPTE_R_R,
-               .set    = "ref",
-               .clear  = "   ",
-       }, {
-               .mask   = HPTE_R_C,
-               .val    = HPTE_R_C,
-               .set    = "changed",
-               .clear  = "       ",
-       }, {
-               .mask   = HPTE_R_N,
-               .val    = HPTE_R_N,
-               .set    = "no execute",
-       }, {
-               .mask   = HPTE_R_WIMG,
-               .val    = HPTE_R_W,
-               .set    = "writethru",
-       }, {
-               .mask   = HPTE_R_WIMG,
-               .val    = HPTE_R_I,
-               .set    = "no cache",
-       }, {
-               .mask   = HPTE_R_WIMG,
-               .val    = HPTE_R_G,
-               .set    = "guarded",
-       }
-};
-
-static int calculate_pagesize(struct pg_state *st, int ps, char s[])
-{
-       static const char units[] = "BKMGTPE";
-       const char *unit = units;
-
-       while (ps > 9 && unit[1]) {
-               ps -= 10;
-               unit++;
-       }
-       seq_printf(st->seq, "  %s_ps: %i%c\t", s, 1<<ps, *unit);
-       return ps;
-}
-
-static void dump_flag_info(struct pg_state *st, const struct flag_info
-               *flag, u64 pte, int num)
-{
-       unsigned int i;
-
-       for (i = 0; i < num; i++, flag++) {
-               const char *s = NULL;
-               u64 val;
-
-               /* flag not defined so don't check it */
-               if (flag->mask == 0)
-                       continue;
-               /* Some 'flags' are actually values */
-               if (flag->is_val) {
-                       val = pte & flag->val;
-                       if (flag->shift)
-                               val = val >> flag->shift;
-                       seq_printf(st->seq, "  %s:%llx", flag->set, val);
-               } else {
-                       if ((pte & flag->mask) == flag->val)
-                               s = flag->set;
-                       else
-                               s = flag->clear;
-                       if (s)
-                               seq_printf(st->seq, "  %s", s);
-               }
-       }
-}
-
-static void dump_hpte_info(struct pg_state *st, unsigned long ea, u64 v, u64 r,
-               unsigned long rpn, int bps, int aps, unsigned long lp)
-{
-       int aps_index;
-
-       while (ea >= st->marker[1].start_address) {
-               st->marker++;
-               seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
-       }
-       seq_printf(st->seq, "0x%lx:\t", ea);
-       seq_printf(st->seq, "AVPN:%llx\t", HPTE_V_AVPN_VAL(v));
-       dump_flag_info(st, v_flag_array, v, ARRAY_SIZE(v_flag_array));
-       seq_printf(st->seq, "  rpn: %lx\t", rpn);
-       dump_flag_info(st, r_flag_array, r, ARRAY_SIZE(r_flag_array));
-
-       calculate_pagesize(st, bps, "base");
-       aps_index = calculate_pagesize(st, aps, "actual");
-       if (aps_index != 2)
-               seq_printf(st->seq, "LP enc: %lx", lp);
-       seq_putc(st->seq, '\n');
-}
-
-
-static int native_find(unsigned long ea, int psize, bool primary, u64 *v, u64
-               *r)
-{
-       struct hash_pte *hptep;
-       unsigned long hash, vsid, vpn, hpte_group, want_v, hpte_v;
-       int i, ssize = mmu_kernel_ssize;
-       unsigned long shift = mmu_psize_defs[psize].shift;
-
-       /* calculate hash */
-       vsid = get_kernel_vsid(ea, ssize);
-       vpn  = hpt_vpn(ea, vsid, ssize);
-       hash = hpt_hash(vpn, shift, ssize);
-       want_v = hpte_encode_avpn(vpn, psize, ssize);
-
-       /* to check in the secondary hash table, we invert the hash */
-       if (!primary)
-               hash = ~hash;
-       hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-       for (i = 0; i < HPTES_PER_GROUP; i++) {
-               hptep = htab_address + hpte_group;
-               hpte_v = be64_to_cpu(hptep->v);
-
-               if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
-                       /* HPTE matches */
-                       *v = be64_to_cpu(hptep->v);
-                       *r = be64_to_cpu(hptep->r);
-                       return 0;
-               }
-               ++hpte_group;
-       }
-       return -1;
-}
-
-#ifdef CONFIG_PPC_PSERIES
-static int pseries_find(unsigned long ea, int psize, bool primary, u64 *v, u64 *r)
-{
-       struct hash_pte ptes[4];
-       unsigned long vsid, vpn, hash, hpte_group, want_v;
-       int i, j, ssize = mmu_kernel_ssize;
-       long lpar_rc = 0;
-       unsigned long shift = mmu_psize_defs[psize].shift;
-
-       /* calculate hash */
-       vsid = get_kernel_vsid(ea, ssize);
-       vpn  = hpt_vpn(ea, vsid, ssize);
-       hash = hpt_hash(vpn, shift, ssize);
-       want_v = hpte_encode_avpn(vpn, psize, ssize);
-
-       /* to check in the secondary hash table, we invert the hash */
-       if (!primary)
-               hash = ~hash;
-       hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
-       /* see if we can find an entry in the hpte with this hash */
-       for (i = 0; i < HPTES_PER_GROUP; i += 4, hpte_group += 4) {
-               lpar_rc = plpar_pte_read_4(0, hpte_group, (void *)ptes);
-
-               if (lpar_rc != H_SUCCESS)
-                       continue;
-               for (j = 0; j < 4; j++) {
-                       if (HPTE_V_COMPARE(ptes[j].v, want_v) &&
-                                       (ptes[j].v & HPTE_V_VALID)) {
-                               /* HPTE matches */
-                               *v = ptes[j].v;
-                               *r = ptes[j].r;
-                               return 0;
-                       }
-               }
-       }
-       return -1;
-}
-#endif
-
-static void decode_r(int bps, unsigned long r, unsigned long *rpn, int *aps,
-               unsigned long *lp_bits)
-{
-       struct mmu_psize_def entry;
-       unsigned long arpn, mask, lp;
-       int penc = -2, idx = 0, shift;
-
-       /*.
-        * The LP field has 8 bits. Depending on the actual page size, some of
-        * these bits are concatenated with the APRN to get the RPN. The rest
-        * of the bits in the LP field is the LP value and is an encoding for
-        * the base page size and the actual page size.
-        *
-        *  -   find the mmu entry for our base page size
-        *  -   go through all page encodings and use the associated mask to
-        *      find an encoding that matches our encoding in the LP field.
-        */
-       arpn = (r & HPTE_R_RPN) >> HPTE_R_RPN_SHIFT;
-       lp = arpn & 0xff;
-
-       entry = mmu_psize_defs[bps];
-       while (idx < MMU_PAGE_COUNT) {
-               penc = entry.penc[idx];
-               if ((penc != -1) && (mmu_psize_defs[idx].shift)) {
-                       shift = mmu_psize_defs[idx].shift -  HPTE_R_RPN_SHIFT;
-                       mask = (0x1 << (shift)) - 1;
-                       if ((lp & mask) == penc) {
-                               *aps = mmu_psize_to_shift(idx);
-                               *lp_bits = lp & mask;
-                               *rpn = arpn >> shift;
-                               return;
-                       }
-               }
-               idx++;
-       }
-}
-
-static int base_hpte_find(unsigned long ea, int psize, bool primary, u64 *v,
-                         u64 *r)
-{
-#ifdef CONFIG_PPC_PSERIES
-       if (firmware_has_feature(FW_FEATURE_LPAR))
-               return pseries_find(ea, psize, primary, v, r);
-#endif
-       return native_find(ea, psize, primary, v, r);
-}
-
-static unsigned long hpte_find(struct pg_state *st, unsigned long ea, int psize)
-{
-       unsigned long slot;
-       u64 v  = 0, r = 0;
-       unsigned long rpn, lp_bits;
-       int base_psize = 0, actual_psize = 0;
-
-       if (ea < PAGE_OFFSET)
-               return -1;
-
-       /* Look in primary table */
-       slot = base_hpte_find(ea, psize, true, &v, &r);
-
-       /* Look in secondary table */
-       if (slot == -1)
-               slot = base_hpte_find(ea, psize, true, &v, &r);
-
-       /* No entry found */
-       if (slot == -1)
-               return -1;
-
-       /*
-        * We found an entry in the hash page table:
-        *  - check that this has the same base page
-        *  - find the actual page size
-        *  - find the RPN
-        */
-       base_psize = mmu_psize_to_shift(psize);
-
-       if ((v & HPTE_V_LARGE) == HPTE_V_LARGE) {
-               decode_r(psize, r, &rpn, &actual_psize, &lp_bits);
-       } else {
-               /* 4K actual page size */
-               actual_psize = 12;
-               rpn = (r & HPTE_R_RPN) >> HPTE_R_RPN_SHIFT;
-               /* In this case there are no LP bits */
-               lp_bits = -1;
-       }
-       /*
-        * We didn't find a matching encoding, so the PTE we found isn't for
-        * this address.
-        */
-       if (actual_psize == -1)
-               return -1;
-
-       dump_hpte_info(st, ea, v, r, rpn, base_psize, actual_psize, lp_bits);
-       return 0;
-}
-
-static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
-{
-       pte_t *pte = pte_offset_kernel(pmd, 0);
-       unsigned long addr, pteval, psize;
-       int i, status;
-
-       for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
-               addr = start + i * PAGE_SIZE;
-               pteval = pte_val(*pte);
-
-               if (addr < VMALLOC_END)
-                       psize = mmu_vmalloc_psize;
-               else
-                       psize = mmu_io_psize;
-#ifdef CONFIG_PPC_64K_PAGES
-               /* check for secret 4K mappings */
-               if (((pteval & H_PAGE_COMBO) == H_PAGE_COMBO) ||
-                       ((pteval & H_PAGE_4K_PFN) == H_PAGE_4K_PFN))
-                       psize = mmu_io_psize;
-#endif
-               /* check for hashpte */
-               status = hpte_find(st, addr, psize);
-
-               if (((pteval & H_PAGE_HASHPTE) != H_PAGE_HASHPTE)
-                               && (status != -1)) {
-               /* found a hpte that is not in the linux page tables */
-                       seq_printf(st->seq, "page probably bolted before linux"
-                               " pagetables were set: addr:%lx, pteval:%lx\n",
-                               addr, pteval);
-               }
-       }
-}
-
-static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
-{
-       pmd_t *pmd = pmd_offset(pud, 0);
-       unsigned long addr;
-       unsigned int i;
-
-       for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
-               addr = start + i * PMD_SIZE;
-               if (!pmd_none(*pmd))
-                       /* pmd exists */
-                       walk_pte(st, pmd, addr);
-       }
-}
-
-static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
-{
-       pud_t *pud = pud_offset(pgd, 0);
-       unsigned long addr;
-       unsigned int i;
-
-       for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
-               addr = start + i * PUD_SIZE;
-               if (!pud_none(*pud))
-                       /* pud exists */
-                       walk_pmd(st, pud, addr);
-       }
-}
-
-static void walk_pagetables(struct pg_state *st)
-{
-       pgd_t *pgd = pgd_offset_k(0UL);
-       unsigned int i;
-       unsigned long addr;
-
-       /*
-        * Traverse the linux pagetable structure and dump pages that are in
-        * the hash pagetable.
-        */
-       for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
-               addr = KERN_VIRT_START + i * PGDIR_SIZE;
-               if (!pgd_none(*pgd))
-                       /* pgd exists */
-                       walk_pud(st, pgd, addr);
-       }
-}
-
-
-static void walk_linearmapping(struct pg_state *st)
-{
-       unsigned long addr;
-
-       /*
-        * Traverse the linear mapping section of virtual memory and dump pages
-        * that are in the hash pagetable.
-        */
-       unsigned long psize = 1 << mmu_psize_defs[mmu_linear_psize].shift;
-
-       for (addr = PAGE_OFFSET; addr < PAGE_OFFSET +
-                       memblock_end_of_DRAM(); addr += psize)
-               hpte_find(st, addr, mmu_linear_psize);
-}
-
-static void walk_vmemmap(struct pg_state *st)
-{
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-       struct vmemmap_backing *ptr = vmemmap_list;
-
-       /*
-        * Traverse the vmemmaped memory and dump pages that are in the hash
-        * pagetable.
-        */
-       while (ptr->list) {
-               hpte_find(st, ptr->virt_addr, mmu_vmemmap_psize);
-               ptr = ptr->list;
-       }
-       seq_puts(st->seq, "---[ vmemmap end ]---\n");
-#endif
-}
-
-static void populate_markers(void)
-{
-       address_markers[0].start_address = PAGE_OFFSET;
-       address_markers[1].start_address = VMALLOC_START;
-       address_markers[2].start_address = VMALLOC_END;
-       address_markers[3].start_address = ISA_IO_BASE;
-       address_markers[4].start_address = ISA_IO_END;
-       address_markers[5].start_address = PHB_IO_BASE;
-       address_markers[6].start_address = PHB_IO_END;
-       address_markers[7].start_address = IOREMAP_BASE;
-       address_markers[8].start_address = IOREMAP_END;
-#ifdef CONFIG_PPC_BOOK3S_64
-       address_markers[9].start_address =  H_VMEMMAP_BASE;
-#else
-       address_markers[9].start_address =  VMEMMAP_BASE;
-#endif
-}
-
-static int ptdump_show(struct seq_file *m, void *v)
-{
-       struct pg_state st = {
-               .seq = m,
-               .start_address = PAGE_OFFSET,
-               .marker = address_markers,
-       };
-       /*
-        * Traverse the 0xc, 0xd and 0xf areas of the kernel virtual memory and
-        * dump pages that are in the hash pagetable.
-        */
-       walk_linearmapping(&st);
-       walk_pagetables(&st);
-       walk_vmemmap(&st);
-       return 0;
-}
-
-static int ptdump_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ptdump_show, NULL);
-}
-
-static const struct file_operations ptdump_fops = {
-       .open           = ptdump_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int ptdump_init(void)
-{
-       struct dentry *debugfs_file;
-
-       if (!radix_enabled()) {
-               populate_markers();
-               debugfs_file = debugfs_create_file("kernel_hash_pagetable",
-                               0400, NULL, NULL, &ptdump_fops);
-               return debugfs_file ? 0 : -ENOMEM;
-       }
-       return 0;
-}
-device_initcall(ptdump_init);
diff --git a/arch/powerpc/mm/dump_linuxpagetables-8xx.c b/arch/powerpc/mm/dump_linuxpagetables-8xx.c
deleted file mode 100644 (file)
index ab9e3f2..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * From split of dump_linuxpagetables.c
- * Copyright 2016, Rashmica Gupta, IBM Corp.
- *
- */
-#include <linux/kernel.h>
-#include <asm/pgtable.h>
-
-#include "dump_linuxpagetables.h"
-
-static const struct flag_info flag_array[] = {
-       {
-               .mask   = _PAGE_SH,
-               .val    = 0,
-               .set    = "user",
-               .clear  = "    ",
-       }, {
-               .mask   = _PAGE_RO | _PAGE_NA,
-               .val    = 0,
-               .set    = "rw",
-       }, {
-               .mask   = _PAGE_RO | _PAGE_NA,
-               .val    = _PAGE_RO,
-               .set    = "r ",
-       }, {
-               .mask   = _PAGE_RO | _PAGE_NA,
-               .val    = _PAGE_NA,
-               .set    = "  ",
-       }, {
-               .mask   = _PAGE_EXEC,
-               .val    = _PAGE_EXEC,
-               .set    = " X ",
-               .clear  = "   ",
-       }, {
-               .mask   = _PAGE_PRESENT,
-               .val    = _PAGE_PRESENT,
-               .set    = "present",
-               .clear  = "       ",
-       }, {
-               .mask   = _PAGE_GUARDED,
-               .val    = _PAGE_GUARDED,
-               .set    = "guarded",
-               .clear  = "       ",
-       }, {
-               .mask   = _PAGE_DIRTY,
-               .val    = _PAGE_DIRTY,
-               .set    = "dirty",
-               .clear  = "     ",
-       }, {
-               .mask   = _PAGE_ACCESSED,
-               .val    = _PAGE_ACCESSED,
-               .set    = "accessed",
-               .clear  = "        ",
-       }, {
-               .mask   = _PAGE_NO_CACHE,
-               .val    = _PAGE_NO_CACHE,
-               .set    = "no cache",
-               .clear  = "        ",
-       }, {
-               .mask   = _PAGE_SPECIAL,
-               .val    = _PAGE_SPECIAL,
-               .set    = "special",
-       }
-};
-
-struct pgtable_level pg_level[5] = {
-       {
-       }, { /* pgd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pud */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pmd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pte */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       },
-};
diff --git a/arch/powerpc/mm/dump_linuxpagetables-book3s64.c b/arch/powerpc/mm/dump_linuxpagetables-book3s64.c
deleted file mode 100644 (file)
index ed6fcf7..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * From split of dump_linuxpagetables.c
- * Copyright 2016, Rashmica Gupta, IBM Corp.
- *
- */
-#include <linux/kernel.h>
-#include <asm/pgtable.h>
-
-#include "dump_linuxpagetables.h"
-
-static const struct flag_info flag_array[] = {
-       {
-               .mask   = _PAGE_PRIVILEGED,
-               .val    = 0,
-               .set    = "user",
-               .clear  = "    ",
-       }, {
-               .mask   = _PAGE_READ,
-               .val    = _PAGE_READ,
-               .set    = "r",
-               .clear  = " ",
-       }, {
-               .mask   = _PAGE_WRITE,
-               .val    = _PAGE_WRITE,
-               .set    = "w",
-               .clear  = " ",
-       }, {
-               .mask   = _PAGE_EXEC,
-               .val    = _PAGE_EXEC,
-               .set    = " X ",
-               .clear  = "   ",
-       }, {
-               .mask   = _PAGE_PTE,
-               .val    = _PAGE_PTE,
-               .set    = "pte",
-               .clear  = "   ",
-       }, {
-               .mask   = _PAGE_PRESENT,
-               .val    = _PAGE_PRESENT,
-               .set    = "valid",
-               .clear  = "     ",
-       }, {
-               .mask   = _PAGE_PRESENT | _PAGE_INVALID,
-               .val    = 0,
-               .set    = "       ",
-               .clear  = "present",
-       }, {
-               .mask   = H_PAGE_HASHPTE,
-               .val    = H_PAGE_HASHPTE,
-               .set    = "hpte",
-               .clear  = "    ",
-       }, {
-               .mask   = _PAGE_DIRTY,
-               .val    = _PAGE_DIRTY,
-               .set    = "dirty",
-               .clear  = "     ",
-       }, {
-               .mask   = _PAGE_ACCESSED,
-               .val    = _PAGE_ACCESSED,
-               .set    = "accessed",
-               .clear  = "        ",
-       }, {
-               .mask   = _PAGE_NON_IDEMPOTENT,
-               .val    = _PAGE_NON_IDEMPOTENT,
-               .set    = "non-idempotent",
-               .clear  = "              ",
-       }, {
-               .mask   = _PAGE_TOLERANT,
-               .val    = _PAGE_TOLERANT,
-               .set    = "tolerant",
-               .clear  = "        ",
-       }, {
-               .mask   = H_PAGE_BUSY,
-               .val    = H_PAGE_BUSY,
-               .set    = "busy",
-       }, {
-#ifdef CONFIG_PPC_64K_PAGES
-               .mask   = H_PAGE_COMBO,
-               .val    = H_PAGE_COMBO,
-               .set    = "combo",
-       }, {
-               .mask   = H_PAGE_4K_PFN,
-               .val    = H_PAGE_4K_PFN,
-               .set    = "4K_pfn",
-       }, {
-#else /* CONFIG_PPC_64K_PAGES */
-               .mask   = H_PAGE_F_GIX,
-               .val    = H_PAGE_F_GIX,
-               .set    = "f_gix",
-               .is_val = true,
-               .shift  = H_PAGE_F_GIX_SHIFT,
-       }, {
-               .mask   = H_PAGE_F_SECOND,
-               .val    = H_PAGE_F_SECOND,
-               .set    = "f_second",
-       }, {
-#endif /* CONFIG_PPC_64K_PAGES */
-               .mask   = _PAGE_SPECIAL,
-               .val    = _PAGE_SPECIAL,
-               .set    = "special",
-       }
-};
-
-struct pgtable_level pg_level[5] = {
-       {
-       }, { /* pgd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pud */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pmd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pte */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       },
-};
diff --git a/arch/powerpc/mm/dump_linuxpagetables-generic.c b/arch/powerpc/mm/dump_linuxpagetables-generic.c
deleted file mode 100644 (file)
index 3fe98a0..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * From split of dump_linuxpagetables.c
- * Copyright 2016, Rashmica Gupta, IBM Corp.
- *
- */
-#include <linux/kernel.h>
-#include <asm/pgtable.h>
-
-#include "dump_linuxpagetables.h"
-
-static const struct flag_info flag_array[] = {
-       {
-               .mask   = _PAGE_USER,
-               .val    = _PAGE_USER,
-               .set    = "user",
-               .clear  = "    ",
-       }, {
-               .mask   = _PAGE_RW,
-               .val    = _PAGE_RW,
-               .set    = "rw",
-               .clear  = "r ",
-       }, {
-               .mask   = _PAGE_EXEC,
-               .val    = _PAGE_EXEC,
-               .set    = " X ",
-               .clear  = "   ",
-       }, {
-               .mask   = _PAGE_PRESENT,
-               .val    = _PAGE_PRESENT,
-               .set    = "present",
-               .clear  = "       ",
-       }, {
-               .mask   = _PAGE_GUARDED,
-               .val    = _PAGE_GUARDED,
-               .set    = "guarded",
-               .clear  = "       ",
-       }, {
-               .mask   = _PAGE_DIRTY,
-               .val    = _PAGE_DIRTY,
-               .set    = "dirty",
-               .clear  = "     ",
-       }, {
-               .mask   = _PAGE_ACCESSED,
-               .val    = _PAGE_ACCESSED,
-               .set    = "accessed",
-               .clear  = "        ",
-       }, {
-               .mask   = _PAGE_WRITETHRU,
-               .val    = _PAGE_WRITETHRU,
-               .set    = "write through",
-               .clear  = "             ",
-       }, {
-               .mask   = _PAGE_NO_CACHE,
-               .val    = _PAGE_NO_CACHE,
-               .set    = "no cache",
-               .clear  = "        ",
-       }, {
-               .mask   = _PAGE_SPECIAL,
-               .val    = _PAGE_SPECIAL,
-               .set    = "special",
-       }
-};
-
-struct pgtable_level pg_level[5] = {
-       {
-       }, { /* pgd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pud */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pmd */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       }, { /* pte */
-               .flag   = flag_array,
-               .num    = ARRAY_SIZE(flag_array),
-       },
-};
diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c
deleted file mode 100644 (file)
index b0da447..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright 2016, Rashmica Gupta, IBM Corp.
- *
- * This traverses the kernel pagetables and dumps the
- * information about the used sections of memory to
- * /sys/kernel/debug/kernel_pagetables.
- *
- * Derived from the arm64 implementation:
- * Copyright (c) 2014, The Linux Foundation, Laura Abbott.
- * (C) Copyright 2008 Intel Corporation, Arjan van de Ven.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
- */
-#include <linux/debugfs.h>
-#include <linux/fs.h>
-#include <linux/hugetlb.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/highmem.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <asm/fixmap.h>
-#include <asm/pgtable.h>
-#include <linux/const.h>
-#include <asm/page.h>
-#include <asm/pgalloc.h>
-
-#include "dump_linuxpagetables.h"
-
-#ifdef CONFIG_PPC32
-#define KERN_VIRT_START        0
-#endif
-
-/*
- * To visualise what is happening,
- *
- *  - PTRS_PER_P** = how many entries there are in the corresponding P**
- *  - P**_SHIFT = how many bits of the address we use to index into the
- * corresponding P**
- *  - P**_SIZE is how much memory we can access through the table - not the
- * size of the table itself.
- * P**={PGD, PUD, PMD, PTE}
- *
- *
- * Each entry of the PGD points to a PUD. Each entry of a PUD points to a
- * PMD. Each entry of a PMD points to a PTE. And every PTE entry points to
- * a page.
- *
- * In the case where there are only 3 levels, the PUD is folded into the
- * PGD: every PUD has only one entry which points to the PMD.
- *
- * The page dumper groups page table entries of the same type into a single
- * description. It uses pg_state to track the range information while
- * iterating over the PTE entries. When the continuity is broken it then
- * dumps out a description of the range - ie PTEs that are virtually contiguous
- * with the same PTE flags are chunked together. This is to make it clear how
- * different areas of the kernel virtual memory are used.
- *
- */
-struct pg_state {
-       struct seq_file *seq;
-       const struct addr_marker *marker;
-       unsigned long start_address;
-       unsigned long start_pa;
-       unsigned long last_pa;
-       unsigned int level;
-       u64 current_flags;
-};
-
-struct addr_marker {
-       unsigned long start_address;
-       const char *name;
-};
-
-static struct addr_marker address_markers[] = {
-       { 0,    "Start of kernel VM" },
-       { 0,    "vmalloc() Area" },
-       { 0,    "vmalloc() End" },
-#ifdef CONFIG_PPC64
-       { 0,    "isa I/O start" },
-       { 0,    "isa I/O end" },
-       { 0,    "phb I/O start" },
-       { 0,    "phb I/O end" },
-       { 0,    "I/O remap start" },
-       { 0,    "I/O remap end" },
-       { 0,    "vmemmap start" },
-#else
-       { 0,    "Early I/O remap start" },
-       { 0,    "Early I/O remap end" },
-#ifdef CONFIG_NOT_COHERENT_CACHE
-       { 0,    "Consistent mem start" },
-       { 0,    "Consistent mem end" },
-#endif
-#ifdef CONFIG_HIGHMEM
-       { 0,    "Highmem PTEs start" },
-       { 0,    "Highmem PTEs end" },
-#endif
-       { 0,    "Fixmap start" },
-       { 0,    "Fixmap end" },
-#endif
-       { -1,   NULL },
-};
-
-static void dump_flag_info(struct pg_state *st, const struct flag_info
-               *flag, u64 pte, int num)
-{
-       unsigned int i;
-
-       for (i = 0; i < num; i++, flag++) {
-               const char *s = NULL;
-               u64 val;
-
-               /* flag not defined so don't check it */
-               if (flag->mask == 0)
-                       continue;
-               /* Some 'flags' are actually values */
-               if (flag->is_val) {
-                       val = pte & flag->val;
-                       if (flag->shift)
-                               val = val >> flag->shift;
-                       seq_printf(st->seq, "  %s:%llx", flag->set, val);
-               } else {
-                       if ((pte & flag->mask) == flag->val)
-                               s = flag->set;
-                       else
-                               s = flag->clear;
-                       if (s)
-                               seq_printf(st->seq, "  %s", s);
-               }
-               st->current_flags &= ~flag->mask;
-       }
-       if (st->current_flags != 0)
-               seq_printf(st->seq, "  unknown flags:%llx", st->current_flags);
-}
-
-static void dump_addr(struct pg_state *st, unsigned long addr)
-{
-       static const char units[] = "KMGTPE";
-       const char *unit = units;
-       unsigned long delta;
-
-#ifdef CONFIG_PPC64
-#define REG            "0x%016lx"
-#else
-#define REG            "0x%08lx"
-#endif
-
-       seq_printf(st->seq, REG "-" REG " ", st->start_address, addr - 1);
-       if (st->start_pa == st->last_pa && st->start_address + PAGE_SIZE != addr) {
-               seq_printf(st->seq, "[" REG "]", st->start_pa);
-               delta = PAGE_SIZE >> 10;
-       } else {
-               seq_printf(st->seq, " " REG " ", st->start_pa);
-               delta = (addr - st->start_address) >> 10;
-       }
-       /* Work out what appropriate unit to use */
-       while (!(delta & 1023) && unit[1]) {
-               delta >>= 10;
-               unit++;
-       }
-       seq_printf(st->seq, "%9lu%c", delta, *unit);
-
-}
-
-static void note_page(struct pg_state *st, unsigned long addr,
-              unsigned int level, u64 val)
-{
-       u64 flag = val & pg_level[level].mask;
-       u64 pa = val & PTE_RPN_MASK;
-
-       /* At first no level is set */
-       if (!st->level) {
-               st->level = level;
-               st->current_flags = flag;
-               st->start_address = addr;
-               st->start_pa = pa;
-               st->last_pa = pa;
-               seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
-       /*
-        * Dump the section of virtual memory when:
-        *   - the PTE flags from one entry to the next differs.
-        *   - we change levels in the tree.
-        *   - the address is in a different section of memory and is thus
-        *   used for a different purpose, regardless of the flags.
-        *   - the pa of this page is not adjacent to the last inspected page
-        */
-       } else if (flag != st->current_flags || level != st->level ||
-                  addr >= st->marker[1].start_address ||
-                  (pa != st->last_pa + PAGE_SIZE &&
-                   (pa != st->start_pa || st->start_pa != st->last_pa))) {
-
-               /* Check the PTE flags */
-               if (st->current_flags) {
-                       dump_addr(st, addr);
-
-                       /* Dump all the flags */
-                       if (pg_level[st->level].flag)
-                               dump_flag_info(st, pg_level[st->level].flag,
-                                         st->current_flags,
-                                         pg_level[st->level].num);
-
-                       seq_putc(st->seq, '\n');
-               }
-
-               /*
-                * Address indicates we have passed the end of the
-                * current section of virtual memory
-                */
-               while (addr >= st->marker[1].start_address) {
-                       st->marker++;
-                       seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
-               }
-               st->start_address = addr;
-               st->start_pa = pa;
-               st->last_pa = pa;
-               st->current_flags = flag;
-               st->level = level;
-       } else {
-               st->last_pa = pa;
-       }
-}
-
-static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
-{
-       pte_t *pte = pte_offset_kernel(pmd, 0);
-       unsigned long addr;
-       unsigned int i;
-
-       for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
-               addr = start + i * PAGE_SIZE;
-               note_page(st, addr, 4, pte_val(*pte));
-
-       }
-}
-
-static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
-{
-       pmd_t *pmd = pmd_offset(pud, 0);
-       unsigned long addr;
-       unsigned int i;
-
-       for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
-               addr = start + i * PMD_SIZE;
-               if (!pmd_none(*pmd) && !pmd_huge(*pmd))
-                       /* pmd exists */
-                       walk_pte(st, pmd, addr);
-               else
-                       note_page(st, addr, 3, pmd_val(*pmd));
-       }
-}
-
-static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
-{
-       pud_t *pud = pud_offset(pgd, 0);
-       unsigned long addr;
-       unsigned int i;
-
-       for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
-               addr = start + i * PUD_SIZE;
-               if (!pud_none(*pud) && !pud_huge(*pud))
-                       /* pud exists */
-                       walk_pmd(st, pud, addr);
-               else
-                       note_page(st, addr, 2, pud_val(*pud));
-       }
-}
-
-static void walk_pagetables(struct pg_state *st)
-{
-       pgd_t *pgd = pgd_offset_k(0UL);
-       unsigned int i;
-       unsigned long addr;
-
-       addr = st->start_address;
-
-       /*
-        * Traverse the linux pagetable structure and dump pages that are in
-        * the hash pagetable.
-        */
-       for (i = 0; i < PTRS_PER_PGD; i++, pgd++, addr += PGDIR_SIZE) {
-               if (!pgd_none(*pgd) && !pgd_huge(*pgd))
-                       /* pgd exists */
-                       walk_pud(st, pgd, addr);
-               else
-                       note_page(st, addr, 1, pgd_val(*pgd));
-       }
-}
-
-static void populate_markers(void)
-{
-       int i = 0;
-
-       address_markers[i++].start_address = PAGE_OFFSET;
-       address_markers[i++].start_address = VMALLOC_START;
-       address_markers[i++].start_address = VMALLOC_END;
-#ifdef CONFIG_PPC64
-       address_markers[i++].start_address = ISA_IO_BASE;
-       address_markers[i++].start_address = ISA_IO_END;
-       address_markers[i++].start_address = PHB_IO_BASE;
-       address_markers[i++].start_address = PHB_IO_END;
-       address_markers[i++].start_address = IOREMAP_BASE;
-       address_markers[i++].start_address = IOREMAP_END;
-#ifdef CONFIG_PPC_BOOK3S_64
-       address_markers[i++].start_address =  H_VMEMMAP_BASE;
-#else
-       address_markers[i++].start_address =  VMEMMAP_BASE;
-#endif
-#else /* !CONFIG_PPC64 */
-       address_markers[i++].start_address = ioremap_bot;
-       address_markers[i++].start_address = IOREMAP_TOP;
-#ifdef CONFIG_NOT_COHERENT_CACHE
-       address_markers[i++].start_address = IOREMAP_TOP;
-       address_markers[i++].start_address = IOREMAP_TOP +
-                                            CONFIG_CONSISTENT_SIZE;
-#endif
-#ifdef CONFIG_HIGHMEM
-       address_markers[i++].start_address = PKMAP_BASE;
-       address_markers[i++].start_address = PKMAP_ADDR(LAST_PKMAP);
-#endif
-       address_markers[i++].start_address = FIXADDR_START;
-       address_markers[i++].start_address = FIXADDR_TOP;
-#endif /* CONFIG_PPC64 */
-}
-
-static int ptdump_show(struct seq_file *m, void *v)
-{
-       struct pg_state st = {
-               .seq = m,
-               .marker = address_markers,
-       };
-
-       if (radix_enabled())
-               st.start_address = PAGE_OFFSET;
-       else
-               st.start_address = KERN_VIRT_START;
-
-       /* Traverse kernel page tables */
-       walk_pagetables(&st);
-       note_page(&st, 0, 0, 0);
-       return 0;
-}
-
-
-static int ptdump_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ptdump_show, NULL);
-}
-
-static const struct file_operations ptdump_fops = {
-       .open           = ptdump_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static void build_pgtable_complete_mask(void)
-{
-       unsigned int i, j;
-
-       for (i = 0; i < ARRAY_SIZE(pg_level); i++)
-               if (pg_level[i].flag)
-                       for (j = 0; j < pg_level[i].num; j++)
-                               pg_level[i].mask |= pg_level[i].flag[j].mask;
-}
-
-static int ptdump_init(void)
-{
-       struct dentry *debugfs_file;
-
-       populate_markers();
-       build_pgtable_complete_mask();
-       debugfs_file = debugfs_create_file("kernel_page_tables", 0400, NULL,
-                       NULL, &ptdump_fops);
-       return debugfs_file ? 0 : -ENOMEM;
-}
-device_initcall(ptdump_init);
diff --git a/arch/powerpc/mm/dump_linuxpagetables.h b/arch/powerpc/mm/dump_linuxpagetables.h
deleted file mode 100644 (file)
index 5d51363..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/types.h>
-
-struct flag_info {
-       u64             mask;
-       u64             val;
-       const char      *set;
-       const char      *clear;
-       bool            is_val;
-       int             shift;
-};
-
-struct pgtable_level {
-       const struct flag_info *flag;
-       size_t num;
-       u64 mask;
-};
-
-extern struct pgtable_level pg_level[5];
diff --git a/arch/powerpc/mm/dump_sr.c b/arch/powerpc/mm/dump_sr.c
deleted file mode 100644 (file)
index 5018436..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright 2018, Christophe Leroy CS S.I.
- * <christophe.leroy@c-s.fr>
- *
- * This dumps the content of Segment Registers
- */
-
-#include <asm/debugfs.h>
-
-static void seg_show(struct seq_file *m, int i)
-{
-       u32 val = mfsrin(i << 28);
-
-       seq_printf(m, "0x%01x0000000-0x%01xfffffff ", i, i);
-       seq_printf(m, "Kern key %d ", (val >> 30) & 1);
-       seq_printf(m, "User key %d ", (val >> 29) & 1);
-       if (val & 0x80000000) {
-               seq_printf(m, "Device 0x%03x", (val >> 20) & 0x1ff);
-               seq_printf(m, "-0x%05x", val & 0xfffff);
-       } else {
-               if (val & 0x10000000)
-                       seq_puts(m, "No Exec ");
-               seq_printf(m, "VSID 0x%06x", val & 0xffffff);
-       }
-       seq_puts(m, "\n");
-}
-
-static int sr_show(struct seq_file *m, void *v)
-{
-       int i;
-
-       seq_puts(m, "---[ User Segments ]---\n");
-       for (i = 0; i < TASK_SIZE >> 28; i++)
-               seg_show(m, i);
-
-       seq_puts(m, "\n---[ Kernel Segments ]---\n");
-       for (; i < 16; i++)
-               seg_show(m, i);
-
-       return 0;
-}
-
-static int sr_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, sr_show, NULL);
-}
-
-static const struct file_operations sr_fops = {
-       .open           = sr_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int __init sr_init(void)
-{
-       struct dentry *debugfs_file;
-
-       debugfs_file = debugfs_create_file("segment_registers", 0400,
-                                          powerpc_debugfs_root, NULL, &sr_fops);
-       return debugfs_file ? 0 : -ENOMEM;
-}
-device_initcall(sr_init);
diff --git a/arch/powerpc/mm/ptdump/8xx.c b/arch/powerpc/mm/ptdump/8xx.c
new file mode 100644 (file)
index 0000000..9e2d8e8
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * From split of dump_linuxpagetables.c
+ * Copyright 2016, Rashmica Gupta, IBM Corp.
+ *
+ */
+#include <linux/kernel.h>
+#include <asm/pgtable.h>
+
+#include "ptdump.h"
+
+static const struct flag_info flag_array[] = {
+       {
+               .mask   = _PAGE_SH,
+               .val    = 0,
+               .set    = "user",
+               .clear  = "    ",
+       }, {
+               .mask   = _PAGE_RO | _PAGE_NA,
+               .val    = 0,
+               .set    = "rw",
+       }, {
+               .mask   = _PAGE_RO | _PAGE_NA,
+               .val    = _PAGE_RO,
+               .set    = "r ",
+       }, {
+               .mask   = _PAGE_RO | _PAGE_NA,
+               .val    = _PAGE_NA,
+               .set    = "  ",
+       }, {
+               .mask   = _PAGE_EXEC,
+               .val    = _PAGE_EXEC,
+               .set    = " X ",
+               .clear  = "   ",
+       }, {
+               .mask   = _PAGE_PRESENT,
+               .val    = _PAGE_PRESENT,
+               .set    = "present",
+               .clear  = "       ",
+       }, {
+               .mask   = _PAGE_GUARDED,
+               .val    = _PAGE_GUARDED,
+               .set    = "guarded",
+               .clear  = "       ",
+       }, {
+               .mask   = _PAGE_DIRTY,
+               .val    = _PAGE_DIRTY,
+               .set    = "dirty",
+               .clear  = "     ",
+       }, {
+               .mask   = _PAGE_ACCESSED,
+               .val    = _PAGE_ACCESSED,
+               .set    = "accessed",
+               .clear  = "        ",
+       }, {
+               .mask   = _PAGE_NO_CACHE,
+               .val    = _PAGE_NO_CACHE,
+               .set    = "no cache",
+               .clear  = "        ",
+       }, {
+               .mask   = _PAGE_SPECIAL,
+               .val    = _PAGE_SPECIAL,
+               .set    = "special",
+       }
+};
+
+struct pgtable_level pg_level[5] = {
+       {
+       }, { /* pgd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pud */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pmd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pte */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       },
+};
diff --git a/arch/powerpc/mm/ptdump/Makefile b/arch/powerpc/mm/ptdump/Makefile
new file mode 100644 (file)
index 0000000..712762b
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y  += ptdump.o
+
+obj-$(CONFIG_4xx)              += shared.o
+obj-$(CONFIG_PPC_8xx)          += 8xx.o
+obj-$(CONFIG_PPC_BOOK3E_MMU)   += shared.o
+obj-$(CONFIG_PPC_BOOK3S_32)    += shared.o bats.o segment_regs.o
+obj-$(CONFIG_PPC_BOOK3S_64)    += book3s64.o hashpagetable.o
diff --git a/arch/powerpc/mm/ptdump/bats.c b/arch/powerpc/mm/ptdump/bats.c
new file mode 100644 (file)
index 0000000..a0d23e9
--- /dev/null
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018, Christophe Leroy CS S.I.
+ * <christophe.leroy@c-s.fr>
+ *
+ * This dumps the content of BATS
+ */
+
+#include <asm/debugfs.h>
+#include <asm/pgtable.h>
+#include <asm/cpu_has_feature.h>
+
+static char *pp_601(int k, int pp)
+{
+       if (pp == 0)
+               return k ? "NA" : "RWX";
+       if (pp == 1)
+               return k ? "ROX" : "RWX";
+       if (pp == 2)
+               return k ? "RWX" : "RWX";
+       return k ? "ROX" : "ROX";
+}
+
+static void bat_show_601(struct seq_file *m, int idx, u32 lower, u32 upper)
+{
+       u32 blpi = upper & 0xfffe0000;
+       u32 k = (upper >> 2) & 3;
+       u32 pp = upper & 3;
+       phys_addr_t pbn = PHYS_BAT_ADDR(lower);
+       u32 bsm = lower & 0x3ff;
+       u32 size = (bsm + 1) << 17;
+
+       seq_printf(m, "%d: ", idx);
+       if (!(lower & 0x40)) {
+               seq_puts(m, "        -\n");
+               return;
+       }
+
+       seq_printf(m, "0x%08x-0x%08x ", blpi, blpi + size - 1);
+#ifdef CONFIG_PHYS_64BIT
+       seq_printf(m, "0x%016llx ", pbn);
+#else
+       seq_printf(m, "0x%08x ", pbn);
+#endif
+
+       seq_printf(m, "Kernel %s User %s", pp_601(k & 2, pp), pp_601(k & 1, pp));
+
+       if (lower & _PAGE_WRITETHRU)
+               seq_puts(m, "write through ");
+       if (lower & _PAGE_NO_CACHE)
+               seq_puts(m, "no cache ");
+       if (lower & _PAGE_COHERENT)
+               seq_puts(m, "coherent ");
+       seq_puts(m, "\n");
+}
+
+#define BAT_SHOW_601(_m, _n, _l, _u) bat_show_601(_m, _n, mfspr(_l), mfspr(_u))
+
+static int bats_show_601(struct seq_file *m, void *v)
+{
+       seq_puts(m, "---[ Block Address Translation ]---\n");
+
+       BAT_SHOW_601(m, 0, SPRN_IBAT0L, SPRN_IBAT0U);
+       BAT_SHOW_601(m, 1, SPRN_IBAT1L, SPRN_IBAT1U);
+       BAT_SHOW_601(m, 2, SPRN_IBAT2L, SPRN_IBAT2U);
+       BAT_SHOW_601(m, 3, SPRN_IBAT3L, SPRN_IBAT3U);
+
+       return 0;
+}
+
+static void bat_show_603(struct seq_file *m, int idx, u32 lower, u32 upper, bool is_d)
+{
+       u32 bepi = upper & 0xfffe0000;
+       u32 bl = (upper >> 2) & 0x7ff;
+       u32 k = upper & 3;
+       phys_addr_t brpn = PHYS_BAT_ADDR(lower);
+       u32 size = (bl + 1) << 17;
+
+       seq_printf(m, "%d: ", idx);
+       if (k == 0) {
+               seq_puts(m, "        -\n");
+               return;
+       }
+
+       seq_printf(m, "0x%08x-0x%08x ", bepi, bepi + size - 1);
+#ifdef CONFIG_PHYS_64BIT
+       seq_printf(m, "0x%016llx ", brpn);
+#else
+       seq_printf(m, "0x%08x ", brpn);
+#endif
+
+       if (k == 1)
+               seq_puts(m, "User ");
+       else if (k == 2)
+               seq_puts(m, "Kernel ");
+       else
+               seq_puts(m, "Kernel/User ");
+
+       if (lower & BPP_RX)
+               seq_puts(m, is_d ? "RO " : "EXEC ");
+       else if (lower & BPP_RW)
+               seq_puts(m, is_d ? "RW " : "EXEC ");
+       else
+               seq_puts(m, is_d ? "NA " : "NX   ");
+
+       if (lower & _PAGE_WRITETHRU)
+               seq_puts(m, "write through ");
+       if (lower & _PAGE_NO_CACHE)
+               seq_puts(m, "no cache ");
+       if (lower & _PAGE_COHERENT)
+               seq_puts(m, "coherent ");
+       if (lower & _PAGE_GUARDED)
+               seq_puts(m, "guarded ");
+       seq_puts(m, "\n");
+}
+
+#define BAT_SHOW_603(_m, _n, _l, _u, _d) bat_show_603(_m, _n, mfspr(_l), mfspr(_u), _d)
+
+static int bats_show_603(struct seq_file *m, void *v)
+{
+       seq_puts(m, "---[ Instruction Block Address Translation ]---\n");
+
+       BAT_SHOW_603(m, 0, SPRN_IBAT0L, SPRN_IBAT0U, false);
+       BAT_SHOW_603(m, 1, SPRN_IBAT1L, SPRN_IBAT1U, false);
+       BAT_SHOW_603(m, 2, SPRN_IBAT2L, SPRN_IBAT2U, false);
+       BAT_SHOW_603(m, 3, SPRN_IBAT3L, SPRN_IBAT3U, false);
+       if (mmu_has_feature(MMU_FTR_USE_HIGH_BATS)) {
+               BAT_SHOW_603(m, 4, SPRN_IBAT4L, SPRN_IBAT4U, false);
+               BAT_SHOW_603(m, 5, SPRN_IBAT5L, SPRN_IBAT5U, false);
+               BAT_SHOW_603(m, 6, SPRN_IBAT6L, SPRN_IBAT6U, false);
+               BAT_SHOW_603(m, 7, SPRN_IBAT7L, SPRN_IBAT7U, false);
+       }
+
+       seq_puts(m, "\n---[ Data Block Address Translation ]---\n");
+
+       BAT_SHOW_603(m, 0, SPRN_DBAT0L, SPRN_DBAT0U, true);
+       BAT_SHOW_603(m, 1, SPRN_DBAT1L, SPRN_DBAT1U, true);
+       BAT_SHOW_603(m, 2, SPRN_DBAT2L, SPRN_DBAT2U, true);
+       BAT_SHOW_603(m, 3, SPRN_DBAT3L, SPRN_DBAT3U, true);
+       if (mmu_has_feature(MMU_FTR_USE_HIGH_BATS)) {
+               BAT_SHOW_603(m, 4, SPRN_DBAT4L, SPRN_DBAT4U, true);
+               BAT_SHOW_603(m, 5, SPRN_DBAT5L, SPRN_DBAT5U, true);
+               BAT_SHOW_603(m, 6, SPRN_DBAT6L, SPRN_DBAT6U, true);
+               BAT_SHOW_603(m, 7, SPRN_DBAT7L, SPRN_DBAT7U, true);
+       }
+
+       return 0;
+}
+
+static int bats_open(struct inode *inode, struct file *file)
+{
+       if (cpu_has_feature(CPU_FTR_601))
+               return single_open(file, bats_show_601, NULL);
+
+       return single_open(file, bats_show_603, NULL);
+}
+
+static const struct file_operations bats_fops = {
+       .open           = bats_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init bats_init(void)
+{
+       struct dentry *debugfs_file;
+
+       debugfs_file = debugfs_create_file("block_address_translation", 0400,
+                                          powerpc_debugfs_root, NULL, &bats_fops);
+       return debugfs_file ? 0 : -ENOMEM;
+}
+device_initcall(bats_init);
diff --git a/arch/powerpc/mm/ptdump/book3s64.c b/arch/powerpc/mm/ptdump/book3s64.c
new file mode 100644 (file)
index 0000000..0dfca72
--- /dev/null
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * From split of dump_linuxpagetables.c
+ * Copyright 2016, Rashmica Gupta, IBM Corp.
+ *
+ */
+#include <linux/kernel.h>
+#include <asm/pgtable.h>
+
+#include "ptdump.h"
+
+static const struct flag_info flag_array[] = {
+       {
+               .mask   = _PAGE_PRIVILEGED,
+               .val    = 0,
+               .set    = "user",
+               .clear  = "    ",
+       }, {
+               .mask   = _PAGE_READ,
+               .val    = _PAGE_READ,
+               .set    = "r",
+               .clear  = " ",
+       }, {
+               .mask   = _PAGE_WRITE,
+               .val    = _PAGE_WRITE,
+               .set    = "w",
+               .clear  = " ",
+       }, {
+               .mask   = _PAGE_EXEC,
+               .val    = _PAGE_EXEC,
+               .set    = " X ",
+               .clear  = "   ",
+       }, {
+               .mask   = _PAGE_PTE,
+               .val    = _PAGE_PTE,
+               .set    = "pte",
+               .clear  = "   ",
+       }, {
+               .mask   = _PAGE_PRESENT,
+               .val    = _PAGE_PRESENT,
+               .set    = "valid",
+               .clear  = "     ",
+       }, {
+               .mask   = _PAGE_PRESENT | _PAGE_INVALID,
+               .val    = 0,
+               .set    = "       ",
+               .clear  = "present",
+       }, {
+               .mask   = H_PAGE_HASHPTE,
+               .val    = H_PAGE_HASHPTE,
+               .set    = "hpte",
+               .clear  = "    ",
+       }, {
+               .mask   = _PAGE_DIRTY,
+               .val    = _PAGE_DIRTY,
+               .set    = "dirty",
+               .clear  = "     ",
+       }, {
+               .mask   = _PAGE_ACCESSED,
+               .val    = _PAGE_ACCESSED,
+               .set    = "accessed",
+               .clear  = "        ",
+       }, {
+               .mask   = _PAGE_NON_IDEMPOTENT,
+               .val    = _PAGE_NON_IDEMPOTENT,
+               .set    = "non-idempotent",
+               .clear  = "              ",
+       }, {
+               .mask   = _PAGE_TOLERANT,
+               .val    = _PAGE_TOLERANT,
+               .set    = "tolerant",
+               .clear  = "        ",
+       }, {
+               .mask   = H_PAGE_BUSY,
+               .val    = H_PAGE_BUSY,
+               .set    = "busy",
+       }, {
+#ifdef CONFIG_PPC_64K_PAGES
+               .mask   = H_PAGE_COMBO,
+               .val    = H_PAGE_COMBO,
+               .set    = "combo",
+       }, {
+               .mask   = H_PAGE_4K_PFN,
+               .val    = H_PAGE_4K_PFN,
+               .set    = "4K_pfn",
+       }, {
+#else /* CONFIG_PPC_64K_PAGES */
+               .mask   = H_PAGE_F_GIX,
+               .val    = H_PAGE_F_GIX,
+               .set    = "f_gix",
+               .is_val = true,
+               .shift  = H_PAGE_F_GIX_SHIFT,
+       }, {
+               .mask   = H_PAGE_F_SECOND,
+               .val    = H_PAGE_F_SECOND,
+               .set    = "f_second",
+       }, {
+#endif /* CONFIG_PPC_64K_PAGES */
+               .mask   = _PAGE_SPECIAL,
+               .val    = _PAGE_SPECIAL,
+               .set    = "special",
+       }
+};
+
+struct pgtable_level pg_level[5] = {
+       {
+       }, { /* pgd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pud */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pmd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pte */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       },
+};
diff --git a/arch/powerpc/mm/ptdump/hashpagetable.c b/arch/powerpc/mm/ptdump/hashpagetable.c
new file mode 100644 (file)
index 0000000..8692946
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2016, Rashmica Gupta, IBM Corp.
+ *
+ * This traverses the kernel virtual memory and dumps the pages that are in
+ * the hash pagetable, along with their flags to
+ * /sys/kernel/debug/kernel_hash_pagetable.
+ *
+ * If radix is enabled then there is no hash page table and so no debugfs file
+ * is generated.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <asm/pgtable.h>
+#include <linux/const.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/plpar_wrappers.h>
+#include <linux/memblock.h>
+#include <asm/firmware.h>
+
+struct pg_state {
+       struct seq_file *seq;
+       const struct addr_marker *marker;
+       unsigned long start_address;
+       unsigned int level;
+       u64 current_flags;
+};
+
+struct addr_marker {
+       unsigned long start_address;
+       const char *name;
+};
+
+static struct addr_marker address_markers[] = {
+       { 0,    "Start of kernel VM" },
+       { 0,    "vmalloc() Area" },
+       { 0,    "vmalloc() End" },
+       { 0,    "isa I/O start" },
+       { 0,    "isa I/O end" },
+       { 0,    "phb I/O start" },
+       { 0,    "phb I/O end" },
+       { 0,    "I/O remap start" },
+       { 0,    "I/O remap end" },
+       { 0,    "vmemmap start" },
+       { -1,   NULL },
+};
+
+struct flag_info {
+       u64             mask;
+       u64             val;
+       const char      *set;
+       const char      *clear;
+       bool            is_val;
+       int             shift;
+};
+
+static const struct flag_info v_flag_array[] = {
+       {
+               .mask   = SLB_VSID_B,
+               .val    = SLB_VSID_B_256M,
+               .set    = "ssize: 256M",
+               .clear  = "ssize: 1T  ",
+       }, {
+               .mask   = HPTE_V_SECONDARY,
+               .val    = HPTE_V_SECONDARY,
+               .set    = "secondary",
+               .clear  = "primary  ",
+       }, {
+               .mask   = HPTE_V_VALID,
+               .val    = HPTE_V_VALID,
+               .set    = "valid  ",
+               .clear  = "invalid",
+       }, {
+               .mask   = HPTE_V_BOLTED,
+               .val    = HPTE_V_BOLTED,
+               .set    = "bolted",
+               .clear  = "",
+       }
+};
+
+static const struct flag_info r_flag_array[] = {
+       {
+               .mask   = HPTE_R_PP0 | HPTE_R_PP,
+               .val    = PP_RWXX,
+               .set    = "prot:RW--",
+       }, {
+               .mask   = HPTE_R_PP0 | HPTE_R_PP,
+               .val    = PP_RWRX,
+               .set    = "prot:RWR-",
+       }, {
+               .mask   = HPTE_R_PP0 | HPTE_R_PP,
+               .val    = PP_RWRW,
+               .set    = "prot:RWRW",
+       }, {
+               .mask   = HPTE_R_PP0 | HPTE_R_PP,
+               .val    = PP_RXRX,
+               .set    = "prot:R-R-",
+       }, {
+               .mask   = HPTE_R_PP0 | HPTE_R_PP,
+               .val    = PP_RXXX,
+               .set    = "prot:R---",
+       }, {
+               .mask   = HPTE_R_KEY_HI | HPTE_R_KEY_LO,
+               .val    = HPTE_R_KEY_HI | HPTE_R_KEY_LO,
+               .set    = "key",
+               .clear  = "",
+               .is_val = true,
+       }, {
+               .mask   = HPTE_R_R,
+               .val    = HPTE_R_R,
+               .set    = "ref",
+               .clear  = "   ",
+       }, {
+               .mask   = HPTE_R_C,
+               .val    = HPTE_R_C,
+               .set    = "changed",
+               .clear  = "       ",
+       }, {
+               .mask   = HPTE_R_N,
+               .val    = HPTE_R_N,
+               .set    = "no execute",
+       }, {
+               .mask   = HPTE_R_WIMG,
+               .val    = HPTE_R_W,
+               .set    = "writethru",
+       }, {
+               .mask   = HPTE_R_WIMG,
+               .val    = HPTE_R_I,
+               .set    = "no cache",
+       }, {
+               .mask   = HPTE_R_WIMG,
+               .val    = HPTE_R_G,
+               .set    = "guarded",
+       }
+};
+
+static int calculate_pagesize(struct pg_state *st, int ps, char s[])
+{
+       static const char units[] = "BKMGTPE";
+       const char *unit = units;
+
+       while (ps > 9 && unit[1]) {
+               ps -= 10;
+               unit++;
+       }
+       seq_printf(st->seq, "  %s_ps: %i%c\t", s, 1<<ps, *unit);
+       return ps;
+}
+
+static void dump_flag_info(struct pg_state *st, const struct flag_info
+               *flag, u64 pte, int num)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++, flag++) {
+               const char *s = NULL;
+               u64 val;
+
+               /* flag not defined so don't check it */
+               if (flag->mask == 0)
+                       continue;
+               /* Some 'flags' are actually values */
+               if (flag->is_val) {
+                       val = pte & flag->val;
+                       if (flag->shift)
+                               val = val >> flag->shift;
+                       seq_printf(st->seq, "  %s:%llx", flag->set, val);
+               } else {
+                       if ((pte & flag->mask) == flag->val)
+                               s = flag->set;
+                       else
+                               s = flag->clear;
+                       if (s)
+                               seq_printf(st->seq, "  %s", s);
+               }
+       }
+}
+
+static void dump_hpte_info(struct pg_state *st, unsigned long ea, u64 v, u64 r,
+               unsigned long rpn, int bps, int aps, unsigned long lp)
+{
+       int aps_index;
+
+       while (ea >= st->marker[1].start_address) {
+               st->marker++;
+               seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+       }
+       seq_printf(st->seq, "0x%lx:\t", ea);
+       seq_printf(st->seq, "AVPN:%llx\t", HPTE_V_AVPN_VAL(v));
+       dump_flag_info(st, v_flag_array, v, ARRAY_SIZE(v_flag_array));
+       seq_printf(st->seq, "  rpn: %lx\t", rpn);
+       dump_flag_info(st, r_flag_array, r, ARRAY_SIZE(r_flag_array));
+
+       calculate_pagesize(st, bps, "base");
+       aps_index = calculate_pagesize(st, aps, "actual");
+       if (aps_index != 2)
+               seq_printf(st->seq, "LP enc: %lx", lp);
+       seq_putc(st->seq, '\n');
+}
+
+
+static int native_find(unsigned long ea, int psize, bool primary, u64 *v, u64
+               *r)
+{
+       struct hash_pte *hptep;
+       unsigned long hash, vsid, vpn, hpte_group, want_v, hpte_v;
+       int i, ssize = mmu_kernel_ssize;
+       unsigned long shift = mmu_psize_defs[psize].shift;
+
+       /* calculate hash */
+       vsid = get_kernel_vsid(ea, ssize);
+       vpn  = hpt_vpn(ea, vsid, ssize);
+       hash = hpt_hash(vpn, shift, ssize);
+       want_v = hpte_encode_avpn(vpn, psize, ssize);
+
+       /* to check in the secondary hash table, we invert the hash */
+       if (!primary)
+               hash = ~hash;
+       hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+       for (i = 0; i < HPTES_PER_GROUP; i++) {
+               hptep = htab_address + hpte_group;
+               hpte_v = be64_to_cpu(hptep->v);
+
+               if (HPTE_V_COMPARE(hpte_v, want_v) && (hpte_v & HPTE_V_VALID)) {
+                       /* HPTE matches */
+                       *v = be64_to_cpu(hptep->v);
+                       *r = be64_to_cpu(hptep->r);
+                       return 0;
+               }
+               ++hpte_group;
+       }
+       return -1;
+}
+
+#ifdef CONFIG_PPC_PSERIES
+static int pseries_find(unsigned long ea, int psize, bool primary, u64 *v, u64 *r)
+{
+       struct hash_pte ptes[4];
+       unsigned long vsid, vpn, hash, hpte_group, want_v;
+       int i, j, ssize = mmu_kernel_ssize;
+       long lpar_rc = 0;
+       unsigned long shift = mmu_psize_defs[psize].shift;
+
+       /* calculate hash */
+       vsid = get_kernel_vsid(ea, ssize);
+       vpn  = hpt_vpn(ea, vsid, ssize);
+       hash = hpt_hash(vpn, shift, ssize);
+       want_v = hpte_encode_avpn(vpn, psize, ssize);
+
+       /* to check in the secondary hash table, we invert the hash */
+       if (!primary)
+               hash = ~hash;
+       hpte_group = (hash & htab_hash_mask) * HPTES_PER_GROUP;
+       /* see if we can find an entry in the hpte with this hash */
+       for (i = 0; i < HPTES_PER_GROUP; i += 4, hpte_group += 4) {
+               lpar_rc = plpar_pte_read_4(0, hpte_group, (void *)ptes);
+
+               if (lpar_rc != H_SUCCESS)
+                       continue;
+               for (j = 0; j < 4; j++) {
+                       if (HPTE_V_COMPARE(ptes[j].v, want_v) &&
+                                       (ptes[j].v & HPTE_V_VALID)) {
+                               /* HPTE matches */
+                               *v = ptes[j].v;
+                               *r = ptes[j].r;
+                               return 0;
+                       }
+               }
+       }
+       return -1;
+}
+#endif
+
+static void decode_r(int bps, unsigned long r, unsigned long *rpn, int *aps,
+               unsigned long *lp_bits)
+{
+       struct mmu_psize_def entry;
+       unsigned long arpn, mask, lp;
+       int penc = -2, idx = 0, shift;
+
+       /*.
+        * The LP field has 8 bits. Depending on the actual page size, some of
+        * these bits are concatenated with the APRN to get the RPN. The rest
+        * of the bits in the LP field is the LP value and is an encoding for
+        * the base page size and the actual page size.
+        *
+        *  -   find the mmu entry for our base page size
+        *  -   go through all page encodings and use the associated mask to
+        *      find an encoding that matches our encoding in the LP field.
+        */
+       arpn = (r & HPTE_R_RPN) >> HPTE_R_RPN_SHIFT;
+       lp = arpn & 0xff;
+
+       entry = mmu_psize_defs[bps];
+       while (idx < MMU_PAGE_COUNT) {
+               penc = entry.penc[idx];
+               if ((penc != -1) && (mmu_psize_defs[idx].shift)) {
+                       shift = mmu_psize_defs[idx].shift -  HPTE_R_RPN_SHIFT;
+                       mask = (0x1 << (shift)) - 1;
+                       if ((lp & mask) == penc) {
+                               *aps = mmu_psize_to_shift(idx);
+                               *lp_bits = lp & mask;
+                               *rpn = arpn >> shift;
+                               return;
+                       }
+               }
+               idx++;
+       }
+}
+
+static int base_hpte_find(unsigned long ea, int psize, bool primary, u64 *v,
+                         u64 *r)
+{
+#ifdef CONFIG_PPC_PSERIES
+       if (firmware_has_feature(FW_FEATURE_LPAR))
+               return pseries_find(ea, psize, primary, v, r);
+#endif
+       return native_find(ea, psize, primary, v, r);
+}
+
+static unsigned long hpte_find(struct pg_state *st, unsigned long ea, int psize)
+{
+       unsigned long slot;
+       u64 v  = 0, r = 0;
+       unsigned long rpn, lp_bits;
+       int base_psize = 0, actual_psize = 0;
+
+       if (ea < PAGE_OFFSET)
+               return -1;
+
+       /* Look in primary table */
+       slot = base_hpte_find(ea, psize, true, &v, &r);
+
+       /* Look in secondary table */
+       if (slot == -1)
+               slot = base_hpte_find(ea, psize, true, &v, &r);
+
+       /* No entry found */
+       if (slot == -1)
+               return -1;
+
+       /*
+        * We found an entry in the hash page table:
+        *  - check that this has the same base page
+        *  - find the actual page size
+        *  - find the RPN
+        */
+       base_psize = mmu_psize_to_shift(psize);
+
+       if ((v & HPTE_V_LARGE) == HPTE_V_LARGE) {
+               decode_r(psize, r, &rpn, &actual_psize, &lp_bits);
+       } else {
+               /* 4K actual page size */
+               actual_psize = 12;
+               rpn = (r & HPTE_R_RPN) >> HPTE_R_RPN_SHIFT;
+               /* In this case there are no LP bits */
+               lp_bits = -1;
+       }
+       /*
+        * We didn't find a matching encoding, so the PTE we found isn't for
+        * this address.
+        */
+       if (actual_psize == -1)
+               return -1;
+
+       dump_hpte_info(st, ea, v, r, rpn, base_psize, actual_psize, lp_bits);
+       return 0;
+}
+
+static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
+{
+       pte_t *pte = pte_offset_kernel(pmd, 0);
+       unsigned long addr, pteval, psize;
+       int i, status;
+
+       for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
+               addr = start + i * PAGE_SIZE;
+               pteval = pte_val(*pte);
+
+               if (addr < VMALLOC_END)
+                       psize = mmu_vmalloc_psize;
+               else
+                       psize = mmu_io_psize;
+#ifdef CONFIG_PPC_64K_PAGES
+               /* check for secret 4K mappings */
+               if (((pteval & H_PAGE_COMBO) == H_PAGE_COMBO) ||
+                       ((pteval & H_PAGE_4K_PFN) == H_PAGE_4K_PFN))
+                       psize = mmu_io_psize;
+#endif
+               /* check for hashpte */
+               status = hpte_find(st, addr, psize);
+
+               if (((pteval & H_PAGE_HASHPTE) != H_PAGE_HASHPTE)
+                               && (status != -1)) {
+               /* found a hpte that is not in the linux page tables */
+                       seq_printf(st->seq, "page probably bolted before linux"
+                               " pagetables were set: addr:%lx, pteval:%lx\n",
+                               addr, pteval);
+               }
+       }
+}
+
+static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
+{
+       pmd_t *pmd = pmd_offset(pud, 0);
+       unsigned long addr;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
+               addr = start + i * PMD_SIZE;
+               if (!pmd_none(*pmd))
+                       /* pmd exists */
+                       walk_pte(st, pmd, addr);
+       }
+}
+
+static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
+{
+       pud_t *pud = pud_offset(pgd, 0);
+       unsigned long addr;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
+               addr = start + i * PUD_SIZE;
+               if (!pud_none(*pud))
+                       /* pud exists */
+                       walk_pmd(st, pud, addr);
+       }
+}
+
+static void walk_pagetables(struct pg_state *st)
+{
+       pgd_t *pgd = pgd_offset_k(0UL);
+       unsigned int i;
+       unsigned long addr;
+
+       /*
+        * Traverse the linux pagetable structure and dump pages that are in
+        * the hash pagetable.
+        */
+       for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
+               addr = KERN_VIRT_START + i * PGDIR_SIZE;
+               if (!pgd_none(*pgd))
+                       /* pgd exists */
+                       walk_pud(st, pgd, addr);
+       }
+}
+
+
+static void walk_linearmapping(struct pg_state *st)
+{
+       unsigned long addr;
+
+       /*
+        * Traverse the linear mapping section of virtual memory and dump pages
+        * that are in the hash pagetable.
+        */
+       unsigned long psize = 1 << mmu_psize_defs[mmu_linear_psize].shift;
+
+       for (addr = PAGE_OFFSET; addr < PAGE_OFFSET +
+                       memblock_end_of_DRAM(); addr += psize)
+               hpte_find(st, addr, mmu_linear_psize);
+}
+
+static void walk_vmemmap(struct pg_state *st)
+{
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+       struct vmemmap_backing *ptr = vmemmap_list;
+
+       /*
+        * Traverse the vmemmaped memory and dump pages that are in the hash
+        * pagetable.
+        */
+       while (ptr->list) {
+               hpte_find(st, ptr->virt_addr, mmu_vmemmap_psize);
+               ptr = ptr->list;
+       }
+       seq_puts(st->seq, "---[ vmemmap end ]---\n");
+#endif
+}
+
+static void populate_markers(void)
+{
+       address_markers[0].start_address = PAGE_OFFSET;
+       address_markers[1].start_address = VMALLOC_START;
+       address_markers[2].start_address = VMALLOC_END;
+       address_markers[3].start_address = ISA_IO_BASE;
+       address_markers[4].start_address = ISA_IO_END;
+       address_markers[5].start_address = PHB_IO_BASE;
+       address_markers[6].start_address = PHB_IO_END;
+       address_markers[7].start_address = IOREMAP_BASE;
+       address_markers[8].start_address = IOREMAP_END;
+#ifdef CONFIG_PPC_BOOK3S_64
+       address_markers[9].start_address =  H_VMEMMAP_BASE;
+#else
+       address_markers[9].start_address =  VMEMMAP_BASE;
+#endif
+}
+
+static int ptdump_show(struct seq_file *m, void *v)
+{
+       struct pg_state st = {
+               .seq = m,
+               .start_address = PAGE_OFFSET,
+               .marker = address_markers,
+       };
+       /*
+        * Traverse the 0xc, 0xd and 0xf areas of the kernel virtual memory and
+        * dump pages that are in the hash pagetable.
+        */
+       walk_linearmapping(&st);
+       walk_pagetables(&st);
+       walk_vmemmap(&st);
+       return 0;
+}
+
+static int ptdump_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ptdump_show, NULL);
+}
+
+static const struct file_operations ptdump_fops = {
+       .open           = ptdump_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int ptdump_init(void)
+{
+       struct dentry *debugfs_file;
+
+       if (!radix_enabled()) {
+               populate_markers();
+               debugfs_file = debugfs_create_file("kernel_hash_pagetable",
+                               0400, NULL, NULL, &ptdump_fops);
+               return debugfs_file ? 0 : -ENOMEM;
+       }
+       return 0;
+}
+device_initcall(ptdump_init);
diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c
new file mode 100644 (file)
index 0000000..3713842
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2016, Rashmica Gupta, IBM Corp.
+ *
+ * This traverses the kernel pagetables and dumps the
+ * information about the used sections of memory to
+ * /sys/kernel/debug/kernel_pagetables.
+ *
+ * Derived from the arm64 implementation:
+ * Copyright (c) 2014, The Linux Foundation, Laura Abbott.
+ * (C) Copyright 2008 Intel Corporation, Arjan van de Ven.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/hugetlb.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <asm/fixmap.h>
+#include <asm/pgtable.h>
+#include <linux/const.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+
+#include "ptdump.h"
+
+#ifdef CONFIG_PPC32
+#define KERN_VIRT_START        0
+#endif
+
+/*
+ * To visualise what is happening,
+ *
+ *  - PTRS_PER_P** = how many entries there are in the corresponding P**
+ *  - P**_SHIFT = how many bits of the address we use to index into the
+ * corresponding P**
+ *  - P**_SIZE is how much memory we can access through the table - not the
+ * size of the table itself.
+ * P**={PGD, PUD, PMD, PTE}
+ *
+ *
+ * Each entry of the PGD points to a PUD. Each entry of a PUD points to a
+ * PMD. Each entry of a PMD points to a PTE. And every PTE entry points to
+ * a page.
+ *
+ * In the case where there are only 3 levels, the PUD is folded into the
+ * PGD: every PUD has only one entry which points to the PMD.
+ *
+ * The page dumper groups page table entries of the same type into a single
+ * description. It uses pg_state to track the range information while
+ * iterating over the PTE entries. When the continuity is broken it then
+ * dumps out a description of the range - ie PTEs that are virtually contiguous
+ * with the same PTE flags are chunked together. This is to make it clear how
+ * different areas of the kernel virtual memory are used.
+ *
+ */
+struct pg_state {
+       struct seq_file *seq;
+       const struct addr_marker *marker;
+       unsigned long start_address;
+       unsigned long start_pa;
+       unsigned long last_pa;
+       unsigned int level;
+       u64 current_flags;
+};
+
+struct addr_marker {
+       unsigned long start_address;
+       const char *name;
+};
+
+static struct addr_marker address_markers[] = {
+       { 0,    "Start of kernel VM" },
+       { 0,    "vmalloc() Area" },
+       { 0,    "vmalloc() End" },
+#ifdef CONFIG_PPC64
+       { 0,    "isa I/O start" },
+       { 0,    "isa I/O end" },
+       { 0,    "phb I/O start" },
+       { 0,    "phb I/O end" },
+       { 0,    "I/O remap start" },
+       { 0,    "I/O remap end" },
+       { 0,    "vmemmap start" },
+#else
+       { 0,    "Early I/O remap start" },
+       { 0,    "Early I/O remap end" },
+#ifdef CONFIG_NOT_COHERENT_CACHE
+       { 0,    "Consistent mem start" },
+       { 0,    "Consistent mem end" },
+#endif
+#ifdef CONFIG_HIGHMEM
+       { 0,    "Highmem PTEs start" },
+       { 0,    "Highmem PTEs end" },
+#endif
+       { 0,    "Fixmap start" },
+       { 0,    "Fixmap end" },
+#endif
+       { -1,   NULL },
+};
+
+static void dump_flag_info(struct pg_state *st, const struct flag_info
+               *flag, u64 pte, int num)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++, flag++) {
+               const char *s = NULL;
+               u64 val;
+
+               /* flag not defined so don't check it */
+               if (flag->mask == 0)
+                       continue;
+               /* Some 'flags' are actually values */
+               if (flag->is_val) {
+                       val = pte & flag->val;
+                       if (flag->shift)
+                               val = val >> flag->shift;
+                       seq_printf(st->seq, "  %s:%llx", flag->set, val);
+               } else {
+                       if ((pte & flag->mask) == flag->val)
+                               s = flag->set;
+                       else
+                               s = flag->clear;
+                       if (s)
+                               seq_printf(st->seq, "  %s", s);
+               }
+               st->current_flags &= ~flag->mask;
+       }
+       if (st->current_flags != 0)
+               seq_printf(st->seq, "  unknown flags:%llx", st->current_flags);
+}
+
+static void dump_addr(struct pg_state *st, unsigned long addr)
+{
+       static const char units[] = "KMGTPE";
+       const char *unit = units;
+       unsigned long delta;
+
+#ifdef CONFIG_PPC64
+#define REG            "0x%016lx"
+#else
+#define REG            "0x%08lx"
+#endif
+
+       seq_printf(st->seq, REG "-" REG " ", st->start_address, addr - 1);
+       if (st->start_pa == st->last_pa && st->start_address + PAGE_SIZE != addr) {
+               seq_printf(st->seq, "[" REG "]", st->start_pa);
+               delta = PAGE_SIZE >> 10;
+       } else {
+               seq_printf(st->seq, " " REG " ", st->start_pa);
+               delta = (addr - st->start_address) >> 10;
+       }
+       /* Work out what appropriate unit to use */
+       while (!(delta & 1023) && unit[1]) {
+               delta >>= 10;
+               unit++;
+       }
+       seq_printf(st->seq, "%9lu%c", delta, *unit);
+
+}
+
+static void note_page(struct pg_state *st, unsigned long addr,
+              unsigned int level, u64 val)
+{
+       u64 flag = val & pg_level[level].mask;
+       u64 pa = val & PTE_RPN_MASK;
+
+       /* At first no level is set */
+       if (!st->level) {
+               st->level = level;
+               st->current_flags = flag;
+               st->start_address = addr;
+               st->start_pa = pa;
+               st->last_pa = pa;
+               seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+       /*
+        * Dump the section of virtual memory when:
+        *   - the PTE flags from one entry to the next differs.
+        *   - we change levels in the tree.
+        *   - the address is in a different section of memory and is thus
+        *   used for a different purpose, regardless of the flags.
+        *   - the pa of this page is not adjacent to the last inspected page
+        */
+       } else if (flag != st->current_flags || level != st->level ||
+                  addr >= st->marker[1].start_address ||
+                  (pa != st->last_pa + PAGE_SIZE &&
+                   (pa != st->start_pa || st->start_pa != st->last_pa))) {
+
+               /* Check the PTE flags */
+               if (st->current_flags) {
+                       dump_addr(st, addr);
+
+                       /* Dump all the flags */
+                       if (pg_level[st->level].flag)
+                               dump_flag_info(st, pg_level[st->level].flag,
+                                         st->current_flags,
+                                         pg_level[st->level].num);
+
+                       seq_putc(st->seq, '\n');
+               }
+
+               /*
+                * Address indicates we have passed the end of the
+                * current section of virtual memory
+                */
+               while (addr >= st->marker[1].start_address) {
+                       st->marker++;
+                       seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+               }
+               st->start_address = addr;
+               st->start_pa = pa;
+               st->last_pa = pa;
+               st->current_flags = flag;
+               st->level = level;
+       } else {
+               st->last_pa = pa;
+       }
+}
+
+static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
+{
+       pte_t *pte = pte_offset_kernel(pmd, 0);
+       unsigned long addr;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
+               addr = start + i * PAGE_SIZE;
+               note_page(st, addr, 4, pte_val(*pte));
+
+       }
+}
+
+static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
+{
+       pmd_t *pmd = pmd_offset(pud, 0);
+       unsigned long addr;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
+               addr = start + i * PMD_SIZE;
+               if (!pmd_none(*pmd) && !pmd_huge(*pmd))
+                       /* pmd exists */
+                       walk_pte(st, pmd, addr);
+               else
+                       note_page(st, addr, 3, pmd_val(*pmd));
+       }
+}
+
+static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
+{
+       pud_t *pud = pud_offset(pgd, 0);
+       unsigned long addr;
+       unsigned int i;
+
+       for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
+               addr = start + i * PUD_SIZE;
+               if (!pud_none(*pud) && !pud_huge(*pud))
+                       /* pud exists */
+                       walk_pmd(st, pud, addr);
+               else
+                       note_page(st, addr, 2, pud_val(*pud));
+       }
+}
+
+static void walk_pagetables(struct pg_state *st)
+{
+       pgd_t *pgd = pgd_offset_k(0UL);
+       unsigned int i;
+       unsigned long addr;
+
+       addr = st->start_address;
+
+       /*
+        * Traverse the linux pagetable structure and dump pages that are in
+        * the hash pagetable.
+        */
+       for (i = 0; i < PTRS_PER_PGD; i++, pgd++, addr += PGDIR_SIZE) {
+               if (!pgd_none(*pgd) && !pgd_huge(*pgd))
+                       /* pgd exists */
+                       walk_pud(st, pgd, addr);
+               else
+                       note_page(st, addr, 1, pgd_val(*pgd));
+       }
+}
+
+static void populate_markers(void)
+{
+       int i = 0;
+
+       address_markers[i++].start_address = PAGE_OFFSET;
+       address_markers[i++].start_address = VMALLOC_START;
+       address_markers[i++].start_address = VMALLOC_END;
+#ifdef CONFIG_PPC64
+       address_markers[i++].start_address = ISA_IO_BASE;
+       address_markers[i++].start_address = ISA_IO_END;
+       address_markers[i++].start_address = PHB_IO_BASE;
+       address_markers[i++].start_address = PHB_IO_END;
+       address_markers[i++].start_address = IOREMAP_BASE;
+       address_markers[i++].start_address = IOREMAP_END;
+#ifdef CONFIG_PPC_BOOK3S_64
+       address_markers[i++].start_address =  H_VMEMMAP_BASE;
+#else
+       address_markers[i++].start_address =  VMEMMAP_BASE;
+#endif
+#else /* !CONFIG_PPC64 */
+       address_markers[i++].start_address = ioremap_bot;
+       address_markers[i++].start_address = IOREMAP_TOP;
+#ifdef CONFIG_NOT_COHERENT_CACHE
+       address_markers[i++].start_address = IOREMAP_TOP;
+       address_markers[i++].start_address = IOREMAP_TOP +
+                                            CONFIG_CONSISTENT_SIZE;
+#endif
+#ifdef CONFIG_HIGHMEM
+       address_markers[i++].start_address = PKMAP_BASE;
+       address_markers[i++].start_address = PKMAP_ADDR(LAST_PKMAP);
+#endif
+       address_markers[i++].start_address = FIXADDR_START;
+       address_markers[i++].start_address = FIXADDR_TOP;
+#endif /* CONFIG_PPC64 */
+}
+
+static int ptdump_show(struct seq_file *m, void *v)
+{
+       struct pg_state st = {
+               .seq = m,
+               .marker = address_markers,
+       };
+
+       if (radix_enabled())
+               st.start_address = PAGE_OFFSET;
+       else
+               st.start_address = KERN_VIRT_START;
+
+       /* Traverse kernel page tables */
+       walk_pagetables(&st);
+       note_page(&st, 0, 0, 0);
+       return 0;
+}
+
+
+static int ptdump_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ptdump_show, NULL);
+}
+
+static const struct file_operations ptdump_fops = {
+       .open           = ptdump_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void build_pgtable_complete_mask(void)
+{
+       unsigned int i, j;
+
+       for (i = 0; i < ARRAY_SIZE(pg_level); i++)
+               if (pg_level[i].flag)
+                       for (j = 0; j < pg_level[i].num; j++)
+                               pg_level[i].mask |= pg_level[i].flag[j].mask;
+}
+
+static int ptdump_init(void)
+{
+       struct dentry *debugfs_file;
+
+       populate_markers();
+       build_pgtable_complete_mask();
+       debugfs_file = debugfs_create_file("kernel_page_tables", 0400, NULL,
+                       NULL, &ptdump_fops);
+       return debugfs_file ? 0 : -ENOMEM;
+}
+device_initcall(ptdump_init);
diff --git a/arch/powerpc/mm/ptdump/ptdump.h b/arch/powerpc/mm/ptdump/ptdump.h
new file mode 100644 (file)
index 0000000..5d51363
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/types.h>
+
+struct flag_info {
+       u64             mask;
+       u64             val;
+       const char      *set;
+       const char      *clear;
+       bool            is_val;
+       int             shift;
+};
+
+struct pgtable_level {
+       const struct flag_info *flag;
+       size_t num;
+       u64 mask;
+};
+
+extern struct pgtable_level pg_level[5];
diff --git a/arch/powerpc/mm/ptdump/segment_regs.c b/arch/powerpc/mm/ptdump/segment_regs.c
new file mode 100644 (file)
index 0000000..5018436
--- /dev/null
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018, Christophe Leroy CS S.I.
+ * <christophe.leroy@c-s.fr>
+ *
+ * This dumps the content of Segment Registers
+ */
+
+#include <asm/debugfs.h>
+
+static void seg_show(struct seq_file *m, int i)
+{
+       u32 val = mfsrin(i << 28);
+
+       seq_printf(m, "0x%01x0000000-0x%01xfffffff ", i, i);
+       seq_printf(m, "Kern key %d ", (val >> 30) & 1);
+       seq_printf(m, "User key %d ", (val >> 29) & 1);
+       if (val & 0x80000000) {
+               seq_printf(m, "Device 0x%03x", (val >> 20) & 0x1ff);
+               seq_printf(m, "-0x%05x", val & 0xfffff);
+       } else {
+               if (val & 0x10000000)
+                       seq_puts(m, "No Exec ");
+               seq_printf(m, "VSID 0x%06x", val & 0xffffff);
+       }
+       seq_puts(m, "\n");
+}
+
+static int sr_show(struct seq_file *m, void *v)
+{
+       int i;
+
+       seq_puts(m, "---[ User Segments ]---\n");
+       for (i = 0; i < TASK_SIZE >> 28; i++)
+               seg_show(m, i);
+
+       seq_puts(m, "\n---[ Kernel Segments ]---\n");
+       for (; i < 16; i++)
+               seg_show(m, i);
+
+       return 0;
+}
+
+static int sr_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, sr_show, NULL);
+}
+
+static const struct file_operations sr_fops = {
+       .open           = sr_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init sr_init(void)
+{
+       struct dentry *debugfs_file;
+
+       debugfs_file = debugfs_create_file("segment_registers", 0400,
+                                          powerpc_debugfs_root, NULL, &sr_fops);
+       return debugfs_file ? 0 : -ENOMEM;
+}
+device_initcall(sr_init);
diff --git a/arch/powerpc/mm/ptdump/shared.c b/arch/powerpc/mm/ptdump/shared.c
new file mode 100644 (file)
index 0000000..f7ed2f1
--- /dev/null
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * From split of dump_linuxpagetables.c
+ * Copyright 2016, Rashmica Gupta, IBM Corp.
+ *
+ */
+#include <linux/kernel.h>
+#include <asm/pgtable.h>
+
+#include "ptdump.h"
+
+static const struct flag_info flag_array[] = {
+       {
+               .mask   = _PAGE_USER,
+               .val    = _PAGE_USER,
+               .set    = "user",
+               .clear  = "    ",
+       }, {
+               .mask   = _PAGE_RW,
+               .val    = _PAGE_RW,
+               .set    = "rw",
+               .clear  = "r ",
+       }, {
+               .mask   = _PAGE_EXEC,
+               .val    = _PAGE_EXEC,
+               .set    = " X ",
+               .clear  = "   ",
+       }, {
+               .mask   = _PAGE_PRESENT,
+               .val    = _PAGE_PRESENT,
+               .set    = "present",
+               .clear  = "       ",
+       }, {
+               .mask   = _PAGE_GUARDED,
+               .val    = _PAGE_GUARDED,
+               .set    = "guarded",
+               .clear  = "       ",
+       }, {
+               .mask   = _PAGE_DIRTY,
+               .val    = _PAGE_DIRTY,
+               .set    = "dirty",
+               .clear  = "     ",
+       }, {
+               .mask   = _PAGE_ACCESSED,
+               .val    = _PAGE_ACCESSED,
+               .set    = "accessed",
+               .clear  = "        ",
+       }, {
+               .mask   = _PAGE_WRITETHRU,
+               .val    = _PAGE_WRITETHRU,
+               .set    = "write through",
+               .clear  = "             ",
+       }, {
+               .mask   = _PAGE_NO_CACHE,
+               .val    = _PAGE_NO_CACHE,
+               .set    = "no cache",
+               .clear  = "        ",
+       }, {
+               .mask   = _PAGE_SPECIAL,
+               .val    = _PAGE_SPECIAL,
+               .set    = "special",
+       }
+};
+
+struct pgtable_level pg_level[5] = {
+       {
+       }, { /* pgd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pud */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pmd */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       }, { /* pte */
+               .flag   = flag_array,
+               .num    = ARRAY_SIZE(flag_array),
+       },
+};