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.
+ */
+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