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