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