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