qemu/hw/remote/proxy.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2018, 2021 Oracle and/or its affiliates.
   3 *
   4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   5 * See the COPYING file in the top-level directory.
   6 *
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "qemu-common.h"
  11
  12#include "hw/remote/proxy.h"
  13#include "hw/pci/pci.h"
  14#include "qapi/error.h"
  15#include "io/channel-util.h"
  16#include "hw/qdev-properties.h"
  17#include "monitor/monitor.h"
  18#include "migration/blocker.h"
  19#include "qemu/sockets.h"
  20#include "hw/remote/mpqemu-link.h"
  21#include "qemu/error-report.h"
  22#include "hw/remote/proxy-memory-listener.h"
  23#include "qom/object.h"
  24#include "qemu/event_notifier.h"
  25#include "sysemu/kvm.h"
  26#include "util/event_notifier-posix.c"
  27
  28static void probe_pci_info(PCIDevice *dev, Error **errp);
  29static void proxy_device_reset(DeviceState *dev);
  30
  31static void proxy_intx_update(PCIDevice *pci_dev)
  32{
  33    PCIProxyDev *dev = PCI_PROXY_DEV(pci_dev);
  34    PCIINTxRoute route;
  35    int pin = pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1;
  36
  37    if (dev->virq != -1) {
  38        kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &dev->intr, dev->virq);
  39        dev->virq = -1;
  40    }
  41
  42    route = pci_device_route_intx_to_irq(pci_dev, pin);
  43
  44    dev->virq = route.irq;
  45
  46    if (dev->virq != -1) {
  47        kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &dev->intr,
  48                                           &dev->resample, dev->virq);
  49    }
  50}
  51
  52static void setup_irqfd(PCIProxyDev *dev)
  53{
  54    PCIDevice *pci_dev = PCI_DEVICE(dev);
  55    MPQemuMsg msg;
  56    Error *local_err = NULL;
  57
  58    event_notifier_init(&dev->intr, 0);
  59    event_notifier_init(&dev->resample, 0);
  60
  61    memset(&msg, 0, sizeof(MPQemuMsg));
  62    msg.cmd = MPQEMU_CMD_SET_IRQFD;
  63    msg.num_fds = 2;
  64    msg.fds[0] = event_notifier_get_fd(&dev->intr);
  65    msg.fds[1] = event_notifier_get_fd(&dev->resample);
  66    msg.size = 0;
  67
  68    if (!mpqemu_msg_send(&msg, dev->ioc, &local_err)) {
  69        error_report_err(local_err);
  70    }
  71
  72    dev->virq = -1;
  73
  74    proxy_intx_update(pci_dev);
  75
  76    pci_device_set_intx_routing_notifier(pci_dev, proxy_intx_update);
  77}
  78
  79static void pci_proxy_dev_realize(PCIDevice *device, Error **errp)
  80{
  81    ERRP_GUARD();
  82    PCIProxyDev *dev = PCI_PROXY_DEV(device);
  83    uint8_t *pci_conf = device->config;
  84    int fd;
  85
  86    if (!dev->fd) {
  87        error_setg(errp, "fd parameter not specified for %s",
  88                   DEVICE(device)->id);
  89        return;
  90    }
  91
  92    fd = monitor_fd_param(monitor_cur(), dev->fd, errp);
  93    if (fd == -1) {
  94        error_prepend(errp, "proxy: unable to parse fd %s: ", dev->fd);
  95        return;
  96    }
  97
  98    if (!fd_is_socket(fd)) {
  99        error_setg(errp, "proxy: fd %d is not a socket", fd);
 100        close(fd);
 101        return;
 102    }
 103
 104    dev->ioc = qio_channel_new_fd(fd, errp);
 105    if (!dev->ioc) {
 106        close(fd);
 107        return;
 108    }
 109
 110    error_setg(&dev->migration_blocker, "%s does not support migration",
 111               TYPE_PCI_PROXY_DEV);
 112    if (migrate_add_blocker(dev->migration_blocker, errp) < 0) {
 113        error_free(dev->migration_blocker);
 114        object_unref(dev->ioc);
 115        return;
 116    }
 117
 118    qemu_mutex_init(&dev->io_mutex);
 119    qio_channel_set_blocking(dev->ioc, true, NULL);
 120
 121    pci_conf[PCI_LATENCY_TIMER] = 0xff;
 122    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
 123
 124    proxy_memory_listener_configure(&dev->proxy_listener, dev->ioc);
 125
 126    setup_irqfd(dev);
 127
 128    probe_pci_info(PCI_DEVICE(dev), errp);
 129}
 130
 131static void pci_proxy_dev_exit(PCIDevice *pdev)
 132{
 133    PCIProxyDev *dev = PCI_PROXY_DEV(pdev);
 134
 135    if (dev->ioc) {
 136        qio_channel_close(dev->ioc, NULL);
 137    }
 138
 139    migrate_del_blocker(dev->migration_blocker);
 140
 141    error_free(dev->migration_blocker);
 142
 143    proxy_memory_listener_deconfigure(&dev->proxy_listener);
 144
 145    event_notifier_cleanup(&dev->intr);
 146    event_notifier_cleanup(&dev->resample);
 147}
 148
 149static void config_op_send(PCIProxyDev *pdev, uint32_t addr, uint32_t *val,
 150                           int len, unsigned int op)
 151{
 152    MPQemuMsg msg = { 0 };
 153    uint64_t ret = -EINVAL;
 154    Error *local_err = NULL;
 155
 156    msg.cmd = op;
 157    msg.data.pci_conf_data.addr = addr;
 158    msg.data.pci_conf_data.val = (op == MPQEMU_CMD_PCI_CFGWRITE) ? *val : 0;
 159    msg.data.pci_conf_data.len = len;
 160    msg.size = sizeof(PciConfDataMsg);
 161
 162    ret = mpqemu_msg_send_and_await_reply(&msg, pdev, &local_err);
 163    if (local_err) {
 164        error_report_err(local_err);
 165    }
 166
 167    if (ret == UINT64_MAX) {
 168        error_report("Failed to perform PCI config %s operation",
 169                     (op == MPQEMU_CMD_PCI_CFGREAD) ? "READ" : "WRITE");
 170    }
 171
 172    if (op == MPQEMU_CMD_PCI_CFGREAD) {
 173        *val = (uint32_t)ret;
 174    }
 175}
 176
 177static uint32_t pci_proxy_read_config(PCIDevice *d, uint32_t addr, int len)
 178{
 179    uint32_t val;
 180
 181    config_op_send(PCI_PROXY_DEV(d), addr, &val, len, MPQEMU_CMD_PCI_CFGREAD);
 182
 183    return val;
 184}
 185
 186static void pci_proxy_write_config(PCIDevice *d, uint32_t addr, uint32_t val,
 187                                   int len)
 188{
 189    /*
 190     * Some of the functions access the copy of remote device's PCI config
 191     * space which is cached in the proxy device. Therefore, maintain
 192     * it updated.
 193     */
 194    pci_default_write_config(d, addr, val, len);
 195
 196    config_op_send(PCI_PROXY_DEV(d), addr, &val, len, MPQEMU_CMD_PCI_CFGWRITE);
 197}
 198
 199static Property proxy_properties[] = {
 200    DEFINE_PROP_STRING("fd", PCIProxyDev, fd),
 201    DEFINE_PROP_END_OF_LIST(),
 202};
 203
 204static void pci_proxy_dev_class_init(ObjectClass *klass, void *data)
 205{
 206    DeviceClass *dc = DEVICE_CLASS(klass);
 207    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 208
 209    k->realize = pci_proxy_dev_realize;
 210    k->exit = pci_proxy_dev_exit;
 211    k->config_read = pci_proxy_read_config;
 212    k->config_write = pci_proxy_write_config;
 213
 214    dc->reset = proxy_device_reset;
 215
 216    device_class_set_props(dc, proxy_properties);
 217}
 218
 219static const TypeInfo pci_proxy_dev_type_info = {
 220    .name          = TYPE_PCI_PROXY_DEV,
 221    .parent        = TYPE_PCI_DEVICE,
 222    .instance_size = sizeof(PCIProxyDev),
 223    .class_init    = pci_proxy_dev_class_init,
 224    .interfaces = (InterfaceInfo[]) {
 225        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
 226        { },
 227    },
 228};
 229
 230static void pci_proxy_dev_register_types(void)
 231{
 232    type_register_static(&pci_proxy_dev_type_info);
 233}
 234
 235type_init(pci_proxy_dev_register_types)
 236
 237static void send_bar_access_msg(PCIProxyDev *pdev, MemoryRegion *mr,
 238                                bool write, hwaddr addr, uint64_t *val,
 239                                unsigned size, bool memory)
 240{
 241    MPQemuMsg msg = { 0 };
 242    long ret = -EINVAL;
 243    Error *local_err = NULL;
 244
 245    msg.size = sizeof(BarAccessMsg);
 246    msg.data.bar_access.addr = mr->addr + addr;
 247    msg.data.bar_access.size = size;
 248    msg.data.bar_access.memory = memory;
 249
 250    if (write) {
 251        msg.cmd = MPQEMU_CMD_BAR_WRITE;
 252        msg.data.bar_access.val = *val;
 253    } else {
 254        msg.cmd = MPQEMU_CMD_BAR_READ;
 255    }
 256
 257    ret = mpqemu_msg_send_and_await_reply(&msg, pdev, &local_err);
 258    if (local_err) {
 259        error_report_err(local_err);
 260    }
 261
 262    if (!write) {
 263        *val = ret;
 264    }
 265}
 266
 267static void proxy_bar_write(void *opaque, hwaddr addr, uint64_t val,
 268                            unsigned size)
 269{
 270    ProxyMemoryRegion *pmr = opaque;
 271
 272    send_bar_access_msg(pmr->dev, &pmr->mr, true, addr, &val, size,
 273                        pmr->memory);
 274}
 275
 276static uint64_t proxy_bar_read(void *opaque, hwaddr addr, unsigned size)
 277{
 278    ProxyMemoryRegion *pmr = opaque;
 279    uint64_t val;
 280
 281    send_bar_access_msg(pmr->dev, &pmr->mr, false, addr, &val, size,
 282                        pmr->memory);
 283
 284    return val;
 285}
 286
 287const MemoryRegionOps proxy_mr_ops = {
 288    .read = proxy_bar_read,
 289    .write = proxy_bar_write,
 290    .endianness = DEVICE_NATIVE_ENDIAN,
 291    .impl = {
 292        .min_access_size = 1,
 293        .max_access_size = 8,
 294    },
 295};
 296
 297static void probe_pci_info(PCIDevice *dev, Error **errp)
 298{
 299    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
 300    uint32_t orig_val, new_val, base_class, val;
 301    PCIProxyDev *pdev = PCI_PROXY_DEV(dev);
 302    DeviceClass *dc = DEVICE_CLASS(pc);
 303    uint8_t type;
 304    int i, size;
 305
 306    config_op_send(pdev, PCI_VENDOR_ID, &val, 2, MPQEMU_CMD_PCI_CFGREAD);
 307    pc->vendor_id = (uint16_t)val;
 308
 309    config_op_send(pdev, PCI_DEVICE_ID, &val, 2, MPQEMU_CMD_PCI_CFGREAD);
 310    pc->device_id = (uint16_t)val;
 311
 312    config_op_send(pdev, PCI_CLASS_DEVICE, &val, 2, MPQEMU_CMD_PCI_CFGREAD);
 313    pc->class_id = (uint16_t)val;
 314
 315    config_op_send(pdev, PCI_SUBSYSTEM_ID, &val, 2, MPQEMU_CMD_PCI_CFGREAD);
 316    pc->subsystem_id = (uint16_t)val;
 317
 318    base_class = pc->class_id >> 4;
 319    switch (base_class) {
 320    case PCI_BASE_CLASS_BRIDGE:
 321        set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
 322        break;
 323    case PCI_BASE_CLASS_STORAGE:
 324        set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 325        break;
 326    case PCI_BASE_CLASS_NETWORK:
 327    case PCI_BASE_CLASS_WIRELESS:
 328        set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
 329        break;
 330    case PCI_BASE_CLASS_INPUT:
 331        set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
 332        break;
 333    case PCI_BASE_CLASS_DISPLAY:
 334        set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 335        break;
 336    case PCI_BASE_CLASS_PROCESSOR:
 337        set_bit(DEVICE_CATEGORY_CPU, dc->categories);
 338        break;
 339    default:
 340        set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 341        break;
 342    }
 343
 344    for (i = 0; i < PCI_NUM_REGIONS; i++) {
 345        config_op_send(pdev, PCI_BASE_ADDRESS_0 + (4 * i), &orig_val, 4,
 346                       MPQEMU_CMD_PCI_CFGREAD);
 347        new_val = 0xffffffff;
 348        config_op_send(pdev, PCI_BASE_ADDRESS_0 + (4 * i), &new_val, 4,
 349                       MPQEMU_CMD_PCI_CFGWRITE);
 350        config_op_send(pdev, PCI_BASE_ADDRESS_0 + (4 * i), &new_val, 4,
 351                       MPQEMU_CMD_PCI_CFGREAD);
 352        size = (~(new_val & 0xFFFFFFF0)) + 1;
 353        config_op_send(pdev, PCI_BASE_ADDRESS_0 + (4 * i), &orig_val, 4,
 354                       MPQEMU_CMD_PCI_CFGWRITE);
 355        type = (new_val & 0x1) ?
 356                   PCI_BASE_ADDRESS_SPACE_IO : PCI_BASE_ADDRESS_SPACE_MEMORY;
 357
 358        if (size) {
 359            g_autofree char *name = g_strdup_printf("bar-region-%d", i);
 360            pdev->region[i].dev = pdev;
 361            pdev->region[i].present = true;
 362            if (type == PCI_BASE_ADDRESS_SPACE_MEMORY) {
 363                pdev->region[i].memory = true;
 364            }
 365            memory_region_init_io(&pdev->region[i].mr, OBJECT(pdev),
 366                                  &proxy_mr_ops, &pdev->region[i],
 367                                  name, size);
 368            pci_register_bar(dev, i, type, &pdev->region[i].mr);
 369        }
 370    }
 371}
 372
 373static void proxy_device_reset(DeviceState *dev)
 374{
 375    PCIProxyDev *pdev = PCI_PROXY_DEV(dev);
 376    MPQemuMsg msg = { 0 };
 377    Error *local_err = NULL;
 378
 379    msg.cmd = MPQEMU_CMD_DEVICE_RESET;
 380    msg.size = 0;
 381
 382    mpqemu_msg_send_and_await_reply(&msg, pdev, &local_err);
 383    if (local_err) {
 384        error_report_err(local_err);
 385    }
 386
 387}
 388