diff mbox series

arm64: mm: avoid virt_to_phys(init_mm.pgd)

Message ID 20191003094932.10216-1-mark.rutland@arm.com (mailing list archive)
State Mainlined
Commit e4365f968fcd5ef4a2f69f11ebc9ee66f47fc879
Headers show
Series arm64: mm: avoid virt_to_phys(init_mm.pgd) | expand

Commit Message

Mark Rutland Oct. 3, 2019, 9:49 a.m. UTC
If we take an unhandled fault in the kernel, we call show_pte() to dump
the {PGDP,PGD,PUD,PMD,PTE} values for the corresponding page table walk,
where the PGDP value is virt_to_phys(mm->pgd).

The boot-time and runtime kernel page tables, init_pg_dir and
swapper_pg_dir respectively, are kernel symbols. Thus, it is not valid
to call virt_to_phys() on either of these, though we'll do so if we take
a fault on a TTBR1 address.

When CONFIG_DEBUG_VIRTUAL is not selected, virt_to_phys() will silently
fix this up. However, when CONFIG_DEBUG_VIRTUAL is selected, this
results in splats as below. Depending on when these occur, they can
happen to suppress information needed to debug the original unhandled
fault, such as the backtrace:

| Unable to handle kernel paging request at virtual address ffff7fffec73cf0f
| Mem abort info:
|   ESR = 0x96000004
|   EC = 0x25: DABT (current EL), IL = 32 bits
|   SET = 0, FnV = 0
|   EA = 0, S1PTW = 0
| Data abort info:
|   ISV = 0, ISS = 0x00000004
|   CM = 0, WnR = 0
| ------------[ cut here ]------------
| virt_to_phys used for non-linear address: 00000000102c9dbe (swapper_pg_dir+0x0/0x1000)
| WARNING: CPU: 1 PID: 7558 at arch/arm64/mm/physaddr.c:15 __virt_to_phys+0xe0/0x170 arch/arm64/mm/physaddr.c:12
| Kernel panic - not syncing: panic_on_warn set ...
| SMP: stopping secondary CPUs
| Dumping ftrace buffer:
|    (ftrace buffer empty)
| Kernel Offset: disabled
| CPU features: 0x0002,23000438
| Memory Limit: none
| Rebooting in 1 seconds..

We can avoid this by ensuring that we call __pa_symbol() for
init_mm.pgd, as this will always be a kernel symbol. As the dumped
{PGD,PUD,PMD,PTE} values are the raw values from the relevant entries we
don't need to handle these specially.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Will Deacon <will@kernel.org>
---
 arch/arm64/mm/fault.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

Comments

Catalin Marinas Oct. 3, 2019, 3:40 p.m. UTC | #1
On Thu, Oct 03, 2019 at 10:49:32AM +0100, Mark Rutland wrote:
> If we take an unhandled fault in the kernel, we call show_pte() to dump
> the {PGDP,PGD,PUD,PMD,PTE} values for the corresponding page table walk,
> where the PGDP value is virt_to_phys(mm->pgd).
> 
> The boot-time and runtime kernel page tables, init_pg_dir and
> swapper_pg_dir respectively, are kernel symbols. Thus, it is not valid
> to call virt_to_phys() on either of these, though we'll do so if we take
> a fault on a TTBR1 address.
> 
> When CONFIG_DEBUG_VIRTUAL is not selected, virt_to_phys() will silently
> fix this up. However, when CONFIG_DEBUG_VIRTUAL is selected, this
> results in splats as below. Depending on when these occur, they can
> happen to suppress information needed to debug the original unhandled
> fault, such as the backtrace:
> 
> | Unable to handle kernel paging request at virtual address ffff7fffec73cf0f
> | Mem abort info:
> |   ESR = 0x96000004
> |   EC = 0x25: DABT (current EL), IL = 32 bits
> |   SET = 0, FnV = 0
> |   EA = 0, S1PTW = 0
> | Data abort info:
> |   ISV = 0, ISS = 0x00000004
> |   CM = 0, WnR = 0
> | ------------[ cut here ]------------
> | virt_to_phys used for non-linear address: 00000000102c9dbe (swapper_pg_dir+0x0/0x1000)
> | WARNING: CPU: 1 PID: 7558 at arch/arm64/mm/physaddr.c:15 __virt_to_phys+0xe0/0x170 arch/arm64/mm/physaddr.c:12
> | Kernel panic - not syncing: panic_on_warn set ...
> | SMP: stopping secondary CPUs
> | Dumping ftrace buffer:
> |    (ftrace buffer empty)
> | Kernel Offset: disabled
> | CPU features: 0x0002,23000438
> | Memory Limit: none
> | Rebooting in 1 seconds..
> 
> We can avoid this by ensuring that we call __pa_symbol() for
> init_mm.pgd, as this will always be a kernel symbol. As the dumped
> {PGD,PUD,PMD,PTE} values are the raw values from the relevant entries we
> don't need to handle these specially.
> 
> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: James Morse <james.morse@arm.com>
> Cc: Will Deacon <will@kernel.org>

Acked-by: Catalin Marinas <catalin.marinas@arm.com>
diff mbox series

Patch

diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 115d7a0e4b08..6acd866f31fd 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -113,6 +113,15 @@  static inline bool is_ttbr1_addr(unsigned long addr)
 	return arch_kasan_reset_tag(addr) >= PAGE_OFFSET;
 }
 
+static inline unsigned long mm_to_pgd_phys(struct mm_struct *mm)
+{
+	/* Either init_pg_dir or swapper_pg_dir */
+	if (mm == &init_mm)
+		return __pa_symbol(mm->pgd);
+
+	return (unsigned long)virt_to_phys(mm->pgd);
+}
+
 /*
  * Dump out the page tables associated with 'addr' in the currently active mm.
  */
@@ -141,7 +150,7 @@  static void show_pte(unsigned long addr)
 
 	pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n",
 		 mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K,
-		 vabits_actual, (unsigned long)virt_to_phys(mm->pgd));
+		 vabits_actual, mm_to_pgd_phys(mm));
 	pgdp = pgd_offset(mm, addr);
 	pgd = READ_ONCE(*pgdp);
 	pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd));