diff mbox series

[3/5] contrib/elf2dmp: improve paging root selection

Message ID 1535546488-30208-4-git-send-email-viktor.prutyanov@virtuozzo.com
State New
Headers show
Series contrib: add elf2dmp tool | expand

Commit Message

Viktor Prutyanov Aug. 29, 2018, 12:41 p.m. UTC
Even if KERNEL_GS_BASEs are absent in QEMU CPU states, there
is a chance to find suitable CR3 value from CPU which runs kernel task.

Signed-off-by: Viktor Prutyanov <viktor.prutyanov@virtuozzo.com>
---
 contrib/elf2dmp/main.c     | 56 +++++++++++++++++++++++++++++++++++++++-------
 contrib/elf2dmp/qemu_elf.c | 16 +++++++++++--
 contrib/elf2dmp/qemu_elf.h |  3 +++
 3 files changed, 65 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
index eb11e66..62f08e0 100644
--- a/contrib/elf2dmp/main.c
+++ b/contrib/elf2dmp/main.c
@@ -188,17 +188,53 @@  static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
     *ctx = win_ctx;
 }
 
-static void fix_dtb(struct va_space *vs, QEMUCPUState *s)
+/*
+ * Finds paging-structure hierarchy base,
+ * if previously set doesn't give access to kernel structures
+ */
+static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
 {
-    uint64_t Prcb = (s->gs.base >> 63) ? s->gs.base : s->kernel_gs_base;
-    void *prcb = va_space_resolve(vs, Prcb);
+    /*
+     * Firstly, test previously set DTB.
+     */
+    if (va_space_resolve(vs, SharedUserData)) {
+        return 0;
+    }
+
+    /*
+     * Secondly, find CPU which run system task.
+     */
+    for (size_t i = 0; i < qe->state_nr; i++) {
+        QEMUCPUState *s = qe->state[i];
 
-    if (!prcb) {
-        va_space_set_dtb(vs, *(uint64_t *)va_space_resolve(vs, Prcb + 0x7000));
+        if (is_system(s)) {
+            va_space_set_dtb(vs, s->cr[3]);
+            printf("DTB 0x%016lx has been found from CPU #%zu"
+                    " as system task CR3\n", vs->dtb, i);
+            return !(va_space_resolve(vs, SharedUserData));
+        }
     }
 
-    assert(va_space_resolve(vs, Prcb));
-    printf("DTB is 0x%016lx\n", vs->dtb);
+    /*
+     * Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and
+     * CR3 as [Prcb+0x7000]
+     */
+    if (qe->has_kernel_gs_base) {
+        QEMUCPUState *s = qe->state[0];
+        uint64_t Prcb = s->kernel_gs_base;
+        uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
+
+        if (!cr3) {
+            return 1;
+        }
+
+        va_space_set_dtb(vs, *cr3);
+        printf("DirectoryTableBase = 0x%016lx has been found from CPU #0"
+                " as interrupt handling CR3\n", vs->dtb);
+        return !(va_space_resolve(vs, SharedUserData));
+    }
+
+    return 1;
 }
 
 static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
@@ -448,7 +484,11 @@  int main(int argc, char *argv[])
     printf("CPU #0 CR3 is 0x%016lx\n", state->cr[3]);
 
     va_space_create(&vs, &ps, state->cr[3]);
-    fix_dtb(&vs, state);
+    if (fix_dtb(&vs, &qemu_elf)) {
+        eprintf("Failed to find paging base\n");
+        err = 1;
+        goto out_elf;
+    }
 
     printf("CPU #0 IDT is at 0x%016lx\n", state->idt.base);
 
diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c
index f7b5ebd..d139db2 100644
--- a/contrib/elf2dmp/qemu_elf.c
+++ b/contrib/elf2dmp/qemu_elf.c
@@ -33,6 +33,11 @@ 
       DIV_ROUND_UP((name_size), 4) +                    \
       DIV_ROUND_UP((desc_size), 4)) * 4)
 
+int is_system(QEMUCPUState *s)
+{
+    return s->gs.base >> 63;
+}
+
 static char *nhdr_get_name(Elf64_Nhdr *nhdr)
 {
     return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
@@ -76,13 +81,20 @@  static int init_states(QEMU_Elf *qe)
         return 1;
     }
 
+    qe->has_kernel_gs_base = 1;
+
     for (Elf64_Nhdr *nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
         if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
             QEMUCPUState *state = nhdr_get_desc(nhdr);
 
             if (state->size < sizeof(*state)) {
-                eprintf("QEMU CPU state size %d doesn't match\n", state->size);
-                return 1;
+                eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
+                        cpu_nr, state->size);
+                /*
+                 * We assume either every QEMU CPU state has KERNEL_GS_BASE or
+                 * no one has.
+                 */
+                qe->has_kernel_gs_base = 0;
             }
             cpu_nr++;
         }
diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h
index 2a28bb0..d85d655 100644
--- a/contrib/elf2dmp/qemu_elf.h
+++ b/contrib/elf2dmp/qemu_elf.h
@@ -31,12 +31,15 @@  typedef struct QEMUCPUState {
     uint64_t kernel_gs_base;
 } QEMUCPUState;
 
+int is_system(QEMUCPUState *s);
+
 typedef struct QEMU_Elf {
     int fd;
     size_t size;
     void *map;
     QEMUCPUState **state;
     size_t state_nr;
+    int has_kernel_gs_base;
 } QEMU_Elf;
 
 int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);