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 24 static void process_config_write(QIOChannel *ioc, PCIDevice *dev, 25 MPQemuMsg *msg, Error **errp); 26 static void process_config_read(QIOChannel *ioc, PCIDevice *dev, 27 MPQemuMsg *msg, Error **errp); 28 static void process_bar_write(QIOChannel *ioc, MPQemuMsg *msg, Error **errp); 29 static void process_bar_read(QIOChannel *ioc, MPQemuMsg *msg, Error **errp); 30 static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev, 31 Error **errp); 32 33 void 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 94 static 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 118 static 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 142 static 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 169 fail: 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 179 static 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 205 fail: 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 216 static 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