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 20 static void process_config_write(QIOChannel *ioc, PCIDevice *dev, 21 MPQemuMsg *msg, Error **errp); 22 static void process_config_read(QIOChannel *ioc, PCIDevice *dev, 23 MPQemuMsg *msg, Error **errp); 24 25 void coroutine_fn mpqemu_remote_msg_loop_co(void *data) 26 { 27 g_autofree RemoteCommDev *com = (RemoteCommDev *)data; 28 PCIDevice *pci_dev = NULL; 29 Error *local_err = NULL; 30 31 assert(com->ioc); 32 33 pci_dev = com->dev; 34 for (; !local_err;) { 35 MPQemuMsg msg = {0}; 36 37 if (!mpqemu_msg_recv(&msg, com->ioc, &local_err)) { 38 break; 39 } 40 41 if (!mpqemu_msg_valid(&msg)) { 42 error_setg(&local_err, "Received invalid message from proxy" 43 "in remote process pid="FMT_pid"", 44 getpid()); 45 break; 46 } 47 48 switch (msg.cmd) { 49 case MPQEMU_CMD_PCI_CFGWRITE: 50 process_config_write(com->ioc, pci_dev, &msg, &local_err); 51 break; 52 case MPQEMU_CMD_PCI_CFGREAD: 53 process_config_read(com->ioc, pci_dev, &msg, &local_err); 54 break; 55 default: 56 error_setg(&local_err, 57 "Unknown command (%d) received for device %s" 58 " (pid="FMT_pid")", 59 msg.cmd, DEVICE(pci_dev)->id, getpid()); 60 } 61 } 62 63 if (local_err) { 64 error_report_err(local_err); 65 qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_ERROR); 66 } else { 67 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 68 } 69 } 70 71 static void process_config_write(QIOChannel *ioc, PCIDevice *dev, 72 MPQemuMsg *msg, Error **errp) 73 { 74 ERRP_GUARD(); 75 PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data; 76 MPQemuMsg ret = { 0 }; 77 78 if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) { 79 error_setg(errp, "Bad address for PCI config write, pid "FMT_pid".", 80 getpid()); 81 ret.data.u64 = UINT64_MAX; 82 } else { 83 pci_default_write_config(dev, conf->addr, conf->val, conf->len); 84 } 85 86 ret.cmd = MPQEMU_CMD_RET; 87 ret.size = sizeof(ret.data.u64); 88 89 if (!mpqemu_msg_send(&ret, ioc, NULL)) { 90 error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ", 91 getpid()); 92 } 93 } 94 95 static void process_config_read(QIOChannel *ioc, PCIDevice *dev, 96 MPQemuMsg *msg, Error **errp) 97 { 98 ERRP_GUARD(); 99 PciConfDataMsg *conf = (PciConfDataMsg *)&msg->data.pci_conf_data; 100 MPQemuMsg ret = { 0 }; 101 102 if ((conf->addr + sizeof(conf->val)) > pci_config_size(dev)) { 103 error_setg(errp, "Bad address for PCI config read, pid "FMT_pid".", 104 getpid()); 105 ret.data.u64 = UINT64_MAX; 106 } else { 107 ret.data.u64 = pci_default_read_config(dev, conf->addr, conf->len); 108 } 109 110 ret.cmd = MPQEMU_CMD_RET; 111 ret.size = sizeof(ret.data.u64); 112 113 if (!mpqemu_msg_send(&ret, ioc, NULL)) { 114 error_prepend(errp, "Error returning code to proxy, pid "FMT_pid": ", 115 getpid()); 116 } 117 } 118