qemu/hw/remote/message.c
Elena Ufimtseva b6cc02d98f multi-process: perform device reset in the remote process
Perform device reset in the remote process when QEMU performs
device reset. This is required to reset the internal state
(like registers, etc...) of emulated devices

Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 7cb220a51f565dc0817bd76e2f540e89c2d2b850.1611938319.git.jag.raman@oracle.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2021-02-10 09:23:28 +00:00

231 lines
6.9 KiB
C

/*
* Copyright © 2020, 2021 Oracle and/or its affiliates.
*
* This work is licensed under the terms of the GNU GPL-v2, version 2 or later.
*
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/remote/machine.h"
#include "io/channel.h"
#include "hw/remote/mpqemu-link.h"
#include "qapi/error.h"
#include "sysemu/runstate.h"
#include "hw/pci/pci.h"
#include "exec/memattrs.h"
#include "hw/remote/memory.h"
#include "hw/remote/iohub.h"
#include "sysemu/reset.h"
static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
MPQemuMsg *msg, Error **errp);
static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
MPQemuMsg *msg, Error **errp);
static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp);
static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev,
Error **errp);
void coroutine_fn mpqemu_remote_msg_loop_co(void *data)
{
g_autofree RemoteCommDev *com = (RemoteCommDev *)data;
PCIDevice *pci_dev = NULL;
Error *local_err = NULL;
assert(com->ioc);
pci_dev = com->dev;
for (; !local_err;) {
MPQemuMsg msg = {0};
if (!mpqemu_msg_recv(&msg, com->ioc, &local_err)) {
break;
}
if (!mpqemu_msg_valid(&msg)) {
error_setg(&local_err, "Received invalid message from proxy"
"in remote process pid="FMT_pid"",
getpid());
break;
}
switch (msg.cmd) {
case MPQEMU_CMD_PCI_CFGWRITE:
process_config_write(com->ioc, pci_dev, &msg, &local_err);
break;
case MPQEMU_CMD_PCI_CFGREAD:
process_config_read(com->ioc, pci_dev, &msg, &local_err);
break;
case MPQEMU_CMD_BAR_WRITE:
process_bar_write(com->ioc, &msg, &local_err);
break;
case MPQEMU_CMD_BAR_READ:
process_bar_read(com->ioc, &msg, &local_err);
break;
case MPQEMU_CMD_SYNC_SYSMEM:
remote_sysmem_reconfig(&msg, &local_err);
break;
case MPQEMU_CMD_SET_IRQFD:
process_set_irqfd_msg(pci_dev, &msg);
break;
case MPQEMU_CMD_DEVICE_RESET:
process_device_reset_msg(com->ioc, pci_dev, &local_err);
break;
default:
error_setg(&local_err,
"Unknown command (%d) received for device %s"
" (pid="FMT_pid")",
msg.cmd, DEVICE(pci_dev)->id, getpid());
}
}
if (local_err) {
error_report_err(local_err);
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR);
} else {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
}
static void process_config_write(QIOChannel *ioc, PCIDevice *dev,
MPQemuMsg *msg, Error **errp)
{
ERRP_GUARD();
PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data;
MPQemuMsg ret = { 0 };
if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) {
error_setg(errp, "Bad address for PCI config write, pid "FMT_pid".",
getpid());
ret.data.u64 = UINT64_MAX;
} else {
pci_default_write_config(dev, conf->addr, conf->val, conf->len);
}
ret.cmd = MPQEMU_CMD_RET;
ret.size = sizeof(ret.data.u64);
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
getpid());
}
}
static void process_config_read(QIOChannel *ioc, PCIDevice *dev,
MPQemuMsg *msg, Error **errp)
{
ERRP_GUARD();
PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data;
MPQemuMsg ret = { 0 };
if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) {
error_setg(errp, "Bad address for PCI config read, pid "FMT_pid".",
getpid());
ret.data.u64 = UINT64_MAX;
} else {
ret.data.u64 = pci_default_read_config(dev, conf->addr, conf->len);
}
ret.cmd = MPQEMU_CMD_RET;
ret.size = sizeof(ret.data.u64);
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
getpid());
}
}
static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
{
ERRP_GUARD();
BarAccessMsg *bar_access = &msg->data.bar_access;
AddressSpace *as =
bar_access->memory ? &address_space_memory : &address_space_io;
MPQemuMsg ret = { 0 };
MemTxResult res;
uint64_t val;
if (!is_power_of_2(bar_access->size) ||
(bar_access->size > sizeof(uint64_t))) {
ret.data.u64 = UINT64_MAX;
goto fail;
}
val = cpu_to_le64(bar_access->val);
res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
(void *)&val, bar_access->size, true);
if (res != MEMTX_OK) {
error_setg(errp, "Bad address %"PRIx64" for mem write, pid "FMT_pid".",
bar_access->addr, getpid());
ret.data.u64 = -1;
}
fail:
ret.cmd = MPQEMU_CMD_RET;
ret.size = sizeof(ret.data.u64);
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
getpid());
}
}
static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp)
{
ERRP_GUARD();
BarAccessMsg *bar_access = &msg->data.bar_access;
MPQemuMsg ret = { 0 };
AddressSpace *as;
MemTxResult res;
uint64_t val = 0;
as = bar_access->memory ? &address_space_memory : &address_space_io;
if (!is_power_of_2(bar_access->size) ||
(bar_access->size > sizeof(uint64_t))) {
val = UINT64_MAX;
goto fail;
}
res = address_space_rw(as, bar_access->addr, MEMTXATTRS_UNSPECIFIED,
(void *)&val, bar_access->size, false);
if (res != MEMTX_OK) {
error_setg(errp, "Bad address %"PRIx64" for mem read, pid "FMT_pid".",
bar_access->addr, getpid());
val = UINT64_MAX;
}
fail:
ret.cmd = MPQEMU_CMD_RET;
ret.data.u64 = le64_to_cpu(val);
ret.size = sizeof(ret.data.u64);
if (!mpqemu_msg_send(&ret, ioc, NULL)) {
error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ",
getpid());
}
}
static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev,
Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
DeviceState *s = DEVICE(dev);
MPQemuMsg ret = { 0 };
if (dc->reset) {
dc->reset(s);
}
ret.cmd = MPQEMU_CMD_RET;
mpqemu_msg_send(&ret, ioc, errp);
}