Hi, Alexey
Thanks for the patch.
在 2020年10月17日 00:00, crash-utility-request(a)redhat.com 写道:
Date: Thu, 15 Oct 2020 13:44:32 -0700
From: Alexey Makhalov <amakhalov(a)vmware.com>
To: <crash-utility(a)redhat.com>, <amakhalov(a)vmware.com>
Subject: [Crash-utility] [PATCH 2/2] kaslr: get offset by walking page
tree
Message-ID: <20201015204432.4695-3-amakhalov(a)vmware.com>
Content-Type: text/plain
This method requires only valid CR3. It walks through
page tree starting from __START_KERNEL_map to get real
_stext and its physical address.
It is used as backup method to get kaslr offset, if
IDTR is not valid (zeroed). It might happen when kernel
invalidates IDT, for example triggering triple fault on
reboot (reboot=t cmdline).
This method does not support PTI (Page Table Isolation)
case where CR3 points to the isolated page tree. So, use
it only when CR3 points to "full" kernel.
Signed-off-by: Alexey Makhalov <amakhalov(a)vmware.com>
---
kaslr_helper.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/kaslr_helper.c b/kaslr_helper.c
index bb19e54..f11cb55 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -322,6 +322,104 @@ quit:
}
/*
+ * Find virtual (VA) and physical (PA) addresses of kernel start
+ *
+ * va:
+ * Actual address of the kernel start (_stext) placed
+ * randomly by kaslr feature. To be more accurate,
+ * VA = _stext(from vmlinux) + kaslr_offset
+ *
+ * pa:
+ * Physical address where the kerenel is placed.
+ *
+ * In nokaslr case, VA = _stext (from vmlinux)
+ * In kaslr case, virtual address of the kernel placement goes
+ * in this range: ffffffff80000000..ffffffff9fffffff, or
+ * __START_KERNEL_map..+512MB
+ *
+ *
https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
+ *
+ * Randomized VA will be the first valid page starting from
+ * ffffffff80000000 (__START_KERNEL_map). Page tree entry of
+ * this page will contain the PA of the kernel start.
+ *
+ * NOTES:
+ * 1. This method does not support PTI (Page Table Isolation)
+ * case where CR3 points to the isolated page tree.
+ * 2. 4-level paging support only, as caller (calc_kaslr_offset)
+ * does not support 5-level paging.
Would it be good to add a flag checking for
the 5-level paging before calling
the find_kernel_start()? Seems that we can not check the machdep->flags with
the 'machdep->flags & VM_5LEVEL', because that is not initialized.
But, is that possible to check the CR4(vmss.regs64[0]->cr[4]) on x86 or the
symbols '__pgtable_l5_enabled'?
Thanks.
Lianbo
+ */
+static int
+find_kernel_start(ulong *va, ulong *pa)
+{
+ int i, pgd_idx, pud_idx, pmd_idx, pte_idx;
+ uint64_t pgd_pte, pud_pte, pmd_pte, pte;
+
+ pgd_idx = pgd_index(__START_KERNEL_map);
+ pud_idx = pud_index(__START_KERNEL_map);
+ pmd_idx = pmd_index(__START_KERNEL_map);
+ pte_idx = pte_index(__START_KERNEL_map);
+
+ for (; pgd_idx < PTRS_PER_PGD; pgd_idx++) {
+ pgd_pte = ULONG(machdep->pgd + pgd_idx * sizeof(uint64_t));
+ if (pgd_pte & _PAGE_PRESENT)
+ break;
+ pud_idx = pmd_idx = pte_idx = 0;
+ }
+ if (pgd_idx == PTRS_PER_PGD)
+ return FALSE;
+
+ FILL_PUD(pgd_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
+ for (; pud_idx < PTRS_PER_PUD; pud_idx++) {
+ pud_pte = ULONG(machdep->pud + pud_idx * sizeof(uint64_t));
+ if (pud_pte & _PAGE_PRESENT)
+ break;
+ pmd_idx = pte_idx = 0;
+ }
+ if (pud_idx == PTRS_PER_PUD)
+ return FALSE;
+ if (pud_pte & _PAGE_PSE) {
+ /* 1GB page */
+ *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
+ ((ulong)pud_idx << PUD_SHIFT);
+ *pa = pud_pte & PHYSICAL_PAGE_MASK;
+ return TRUE;
+ }
+
+ FILL_PMD(pud_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
+ for (; pmd_idx < PTRS_PER_PMD; pmd_idx++) {
+ pmd_pte = ULONG(machdep->pmd + pmd_idx * sizeof(uint64_t));
+ if (pmd_pte & _PAGE_PRESENT)
+ break;
+ pte_idx = 0;
+ }
+ if (pmd_idx == PTRS_PER_PMD)
+ return FALSE;
+ if (pmd_pte & _PAGE_PSE) {
+ /* 2MB page */
+ *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
+ ((ulong)pud_idx << PUD_SHIFT) | (pmd_idx << PMD_SHIFT);
+ *pa = pmd_pte & PHYSICAL_PAGE_MASK;
+ return TRUE;
+ }
+
+ FILL_PTBL(pmd_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
+ for (; pte_idx < PTRS_PER_PTE; pte_idx++) {
+ pte = ULONG(machdep->ptbl + pte_idx * sizeof(uint64_t));
+ if (pte & _PAGE_PRESENT)
+ break;
+ }
+ if (pte_idx == PTRS_PER_PTE)
+ return FALSE;
+
+ *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
+ ((ulong)pud_idx << PUD_SHIFT) | (pmd_idx << PMD_SHIFT) |
+ (pte_idx << PAGE_SHIFT);
+ *pa = pmd_pte & PHYSICAL_PAGE_MASK;
+ return TRUE;
+}
+
+/*
* Calculate kaslr_offset and phys_base
*
* kaslr_offset:
@@ -445,6 +543,22 @@ retry:
goto quit;
}
+ if (idtr == 0 && st->_stext_vmlinux && (st->_stext_vmlinux !=
UNINITIALIZED)) {
+ ulong va, pa;
+ ret = find_kernel_start(&va, &pa);
+ if (ret == FALSE)
+ goto quit;
+ if (CRASHDEBUG(1)) {
+ fprintf(fp, "calc_kaslr_offset: _stext(vmlinux): %lx\n",
st->_stext_vmlinux);
+ fprintf(fp, "calc_kaslr_offset: kernel start VA: %lx\n", va);
+ fprintf(fp, "calc_kaslr_offset: kernel start PA: %lx\n", pa);
+ }
+ kaslr_offset = va - st->_stext_vmlinux;
+ phys_base = pa - (va - __START_KERNEL_map);
+
+ goto found;
+ }
+
/* Convert virtual address of IDT table to physical address */
if (!kvtop(NULL, idtr, &idtr_paddr, verbose)) {
if (SADUMP_DUMPFILE())
@@ -505,6 +619,7 @@ retry:
fprintf(fp, "kaslr_helper: asssuming the kdump 1st kernel.\n");
}
+found:
if (CRASHDEBUG(1)) {
fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n",
kaslr_offset);
-- 2.11.0