uint8_t vmi_get_address_width( vmi_instance_t vmi) { uint8_t width = 0; driver_get_address_width(vmi, &width); return width; }
/* * check that this vm uses a paging method that we support * and set pm/cr3/pae/pse/lme flags optionally on the given pointers */ status_t probe_memory_layout_x86(vmi_instance_t vmi) { // To get the paging layout, the following bits are needed: // 1. CR0.PG // 2. CR4.PAE // 3. Either (a) IA32_EFER.LME, or (b) the guest's address width (32 or // 64). Not all backends allow us to read an MSR; in particular, Xen's PV // backend doessn't. status_t ret = VMI_FAILURE; page_mode_t pm = VMI_PM_UNKNOWN; uint8_t dom_addr_width = 0; // domain address width (bytes) /* pull info from registers, if we can */ reg_t cr0, cr3, cr4, efer; int pae = 0, pse = 0, lme = 0; uint8_t msr_efer_lme = 0; // LME bit in MSR_EFER /* get the control register values */ if (driver_get_vcpureg(vmi, &cr0, CR0, 0) == VMI_FAILURE) { errprint("**failed to get CR0\n"); goto _exit; } /* PG Flag --> CR0, bit 31 == 1 --> paging enabled */ if (!VMI_GET_BIT(cr0, 31)) { dbprint(VMI_DEBUG_CORE, "Paging disabled for this VM, only physical addresses supported.\n"); vmi->page_mode = VMI_PM_UNKNOWN; vmi->pae = 0; vmi->pse = 0; vmi->lme = 0; ret = VMI_SUCCESS; goto _exit; } // // Paging enabled (PG==1) // if (driver_get_vcpureg(vmi, &cr4, CR4, 0) == VMI_FAILURE) { errprint("**failed to get CR4\n"); goto _exit; } /* PSE Flag --> CR4, bit 5 */ pae = VMI_GET_BIT(cr4, 5); dbprint(VMI_DEBUG_CORE, "**set pae = %d\n", pae); /* PSE Flag --> CR4, bit 4 */ pse = VMI_GET_BIT(cr4, 4); dbprint(VMI_DEBUG_CORE, "**set pse = %d\n", pse); ret = driver_get_vcpureg(vmi, &efer, MSR_EFER, 0); if (VMI_SUCCESS == ret) { lme = VMI_GET_BIT(efer, 8); dbprint(VMI_DEBUG_CORE, "**set lme = %d\n", lme); } else { dbprint(VMI_DEBUG_CORE, "**failed to get MSR_EFER, trying method #2\n"); // does this trick work in all cases? ret = driver_get_address_width(vmi, &dom_addr_width); if (VMI_FAILURE == ret) { errprint ("Failed to get domain address width. Giving up.\n"); goto _exit; } lme = (8 == dom_addr_width); dbprint (VMI_DEBUG_CORE, "**found guest address width is %d bytes; assuming IA32_EFER.LME = %d\n", dom_addr_width, lme); } // if // Get current cr3 for sanity checking if (driver_get_vcpureg(vmi, &cr3, CR3, 0) == VMI_FAILURE) { errprint("**failed to get CR3\n"); goto _exit; } // now determine addressing mode if (0 == pae) { dbprint(VMI_DEBUG_CORE, "**32-bit paging\n"); pm = VMI_PM_LEGACY; cr3 &= 0xFFFFF000ull; } // PAE == 1; determine IA-32e or PAE else if (lme) { // PAE == 1, LME == 1 dbprint(VMI_DEBUG_CORE, "**IA-32e paging\n"); pm = VMI_PM_IA32E; cr3 &= 0xFFFFFFFFFFFFF000ull; } else { // PAE == 1, LME == 0 dbprint(VMI_DEBUG_CORE, "**PAE paging\n"); pm = VMI_PM_PAE; cr3 &= 0xFFFFFFE0; } // if-else dbprint(VMI_DEBUG_CORE, "**sanity checking cr3 = 0x%.16"PRIx64"\n", cr3); /* testing to see CR3 value */ if (!driver_is_pv(vmi) && cr3 >= vmi->max_physical_address) { // sanity check on CR3 dbprint(VMI_DEBUG_CORE, "** Note cr3 value [0x%"PRIx64"] exceeds memsize [0x%"PRIx64"]\n", cr3, vmi->size); } vmi->page_mode = pm; vmi->pae = pae; vmi->pse = pse; vmi->lme = lme; _exit: return ret; }
static addr_t linux_get_taskstruct_addr_from_pgd( vmi_instance_t vmi, addr_t pgd) { addr_t list_head = 0, next_process = 0; addr_t task_pgd = 0; uint8_t width = 0; int tasks_offset = 0; int mm_offset = 0; int pgd_offset = 0; linux_instance_t os = NULL; if (vmi->os_data == NULL) { errprint("VMI_ERROR: No os_data initialized\n"); return 0; } os = vmi->os_data; tasks_offset = os->tasks_offset; mm_offset = os->mm_offset; pgd_offset = os->pgd_offset; /* First we need a pointer to the initial entry in the tasks list. * Note that this is task_struct->tasks, not the base addr * of task_struct: task_struct base = $entry - tasks_offset. */ next_process = vmi->init_task; list_head = next_process; /* May fail for some drivers, but handle gracefully below by * testing width */ if ( VMI_FAILURE == driver_get_address_width(vmi, &width) ) return 0; do { addr_t ptr = 0; vmi_read_addr_va(vmi, next_process + mm_offset, 0, &ptr); /* task_struct->mm is NULL when Linux is executing on the behalf * of a task, or if the task represents a kthread. In this context, * task_struct->active_mm is non-NULL and we can use it as * a fallback. task_struct->active_mm can be found very reliably * at task_struct->mm + 1 pointer width */ if(!ptr && width) vmi_read_addr_va(vmi, next_process + mm_offset + width, 0, &ptr); vmi_read_addr_va(vmi, ptr + pgd_offset, 0, &task_pgd); task_pgd = vmi_translate_kv2p(vmi, task_pgd); if (task_pgd == pgd) { return next_process; } vmi_read_addr_va(vmi, next_process + tasks_offset, 0, &next_process); next_process -= tasks_offset; /* if we are back at the list head, we are done */ } while (list_head != next_process); return 0; }