xref: /openbmc/qemu/hw/remote/message.c (revision d0bf492f)
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 
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 
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 
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 
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 
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 
215 static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev,
216                                      Error **errp)
217 {
218     DeviceClass *dc = DEVICE_GET_CLASS(dev);
219     DeviceState *s = DEVICE(dev);
220     MPQemuMsg ret = { 0 };
221 
222     if (dc->reset) {
223         dc->reset(s);
224     }
225 
226     ret.cmd = MPQEMU_CMD_RET;
227 
228     mpqemu_msg_send(&ret, ioc, errp);
229 }
230