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