qemu/accel/hvf/hvf-accel-ops.c
<<
>>
Prefs
   1/*
   2 * Copyright 2008 IBM Corporation
   3 *           2008 Red Hat, Inc.
   4 * Copyright 2011 Intel Corporation
   5 * Copyright 2016 Veertu, Inc.
   6 * Copyright 2017 The Android Open Source Project
   7 *
   8 * QEMU Hypervisor.framework support
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of version 2 of the GNU General Public
  12 * License as published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  21 *
  22 * This file contain code under public domain from the hvdos project:
  23 * https://github.com/mist64/hvdos
  24 *
  25 * Parts Copyright (c) 2011 NetApp, Inc.
  26 * All rights reserved.
  27 *
  28 * Redistribution and use in source and binary forms, with or without
  29 * modification, are permitted provided that the following conditions
  30 * are met:
  31 * 1. Redistributions of source code must retain the above copyright
  32 *    notice, this list of conditions and the following disclaimer.
  33 * 2. Redistributions in binary form must reproduce the above copyright
  34 *    notice, this list of conditions and the following disclaimer in the
  35 *    documentation and/or other materials provided with the distribution.
  36 *
  37 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
  38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  40 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
  41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47 * SUCH DAMAGE.
  48 */
  49
  50#include "qemu/osdep.h"
  51#include "qemu/error-report.h"
  52#include "qemu/main-loop.h"
  53#include "exec/address-spaces.h"
  54#include "exec/exec-all.h"
  55#include "sysemu/cpus.h"
  56#include "sysemu/hvf.h"
  57#include "sysemu/hvf_int.h"
  58#include "sysemu/runstate.h"
  59#include "qemu/guest-random.h"
  60
  61HVFState *hvf_state;
  62
  63/* Memory slots */
  64
  65hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
  66{
  67    hvf_slot *slot;
  68    int x;
  69    for (x = 0; x < hvf_state->num_slots; ++x) {
  70        slot = &hvf_state->slots[x];
  71        if (slot->size && start < (slot->start + slot->size) &&
  72            (start + size) > slot->start) {
  73            return slot;
  74        }
  75    }
  76    return NULL;
  77}
  78
  79struct mac_slot {
  80    int present;
  81    uint64_t size;
  82    uint64_t gpa_start;
  83    uint64_t gva;
  84};
  85
  86struct mac_slot mac_slots[32];
  87
  88static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
  89{
  90    struct mac_slot *macslot;
  91    hv_return_t ret;
  92
  93    macslot = &mac_slots[slot->slot_id];
  94
  95    if (macslot->present) {
  96        if (macslot->size != slot->size) {
  97            macslot->present = 0;
  98            ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
  99            assert_hvf_ok(ret);
 100        }
 101    }
 102
 103    if (!slot->size) {
 104        return 0;
 105    }
 106
 107    macslot->present = 1;
 108    macslot->gpa_start = slot->start;
 109    macslot->size = slot->size;
 110    ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
 111    assert_hvf_ok(ret);
 112    return 0;
 113}
 114
 115static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
 116{
 117    hvf_slot *mem;
 118    MemoryRegion *area = section->mr;
 119    bool writeable = !area->readonly && !area->rom_device;
 120    hv_memory_flags_t flags;
 121
 122    if (!memory_region_is_ram(area)) {
 123        if (writeable) {
 124            return;
 125        } else if (!memory_region_is_romd(area)) {
 126            /*
 127             * If the memory device is not in romd_mode, then we actually want
 128             * to remove the hvf memory slot so all accesses will trap.
 129             */
 130             add = false;
 131        }
 132    }
 133
 134    mem = hvf_find_overlap_slot(
 135            section->offset_within_address_space,
 136            int128_get64(section->size));
 137
 138    if (mem && add) {
 139        if (mem->size == int128_get64(section->size) &&
 140            mem->start == section->offset_within_address_space &&
 141            mem->mem == (memory_region_get_ram_ptr(area) +
 142            section->offset_within_region)) {
 143            return; /* Same region was attempted to register, go away. */
 144        }
 145    }
 146
 147    /* Region needs to be reset. set the size to 0 and remap it. */
 148    if (mem) {
 149        mem->size = 0;
 150        if (do_hvf_set_memory(mem, 0)) {
 151            error_report("Failed to reset overlapping slot");
 152            abort();
 153        }
 154    }
 155
 156    if (!add) {
 157        return;
 158    }
 159
 160    if (area->readonly ||
 161        (!memory_region_is_ram(area) && memory_region_is_romd(area))) {
 162        flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
 163    } else {
 164        flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
 165    }
 166
 167    /* Now make a new slot. */
 168    int x;
 169
 170    for (x = 0; x < hvf_state->num_slots; ++x) {
 171        mem = &hvf_state->slots[x];
 172        if (!mem->size) {
 173            break;
 174        }
 175    }
 176
 177    if (x == hvf_state->num_slots) {
 178        error_report("No free slots");
 179        abort();
 180    }
 181
 182    mem->size = int128_get64(section->size);
 183    mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
 184    mem->start = section->offset_within_address_space;
 185    mem->region = area;
 186
 187    if (do_hvf_set_memory(mem, flags)) {
 188        error_report("Error registering new memory slot");
 189        abort();
 190    }
 191}
 192
 193static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
 194{
 195    if (!cpu->vcpu_dirty) {
 196        hvf_get_registers(cpu);
 197        cpu->vcpu_dirty = true;
 198    }
 199}
 200
 201static void hvf_cpu_synchronize_state(CPUState *cpu)
 202{
 203    if (!cpu->vcpu_dirty) {
 204        run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
 205    }
 206}
 207
 208static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu,
 209                                             run_on_cpu_data arg)
 210{
 211    /* QEMU state is the reference, push it to HVF now and on next entry */
 212    cpu->vcpu_dirty = true;
 213}
 214
 215static void hvf_cpu_synchronize_post_reset(CPUState *cpu)
 216{
 217    run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
 218}
 219
 220static void hvf_cpu_synchronize_post_init(CPUState *cpu)
 221{
 222    run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
 223}
 224
 225static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
 226{
 227    run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
 228}
 229
 230static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
 231{
 232    hvf_slot *slot;
 233
 234    slot = hvf_find_overlap_slot(
 235            section->offset_within_address_space,
 236            int128_get64(section->size));
 237
 238    /* protect region against writes; begin tracking it */
 239    if (on) {
 240        slot->flags |= HVF_SLOT_LOG;
 241        hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
 242                      HV_MEMORY_READ);
 243    /* stop tracking region*/
 244    } else {
 245        slot->flags &= ~HVF_SLOT_LOG;
 246        hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
 247                      HV_MEMORY_READ | HV_MEMORY_WRITE);
 248    }
 249}
 250
 251static void hvf_log_start(MemoryListener *listener,
 252                          MemoryRegionSection *section, int old, int new)
 253{
 254    if (old != 0) {
 255        return;
 256    }
 257
 258    hvf_set_dirty_tracking(section, 1);
 259}
 260
 261static void hvf_log_stop(MemoryListener *listener,
 262                         MemoryRegionSection *section, int old, int new)
 263{
 264    if (new != 0) {
 265        return;
 266    }
 267
 268    hvf_set_dirty_tracking(section, 0);
 269}
 270
 271static void hvf_log_sync(MemoryListener *listener,
 272                         MemoryRegionSection *section)
 273{
 274    /*
 275     * sync of dirty pages is handled elsewhere; just make sure we keep
 276     * tracking the region.
 277     */
 278    hvf_set_dirty_tracking(section, 1);
 279}
 280
 281static void hvf_region_add(MemoryListener *listener,
 282                           MemoryRegionSection *section)
 283{
 284    hvf_set_phys_mem(section, true);
 285}
 286
 287static void hvf_region_del(MemoryListener *listener,
 288                           MemoryRegionSection *section)
 289{
 290    hvf_set_phys_mem(section, false);
 291}
 292
 293static MemoryListener hvf_memory_listener = {
 294    .priority = 10,
 295    .region_add = hvf_region_add,
 296    .region_del = hvf_region_del,
 297    .log_start = hvf_log_start,
 298    .log_stop = hvf_log_stop,
 299    .log_sync = hvf_log_sync,
 300};
 301
 302static void dummy_signal(int sig)
 303{
 304}
 305
 306bool hvf_allowed;
 307
 308static int hvf_accel_init(MachineState *ms)
 309{
 310    int x;
 311    hv_return_t ret;
 312    HVFState *s;
 313
 314    ret = hv_vm_create(HV_VM_DEFAULT);
 315    assert_hvf_ok(ret);
 316
 317    s = g_new0(HVFState, 1);
 318
 319    s->num_slots = 32;
 320    for (x = 0; x < s->num_slots; ++x) {
 321        s->slots[x].size = 0;
 322        s->slots[x].slot_id = x;
 323    }
 324
 325    hvf_state = s;
 326    memory_listener_register(&hvf_memory_listener, &address_space_memory);
 327    return 0;
 328}
 329
 330static void hvf_accel_class_init(ObjectClass *oc, void *data)
 331{
 332    AccelClass *ac = ACCEL_CLASS(oc);
 333    ac->name = "HVF";
 334    ac->init_machine = hvf_accel_init;
 335    ac->allowed = &hvf_allowed;
 336}
 337
 338static const TypeInfo hvf_accel_type = {
 339    .name = TYPE_HVF_ACCEL,
 340    .parent = TYPE_ACCEL,
 341    .class_init = hvf_accel_class_init,
 342};
 343
 344static void hvf_type_init(void)
 345{
 346    type_register_static(&hvf_accel_type);
 347}
 348
 349type_init(hvf_type_init);
 350
 351static void hvf_vcpu_destroy(CPUState *cpu)
 352{
 353    hv_return_t ret = hv_vcpu_destroy(cpu->hvf->fd);
 354    assert_hvf_ok(ret);
 355
 356    hvf_arch_vcpu_destroy(cpu);
 357    g_free(cpu->hvf);
 358    cpu->hvf = NULL;
 359}
 360
 361static int hvf_init_vcpu(CPUState *cpu)
 362{
 363    int r;
 364
 365    cpu->hvf = g_malloc0(sizeof(*cpu->hvf));
 366
 367    /* init cpu signals */
 368    sigset_t set;
 369    struct sigaction sigact;
 370
 371    memset(&sigact, 0, sizeof(sigact));
 372    sigact.sa_handler = dummy_signal;
 373    sigaction(SIG_IPI, &sigact, NULL);
 374
 375    pthread_sigmask(SIG_BLOCK, NULL, &set);
 376    sigdelset(&set, SIG_IPI);
 377
 378    r = hv_vcpu_create((hv_vcpuid_t *)&cpu->hvf->fd, HV_VCPU_DEFAULT);
 379    cpu->vcpu_dirty = 1;
 380    assert_hvf_ok(r);
 381
 382    return hvf_arch_init_vcpu(cpu);
 383}
 384
 385/*
 386 * The HVF-specific vCPU thread function. This one should only run when the host
 387 * CPU supports the VMX "unrestricted guest" feature.
 388 */
 389static void *hvf_cpu_thread_fn(void *arg)
 390{
 391    CPUState *cpu = arg;
 392
 393    int r;
 394
 395    assert(hvf_enabled());
 396
 397    rcu_register_thread();
 398
 399    qemu_mutex_lock_iothread();
 400    qemu_thread_get_self(cpu->thread);
 401
 402    cpu->thread_id = qemu_get_thread_id();
 403    cpu->can_do_io = 1;
 404    current_cpu = cpu;
 405
 406    hvf_init_vcpu(cpu);
 407
 408    /* signal CPU creation */
 409    cpu_thread_signal_created(cpu);
 410    qemu_guest_random_seed_thread_part2(cpu->random_seed);
 411
 412    do {
 413        if (cpu_can_run(cpu)) {
 414            r = hvf_vcpu_exec(cpu);
 415            if (r == EXCP_DEBUG) {
 416                cpu_handle_guest_debug(cpu);
 417            }
 418        }
 419        qemu_wait_io_event(cpu);
 420    } while (!cpu->unplug || cpu_can_run(cpu));
 421
 422    hvf_vcpu_destroy(cpu);
 423    cpu_thread_signal_destroyed(cpu);
 424    qemu_mutex_unlock_iothread();
 425    rcu_unregister_thread();
 426    return NULL;
 427}
 428
 429static void hvf_start_vcpu_thread(CPUState *cpu)
 430{
 431    char thread_name[VCPU_THREAD_NAME_SIZE];
 432
 433    /*
 434     * HVF currently does not support TCG, and only runs in
 435     * unrestricted-guest mode.
 436     */
 437    assert(hvf_enabled());
 438
 439    cpu->thread = g_malloc0(sizeof(QemuThread));
 440    cpu->halt_cond = g_malloc0(sizeof(QemuCond));
 441    qemu_cond_init(cpu->halt_cond);
 442
 443    snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
 444             cpu->cpu_index);
 445    qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn,
 446                       cpu, QEMU_THREAD_JOINABLE);
 447}
 448
 449static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
 450{
 451    AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
 452
 453    ops->create_vcpu_thread = hvf_start_vcpu_thread;
 454
 455    ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset;
 456    ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
 457    ops->synchronize_state = hvf_cpu_synchronize_state;
 458    ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm;
 459};
 460static const TypeInfo hvf_accel_ops_type = {
 461    .name = ACCEL_OPS_NAME("hvf"),
 462
 463    .parent = TYPE_ACCEL_OPS,
 464    .class_init = hvf_accel_ops_class_init,
 465    .abstract = true,
 466};
 467static void hvf_accel_ops_register_types(void)
 468{
 469    type_register_static(&hvf_accel_ops_type);
 470}
 471type_init(hvf_accel_ops_register_types);
 472