1.) When I tested live system with "crash vmlinux /proc/kcore" in kernel v5.7,
I met the following crash issue:
........................................
crash: seek error: kernel virtual address: ffff75e9fffff000 type: "pud
page"
........................................
2.) The root cause is the PTOV does not work correctly for some kernel,
and then arm64_vtop_4level_4k() does not work correctly too.
Why PTOV does not work?
The PHYS_OFFSET is just wrapper of memstart_addr.
...............................
#define PHYS_OFFSET ({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })
...............................
Because memstart_addr is changed after physvirt_offset is initialized.
so the NUMBER(PHYS_OFFSET) does not return the correct value.
3.) How many kernel versions have this bug?
1) In kernel v5.4, the patch:
"5383cc6efed137 arm64: mm: Introduce vabits_actual"
makes the NUMBER(PHYS_OFFSET) do not work correctly.
2) In kernel v5.10, the patch:
"7bc1a0f9e17658 arm64: mm: use single quantity
to represent the PA to VA translation"
makes the NUMBER(PHYS_OFFSET) work again.
4.) What does this patch do?
This patch uses the same method as makedumpfile does:
Use the PT_LOAD segments to get the phys_offset.
Signed-off-by: Huang Shijie <shijie(a)os.amperecomputing.com>
---
arm64.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/arm64.c b/arm64.c
index e3fa316..d81c2b9 100644
--- a/arm64.c
+++ b/arm64.c
@@ -1426,6 +1426,51 @@ arm64_calc_physvirt_offset(void)
}
+/*
+ * Check if an virtual address is a linear address.
+ */
+#define PAGE_END (_PAGE_END(ms->VA_BITS_ACTUAL))
+static int arm64_is_linear_addr(struct machine_specific *ms,
+ unsigned long va)
+{
+ return (va - PAGE_OFFSET) < (PAGE_END - PAGE_OFFSET);
+}
+
+/*
+ * This function only works for kernel range: [5.4, 5.10).
+ *
+ * 1) In kernel v5.4, the patch:
+ * "5383cc6efed137 arm64: mm: Introduce vabits_actual"
+ *
+ * makes the NUMBER(PHYS_OFFSET) do not work correctly.
+ *
+ * 2) In kernel v5.10, the patch:
+ * "7bc1a0f9e17658 arm64: mm: use single quantity
+ * to represent the PA to VA translation"
+ *
+ * makes the NUMBER(PHYS_OFFSET) work again.
+ *
+ * This function tries to get the phys_offset from PT_LOAD segments.
+ * This method was originally used by the makedumpfile tool.
+ */
+static void arm64_get_phys_offset_by_pt_load(struct machine_specific *ms)
+{
+ int i;
+ Elf64_Phdr *h;
+ struct proc_kcore_data *pkd = arm64_get_pkd();
+
+ for (i = 0; i < pkd->segments; i++) {
+ h = &pkd->load64[i];
+
+ if (arm64_is_linear_addr(ms, h->p_vaddr)) {
+ ms->phys_offset = h->p_paddr - (h->p_vaddr & ~PAGE_OFFSET);
+ return;
+ }
+ }
+
+ error(FATAL, "We cannot get the correct phys_offset!\n");
+}
+
static void
arm64_calc_phys_offset(void)
{
@@ -1454,6 +1499,14 @@ arm64_calc_phys_offset(void)
if ((machdep->flags & NEW_VMEMMAP) &&
ms->kimage_voffset && (sp =
kernel_symbol_search("memstart_addr"))) {
if (pc->flags & PROC_KCORE) {
+ unsigned long v = arm64_get_kernel_version();
+
+ /* Do special operation for kernel [5.4, 5.10) */
+ if (LINUX(5, 4, 0) <= v && v < LINUX(5, 10, 0)) {
+ arm64_get_phys_offset_by_pt_load(ms);
+ return;
+ }
+
if ((string = pc->read_vmcoreinfo("NUMBER(PHYS_OFFSET)"))) {
ms->phys_offset = htol(string, QUIET, NULL);
free(string);
--
2.30.2