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