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
Show replies by date
KALLSYMS_V2 and KMOD_V2 both used bit 0x2000 of kt->flags. On a kernel
built without CONFIG_MODULES there is no "modules" or "module_list"
symbol, but KALLSYMS_V2 is still set. module_init() then took the
KMOD_V2 path, tried to resolve "modules" and aborted the session:
WARNING: cannot determine how modules are linked
crash: cannot resolve: "modules"
Give KALLSYMS_V2 its own bit. With that, a CONFIG_MODULES=n dumpfile
reports "no kernel module access" and the rest of the session works
normally.
Signed-off-by: Dave Young <yangrr.2009(a)tsinghua.org.cn>
---
defs.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/defs.h b/defs.h
index d05b3f34e2f4..157077a10064 100644
--- a/defs.h
+++ b/defs.h
@@ -656,7 +656,12 @@ struct new_utsname {
#define GCC_3_3_2 (0x800)
#define KMOD_V1 (0x1000)
#define KMOD_V2 (0x2000)
-#define KALLSYMS_V2 (0x2000)
+/*
+ * KALLSYMS_V2 must not share a bit with KMOD_V2: on a kernel built
+ * without CONFIG_MODULES there is no "modules" symbol, but KALLSYMS_V2
+ * is still set and module_init() would mistake it for module support.
+ */
+#define KALLSYMS_V2 (0x80000000)
#define TVEC_BASES_V2 (0x4000)
#define GCC_3_3_3 (0x8000)
#define USE_OLD_BT (0x10000)
--
2.51.1