xref: /openbmc/qemu/chardev/spice.c (revision 7ae71462)
1 #include "qemu/osdep.h"
2 #include "trace.h"
3 #include "ui/qemu-spice.h"
4 #include "chardev/char.h"
5 #include "chardev/spice.h"
6 #include "qapi/error.h"
7 #include "qemu/error-report.h"
8 #include "qemu/module.h"
9 #include "qemu/option.h"
10 #include <spice/protocol.h>
11 
12 typedef struct SpiceCharSource {
13     GSource               source;
14     SpiceChardev       *scd;
15 } SpiceCharSource;
16 
17 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
18 {
19     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
20     Chardev *chr = CHARDEV(scd);
21     ssize_t out = 0;
22     ssize_t last_out;
23     uint8_t* p = (uint8_t*)buf;
24 
25     while (len > 0) {
26         int can_write = qemu_chr_be_can_write(chr);
27         last_out = MIN(len, can_write);
28         if (last_out <= 0) {
29             break;
30         }
31         qemu_chr_be_write(chr, p, last_out);
32         out += last_out;
33         len -= last_out;
34         p += last_out;
35     }
36 
37     trace_spice_vmc_write(out, len + out);
38     return out;
39 }
40 
41 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
42 {
43     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
44     int bytes = MIN(len, scd->datalen);
45 
46     if (bytes > 0) {
47         memcpy(buf, scd->datapos, bytes);
48         scd->datapos += bytes;
49         scd->datalen -= bytes;
50         assert(scd->datalen >= 0);
51     }
52     if (scd->datalen == 0) {
53         scd->datapos = 0;
54         scd->blocked = false;
55     }
56     trace_spice_vmc_read(bytes, len);
57     return bytes;
58 }
59 
60 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
61 {
62     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
63     Chardev *chr = CHARDEV(scd);
64     int chr_event;
65 
66     switch (event) {
67     case SPICE_PORT_EVENT_BREAK:
68         chr_event = CHR_EVENT_BREAK;
69         break;
70     default:
71         return;
72     }
73 
74     trace_spice_vmc_event(chr_event);
75     qemu_chr_be_event(chr, chr_event);
76 }
77 
78 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
79 {
80     SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
81     Chardev *chr = CHARDEV(scd);
82 
83     if ((chr->be_open && connected) ||
84         (!chr->be_open && !connected)) {
85         return;
86     }
87 
88     qemu_chr_be_event(chr,
89                       connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
90 }
91 
92 static SpiceCharDeviceInterface vmc_interface = {
93     .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
94     .base.description   = "spice virtual channel char device",
95     .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
96     .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
97     .state              = vmc_state,
98     .write              = vmc_write,
99     .read               = vmc_read,
100     .event              = vmc_event,
101     .flags              = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE,
102 };
103 
104 
105 static void vmc_register_interface(SpiceChardev *scd)
106 {
107     if (scd->active) {
108         return;
109     }
110     scd->sin.base.sif = &vmc_interface.base;
111     qemu_spice.add_interface(&scd->sin.base);
112     scd->active = true;
113     trace_spice_vmc_register_interface(scd);
114 }
115 
116 static void vmc_unregister_interface(SpiceChardev *scd)
117 {
118     if (!scd->active) {
119         return;
120     }
121     spice_server_remove_interface(&scd->sin.base);
122     scd->active = false;
123     trace_spice_vmc_unregister_interface(scd);
124 }
125 
126 static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
127 {
128     SpiceCharSource *src = (SpiceCharSource *)source;
129     Chardev *chr = CHARDEV(src->scd);
130 
131     *timeout = -1;
132 
133     if (!chr->be_open) {
134         return true;
135     }
136 
137     return !src->scd->blocked;
138 }
139 
140 static gboolean spice_char_source_check(GSource *source)
141 {
142     SpiceCharSource *src = (SpiceCharSource *)source;
143     Chardev *chr = CHARDEV(src->scd);
144 
145     if (!chr->be_open) {
146         return true;
147     }
148 
149     return !src->scd->blocked;
150 }
151 
152 static gboolean spice_char_source_dispatch(GSource *source,
153     GSourceFunc callback, gpointer user_data)
154 {
155     SpiceCharSource *src = (SpiceCharSource *)source;
156     Chardev *chr = CHARDEV(src->scd);
157     GIOFunc func = (GIOFunc)callback;
158     GIOCondition cond = chr->be_open ? G_IO_OUT : G_IO_HUP;
159 
160     return func(NULL, cond, user_data);
161 }
162 
163 static GSourceFuncs SpiceCharSourceFuncs = {
164     .prepare  = spice_char_source_prepare,
165     .check    = spice_char_source_check,
166     .dispatch = spice_char_source_dispatch,
167 };
168 
169 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
170 {
171     SpiceChardev *scd = SPICE_CHARDEV(chr);
172     SpiceCharSource *src;
173 
174     assert(cond & G_IO_OUT);
175 
176     src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs,
177                                           sizeof(SpiceCharSource));
178     src->scd = scd;
179 
180     return (GSource *)src;
181 }
182 
183 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
184 {
185     SpiceChardev *s = SPICE_CHARDEV(chr);
186     int read_bytes;
187 
188     assert(s->datalen == 0);
189 
190     if (!chr->be_open) {
191         trace_spice_chr_discard_write(len);
192         return len;
193     }
194 
195     s->datapos = buf;
196     s->datalen = len;
197     spice_server_char_device_wakeup(&s->sin);
198     read_bytes = len - s->datalen;
199     if (read_bytes != len) {
200         /* We'll get passed in the unconsumed data with the next call */
201         s->datalen = 0;
202         s->datapos = NULL;
203         s->blocked = true;
204     }
205     return read_bytes;
206 }
207 
208 static void char_spice_finalize(Object *obj)
209 {
210     SpiceChardev *s = SPICE_CHARDEV(obj);
211 
212     vmc_unregister_interface(s);
213 
214     g_free((char *)s->sin.subtype);
215     g_free((char *)s->sin.portname);
216 }
217 
218 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
219 {
220     SpiceChardev *s = SPICE_CHARDEV(chr);
221     if (fe_open) {
222         vmc_register_interface(s);
223     } else {
224         vmc_unregister_interface(s);
225     }
226 }
227 
228 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
229 {
230     SpiceChardev *s = SPICE_CHARDEV(chr);
231 
232     if (fe_open) {
233         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
234     } else {
235         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_CLOSED);
236     }
237 }
238 
239 static void spice_chr_accept_input(struct Chardev *chr)
240 {
241     SpiceChardev *s = SPICE_CHARDEV(chr);
242 
243     spice_server_char_device_wakeup(&s->sin);
244 }
245 
246 static void chr_open(Chardev *chr, const char *subtype)
247 {
248     SpiceChardev *s = SPICE_CHARDEV(chr);
249 
250     s->active = false;
251     s->sin.subtype = g_strdup(subtype);
252 }
253 
254 static void qemu_chr_open_spice_vmc(Chardev *chr,
255                                     ChardevBackend *backend,
256                                     bool *be_opened,
257                                     Error **errp)
258 {
259     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
260     const char *type = spicevmc->type;
261     const char **psubtype = spice_server_char_device_recognized_subtypes();
262 
263     for (; *psubtype != NULL; ++psubtype) {
264         if (strcmp(type, *psubtype) == 0) {
265             break;
266         }
267     }
268     if (*psubtype == NULL) {
269         char *subtypes = g_strjoinv(", ",
270             (gchar **)spice_server_char_device_recognized_subtypes());
271 
272         error_setg(errp, "unsupported type name: %s", type);
273         error_append_hint(errp, "allowed spice char type names: %s\n",
274                           subtypes);
275 
276         g_free(subtypes);
277         return;
278     }
279 
280     *be_opened = false;
281 #if SPICE_SERVER_VERSION < 0x000e02
282     /* Spice < 0.14.2 doesn't explicitly open smartcard chardev */
283     if (strcmp(type, "smartcard") == 0) {
284         *be_opened = true;
285     }
286 #endif
287     chr_open(chr, type);
288 }
289 
290 static void qemu_chr_open_spice_port(Chardev *chr,
291                                      ChardevBackend *backend,
292                                      bool *be_opened,
293                                      Error **errp)
294 {
295     ChardevSpicePort *spiceport = backend->u.spiceport.data;
296     const char *name = spiceport->fqdn;
297     SpiceChardev *s;
298 
299     if (name == NULL) {
300         error_setg(errp, "missing name parameter");
301         return;
302     }
303 
304     if (!using_spice) {
305         error_setg(errp, "spice not enabled");
306         return;
307     }
308 
309     chr_open(chr, "port");
310 
311     *be_opened = false;
312     s = SPICE_CHARDEV(chr);
313     s->sin.portname = g_strdup(name);
314 
315     vmc_register_interface(s);
316 }
317 
318 static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
319                                      Error **errp)
320 {
321     const char *name = qemu_opt_get(opts, "name");
322     ChardevSpiceChannel *spicevmc;
323 
324     if (name == NULL) {
325         error_setg(errp, "chardev: spice channel: no name given");
326         return;
327     }
328     backend->type = CHARDEV_BACKEND_KIND_SPICEVMC;
329     spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
330     qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
331     spicevmc->type = g_strdup(name);
332 }
333 
334 static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
335                                       Error **errp)
336 {
337     const char *name = qemu_opt_get(opts, "name");
338     ChardevSpicePort *spiceport;
339 
340     if (name == NULL) {
341         error_setg(errp, "chardev: spice port: no name given");
342         return;
343     }
344     backend->type = CHARDEV_BACKEND_KIND_SPICEPORT;
345     spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
346     qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
347     spiceport->fqdn = g_strdup(name);
348 }
349 
350 static void char_spice_class_init(ObjectClass *oc, void *data)
351 {
352     ChardevClass *cc = CHARDEV_CLASS(oc);
353 
354     cc->chr_write = spice_chr_write;
355     cc->chr_add_watch = spice_chr_add_watch;
356     cc->chr_accept_input = spice_chr_accept_input;
357 }
358 
359 static const TypeInfo char_spice_type_info = {
360     .name = TYPE_CHARDEV_SPICE,
361     .parent = TYPE_CHARDEV,
362     .instance_size = sizeof(SpiceChardev),
363     .instance_finalize = char_spice_finalize,
364     .class_init = char_spice_class_init,
365     .abstract = true,
366 };
367 module_obj(TYPE_CHARDEV_SPICE);
368 
369 static void char_spicevmc_class_init(ObjectClass *oc, void *data)
370 {
371     ChardevClass *cc = CHARDEV_CLASS(oc);
372 
373     cc->parse = qemu_chr_parse_spice_vmc;
374     cc->open = qemu_chr_open_spice_vmc;
375     cc->chr_set_fe_open = spice_vmc_set_fe_open;
376 }
377 
378 static const TypeInfo char_spicevmc_type_info = {
379     .name = TYPE_CHARDEV_SPICEVMC,
380     .parent = TYPE_CHARDEV_SPICE,
381     .class_init = char_spicevmc_class_init,
382 };
383 module_obj(TYPE_CHARDEV_SPICEVMC);
384 
385 static void char_spiceport_class_init(ObjectClass *oc, void *data)
386 {
387     ChardevClass *cc = CHARDEV_CLASS(oc);
388 
389     cc->parse = qemu_chr_parse_spice_port;
390     cc->open = qemu_chr_open_spice_port;
391     cc->chr_set_fe_open = spice_port_set_fe_open;
392 }
393 
394 static const TypeInfo char_spiceport_type_info = {
395     .name = TYPE_CHARDEV_SPICEPORT,
396     .parent = TYPE_CHARDEV_SPICE,
397     .class_init = char_spiceport_class_init,
398 };
399 module_obj(TYPE_CHARDEV_SPICEPORT);
400 
401 static void register_types(void)
402 {
403     type_register_static(&char_spice_type_info);
404     type_register_static(&char_spicevmc_type_info);
405     type_register_static(&char_spiceport_type_info);
406 }
407 
408 type_init(register_types);
409 
410 module_dep("ui-spice-core");
411