xref: /openbmc/qemu/chardev/spice.c (revision 6016b7b4)
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 #if SPICE_SERVER_VERSION >= 0x000c06
102     .flags              = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE,
103 #endif
104 };
105 
106 
107 static void vmc_register_interface(SpiceChardev *scd)
108 {
109     if (scd->active) {
110         return;
111     }
112     scd->sin.base.sif = &vmc_interface.base;
113     qemu_spice.add_interface(&scd->sin.base);
114     scd->active = true;
115     trace_spice_vmc_register_interface(scd);
116 }
117 
118 static void vmc_unregister_interface(SpiceChardev *scd)
119 {
120     if (!scd->active) {
121         return;
122     }
123     spice_server_remove_interface(&scd->sin.base);
124     scd->active = false;
125     trace_spice_vmc_unregister_interface(scd);
126 }
127 
128 static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
129 {
130     SpiceCharSource *src = (SpiceCharSource *)source;
131     Chardev *chr = CHARDEV(src->scd);
132 
133     *timeout = -1;
134 
135     if (!chr->be_open) {
136         return true;
137     }
138 
139     return !src->scd->blocked;
140 }
141 
142 static gboolean spice_char_source_check(GSource *source)
143 {
144     SpiceCharSource *src = (SpiceCharSource *)source;
145     Chardev *chr = CHARDEV(src->scd);
146 
147     if (!chr->be_open) {
148         return true;
149     }
150 
151     return !src->scd->blocked;
152 }
153 
154 static gboolean spice_char_source_dispatch(GSource *source,
155     GSourceFunc callback, gpointer user_data)
156 {
157     SpiceCharSource *src = (SpiceCharSource *)source;
158     Chardev *chr = CHARDEV(src->scd);
159     GIOFunc func = (GIOFunc)callback;
160     GIOCondition cond = chr->be_open ? G_IO_OUT : G_IO_HUP;
161 
162     return func(NULL, cond, user_data);
163 }
164 
165 static GSourceFuncs SpiceCharSourceFuncs = {
166     .prepare  = spice_char_source_prepare,
167     .check    = spice_char_source_check,
168     .dispatch = spice_char_source_dispatch,
169 };
170 
171 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
172 {
173     SpiceChardev *scd = SPICE_CHARDEV(chr);
174     SpiceCharSource *src;
175 
176     assert(cond & G_IO_OUT);
177 
178     src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs,
179                                           sizeof(SpiceCharSource));
180     src->scd = scd;
181 
182     return (GSource *)src;
183 }
184 
185 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
186 {
187     SpiceChardev *s = SPICE_CHARDEV(chr);
188     int read_bytes;
189 
190     assert(s->datalen == 0);
191 
192     if (!chr->be_open) {
193         trace_spice_chr_discard_write(len);
194         return len;
195     }
196 
197     s->datapos = buf;
198     s->datalen = len;
199     spice_server_char_device_wakeup(&s->sin);
200     read_bytes = len - s->datalen;
201     if (read_bytes != len) {
202         /* We'll get passed in the unconsumed data with the next call */
203         s->datalen = 0;
204         s->datapos = NULL;
205         s->blocked = true;
206     }
207     return read_bytes;
208 }
209 
210 static void char_spice_finalize(Object *obj)
211 {
212     SpiceChardev *s = SPICE_CHARDEV(obj);
213 
214     vmc_unregister_interface(s);
215 
216     g_free((char *)s->sin.subtype);
217     g_free((char *)s->sin.portname);
218 }
219 
220 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
221 {
222     SpiceChardev *s = SPICE_CHARDEV(chr);
223     if (fe_open) {
224         vmc_register_interface(s);
225     } else {
226         vmc_unregister_interface(s);
227     }
228 }
229 
230 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
231 {
232     SpiceChardev *s = SPICE_CHARDEV(chr);
233 
234     if (fe_open) {
235         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
236     } else {
237         spice_server_port_event(&s->sin, SPICE_PORT_EVENT_CLOSED);
238     }
239 }
240 
241 static void spice_chr_accept_input(struct Chardev *chr)
242 {
243     SpiceChardev *s = SPICE_CHARDEV(chr);
244 
245     spice_server_char_device_wakeup(&s->sin);
246 }
247 
248 static void chr_open(Chardev *chr, const char *subtype)
249 {
250     SpiceChardev *s = SPICE_CHARDEV(chr);
251 
252     s->active = false;
253     s->sin.subtype = g_strdup(subtype);
254 }
255 
256 static void qemu_chr_open_spice_vmc(Chardev *chr,
257                                     ChardevBackend *backend,
258                                     bool *be_opened,
259                                     Error **errp)
260 {
261     ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
262     const char *type = spicevmc->type;
263     const char **psubtype = spice_server_char_device_recognized_subtypes();
264 
265     for (; *psubtype != NULL; ++psubtype) {
266         if (strcmp(type, *psubtype) == 0) {
267             break;
268         }
269     }
270     if (*psubtype == NULL) {
271         char *subtypes = g_strjoinv(", ",
272             (gchar **)spice_server_char_device_recognized_subtypes());
273 
274         error_setg(errp, "unsupported type name: %s", type);
275         error_append_hint(errp, "allowed spice char type names: %s\n",
276                           subtypes);
277 
278         g_free(subtypes);
279         return;
280     }
281 
282     *be_opened = false;
283 #if SPICE_SERVER_VERSION < 0x000e02
284     /* Spice < 0.14.2 doesn't explicitly open smartcard chardev */
285     if (strcmp(type, "smartcard") == 0) {
286         *be_opened = true;
287     }
288 #endif
289     chr_open(chr, type);
290 }
291 
292 static void qemu_chr_open_spice_port(Chardev *chr,
293                                      ChardevBackend *backend,
294                                      bool *be_opened,
295                                      Error **errp)
296 {
297     ChardevSpicePort *spiceport = backend->u.spiceport.data;
298     const char *name = spiceport->fqdn;
299     SpiceChardev *s;
300 
301     if (name == NULL) {
302         error_setg(errp, "missing name parameter");
303         return;
304     }
305 
306     if (!using_spice) {
307         error_setg(errp, "spice not enabled");
308         return;
309     }
310 
311     chr_open(chr, "port");
312 
313     *be_opened = false;
314     s = SPICE_CHARDEV(chr);
315     s->sin.portname = g_strdup(name);
316 
317     vmc_register_interface(s);
318 }
319 
320 static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
321                                      Error **errp)
322 {
323     const char *name = qemu_opt_get(opts, "name");
324     ChardevSpiceChannel *spicevmc;
325 
326     if (name == NULL) {
327         error_setg(errp, "chardev: spice channel: no name given");
328         return;
329     }
330     backend->type = CHARDEV_BACKEND_KIND_SPICEVMC;
331     spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
332     qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
333     spicevmc->type = g_strdup(name);
334 }
335 
336 static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
337                                       Error **errp)
338 {
339     const char *name = qemu_opt_get(opts, "name");
340     ChardevSpicePort *spiceport;
341 
342     if (name == NULL) {
343         error_setg(errp, "chardev: spice port: no name given");
344         return;
345     }
346     backend->type = CHARDEV_BACKEND_KIND_SPICEPORT;
347     spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
348     qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
349     spiceport->fqdn = g_strdup(name);
350 }
351 
352 static void char_spice_class_init(ObjectClass *oc, void *data)
353 {
354     ChardevClass *cc = CHARDEV_CLASS(oc);
355 
356     cc->chr_write = spice_chr_write;
357     cc->chr_add_watch = spice_chr_add_watch;
358     cc->chr_accept_input = spice_chr_accept_input;
359 }
360 
361 static const TypeInfo char_spice_type_info = {
362     .name = TYPE_CHARDEV_SPICE,
363     .parent = TYPE_CHARDEV,
364     .instance_size = sizeof(SpiceChardev),
365     .instance_finalize = char_spice_finalize,
366     .class_init = char_spice_class_init,
367     .abstract = true,
368 };
369 module_obj(TYPE_CHARDEV_SPICE);
370 
371 static void char_spicevmc_class_init(ObjectClass *oc, void *data)
372 {
373     ChardevClass *cc = CHARDEV_CLASS(oc);
374 
375     cc->parse = qemu_chr_parse_spice_vmc;
376     cc->open = qemu_chr_open_spice_vmc;
377     cc->chr_set_fe_open = spice_vmc_set_fe_open;
378 }
379 
380 static const TypeInfo char_spicevmc_type_info = {
381     .name = TYPE_CHARDEV_SPICEVMC,
382     .parent = TYPE_CHARDEV_SPICE,
383     .class_init = char_spicevmc_class_init,
384 };
385 module_obj(TYPE_CHARDEV_SPICEVMC);
386 
387 static void char_spiceport_class_init(ObjectClass *oc, void *data)
388 {
389     ChardevClass *cc = CHARDEV_CLASS(oc);
390 
391     cc->parse = qemu_chr_parse_spice_port;
392     cc->open = qemu_chr_open_spice_port;
393     cc->chr_set_fe_open = spice_port_set_fe_open;
394 }
395 
396 static const TypeInfo char_spiceport_type_info = {
397     .name = TYPE_CHARDEV_SPICEPORT,
398     .parent = TYPE_CHARDEV_SPICE,
399     .class_init = char_spiceport_class_init,
400 };
401 module_obj(TYPE_CHARDEV_SPICEPORT);
402 
403 static void register_types(void)
404 {
405     type_register_static(&char_spice_type_info);
406     type_register_static(&char_spicevmc_type_info);
407     type_register_static(&char_spiceport_type_info);
408 }
409 
410 type_init(register_types);
411 
412 module_dep("ui-spice-core");
413