Since kernel commit 7bc1a0f9e176 ("arm64: mm: use single quantity to
represent the PA to VA translation") memstart_addr is no longer always
a physical address. For a 52-bit VA kernel that runs with fewer than
52 bits of VA, the kernel adjusts it by _PAGE_OFFSET(48) -
_PAGE_OFFSET(52), which makes it negative. crash stored that value in
phys_offset and used it as the physical base of the dumpfile, so a
dumpfile from such a kernel failed during initialization:
crash: seek error: kernel virtual address: ffff00003fde1c40
type: "memory section root table"
Convert the negative value back to the real physical base before it is
stored in phys_offset. Apply the conversion to every source of
phys_offset: vmcoreinfo, the memstart_addr symbol, /proc/iomem, diskdump
and kdump. Use the named ARM64_FLIP_PAGE_OFFSET_48 and
ARM64_FLIP_PAGE_OFFSET_52 constants for the adjustment.
This keeps live /proc/kcore debugging on 5.4-5.10 kernels working as
well; that path still reads the kernel's physvirt_offset and uses it to
correct phys_offset.
Verified on a 7.3 arm64 dumpfile (4K pages, 52-bit VA configured,
48-bit VA active, KASLR) where PHYS_OFFSET is exported as
0xfff1000040000000 and the physical base is 0x40000000.
Signed-off-by: Dave Young <yangrr.2009(a)tsinghua.org.cn>
---
arm64.c | 42 ++++++++++++++++++++++++++++++++++++------
defs.h | 7 +++++++
2 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/arm64.c b/arm64.c
index 654b9726cfe1..01b3bf783a8c 100644
--- a/arm64.c
+++ b/arm64.c
@@ -1719,7 +1719,11 @@ arm64_calc_physvirt_offset(void)
machdep->flags |= HAS_PHYSVIRT_OFFSET;
ms->physvirt_offset = physvirt_offset;
- /* Update the ms->phys_offset which is wrong */
+ /*
+ * physvirt_offset is authoritative on 5.4-5.10
+ * kernels; reading it is what makes live
+ * /proc/kcore debugging work.
+ */
ms->phys_offset = ms->physvirt_offset + ms->page_offset;
return;
}
@@ -1739,6 +1743,30 @@ arm64_calc_physvirt_offset(void)
}
+/*
+ * Store the kernel's "memstart_addr" value as the physical base address
+ * of the dumpfile.
+ *
+ * Since kernel commit 7bc1a0f9e176 ("arm64: mm: use single quantity to
+ * represent the PA to VA translation") memstart_addr is no longer always
+ * a physical address: for a 52-bit VA kernel running with less than 52
+ * bits of VA, the kernel adjusts it by _PAGE_OFFSET(48) - _PAGE_OFFSET(52)
+ * so that it becomes negative. crash needs the real physical base for
+ * address translation.
+ */
+static void
+arm64_store_phys_offset(ulong value)
+{
+ struct machine_specific *ms = machdep->machspec;
+ long memstart_addr = (long)value;
+
+ if (memstart_addr < 0)
+ ms->phys_offset = memstart_addr +
+ ARM64_FLIP_PAGE_OFFSET_48 - ARM64_FLIP_PAGE_OFFSET_52;
+ else
+ ms->phys_offset = value;
+}
+
static void
arm64_calc_phys_offset(void)
{
@@ -1766,8 +1794,10 @@ arm64_calc_phys_offset(void)
if ((machdep->flags & NEW_VMEMMAP) &&
ms->kimage_voffset && (sp =
kernel_symbol_search("memstart_addr"))) {
if (pc->flags & PROC_KCORE) {
- if (arm64_get_vmcoreinfo(&ms->phys_offset, "NUMBER(PHYS_OFFSET)",
NUM_HEX))
+ if (arm64_get_vmcoreinfo(&phys_offset, "NUMBER(PHYS_OFFSET)", NUM_HEX))
{
+ arm64_store_phys_offset(phys_offset);
return;
+ }
vaddr = symbol_value_from_proc_kallsyms("memstart_addr");
if (vaddr == BADVAL)
vaddr = sp->value;
@@ -1778,7 +1808,7 @@ arm64_calc_phys_offset(void)
}
if (READMEM(pc->mfd, &phys_offset, sizeof(phys_offset),
vaddr, paddr) > 0) {
- ms->phys_offset = phys_offset;
+ arm64_store_phys_offset(phys_offset);
return;
}
@@ -1813,11 +1843,11 @@ arm64_calc_phys_offset(void)
if (errflag)
return;
- ms->phys_offset = phys_offset;
+ arm64_store_phys_offset(phys_offset);
} else if (DISKDUMP_DUMPFILE() && diskdump_phys_base(&phys_offset)) {
- ms->phys_offset = phys_offset;
+ arm64_store_phys_offset(phys_offset);
} else if (KDUMP_DUMPFILE() && arm64_kdump_phys_base(&phys_offset)) {
- ms->phys_offset = phys_offset;
+ arm64_store_phys_offset(phys_offset);
} else {
error(WARNING,
"phys_offset cannot be determined from the dumpfile.\n");
diff --git a/defs.h b/defs.h
index ad874e99c335..d05b3f34e2f4 100644
--- a/defs.h
+++ b/defs.h
@@ -3509,6 +3509,13 @@ typedef signed int s32;
<< (machdep->machspec->VA_BITS - 1))
/* kernels >= v5.4 the kernel VA space is flipped */
#define ARM64_FLIP_PAGE_OFFSET (-(1UL) << machdep->machspec->VA_BITS)
+/*
+ * Fixed 48/52-bit page offsets, used to decode the kernel's negative
+ * memstart_addr when CONFIG_ARM64_VA_BITS_52 is enabled but fewer than
+ * 52 VA bits are active.
+ */
+#define ARM64_FLIP_PAGE_OFFSET_48 (-(1UL) << 48)
+#define ARM64_FLIP_PAGE_OFFSET_52 (-(1UL) << 52)
#define ARM64_USERSPACE_TOP ((1UL) << machdep->machspec->VA_BITS)
#define ARM64_USERSPACE_TOP_ACTUAL ((1UL) <<
machdep->machspec->VA_BITS_ACTUAL)
--
2.51.1