qemu/hw/remote/message.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2020, 2021 Oracle and/or its affiliates.
   3 *
   4 * This work is licensed under the terms of the GNU GPL-v2, version 2 or later.
   5 *
   6 * See the COPYING file in the top-level directory.
   7 *
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qemu-common.h"
  12
  13#include "hw/remote/machine.h"
  14#include "io/channel.h"
  15#include "hw/remote/mpqemu-link.h"
  16#include "qapi/error.h"
  17#include "sysemu/runstate.h"
  18#include "hw/pci/pci.h"
  19#include "exec/memattrs.h"
  20#include "hw/remote/memory.h"
  21#include "hw/remote/iohub.h"
  22#include "sysemu/reset.h"
  23
  24static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
  25                                 MPQemuMsg *msg, Error **errp);
  26static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
  27                                MPQemuMsg *msg, Error **errp);
  28static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
  29static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
  30static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev,
  31                                     Error **errp);
  32
  33void coroutine_fn mpqemu_remote_msg_loop_co(void *data)
  34{
  35    g_autofree RemoteCommDev *com = (RemoteCommDev *)data;
  36    PCIDevice *pci_dev = NULL;
  37    Error *local_err = NULL;
  38
  39    assert(com->ioc);
  40
  41    pci_dev = com->dev;
  42    for (; !local_err;) {
  43        MPQemuMsg msg = {0};
  44
  45        if (!mpqemu_msg_recv(&msg, com->ioc, &local_err)) {
  46            break;
  47        }
  48
  49        if (!mpqemu_msg_valid(&msg)) {
  50            error_setg(&local_err, "Received invalid message from proxy"
  51                                   "in remote process pid="FMT_pid"",
  52                                   getpid());
  53            break;
  54        }
  55
  56        switch (msg.cmd) {
  57        case MPQEMU_CMD_PCI_CFGWRITE:
  58            process_config_write(com->ioc, pci_dev, &msg, &local_err);
  59            break;
  60        case MPQEMU_CMD_PCI_CFGREAD:
  61            process_config_read(com->ioc, pci_dev, &msg, &local_err);
  62            break;
  63        case MPQEMU_CMD_BAR_WRITE:
  64            process_bar_write(com->ioc, &msg, &local_err);
  65            break;
  66        case MPQEMU_CMD_BAR_READ:
  67            process_bar_read(com->ioc, &msg, &local_err);
  68            break;
  69        case MPQEMU_CMD_SYNC_SYSMEM:
  70            remote_sysmem_reconfig(&msg, &local_err);
  71            break;
  72        case MPQEMU_CMD_SET_IRQFD:
  73            process_set_irqfd_msg(pci_dev, &msg);
  74            break;
  75        case MPQEMU_CMD_DEVICE_RESET:
  76            process_device_reset_msg(com->ioc, pci_dev, &local_err);
  77            break;
  78        default:
  79            error_setg(&local_err,
  80                       "Unknown command (%d) received for device %s"
  81                       " (pid="FMT_pid")",
  82                       msg.cmd, DEVICE(pci_dev)->id, getpid());
  83        }
  84    }
  85
  86    if (local_err) {
  87        error_report_err(local_err);
  88        qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR);
  89    } else {
  90        qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
  91    }
  92}
  93
  94static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
  95                                 MPQemuMsg *msg, Error **errp)
  96{
  97    ERRP_GUARD();
  98    PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data;
  99    MPQemuMsg ret = { 0 };
 100
 101    if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) {
 102        error_setg(errp, "Bad address for PCI config write, pid "FMT_pid".",
 103                   getpid());
 104        ret.data.u64 = UINT64_MAX;
 105    } else {
 106        pci_default_write_config(dev, conf->addr, conf->val, conf->len);
 107    }
 108
 109    ret.cmd = MPQEMU_CMD_RET;
 110    ret.size = sizeof(ret.data.u64);
 111
 112    if (!mpqemu_msg_send(&ret, ioc, NULL)) {
 113        error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
 114                      getpid());
 115    }
 116}
 117
 118static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
 119                                MPQemuMsg *msg, Error **errp)
 120{
 121    ERRP_GUARD();
 122    PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data;
 123    MPQemuMsg ret = { 0 };
 124
 125    if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) {
 126        error_setg(errp, "Bad address for PCI config read, pid "FMT_pid".",
 127                   getpid());
 128        ret.data.u64 = UINT64_MAX;
 129    } else {
 130        ret.data.u64 = pci_default_read_config(dev, conf->addr, conf->len);
 131    }
 132
 133    ret.cmd = MPQEMU_CMD_RET;
 134    ret.size = sizeof(ret.data.u64);
 135
 136    if (!mpqemu_msg_send(&ret, ioc, NULL)) {
 137        error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
 138                      getpid());
 139    }
 140}
 141
 142static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
 143{
 144    ERRP_GUARD();
 145    BarAccessMsg *bar_access = &msg->data.bar_access;
 146    AddressSpace *as =
 147        bar_access->memory ? &address_space_memory : &address_space_io;
 148    MPQemuMsg ret = { 0 };
 149    MemTxResult res;
 150    uint64_t val;
 151
 152    if (!is_power_of_2(bar_access->size) ||
 153       (bar_access->size > sizeof(uint64_t))) {
 154        ret.data.u64 = UINT64_MAX;
 155        goto fail;
 156    }
 157
 158    val = cpu_to_le64(bar_access->val);
 159
 160    res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
 161                           (void *)&val, bar_access->size, true);
 162
 163    if (res != MEMTX_OK) {
 164        error_setg(errp, "Bad address %"PRIx64" for mem write, pid "FMT_pid".",
 165                   bar_access->addr, getpid());
 166        ret.data.u64 = -1;
 167    }
 168
 169fail:
 170    ret.cmd = MPQEMU_CMD_RET;
 171    ret.size = sizeof(ret.data.u64);
 172
 173    if (!mpqemu_msg_send(&ret, ioc, NULL)) {
 174        error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
 175                      getpid());
 176    }
 177}
 178
 179static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
 180{
 181    ERRP_GUARD();
 182    BarAccessMsg *bar_access = &msg->data.bar_access;
 183    MPQemuMsg ret = { 0 };
 184    AddressSpace *as;
 185    MemTxResult res;
 186    uint64_t val = 0;
 187
 188    as = bar_access->memory ? &address_space_memory : &address_space_io;
 189
 190    if (!is_power_of_2(bar_access->size) ||
 191       (bar_access->size > sizeof(uint64_t))) {
 192        val = UINT64_MAX;
 193        goto fail;
 194    }
 195
 196    res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
 197                           (void *)&val, bar_access->size, false);
 198
 199    if (res != MEMTX_OK) {
 200        error_setg(errp, "Bad address %"PRIx64" for mem read, pid "FMT_pid".",
 201                   bar_access->addr, getpid());
 202        val = UINT64_MAX;
 203    }
 204
 205fail:
 206    ret.cmd = MPQEMU_CMD_RET;
 207    ret.data.u64 = le64_to_cpu(val);
 208    ret.size = sizeof(ret.data.u64);
 209
 210    if (!mpqemu_msg_send(&ret, ioc, NULL)) {
 211        error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
 212                      getpid());
 213    }
 214}
 215
 216static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev,
 217                                     Error **errp)
 218{
 219    DeviceClass *dc = DEVICE_GET_CLASS(dev);
 220    DeviceState *s = DEVICE(dev);
 221    MPQemuMsg ret = { 0 };
 222
 223    if (dc->reset) {
 224        dc->reset(s);
 225    }
 226
 227    ret.cmd = MPQEMU_CMD_RET;
 228
 229    mpqemu_msg_send(&ret, ioc, errp);
 230}
 231