@@ -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);
@@ -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++;
}
@@ -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);
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(-)