After commit ebd9aea1f27e ("arm64: head: drop idmap_ptrs_per_pgd") in
Linux kernel-v6.0, kernel removed the idmap_ptrs_per_pgd. This commit
cause the following error.
crash: invalid kernel virtual address: ffff800083700000 type: "64-bit KVADDR"
We cannot use "idmap_ptrs_per_pgd" to know the size of
"ptrs_per_pgd".
We use VA_BITS to know the size of "ptrs_per_pgd" instead.
Signed-off-by: Kuan-Ying Lee <kuan-ying.lee(a)canonical.com>
---
arm64.c | 18 ++++++++++++++++--
defs.h | 3 ++-
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/arm64.c b/arm64.c
index 6cc1bd8f3329..d0b2fa63a089 100644
--- a/arm64.c
+++ b/arm64.c
@@ -445,6 +445,10 @@ arm64_init(int when)
break;
case 65536:
+ /*
+ * idmap_ptrs_per_pgd has been removed in kernel commit ebd9aea1f27e ("arm64:
+ * head: drop idmap_ptrs_per_pgd"). This commit is included after Linux-v6.0.
+ */
if (kernel_symbol_exists("idmap_ptrs_per_pgd") &&
readmem(symbol_value("idmap_ptrs_per_pgd"), KVADDR,
&value, sizeof(ulong), "idmap_ptrs_per_pgd",
QUIET|RETURN_ON_ERROR))
@@ -452,8 +456,14 @@ arm64_init(int when)
if (machdep->machspec->VA_BITS > PGDIR_SHIFT_L3_64K) {
machdep->flags |= VM_L3_64K;
- if (!machdep->ptrs_per_pgd)
- machdep->ptrs_per_pgd = PTRS_PER_PGD_L3_64K;
+ if (!machdep->ptrs_per_pgd) {
+ if (machdep->machspec->VA_BITS == 52)
+ machdep->ptrs_per_pgd = PTRS_PER_PGD_L3_64K_52;
+ else if (machdep->machspec->VA_BITS == 48)
+ machdep->ptrs_per_pgd = PTRS_PER_PGD_L3_64K_48;
+ else
+ error(FATAL, "wrong VA_BITS for 64K page.");
+ }
if ((machdep->pgd =
(char *)malloc(machdep->ptrs_per_pgd * 8)) == NULL)
error(FATAL, "cannot malloc pgd space.");
@@ -1972,6 +1982,10 @@ arm64_vtop_3level_64k(ulong pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
pgd_base = (ulong *)pgd;
FILL_PGD(pgd_base, KVADDR, machdep->ptrs_per_pgd * sizeof(ulong));
+ /*
+ * We need to use machdep->ptrs_per_pgd to mask vaddr instead of using macro,
because
+ * 48-bits and 52-bits have different size of ptrs_per_pgd.
+ */
pgd_ptr = pgd_base + (((vaddr) >> PGDIR_SHIFT_L3_64K) &
(machdep->ptrs_per_pgd - 1));
pgd_val = ULONG(machdep->pgd + PGDIR_OFFSET_L3_64K(pgd_ptr));
if (verbose)
diff --git a/defs.h b/defs.h
index 2d881344cba6..9f4d676e25f9 100644
--- a/defs.h
+++ b/defs.h
@@ -3333,7 +3333,8 @@ typedef signed int s32;
* 3-levels / 64K pages
* 48-bit, 52-bit VA
*/
-#define PTRS_PER_PGD_L3_64K (64)
+#define PTRS_PER_PGD_L3_64K_48 ((1UL) << (48 - 42)) // 48-bit VA
+#define PTRS_PER_PGD_L3_64K_52 ((1UL) << (52 - 42)) // 52-bit VA
#define PTRS_PER_PMD_L3_64K (8192)
#define PTRS_PER_PTE_L3_64K (8192)
#define PGDIR_SHIFT_L3_64K (42)
--
2.43.0