From: SeokYeon Hwang Date: Thu, 3 Nov 2016 11:08:11 +0000 (+0900) Subject: hax: sync with recent updates X-Git-Tag: TizenStudio_2.0_p2.4~17^2~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4d4fd2839367e8f8082412b3e47156044160087b;p=sdk%2Femulator%2Fqemu.git hax: sync with recent updates Some CPU state synchronization has been missing on HAX. So, some logics are updated. This updates are refered to recent KVM sources and Intel's updated sources. Change-Id: I181a3c98076ab114575875f0f566e12db457e267 Signed-off-by: SeokYeon Hwang --- diff --git a/Makefile.target b/Makefile.target index 29c3dda..2c224bb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -147,13 +147,16 @@ obj-$(CONFIG_XEN_I386) += xen-hvm.o xen-mapcache.o obj-$(call lnot,$(CONFIG_XEN)) += xen-common-stub.o obj-$(call lnot,$(CONFIG_XEN_I386)) += xen-hvm-stub.o -# HAX support -ifdef CONFIG_WIN32 -obj-$(CONFIG_HAX) += target-i386/hax-all.o target-i386/hax-windows.o -endif -ifdef CONFIG_DARWIN -obj-$(CONFIG_HAX) += target-i386/hax-all.o target-i386/hax-darwin.o +# HAX support, only when targetting i386 or x86_64 +ifeq (y,$(CONFIG_HAX)) +ifneq (,$(filter i386 x86_64,$(TARGET_NAME))) +obj-y += target-i386/hax-all.o target-i386/hax-slot.o +obj-$(CONFIG_WIN32) += target-i386/hax-windows.o +obj-$(CONFIG_DARWIN) += target-i386/hax-darwin.o +else +obj-y += hax-stub.o endif +endif # CONFIG_HAX obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o # Hardware support diff --git a/cpus.c b/cpus.c index 07d6bd7..5d3d0f3 100644 --- a/cpus.c +++ b/cpus.c @@ -710,6 +710,11 @@ void cpu_synchronize_all_states(void) CPU_FOREACH(cpu) { cpu_synchronize_state(cpu); +#ifdef CONFIG_HAX + if (hax_enabled() && hax_ug_platform()) { + hax_cpu_synchronize_state(cpu); + } +#endif } } diff --git a/include/sysemu/hax.h b/include/sysemu/hax.h index 58fb039..ac4cf7d 100644 --- a/include/sysemu/hax.h +++ b/include/sysemu/hax.h @@ -44,8 +44,9 @@ int hax_vcpu_exec(CPUState *cpu); int hax_smp_cpu_exec(CPUState *cpu); void hax_cpu_synchronize_post_reset(CPUState *cpu); void hax_cpu_synchronize_post_init(CPUState *cpu); +void hax_cpu_synchronize_state(CPUState *cpu); int hax_populate_ram(uint64_t va, uint32_t size); -int hax_set_phys_mem(MemoryRegionSection *section); +int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags); int hax_vcpu_emulation_mode(CPUState *cpu); int hax_stop_emulation(CPUState *cpu); int hax_stop_translate(CPUState *cpu); diff --git a/target-i386/hax-all.c b/target-i386/hax-all.c index 916fcd4..dfff527 100644 --- a/target-i386/hax-all.c +++ b/target-i386/hax-all.c @@ -26,6 +26,7 @@ #include "strings.h" #include "hax-i386.h" +#include "hax-slot.h" #include "hw/boards.h" #include "sysemu/accel.h" #include "exec/address-spaces.h" @@ -371,7 +372,8 @@ struct hax_vm *hax_vm_create(struct hax_state *hax) } hax->vm = vm; - dprint("End of VM create, id %d\n", vm->id); + hax_slot_init_registry(); + return vm; error: @@ -384,6 +386,7 @@ int hax_vm_destroy(struct hax_vm *vm) { int i; + hax_slot_free_registry(); for (i = 0; i < HAX_MAX_VCPU; i++) if (vm->vcpus[i]) { @@ -396,6 +399,41 @@ int hax_vm_destroy(struct hax_vm *vm) return 0; } +static void hax_set_phys_mem(MemoryRegionSection *section) +{ + MemoryRegion *mr = section->mr; + hwaddr start_pa = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + unsigned int delta; + void *host_ptr; + int flags; + + /* We only care about RAM and ROM */ + if (!memory_region_is_ram(mr)) { + return; + } + + /* Adjust start_pa and size so that they are page-aligned. (Cf + * kvm_set_phys_mem() in kvm-all.c). + */ + delta = TARGET_PAGE_SIZE - (start_pa & ~TARGET_PAGE_MASK); + delta &= ~TARGET_PAGE_MASK; + if (delta > size) { + return; + } + start_pa += delta; + size -= delta; + size &= TARGET_PAGE_MASK; + if (!size || start_pa & ~TARGET_PAGE_MASK) { + return; + } + + host_ptr = memory_region_get_ram_ptr(mr) + section->offset_within_region + + delta; + flags = memory_region_is_rom(mr) ? 1 : 0; + hax_slot_register(start_pa, size, (uintptr_t) host_ptr, flags); +} + static void hax_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -405,7 +443,7 @@ hax_region_add(MemoryListener *listener, MemoryRegionSection *section) static void hax_region_del(MemoryListener *listener, MemoryRegionSection *section) { - hax_set_phys_mem(section); + // Memory mappings will be removed at VM close. } @@ -417,11 +455,17 @@ hax_region_del(MemoryListener *listener, MemoryRegionSection *section) static void hax_log_sync(MemoryListener *listener, MemoryRegionSection *section) { MemoryRegion *mr = section->mr; + + if (!memory_region_is_ram(mr)) { + /* Skip MMIO regions */ + return; + } + unsigned long c; unsigned int len = ((int128_get64(section->size) / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) / HOST_LONG_BITS; unsigned long bitmap[len]; - int i, j; + unsigned int i, j; for (i = 0; i < len; i++) { bitmap[i] = 1; @@ -767,13 +811,6 @@ static int hax_vcpu_hax_exec(CPUArchState *env, int ug_platform) break; } -#if 0 - if (cpu->hax_vcpu_dirty) { - hax_vcpu_sync_state(env, 1); - cpu->hax_vcpu_dirty = 0; - } -#endif - hax_vcpu_interrupt(env); if (!ug_platform) { @@ -863,37 +900,52 @@ static int hax_vcpu_hax_exec(CPUArchState *env, int ug_platform) return ret; } -#if 0 -static void do_hax_cpu_synchronize_state(void *_env) +static void do_hax_cpu_synchronize_state(void *arg) { - CPUArchState *env = _env; - CPUState *cpu = ENV_GET_CPU(env); - if (!cpu->hax_vcpu_dirty) { - hax_vcpu_sync_state(env, 0); - cpu->hax_vcpu_dirty = 1; - } + CPUState *cpu = arg; + CPUArchState *env = cpu->env_ptr; + + hax_arch_get_registers(env); + cpu->hax_vcpu_dirty = true; } -static void hax_cpu_synchronize_state(CPUState *cpu) +void hax_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->hax_vcpu_dirty) { - run_on_cpu(cpu, do_hax_cpu_synchronize_state, cpu); - } + /* TODO: Do not sync if cpu->hax_vcpu_dirty is true. (Cf + * kvm_cpu_synchronize_state() in kvm-all.c) + * This would require that this flag be updated properly and consistently + * wherever a vCPU state sync between QEMU and HAX takes place. For now, + * just perform the sync regardless of hax_vcpu_dirty. + */ + run_on_cpu(cpu, do_hax_cpu_synchronize_state, cpu); } -#endif -void hax_cpu_synchronize_post_reset(CPUState *cpu) +static void do_hax_cpu_synchronize_post_reset(void *arg) { - CPUArchState *env = (CPUArchState *)(cpu->env_ptr); + CPUState *cpu = arg; + CPUArchState *env = cpu->env_ptr; + hax_vcpu_sync_state(env, 1); - cpu->hax_vcpu_dirty = 0; + cpu->hax_vcpu_dirty = false; } -void hax_cpu_synchronize_post_init(CPUState *cpu) +void hax_cpu_synchronize_post_reset(CPUState * cpu) { - CPUArchState *env = (CPUArchState *)(cpu->env_ptr); + run_on_cpu(cpu, do_hax_cpu_synchronize_post_reset, cpu); +} + +static void do_hax_cpu_synchronize_post_init(void *arg) +{ + CPUState *cpu = arg; + CPUArchState *env = cpu->env_ptr; + hax_vcpu_sync_state(env, 1); - cpu->hax_vcpu_dirty = 0; + cpu->hax_vcpu_dirty = false; +} + +void hax_cpu_synchronize_post_init(CPUState * cpu) +{ + run_on_cpu(cpu, do_hax_cpu_synchronize_post_init, cpu); } /* @@ -1145,7 +1197,16 @@ static int hax_sync_vcpu_register(CPUArchState *env, int set) hax_getput_reg(®s._rdi, &env->regs[R_EDI], set); hax_getput_reg(®s._rsp, &env->regs[R_ESP], set); hax_getput_reg(®s._rbp, &env->regs[R_EBP], set); - +#ifdef TARGET_X86_64 + hax_getput_reg(®s._r8, &env->regs[8], set); + hax_getput_reg(®s._r9, &env->regs[9], set); + hax_getput_reg(®s._r10, &env->regs[10], set); + hax_getput_reg(®s._r11, &env->regs[11], set); + hax_getput_reg(®s._r12, &env->regs[12], set); + hax_getput_reg(®s._r13, &env->regs[13], set); + hax_getput_reg(®s._r14, &env->regs[14], set); + hax_getput_reg(®s._r15, &env->regs[15], set); +#endif hax_getput_reg(®s._rflags, &env->eflags, set); hax_getput_reg(®s._rip, &env->eip, set); @@ -1196,6 +1257,14 @@ static int hax_get_msrs(CPUArchState *env) msrs[n++].entry = MSR_IA32_SYSENTER_ESP; msrs[n++].entry = MSR_IA32_SYSENTER_EIP; msrs[n++].entry = MSR_IA32_TSC; +#ifdef TARGET_X86_64 + msrs[n++].entry = MSR_EFER; + msrs[n++].entry = MSR_STAR; + msrs[n++].entry = MSR_LSTAR; + msrs[n++].entry = MSR_CSTAR; + msrs[n++].entry = MSR_FMASK; + msrs[n++].entry = MSR_KERNELGSBASE; +#endif md.nr_msr = n; ret = hax_sync_msr(env, &md, 0); if (ret < 0) @@ -1215,6 +1284,26 @@ static int hax_get_msrs(CPUArchState *env) case MSR_IA32_TSC: env->tsc = msrs[i].value; break; +#ifdef TARGET_X86_64 + case MSR_EFER: + env->efer = msrs[i].value; + break; + case MSR_STAR: + env->star = msrs[i].value; + break; + case MSR_LSTAR: + env->lstar = msrs[i].value; + break; + case MSR_CSTAR: + env->cstar = msrs[i].value; + break; + case MSR_FMASK: + env->fmask = msrs[i].value; + break; + case MSR_KERNELGSBASE: + env->kernelgsbase = msrs[i].value; + break; +#endif } } @@ -1233,6 +1322,14 @@ static int hax_set_msrs(CPUArchState *env) hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); hax_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); +#ifdef TARGET_X86_64 + hax_msr_entry_set(&msrs[n++], MSR_EFER, env->efer); + hax_msr_entry_set(&msrs[n++], MSR_STAR, env->star); + hax_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); + hax_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); + hax_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); + hax_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); +#endif md.nr_msr = n; md.done = 0; @@ -1255,9 +1352,13 @@ static int hax_get_fpu(CPUArchState *env) for (i = 0; i < 8; ++i) env->fptags[i] = !((fpu.ftw >> i) & 1); memcpy(env->fpregs, fpu.st_mm, sizeof(env->fpregs)); + for (i = 0; i < 8; ++i) { + memcpy(&env->xmm_regs[i], fpu.mmx_1[i], sizeof(fpu.mmx_1[i])); + } + for (i = 0; i < 8; ++i) { + memcpy(&env->xmm_regs[8 + i], fpu.mmx_2[i], sizeof(fpu.mmx_2[i])); + } - memcpy(env->xmm_regs, fpu.mmx_1, sizeof(fpu.mmx_1)); - memcpy((ZMMReg *)(env->xmm_regs) + 8, fpu.mmx_2, sizeof(fpu.mmx_2)); env->mxcsr = fpu.mxcsr; return 0; @@ -1277,8 +1378,12 @@ static int hax_set_fpu(CPUArchState *env) fpu.ftw |= (!env->fptags[i]) << i; memcpy(fpu.st_mm, env->fpregs, sizeof (env->fpregs)); - memcpy(fpu.mmx_1, env->xmm_regs, sizeof (fpu.mmx_1)); - memcpy(fpu.mmx_2, (ZMMReg *)(env->xmm_regs) + 8, sizeof (fpu.mmx_2)); + for (i = 0; i < 8; i++) { + memcpy(fpu.mmx_1[i], &env->xmm_regs[i], sizeof(fpu.mmx_1[i])); + } + for (i = 0; i < 8; i++) { + memcpy(fpu.mmx_2[i], &env->xmm_regs[i + 8], sizeof(fpu.mmx_2[i])); + } fpu.mxcsr = env->mxcsr; diff --git a/target-i386/hax-darwin.c b/target-i386/hax-darwin.c index b8dcbd4..be30d74 100644 --- a/target-i386/hax-darwin.c +++ b/target-i386/hax-darwin.c @@ -63,43 +63,23 @@ int hax_populate_ram(uint64_t va, uint32_t size) return 0; } -int hax_set_phys_mem(MemoryRegionSection *section) +int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags) { - struct hax_set_ram_info info, *pinfo = &info; - MemoryRegion *mr = section->mr; - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); + struct hax_set_ram_info info; int ret; - /*We only care for the RAM and ROM*/ - if(!memory_region_is_ram(mr)) - return 0; - - if ( (start_addr & ~TARGET_PAGE_MASK) || (size & ~TARGET_PAGE_MASK)) - { - dprint("set_phys_mem %llx %lx requires page aligned addr and size\n", start_addr, size); - exit(1); - return -1; - } - - info.pa_start = start_addr; + info.pa_start = start_pa; info.size = size; - info.va = (uint64_t)(intptr_t)(memory_region_get_ram_ptr(mr) + section->offset_within_region); - info.flags = memory_region_is_rom(mr) ? 1 : 0; + info.va = host_va; + info.flags = (uint8_t) flags; - ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_SET_RAM, pinfo); - if (ret < 0) - { - dprint("has set phys mem failed\n"); - exit(1); + ret = ioctl(hax_global.vm->fd, HAX_VM_IOCTL_SET_RAM, &info); + if (ret < 0) { + return -errno; } - - return ret; - + return 0; } - - int hax_capability(struct hax_state *hax, struct hax_capabilityinfo *cap) { int ret; diff --git a/target-i386/hax-slot.c b/target-i386/hax-slot.c new file mode 100644 index 0000000..b0b3ed9 --- /dev/null +++ b/target-i386/hax-slot.c @@ -0,0 +1,328 @@ +/* +** HAX memory slot operations +** +** Copyright (c) 2015-16 Intel Corporation +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#include "target-i386/hax-slot.h" +#include "target-i386/hax-i386.h" +#include "qemu/queue.h" + +//#define DEBUG_HAX_SLOT + +#ifdef DEBUG_HAX_SLOT +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/** + * HAXSlot: describes a guest physical memory region and its mapping + * + * @start_pa: a guest physical address marking the start of the region; must be + * page-aligned + * @end_pa: a guest physical address marking the end of the region; must be + * page-aligned + * @hva_pa_delta: the host virtual address to which guest physical address 0 is + * mapped; in other words, for any guest physical address within + * the region (start_pa <= pa < end_pa), the corresponding host + * virtual address is calculated by host_va = pa + hva_pa_delta + * @flags: parameters for the mapping; must be non-negative + * @entry: additional fields for linking #HAXSlot instances together + */ +typedef struct HAXSlot { + uint64_t start_pa; + uint64_t end_pa; + uint64_t hva_pa_delta; + int flags; + QTAILQ_ENTRY(HAXSlot) entry; +} HAXSlot; + +/* A doubly-linked list (actually a tail queue) of all registered slots */ +static QTAILQ_HEAD(HAXSlotListHead, HAXSlot) slot_list = + QTAILQ_HEAD_INITIALIZER(slot_list); + +void hax_slot_init_registry(void) +{ + HAXSlot *initial_slot; + + g_assert(QTAILQ_EMPTY(&slot_list)); + + initial_slot = (HAXSlot *) g_malloc0(sizeof(*initial_slot)); + /* Implied: initial_slot->start_pa = 0; */ + /* Ideally we want to set end_pa to 2^64, but that is too large for + * uint64_t. We don't need to support such a large guest physical address + * space anyway; (2^64 - TARGET_PAGE_SIZE) should be (more than) enough. + */ + initial_slot->end_pa = TARGET_PAGE_MASK; + /* hva_pa_delta and flags are initialized with invalid values */ + initial_slot->hva_pa_delta = ~TARGET_PAGE_MASK; + initial_slot->flags = -1; + QTAILQ_INSERT_TAIL(&slot_list, initial_slot, entry); +} + +void hax_slot_free_registry(void) +{ + DPRINTF("%s: Deleting all registered slots\n", __func__); + while (!QTAILQ_EMPTY(&slot_list)) { + HAXSlot *slot = QTAILQ_FIRST(&slot_list); + QTAILQ_REMOVE(&slot_list, slot, entry); + g_free(slot); + } +} + +/** + * hax_slot_dump: dumps a slot to stdout (for debugging) + * + * @slot: the slot to dump + */ +static void hax_slot_dump(HAXSlot *slot) +{ + DPRINTF("[ start_pa=0x%016" PRIx64 ", end_pa=0x%016" PRIx64 + ", hva_pa_delta=0x%016" PRIx64 ", flags=%d ]\n", slot->start_pa, + slot->end_pa, slot->hva_pa_delta, slot->flags); +} + +/** + * hax_slot_dump_list: dumps @slot_list to stdout (for debugging) + */ +static void hax_slot_dump_list(void) +{ +#ifdef DEBUG_HAX_SLOT + HAXSlot *slot; + int i = 0; + + DPRINTF("**** BEGIN HAX SLOT LIST DUMP ****\n"); + QTAILQ_FOREACH(slot, &slot_list, entry) { + DPRINTF("Slot %d:\n\t", i++); + hax_slot_dump(slot); + } + DPRINTF("**** END HAX SLOT LIST DUMP ****\n"); +#endif +} + +/** + * hax_slot_find: locates the slot containing a guest physical address + * + * Traverses @slot_list, starting from @start_slot, and returns the slot which + * contains @pa. There should be one and only one such slot, because: + * + * 1) @slot_list is initialized with a slot which covers all valid @pa values. + * This coverage stays unchanged as new slots are inserted into @slot_list. + * 2) @slot_list does not contain overlapping slots. + * + * @start_slot: the first slot from which @slot_list is traversed and searched; + * must not be %NULL + * @pa: the guest physical address to locate; must not be less than the lower + * bound of @start_slot + */ +static HAXSlot * hax_slot_find(HAXSlot *start_slot, uint64_t pa) +{ + HAXSlot *slot; + + g_assert(start_slot); + g_assert(start_slot->start_pa <= pa); + + slot = start_slot; + do { + if (slot->end_pa > pa) { + return slot; + } + slot = QTAILQ_NEXT(slot, entry); + } while (slot); + + /* Should never reach here */ + g_assert_not_reached(); + return NULL; +} + +/** + * hax_slot_split: splits a slot into two + * + * Shrinks @slot and creates a new slot from the vacated region. Returns the + * new slot. + * + * @slot: the slot to be split/shrinked + * @pa: the splitting point; must be page-aligned and within @slot + */ +static HAXSlot * hax_slot_split(HAXSlot *slot, uint64_t pa) +{ + HAXSlot *new_slot; + + g_assert(slot); + g_assert(pa > slot->start_pa && pa < slot->end_pa); + g_assert(!(pa & ~TARGET_PAGE_MASK)); + + new_slot = (HAXSlot *) g_malloc0(sizeof(*new_slot)); + new_slot->start_pa = pa; + new_slot->end_pa = slot->end_pa; + new_slot->hva_pa_delta = slot->hva_pa_delta; + new_slot->flags = slot->flags; + + slot->end_pa = pa; + QTAILQ_INSERT_AFTER(&slot_list, slot, new_slot, entry); + return new_slot; +} + +/** + * hax_slot_can_merge: tests if two slots are compatible + * + * Two slots are considered compatible if they share the same memory mapping + * attributes. Compatible slots can be merged if they overlap or are adjacent. + * + * Returns %true if @slot1 and @slot2 are compatible. + * + * @slot1: one of the slots to be tested; must not be %NULL + * @slot2: the other slot to be tested; must not be %NULL + */ +static bool hax_slot_can_merge(HAXSlot *slot1, HAXSlot *slot2) +{ + g_assert(slot1 && slot2); + + return slot1->hva_pa_delta == slot2->hva_pa_delta + && slot1->flags == slot2->flags; +} + +/** + * hax_slot_insert: inserts a slot into @slot_list, with the potential side + * effect of creating/updating memory mappings + * + * Causes memory mapping attributes of @slot to override those of overlapping + * slots (including partial slots) in @slot_list. For any slot whose mapping + * attributes have changed, performs an ioctl to enforce the new mapping. + * + * Aborts QEMU on error. + * + * @slot: the slot to be inserted + */ +static void hax_slot_insert(HAXSlot *slot) +{ + HAXSlot *low_slot, *high_slot; + HAXSlot *low_slot_prev, *high_slot_next; + HAXSlot *old_slot, *old_slot_next; + + g_assert(!QTAILQ_EMPTY(&slot_list)); + + low_slot = hax_slot_find(QTAILQ_FIRST(&slot_list), slot->start_pa); + g_assert(low_slot); + low_slot_prev = QTAILQ_PREV(low_slot, HAXSlotListHead, entry); + + /* Adjust slot and/or low_slot such that their lower bounds (start_pa) + * align. + */ + if (hax_slot_can_merge(low_slot, slot)) { + slot->start_pa = low_slot->start_pa; + } else if (slot->start_pa == low_slot->start_pa && low_slot_prev + && hax_slot_can_merge(low_slot_prev, slot)) { + low_slot = low_slot_prev; + slot->start_pa = low_slot->start_pa; + } else if (slot->start_pa != low_slot->start_pa) { + /* low_slot->start_pa < slot->start_pa < low_slot->end_pa */ + low_slot = hax_slot_split(low_slot, slot->start_pa); + g_assert(low_slot); + } + /* Now we have slot->start_pa == low_slot->start_pa */ + + high_slot = hax_slot_find(low_slot, slot->end_pa - 1); + g_assert(high_slot); + high_slot_next = QTAILQ_NEXT(high_slot, entry); + + /* Adjust slot and/or high_slot such that their upper bounds (end_pa) + * align. + */ + if (hax_slot_can_merge(slot, high_slot)) { + slot->end_pa = high_slot->end_pa; + } else if (slot->end_pa == high_slot->end_pa && high_slot_next + && hax_slot_can_merge(slot, high_slot_next)) { + high_slot = high_slot_next; + slot->end_pa = high_slot->end_pa; + } else if (slot->end_pa != high_slot->end_pa) { + /* high_slot->start_pa < slot->end_pa < high_slot->end_pa */ + high_slot_next = hax_slot_split(high_slot, slot->end_pa); + g_assert(high_slot_next); + } + /* Now we have slot->end_pa == high_slot->end_pa */ + + /* We are ready for substitution: replace all slots between low_slot and + * high_slot (inclusive) with slot. */ + + /* Step 1: insert slot into the list, before low_slot */ + QTAILQ_INSERT_BEFORE(low_slot, slot, entry); + + /* Step 2: remove low_slot..high_slot, one by one */ + for (old_slot = low_slot; + /* This condition always evaluates to 1. See: + * https://en.wikipedia.org/wiki/Comma_operator + */ + old_slot_next = QTAILQ_NEXT(old_slot, entry), 1; + old_slot = old_slot_next) { + g_assert(old_slot); + + QTAILQ_REMOVE(&slot_list, old_slot, entry); + if (!hax_slot_can_merge(slot, old_slot)) { + /* Mapping for guest memory region [old_slot->start_pa, + * old_slot->end_pa) has changed - must do ioctl. */ + /* TODO: Further reduce the number of ioctl calls by preprocessing + * the low_slot..high_slot sublist and combining any two adjacent + * slots that are both incompatible with slot. + */ + uint32_t size = old_slot->end_pa - old_slot->start_pa; + uint64_t host_va = old_slot->start_pa + slot->hva_pa_delta; + int err; + + DPRINTF("%s: Doing ioctl (size=0x%08" PRIx32 ")\n", __func__, size); + /* Use the new host_va and flags */ + err = hax_set_ram(old_slot->start_pa, size, host_va, slot->flags); + if (err) { + fprintf(stderr, "%s: Failed to set memory mapping (err=%d)\n", + __func__, err); + abort(); + } + } + g_free(old_slot); + + /* Exit the infinite loop following the removal of high_slot */ + if (old_slot == high_slot) { + break; + } + } +} + +void hax_slot_register(uint64_t start_pa, uint32_t size, uint64_t host_va, + int flags) +{ + uint64_t end_pa = start_pa + size; + HAXSlot *slot; + + g_assert(!(start_pa & ~TARGET_PAGE_MASK)); + g_assert(!(end_pa & ~TARGET_PAGE_MASK)); + g_assert(start_pa < end_pa); + g_assert(host_va); + g_assert(flags >= 0); + + slot = g_malloc0(sizeof(*slot)); + slot->start_pa = start_pa; + slot->end_pa = end_pa; + slot->hva_pa_delta = host_va - start_pa; + slot->flags = flags; + + DPRINTF("%s: Inserting slot:\n\t", __func__); + hax_slot_dump(slot); + hax_slot_dump_list(); + + hax_slot_insert(slot); + + DPRINTF("%s: Done\n", __func__); + hax_slot_dump_list(); +} diff --git a/target-i386/hax-slot.h b/target-i386/hax-slot.h new file mode 100644 index 0000000..d991c53 --- /dev/null +++ b/target-i386/hax-slot.h @@ -0,0 +1,58 @@ +/* +** HAX memory slot operations +** +** Copyright (c) 2015-16 Intel Corporation +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#ifndef _HAX_SLOT_H +#define _HAX_SLOT_H + +#include + +/** + * hax_slot_init_registry: initializes the registry of memory slots. + * + * Should be called during HAX initialization, before any call to + * hax_slot_register(). + */ +void hax_slot_init_registry(void); + +/** + * hax_slot_free_registry: destroys the registry of memory slots. + * + * Should be called during HAX cleanup to free up resources used by the + * registry of memory slots. + */ +void hax_slot_free_registry(void); + +/** + * hax_slot_register: registers a memory slot, updating HAX memory mappings if + * necessary. + * + * Must be called after hax_slot_init_registry(). Can be called multiple times + * to create new memory mappings or update existing ones. This function is smart + * enough to avoid asking the HAXM driver to do the same mapping twice for any + * guest physical page. + * + * Aborts QEMU on error. + * + * @start_pa: a guest physical address marking the start of the slot to + * register; must be page-aligned + * @size: size of the slot to register; must be page-aligned and positive + * @host_va: a host virtual address to which @start_pa should be mapped + * @flags: parameters for the mapping, passed verbatim to the HAXM driver if + * necessary; must be non-negative + */ +void hax_slot_register(uint64_t start_pa, uint32_t size, uint64_t host_va, + int flags); + +#endif diff --git a/target-i386/hax-windows.c b/target-i386/hax-windows.c index 5ff5916..f24a9e1 100644 --- a/target-i386/hax-windows.c +++ b/target-i386/hax-windows.c @@ -90,48 +90,21 @@ int hax_populate_ram(uint64_t va, uint32_t size) return 0; } - -/* - * much simpler than kvm, at least in first stage because: - * We don't need consider the device pass-through, we don't need - * consider the framebuffer, and we may even remove the bios at all - */ - -int hax_set_phys_mem(MemoryRegionSection *section) +int hax_set_ram(uint64_t start_pa, uint32_t size, uint64_t host_va, int flags) { - struct hax_set_ram_info info, *pinfo = &info; - MemoryRegion *mr = section->mr; - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - HANDLE hDeviceVM; + struct hax_set_ram_info info; + HANDLE hDeviceVM = hax_global.vm->fd; DWORD dSize = 0; - int ret = 0; - - /* We only care for the RAM and ROM */ - if (!memory_region_is_ram(mr)) { - return 0; - } - - if ( (start_addr & ~TARGET_PAGE_MASK) || (size & ~TARGET_PAGE_MASK)) { - dprint( - "set_phys_mem %" PRIx64 " %" PRIxPTR " requires page aligned addr and size\n", - start_addr, size); - return -1; - } + int ret; - info.pa_start = start_addr; + info.pa_start = start_pa; info.size = size; - info.va = (uint64_t)(intptr_t)(memory_region_get_ram_ptr(mr) + section->offset_within_region); - info.flags = memory_region_is_rom(mr) ? 1 : 0; - - hDeviceVM = hax_global.vm->fd; + info.va = host_va; + info.flags = (uint8_t) flags; - ret = DeviceIoControl(hDeviceVM, - HAX_VM_IOCTL_SET_RAM, - pinfo, sizeof(*pinfo), - NULL, 0, - &dSize, - (LPOVERLAPPED) NULL); + ret = DeviceIoControl(hDeviceVM, HAX_VM_IOCTL_SET_RAM, + &info, sizeof(info), NULL, 0, &dSize, + (LPOVERLAPPED) NULL); if (!ret) return -EFAULT;