xref: /openbmc/qemu/hw/remote/message.c (revision fc8c745d)
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