x86/mm: Skip the hypervisor range when walking PGD
authorBoris Ostrovsky <boris.ostrovsky@oracle.com>
Thu, 5 Nov 2015 18:56:35 +0000 (13:56 -0500)
committerThomas Gleixner <tglx@linutronix.de>
Sat, 7 Nov 2015 09:39:39 +0000 (10:39 +0100)
The range between 0xffff800000000000 and 0xffff87ffffffffff is reserved
for hypervisor and therefore we should not try to follow PGD's indexes
corresponding to those addresses.

While this has always been a problem, with the new W+X warning
mechanism ptdump_walk_pgd_level_core() can now be called during boot,
causing a PV Xen guest to crash.

[ tglx: Replaced the macro with a readable inline ]

Fixes: e1a58320a38d "x86/mm: Warn on W^X mappings"
Reported-by: Sander Eikelenboom <linux@eikelenboom.it>
Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: xen-devel@lists.xen.org
Link: http://lkml.kernel.org/r/1446749795-27764-1-git-send-email-boris.ostrovsky@oracle.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/mm/dump_pagetables.c

index 1bf417e9cc1309e3353f08fee9dad6915fb34c0c..a035c2aa780190c8c198e1bd8c2bec20a18d8ecb 100644 (file)
@@ -358,6 +358,21 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t addr,
 #define pgd_none(a)  pud_none(__pud(pgd_val(a)))
 #endif
 
+#ifdef CONFIG_X86_64
+static inline bool is_hypervisor_range(int idx)
+{
+       /*
+        * ffff800000000000 - ffff87ffffffffff is reserved for
+        * the hypervisor.
+        */
+       return paravirt_enabled() &&
+               (idx >= pgd_index(__PAGE_OFFSET) - 16) &&
+               (idx < pgd_index(__PAGE_OFFSET));
+}
+#else
+static inline bool is_hypervisor_range(int idx) { return false; }
+#endif
+
 static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
                                       bool checkwx)
 {
@@ -381,7 +396,7 @@ static void ptdump_walk_pgd_level_core(struct seq_file *m, pgd_t *pgd,
 
        for (i = 0; i < PTRS_PER_PGD; i++) {
                st.current_address = normalize_addr(i * PGD_LEVEL_MULT);
-               if (!pgd_none(*start)) {
+               if (!pgd_none(*start) && !is_hypervisor_range(i)) {
                        if (pgd_large(*start) || !pgd_present(*start)) {
                                prot = pgd_flags(*start);
                                note_page(m, &st, __pgprot(prot), 1);