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