qemu/hw/misc/hyperv_testdev.c
<<
>>
Prefs
   1/*
   2 * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
   3 *
   4 * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
   5 *
   6 * Authors:
   7 *  Andrey Smetanin <asmetanin@virtuozzo.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 *
  12 */
  13
  14#include "qemu/osdep.h"
  15#include <linux/kvm.h>
  16#include "hw/hw.h"
  17#include "hw/qdev.h"
  18#include "hw/isa/isa.h"
  19#include "sysemu/kvm.h"
  20#include "target/i386/hyperv.h"
  21#include "kvm_i386.h"
  22
  23#define HV_TEST_DEV_MAX_SINT_ROUTES 64
  24
  25struct HypervTestDev {
  26    ISADevice parent_obj;
  27    MemoryRegion sint_control;
  28    HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
  29};
  30typedef struct HypervTestDev HypervTestDev;
  31
  32#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
  33#define HYPERV_TEST_DEV(obj) \
  34        OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
  35
  36enum {
  37    HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
  38    HV_TEST_DEV_SINT_ROUTE_DESTROY,
  39    HV_TEST_DEV_SINT_ROUTE_SET_SINT
  40};
  41
  42static int alloc_sint_route_index(HypervTestDev *dev)
  43{
  44    int i;
  45
  46    for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
  47        if (dev->sint_route[i] == NULL) {
  48            return i;
  49        }
  50    }
  51    return -1;
  52}
  53
  54static void free_sint_route_index(HypervTestDev *dev, int i)
  55{
  56    assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
  57    dev->sint_route[i] = NULL;
  58}
  59
  60static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id,
  61                                 uint32_t sint)
  62{
  63    HvSintRoute *sint_route;
  64    int i;
  65
  66    for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
  67        sint_route = dev->sint_route[i];
  68        if (sint_route && sint_route->vcpu_id == vcpu_id &&
  69            sint_route->sint == sint) {
  70            return i;
  71        }
  72    }
  73    return -1;
  74}
  75
  76static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
  77                                      uint32_t vcpu_id, uint32_t sint)
  78{
  79    int i;
  80    HvSintRoute *sint_route;
  81
  82    switch (ctl) {
  83    case HV_TEST_DEV_SINT_ROUTE_CREATE:
  84        i = alloc_sint_route_index(dev);
  85        assert(i >= 0);
  86        sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL);
  87        assert(sint_route);
  88        dev->sint_route[i] = sint_route;
  89        break;
  90    case HV_TEST_DEV_SINT_ROUTE_DESTROY:
  91        i = find_sint_route_index(dev, vcpu_id, sint);
  92        assert(i >= 0);
  93        sint_route = dev->sint_route[i];
  94        kvm_hv_sint_route_destroy(sint_route);
  95        free_sint_route_index(dev, i);
  96        break;
  97    case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
  98        i = find_sint_route_index(dev, vcpu_id, sint);
  99        assert(i >= 0);
 100        sint_route = dev->sint_route[i];
 101        kvm_hv_sint_route_set_sint(sint_route);
 102        break;
 103    default:
 104        break;
 105    }
 106}
 107
 108static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
 109                                uint32_t len)
 110{
 111    HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
 112    uint8_t ctl;
 113
 114    ctl = (data >> 16ULL) & 0xFF;
 115    switch (ctl) {
 116    case HV_TEST_DEV_SINT_ROUTE_CREATE:
 117    case HV_TEST_DEV_SINT_ROUTE_DESTROY:
 118    case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
 119        uint8_t sint = data & 0xFF;
 120        uint8_t vcpu_id = (data >> 8ULL) & 0xFF;
 121        hv_synic_test_dev_control(dev, ctl, vcpu_id, sint);
 122        break;
 123    }
 124    default:
 125        break;
 126    }
 127}
 128
 129static const MemoryRegionOps synic_test_sint_ops = {
 130    .write = hv_test_dev_control,
 131    .valid.min_access_size = 4,
 132    .valid.max_access_size = 4,
 133    .endianness = DEVICE_LITTLE_ENDIAN,
 134};
 135
 136static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
 137{
 138    ISADevice *isa = ISA_DEVICE(d);
 139    HypervTestDev *dev = HYPERV_TEST_DEV(d);
 140    MemoryRegion *io = isa_address_space_io(isa);
 141
 142    memset(dev->sint_route, 0, sizeof(dev->sint_route));
 143    memory_region_init_io(&dev->sint_control, OBJECT(dev),
 144                          &synic_test_sint_ops, dev,
 145                          "hyperv-testdev-ctl", 4);
 146    memory_region_add_subregion(io, 0x3000, &dev->sint_control);
 147}
 148
 149static void hv_test_dev_class_init(ObjectClass *klass, void *data)
 150{
 151    DeviceClass *dc = DEVICE_CLASS(klass);
 152
 153    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 154    dc->realize = hv_test_dev_realizefn;
 155}
 156
 157static const TypeInfo hv_test_dev_info = {
 158    .name           = TYPE_HYPERV_TEST_DEV,
 159    .parent         = TYPE_ISA_DEVICE,
 160    .instance_size  = sizeof(HypervTestDev),
 161    .class_init     = hv_test_dev_class_init,
 162};
 163
 164static void hv_test_dev_register_types(void)
 165{
 166    type_register_static(&hv_test_dev_info);
 167}
 168type_init(hv_test_dev_register_types);
 169