hax: sync with recent updates
authorSeokYeon Hwang <syeon.hwang@samsung.com>
Thu, 3 Nov 2016 11:08:11 +0000 (20:08 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Sat, 5 Nov 2016 05:03:04 +0000 (14:03 +0900)
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 <syeon.hwang@samsung.com>
Makefile.target
cpus.c
include/sysemu/hax.h
target-i386/hax-all.c
target-i386/hax-darwin.c
target-i386/hax-slot.c [new file with mode: 0644]
target-i386/hax-slot.h [new file with mode: 0644]
target-i386/hax-windows.c

index 29c3dda..2c224bb 100644 (file)
@@ -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 (file)
--- 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
     }
 }
 
index 58fb039..ac4cf7d 100644 (file)
@@ -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);
index 916fcd4..dfff527 100644 (file)
@@ -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(&regs._rdi, &env->regs[R_EDI], set);
     hax_getput_reg(&regs._rsp, &env->regs[R_ESP], set);
     hax_getput_reg(&regs._rbp, &env->regs[R_EBP], set);
-
+#ifdef TARGET_X86_64
+    hax_getput_reg(&regs._r8, &env->regs[8], set);
+    hax_getput_reg(&regs._r9, &env->regs[9], set);
+    hax_getput_reg(&regs._r10, &env->regs[10], set);
+    hax_getput_reg(&regs._r11, &env->regs[11], set);
+    hax_getput_reg(&regs._r12, &env->regs[12], set);
+    hax_getput_reg(&regs._r13, &env->regs[13], set);
+    hax_getput_reg(&regs._r14, &env->regs[14], set);
+    hax_getput_reg(&regs._r15, &env->regs[15], set);
+#endif
     hax_getput_reg(&regs._rflags, &env->eflags, set);
     hax_getput_reg(&regs._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;
 
index b8dcbd4..be30d74 100644 (file)
@@ -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 (file)
index 0000000..b0b3ed9
--- /dev/null
@@ -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 (file)
index 0000000..d991c53
--- /dev/null
@@ -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 <inttypes.h>
+
+/**
+ * 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
index 5ff5916..f24a9e1 100644 (file)
@@ -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;