root / hw / virtio-serial-bus.c @ 43997225
History | View | Annotate | Download (27.1 kB)
1 | 98b19252 | Amit Shah | /*
|
---|---|---|---|
2 | 98b19252 | Amit Shah | * A bus for connecting virtio serial and console ports
|
3 | 98b19252 | Amit Shah | *
|
4 | 71c092e9 | Amit Shah | * Copyright (C) 2009, 2010 Red Hat, Inc.
|
5 | 98b19252 | Amit Shah | *
|
6 | 98b19252 | Amit Shah | * Author(s):
|
7 | 98b19252 | Amit Shah | * Amit Shah <amit.shah@redhat.com>
|
8 | 98b19252 | Amit Shah | *
|
9 | 98b19252 | Amit Shah | * Some earlier parts are:
|
10 | 98b19252 | Amit Shah | * Copyright IBM, Corp. 2008
|
11 | 98b19252 | Amit Shah | * authored by
|
12 | 98b19252 | Amit Shah | * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
13 | 98b19252 | Amit Shah | *
|
14 | 98b19252 | Amit Shah | * This work is licensed under the terms of the GNU GPL, version 2. See
|
15 | 98b19252 | Amit Shah | * the COPYING file in the top-level directory.
|
16 | 6b620ca3 | Paolo Bonzini | *
|
17 | 6b620ca3 | Paolo Bonzini | * Contributions after 2012-01-13 are licensed under the terms of the
|
18 | 6b620ca3 | Paolo Bonzini | * GNU GPL, version 2 or (at your option) any later version.
|
19 | 98b19252 | Amit Shah | */
|
20 | 98b19252 | Amit Shah | |
21 | e4d5639d | Amit Shah | #include "iov.h" |
22 | 98b19252 | Amit Shah | #include "monitor.h" |
23 | 98b19252 | Amit Shah | #include "qemu-queue.h" |
24 | 98b19252 | Amit Shah | #include "sysbus.h" |
25 | 49e3fdd7 | Amit Shah | #include "trace.h" |
26 | 98b19252 | Amit Shah | #include "virtio-serial.h" |
27 | 98b19252 | Amit Shah | |
28 | 98b19252 | Amit Shah | /* The virtio-serial bus on top of which the ports will ride as devices */
|
29 | 98b19252 | Amit Shah | struct VirtIOSerialBus {
|
30 | 98b19252 | Amit Shah | BusState qbus; |
31 | 98b19252 | Amit Shah | |
32 | 98b19252 | Amit Shah | /* This is the parent device that provides the bus for ports. */
|
33 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
34 | 98b19252 | Amit Shah | |
35 | 98b19252 | Amit Shah | /* The maximum number of ports that can ride on top of this bus */
|
36 | 98b19252 | Amit Shah | uint32_t max_nr_ports; |
37 | 98b19252 | Amit Shah | }; |
38 | 98b19252 | Amit Shah | |
39 | 98b19252 | Amit Shah | struct VirtIOSerial {
|
40 | 98b19252 | Amit Shah | VirtIODevice vdev; |
41 | 98b19252 | Amit Shah | |
42 | 98b19252 | Amit Shah | VirtQueue *c_ivq, *c_ovq; |
43 | 98b19252 | Amit Shah | /* Arrays of ivqs and ovqs: one per port */
|
44 | 98b19252 | Amit Shah | VirtQueue **ivqs, **ovqs; |
45 | 98b19252 | Amit Shah | |
46 | 5e52e5f9 | Markus Armbruster | VirtIOSerialBus bus; |
47 | 98b19252 | Amit Shah | |
48 | 8b53a865 | Amit Shah | DeviceState *qdev; |
49 | 8b53a865 | Amit Shah | |
50 | 98b19252 | Amit Shah | QTAILQ_HEAD(, VirtIOSerialPort) ports; |
51 | 055b889f | Amit Shah | |
52 | 055b889f | Amit Shah | /* bitmap for identifying active ports */
|
53 | 055b889f | Amit Shah | uint32_t *ports_map; |
54 | 055b889f | Amit Shah | |
55 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
56 | 98b19252 | Amit Shah | }; |
57 | 98b19252 | Amit Shah | |
58 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id)
|
59 | 98b19252 | Amit Shah | { |
60 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
61 | 98b19252 | Amit Shah | |
62 | 055b889f | Amit Shah | if (id == VIRTIO_CONSOLE_BAD_ID) {
|
63 | 055b889f | Amit Shah | return NULL; |
64 | 055b889f | Amit Shah | } |
65 | 055b889f | Amit Shah | |
66 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
67 | 98b19252 | Amit Shah | if (port->id == id)
|
68 | 98b19252 | Amit Shah | return port;
|
69 | 98b19252 | Amit Shah | } |
70 | 98b19252 | Amit Shah | return NULL; |
71 | 98b19252 | Amit Shah | } |
72 | 98b19252 | Amit Shah | |
73 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq)
|
74 | 98b19252 | Amit Shah | { |
75 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
76 | 98b19252 | Amit Shah | |
77 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
78 | 98b19252 | Amit Shah | if (port->ivq == vq || port->ovq == vq)
|
79 | 98b19252 | Amit Shah | return port;
|
80 | 98b19252 | Amit Shah | } |
81 | 98b19252 | Amit Shah | return NULL; |
82 | 98b19252 | Amit Shah | } |
83 | 98b19252 | Amit Shah | |
84 | 6663a195 | Amit Shah | static bool use_multiport(VirtIOSerial *vser) |
85 | 6663a195 | Amit Shah | { |
86 | 6663a195 | Amit Shah | return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); |
87 | 6663a195 | Amit Shah | } |
88 | 6663a195 | Amit Shah | |
89 | 98b19252 | Amit Shah | static size_t write_to_port(VirtIOSerialPort *port,
|
90 | 98b19252 | Amit Shah | const uint8_t *buf, size_t size)
|
91 | 98b19252 | Amit Shah | { |
92 | 98b19252 | Amit Shah | VirtQueueElement elem; |
93 | 98b19252 | Amit Shah | VirtQueue *vq; |
94 | e4d5639d | Amit Shah | size_t offset; |
95 | 98b19252 | Amit Shah | |
96 | 98b19252 | Amit Shah | vq = port->ivq; |
97 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
98 | 98b19252 | Amit Shah | return 0; |
99 | 98b19252 | Amit Shah | } |
100 | 98b19252 | Amit Shah | |
101 | e4d5639d | Amit Shah | offset = 0;
|
102 | 98b19252 | Amit Shah | while (offset < size) {
|
103 | e4d5639d | Amit Shah | size_t len; |
104 | 98b19252 | Amit Shah | |
105 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
106 | 98b19252 | Amit Shah | break;
|
107 | 98b19252 | Amit Shah | } |
108 | 98b19252 | Amit Shah | |
109 | e4d5639d | Amit Shah | len = iov_from_buf(elem.in_sg, elem.in_num, |
110 | 348e7b8d | Hannes Reinecke | buf + offset, 0, size - offset);
|
111 | e4d5639d | Amit Shah | offset += len; |
112 | 98b19252 | Amit Shah | |
113 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
114 | 98b19252 | Amit Shah | } |
115 | 98b19252 | Amit Shah | |
116 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
117 | 98b19252 | Amit Shah | return offset;
|
118 | 98b19252 | Amit Shah | } |
119 | 98b19252 | Amit Shah | |
120 | 6bff8656 | Amit Shah | static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) |
121 | a69c7600 | Amit Shah | { |
122 | a69c7600 | Amit Shah | VirtQueueElement elem; |
123 | a69c7600 | Amit Shah | |
124 | 7185f931 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
125 | 7185f931 | Amit Shah | return;
|
126 | 7185f931 | Amit Shah | } |
127 | 6bff8656 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
128 | 6bff8656 | Amit Shah | virtqueue_push(vq, &elem, 0);
|
129 | 6bff8656 | Amit Shah | } |
130 | 6bff8656 | Amit Shah | virtio_notify(vdev, vq); |
131 | 6bff8656 | Amit Shah | } |
132 | 6bff8656 | Amit Shah | |
133 | 9ed7b059 | Amit Shah | static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, |
134 | 6bff8656 | Amit Shah | VirtIODevice *vdev) |
135 | a69c7600 | Amit Shah | { |
136 | f82e35e3 | Anthony Liguori | VirtIOSerialPortClass *vsc; |
137 | a15bb0d6 | Markus Armbruster | |
138 | 6bff8656 | Amit Shah | assert(port); |
139 | fd11a78b | Amit Shah | assert(virtio_queue_ready(vq)); |
140 | a69c7600 | Amit Shah | |
141 | f82e35e3 | Anthony Liguori | vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); |
142 | a15bb0d6 | Markus Armbruster | |
143 | f1925dff | Amit Shah | while (!port->throttled) {
|
144 | 471344db | Amit Shah | unsigned int i; |
145 | a69c7600 | Amit Shah | |
146 | f1925dff | Amit Shah | /* Pop an elem only if we haven't left off a previous one mid-way */
|
147 | f1925dff | Amit Shah | if (!port->elem.out_num) {
|
148 | f1925dff | Amit Shah | if (!virtqueue_pop(vq, &port->elem)) {
|
149 | f1925dff | Amit Shah | break;
|
150 | f1925dff | Amit Shah | } |
151 | f1925dff | Amit Shah | port->iov_idx = 0;
|
152 | f1925dff | Amit Shah | port->iov_offset = 0;
|
153 | f1925dff | Amit Shah | } |
154 | a69c7600 | Amit Shah | |
155 | f1925dff | Amit Shah | for (i = port->iov_idx; i < port->elem.out_num; i++) {
|
156 | f1925dff | Amit Shah | size_t buf_size; |
157 | f1925dff | Amit Shah | ssize_t ret; |
158 | f1925dff | Amit Shah | |
159 | f1925dff | Amit Shah | buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; |
160 | f82e35e3 | Anthony Liguori | ret = vsc->have_data(port, |
161 | a15bb0d6 | Markus Armbruster | port->elem.out_sg[i].iov_base |
162 | a15bb0d6 | Markus Armbruster | + port->iov_offset, |
163 | a15bb0d6 | Markus Armbruster | buf_size); |
164 | f1925dff | Amit Shah | if (ret < 0 && ret != -EAGAIN) { |
165 | f1925dff | Amit Shah | /* We don't handle any other type of errors here */
|
166 | f1925dff | Amit Shah | abort(); |
167 | f1925dff | Amit Shah | } |
168 | f1925dff | Amit Shah | if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { |
169 | ed8e5a85 | Christian Borntraeger | /*
|
170 | ed8e5a85 | Christian Borntraeger | * this is a temporary check until chardevs can signal to
|
171 | ed8e5a85 | Christian Borntraeger | * frontends that they are writable again. This prevents
|
172 | ed8e5a85 | Christian Borntraeger | * the console from going into throttled mode (forever)
|
173 | ed8e5a85 | Christian Borntraeger | * if virtio-console is connected to a pty without a
|
174 | ed8e5a85 | Christian Borntraeger | * listener. Otherwise the guest spins forever.
|
175 | ed8e5a85 | Christian Borntraeger | * We can revert this if
|
176 | ed8e5a85 | Christian Borntraeger | * 1: chardevs can notify frondends
|
177 | ed8e5a85 | Christian Borntraeger | * 2: the guest driver does not spin in these cases
|
178 | ed8e5a85 | Christian Borntraeger | */
|
179 | f82e35e3 | Anthony Liguori | if (!vsc->is_console) {
|
180 | ed8e5a85 | Christian Borntraeger | virtio_serial_throttle_port(port, true);
|
181 | ed8e5a85 | Christian Borntraeger | } |
182 | f1925dff | Amit Shah | port->iov_idx = i; |
183 | f1925dff | Amit Shah | if (ret > 0) { |
184 | f1925dff | Amit Shah | port->iov_offset += ret; |
185 | f1925dff | Amit Shah | } |
186 | f1925dff | Amit Shah | break;
|
187 | f1925dff | Amit Shah | } |
188 | f1925dff | Amit Shah | port->iov_offset = 0;
|
189 | a69c7600 | Amit Shah | } |
190 | f1925dff | Amit Shah | if (port->throttled) {
|
191 | f1925dff | Amit Shah | break;
|
192 | f1925dff | Amit Shah | } |
193 | f1925dff | Amit Shah | virtqueue_push(vq, &port->elem, 0);
|
194 | f1925dff | Amit Shah | port->elem.out_num = 0;
|
195 | a69c7600 | Amit Shah | } |
196 | a69c7600 | Amit Shah | virtio_notify(vdev, vq); |
197 | a69c7600 | Amit Shah | } |
198 | a69c7600 | Amit Shah | |
199 | 6bff8656 | Amit Shah | static void flush_queued_data(VirtIOSerialPort *port) |
200 | 9ed7b059 | Amit Shah | { |
201 | a1c59752 | Amit Shah | assert(port); |
202 | 9ed7b059 | Amit Shah | |
203 | 6b611d3a | Amit Shah | if (!virtio_queue_ready(port->ovq)) {
|
204 | 6b611d3a | Amit Shah | return;
|
205 | 6b611d3a | Amit Shah | } |
206 | 6bff8656 | Amit Shah | do_flush_queued_data(port, port->ovq, &port->vser->vdev); |
207 | 9ed7b059 | Amit Shah | } |
208 | 9ed7b059 | Amit Shah | |
209 | 98b19252 | Amit Shah | static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) |
210 | 98b19252 | Amit Shah | { |
211 | 98b19252 | Amit Shah | VirtQueueElement elem; |
212 | 98b19252 | Amit Shah | VirtQueue *vq; |
213 | 98b19252 | Amit Shah | struct virtio_console_control *cpkt;
|
214 | 98b19252 | Amit Shah | |
215 | 98b19252 | Amit Shah | vq = port->vser->c_ivq; |
216 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
217 | 98b19252 | Amit Shah | return 0; |
218 | 98b19252 | Amit Shah | } |
219 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
220 | 98b19252 | Amit Shah | return 0; |
221 | 98b19252 | Amit Shah | } |
222 | 98b19252 | Amit Shah | |
223 | 98b19252 | Amit Shah | cpkt = (struct virtio_console_control *)buf;
|
224 | 98b19252 | Amit Shah | stl_p(&cpkt->id, port->id); |
225 | 98b19252 | Amit Shah | memcpy(elem.in_sg[0].iov_base, buf, len);
|
226 | 98b19252 | Amit Shah | |
227 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
228 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
229 | 98b19252 | Amit Shah | return len;
|
230 | 98b19252 | Amit Shah | } |
231 | 98b19252 | Amit Shah | |
232 | 98b19252 | Amit Shah | static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
|
233 | 98b19252 | Amit Shah | uint16_t value) |
234 | 98b19252 | Amit Shah | { |
235 | 98b19252 | Amit Shah | struct virtio_console_control cpkt;
|
236 | 98b19252 | Amit Shah | |
237 | 98b19252 | Amit Shah | stw_p(&cpkt.event, event); |
238 | 98b19252 | Amit Shah | stw_p(&cpkt.value, value); |
239 | 98b19252 | Amit Shah | |
240 | 49e3fdd7 | Amit Shah | trace_virtio_serial_send_control_event(port->id, event, value); |
241 | 98b19252 | Amit Shah | return send_control_msg(port, &cpkt, sizeof(cpkt)); |
242 | 98b19252 | Amit Shah | } |
243 | 98b19252 | Amit Shah | |
244 | 98b19252 | Amit Shah | /* Functions for use inside qemu to open and read from/write to ports */
|
245 | 98b19252 | Amit Shah | int virtio_serial_open(VirtIOSerialPort *port)
|
246 | 98b19252 | Amit Shah | { |
247 | 6663a195 | Amit Shah | /* Don't allow opening an already-open port */
|
248 | 6663a195 | Amit Shah | if (port->host_connected) {
|
249 | 6663a195 | Amit Shah | return 0; |
250 | 6663a195 | Amit Shah | } |
251 | 6663a195 | Amit Shah | /* Send port open notification to the guest */
|
252 | 6663a195 | Amit Shah | port->host_connected = true;
|
253 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
254 | 6663a195 | Amit Shah | |
255 | 98b19252 | Amit Shah | return 0; |
256 | 98b19252 | Amit Shah | } |
257 | 98b19252 | Amit Shah | |
258 | 98b19252 | Amit Shah | int virtio_serial_close(VirtIOSerialPort *port)
|
259 | 98b19252 | Amit Shah | { |
260 | 6663a195 | Amit Shah | port->host_connected = false;
|
261 | 9ed7b059 | Amit Shah | /*
|
262 | 9ed7b059 | Amit Shah | * If there's any data the guest sent which the app didn't
|
263 | 9ed7b059 | Amit Shah | * consume, reset the throttling flag and discard the data.
|
264 | 9ed7b059 | Amit Shah | */
|
265 | 9ed7b059 | Amit Shah | port->throttled = false;
|
266 | 6bff8656 | Amit Shah | discard_vq_data(port->ovq, &port->vser->vdev); |
267 | 9ed7b059 | Amit Shah | |
268 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
|
269 | 6663a195 | Amit Shah | |
270 | 98b19252 | Amit Shah | return 0; |
271 | 98b19252 | Amit Shah | } |
272 | 98b19252 | Amit Shah | |
273 | 98b19252 | Amit Shah | /* Individual ports/apps call this function to write to the guest. */
|
274 | 98b19252 | Amit Shah | ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
|
275 | 98b19252 | Amit Shah | size_t size) |
276 | 98b19252 | Amit Shah | { |
277 | 6663a195 | Amit Shah | if (!port || !port->host_connected || !port->guest_connected) {
|
278 | 6663a195 | Amit Shah | return 0; |
279 | 6663a195 | Amit Shah | } |
280 | 98b19252 | Amit Shah | return write_to_port(port, buf, size);
|
281 | 98b19252 | Amit Shah | } |
282 | 98b19252 | Amit Shah | |
283 | 98b19252 | Amit Shah | /*
|
284 | 98b19252 | Amit Shah | * Readiness of the guest to accept data on a port.
|
285 | 98b19252 | Amit Shah | * Returns max. data the guest can receive
|
286 | 98b19252 | Amit Shah | */
|
287 | 98b19252 | Amit Shah | size_t virtio_serial_guest_ready(VirtIOSerialPort *port) |
288 | 98b19252 | Amit Shah | { |
289 | 98b19252 | Amit Shah | VirtQueue *vq = port->ivq; |
290 | 98b19252 | Amit Shah | |
291 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq) ||
|
292 | 98b19252 | Amit Shah | !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
293 | 98b19252 | Amit Shah | virtio_queue_empty(vq)) { |
294 | 98b19252 | Amit Shah | return 0; |
295 | 98b19252 | Amit Shah | } |
296 | 6663a195 | Amit Shah | if (use_multiport(port->vser) && !port->guest_connected) {
|
297 | 6663a195 | Amit Shah | return 0; |
298 | 6663a195 | Amit Shah | } |
299 | 98b19252 | Amit Shah | |
300 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 4096, 0)) { |
301 | 98b19252 | Amit Shah | return 4096; |
302 | 98b19252 | Amit Shah | } |
303 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 1, 0)) { |
304 | 98b19252 | Amit Shah | return 1; |
305 | 98b19252 | Amit Shah | } |
306 | 98b19252 | Amit Shah | return 0; |
307 | 98b19252 | Amit Shah | } |
308 | 98b19252 | Amit Shah | |
309 | 199646d8 | Alon Levy | static void flush_queued_data_bh(void *opaque) |
310 | 199646d8 | Alon Levy | { |
311 | 199646d8 | Alon Levy | VirtIOSerialPort *port = opaque; |
312 | 199646d8 | Alon Levy | |
313 | 199646d8 | Alon Levy | flush_queued_data(port); |
314 | 199646d8 | Alon Levy | } |
315 | 199646d8 | Alon Levy | |
316 | 9ed7b059 | Amit Shah | void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) |
317 | 9ed7b059 | Amit Shah | { |
318 | 9ed7b059 | Amit Shah | if (!port) {
|
319 | 9ed7b059 | Amit Shah | return;
|
320 | 9ed7b059 | Amit Shah | } |
321 | 9ed7b059 | Amit Shah | |
322 | 49e3fdd7 | Amit Shah | trace_virtio_serial_throttle_port(port->id, throttle); |
323 | 9ed7b059 | Amit Shah | port->throttled = throttle; |
324 | 9ed7b059 | Amit Shah | if (throttle) {
|
325 | 9ed7b059 | Amit Shah | return;
|
326 | 9ed7b059 | Amit Shah | } |
327 | 199646d8 | Alon Levy | qemu_bh_schedule(port->bh); |
328 | 9ed7b059 | Amit Shah | } |
329 | 9ed7b059 | Amit Shah | |
330 | 98b19252 | Amit Shah | /* Guest wants to notify us of some event */
|
331 | e61da14d | Amit Shah | static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) |
332 | 98b19252 | Amit Shah | { |
333 | 98b19252 | Amit Shah | struct VirtIOSerialPort *port;
|
334 | f82e35e3 | Anthony Liguori | VirtIOSerialPortClass *vsc; |
335 | 98b19252 | Amit Shah | struct virtio_console_control cpkt, *gcpkt;
|
336 | 160600fd | Amit Shah | uint8_t *buffer; |
337 | 160600fd | Amit Shah | size_t buffer_len; |
338 | 98b19252 | Amit Shah | |
339 | 98b19252 | Amit Shah | gcpkt = buf; |
340 | 98b19252 | Amit Shah | |
341 | e61da14d | Amit Shah | if (len < sizeof(cpkt)) { |
342 | e61da14d | Amit Shah | /* The guest sent an invalid control packet */
|
343 | e61da14d | Amit Shah | return;
|
344 | e61da14d | Amit Shah | } |
345 | e61da14d | Amit Shah | |
346 | 98b19252 | Amit Shah | cpkt.event = lduw_p(&gcpkt->event); |
347 | 98b19252 | Amit Shah | cpkt.value = lduw_p(&gcpkt->value); |
348 | 98b19252 | Amit Shah | |
349 | 49e3fdd7 | Amit Shah | trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); |
350 | 49e3fdd7 | Amit Shah | |
351 | d2e4d08b | Luiz Capitulino | if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) {
|
352 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
353 | 6daf194d | Markus Armbruster | error_report("virtio-serial-bus: Guest failure in adding device %s",
|
354 | 5e52e5f9 | Markus Armbruster | vser->bus.qbus.name); |
355 | d2e4d08b | Luiz Capitulino | return;
|
356 | 4048c7c3 | Amit Shah | } |
357 | 055b889f | Amit Shah | /*
|
358 | 055b889f | Amit Shah | * The device is up, we can now tell the device about all the
|
359 | 055b889f | Amit Shah | * ports we have here.
|
360 | 055b889f | Amit Shah | */
|
361 | 055b889f | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
362 | 055b889f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
|
363 | 055b889f | Amit Shah | } |
364 | d2e4d08b | Luiz Capitulino | return;
|
365 | d2e4d08b | Luiz Capitulino | } |
366 | 055b889f | Amit Shah | |
367 | d2e4d08b | Luiz Capitulino | port = find_port_by_id(vser, ldl_p(&gcpkt->id)); |
368 | d2e4d08b | Luiz Capitulino | if (!port) {
|
369 | 95c9cde2 | Amit Shah | error_report("virtio-serial-bus: Unexpected port id %u for device %s",
|
370 | d2e4d08b | Luiz Capitulino | ldl_p(&gcpkt->id), vser->bus.qbus.name); |
371 | d2e4d08b | Luiz Capitulino | return;
|
372 | d2e4d08b | Luiz Capitulino | } |
373 | d2e4d08b | Luiz Capitulino | |
374 | 49e3fdd7 | Amit Shah | trace_virtio_serial_handle_control_message_port(port->id); |
375 | 49e3fdd7 | Amit Shah | |
376 | f82e35e3 | Anthony Liguori | vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); |
377 | d2e4d08b | Luiz Capitulino | |
378 | d2e4d08b | Luiz Capitulino | switch(cpkt.event) {
|
379 | 98b19252 | Amit Shah | case VIRTIO_CONSOLE_PORT_READY:
|
380 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
381 | 6daf194d | Markus Armbruster | error_report("virtio-serial-bus: Guest failure in adding port %u for device %s",
|
382 | 5e52e5f9 | Markus Armbruster | port->id, vser->bus.qbus.name); |
383 | 4048c7c3 | Amit Shah | break;
|
384 | 4048c7c3 | Amit Shah | } |
385 | 98b19252 | Amit Shah | /*
|
386 | 98b19252 | Amit Shah | * Now that we know the guest asked for the port name, we're
|
387 | 98b19252 | Amit Shah | * sure the guest has initialised whatever state is necessary
|
388 | 98b19252 | Amit Shah | * for this port. Now's a good time to let the guest know if
|
389 | 98b19252 | Amit Shah | * this port is a console port so that the guest can hook it
|
390 | 98b19252 | Amit Shah | * up to hvc.
|
391 | 98b19252 | Amit Shah | */
|
392 | f82e35e3 | Anthony Liguori | if (vsc->is_console) {
|
393 | 98b19252 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
|
394 | 98b19252 | Amit Shah | } |
395 | 6663a195 | Amit Shah | |
396 | 160600fd | Amit Shah | if (port->name) {
|
397 | 160600fd | Amit Shah | stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); |
398 | 160600fd | Amit Shah | stw_p(&cpkt.value, 1);
|
399 | 160600fd | Amit Shah | |
400 | 160600fd | Amit Shah | buffer_len = sizeof(cpkt) + strlen(port->name) + 1; |
401 | 7267c094 | Anthony Liguori | buffer = g_malloc(buffer_len); |
402 | 160600fd | Amit Shah | |
403 | 160600fd | Amit Shah | memcpy(buffer, &cpkt, sizeof(cpkt));
|
404 | 160600fd | Amit Shah | memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
|
405 | 160600fd | Amit Shah | buffer[buffer_len - 1] = 0; |
406 | 160600fd | Amit Shah | |
407 | 160600fd | Amit Shah | send_control_msg(port, buffer, buffer_len); |
408 | 7267c094 | Anthony Liguori | g_free(buffer); |
409 | 160600fd | Amit Shah | } |
410 | 160600fd | Amit Shah | |
411 | 6663a195 | Amit Shah | if (port->host_connected) {
|
412 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
413 | 6663a195 | Amit Shah | } |
414 | 6663a195 | Amit Shah | |
415 | 98b19252 | Amit Shah | /*
|
416 | 98b19252 | Amit Shah | * When the guest has asked us for this information it means
|
417 | 98b19252 | Amit Shah | * the guest is all setup and has its virtqueues
|
418 | 98b19252 | Amit Shah | * initialised. If some app is interested in knowing about
|
419 | 98b19252 | Amit Shah | * this event, let it know.
|
420 | 98b19252 | Amit Shah | */
|
421 | f82e35e3 | Anthony Liguori | if (vsc->guest_ready) {
|
422 | f82e35e3 | Anthony Liguori | vsc->guest_ready(port); |
423 | 98b19252 | Amit Shah | } |
424 | 98b19252 | Amit Shah | break;
|
425 | 6663a195 | Amit Shah | |
426 | 6663a195 | Amit Shah | case VIRTIO_CONSOLE_PORT_OPEN:
|
427 | 6663a195 | Amit Shah | port->guest_connected = cpkt.value; |
428 | f82e35e3 | Anthony Liguori | if (cpkt.value && vsc->guest_open) {
|
429 | 6663a195 | Amit Shah | /* Send the guest opened notification if an app is interested */
|
430 | f82e35e3 | Anthony Liguori | vsc->guest_open(port); |
431 | 6663a195 | Amit Shah | } |
432 | 6663a195 | Amit Shah | |
433 | f82e35e3 | Anthony Liguori | if (!cpkt.value && vsc->guest_close) {
|
434 | 6663a195 | Amit Shah | /* Send the guest closed notification if an app is interested */
|
435 | f82e35e3 | Anthony Liguori | vsc->guest_close(port); |
436 | 6663a195 | Amit Shah | } |
437 | 6663a195 | Amit Shah | break;
|
438 | 98b19252 | Amit Shah | } |
439 | 98b19252 | Amit Shah | } |
440 | 98b19252 | Amit Shah | |
441 | 98b19252 | Amit Shah | static void control_in(VirtIODevice *vdev, VirtQueue *vq) |
442 | 98b19252 | Amit Shah | { |
443 | 98b19252 | Amit Shah | } |
444 | 98b19252 | Amit Shah | |
445 | 98b19252 | Amit Shah | static void control_out(VirtIODevice *vdev, VirtQueue *vq) |
446 | 98b19252 | Amit Shah | { |
447 | 98b19252 | Amit Shah | VirtQueueElement elem; |
448 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
449 | e61da14d | Amit Shah | uint8_t *buf; |
450 | e61da14d | Amit Shah | size_t len; |
451 | 98b19252 | Amit Shah | |
452 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
453 | 98b19252 | Amit Shah | |
454 | e61da14d | Amit Shah | len = 0;
|
455 | e61da14d | Amit Shah | buf = NULL;
|
456 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
457 | e61da14d | Amit Shah | size_t cur_len, copied; |
458 | e61da14d | Amit Shah | |
459 | e61da14d | Amit Shah | cur_len = iov_size(elem.out_sg, elem.out_num); |
460 | e61da14d | Amit Shah | /*
|
461 | e61da14d | Amit Shah | * Allocate a new buf only if we didn't have one previously or
|
462 | e61da14d | Amit Shah | * if the size of the buf differs
|
463 | e61da14d | Amit Shah | */
|
464 | e61da14d | Amit Shah | if (cur_len > len) {
|
465 | 7267c094 | Anthony Liguori | g_free(buf); |
466 | e61da14d | Amit Shah | |
467 | 7267c094 | Anthony Liguori | buf = g_malloc(cur_len); |
468 | e61da14d | Amit Shah | len = cur_len; |
469 | e61da14d | Amit Shah | } |
470 | e61da14d | Amit Shah | copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len);
|
471 | e61da14d | Amit Shah | |
472 | e61da14d | Amit Shah | handle_control_message(vser, buf, copied); |
473 | 1e4476aa | Amit Shah | virtqueue_push(vq, &elem, 0);
|
474 | 98b19252 | Amit Shah | } |
475 | 7267c094 | Anthony Liguori | g_free(buf); |
476 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
477 | 98b19252 | Amit Shah | } |
478 | 98b19252 | Amit Shah | |
479 | 98b19252 | Amit Shah | /* Guest wrote something to some port. */
|
480 | 98b19252 | Amit Shah | static void handle_output(VirtIODevice *vdev, VirtQueue *vq) |
481 | 98b19252 | Amit Shah | { |
482 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
483 | a69c7600 | Amit Shah | VirtIOSerialPort *port; |
484 | 98b19252 | Amit Shah | |
485 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
486 | a69c7600 | Amit Shah | port = find_port_by_vq(vser, vq); |
487 | 98b19252 | Amit Shah | |
488 | 03ecd2c8 | Amit Shah | if (!port || !port->host_connected) {
|
489 | 6bff8656 | Amit Shah | discard_vq_data(vq, vdev); |
490 | 6bff8656 | Amit Shah | return;
|
491 | 6bff8656 | Amit Shah | } |
492 | e9b382b0 | Amit Shah | |
493 | e9b382b0 | Amit Shah | if (!port->throttled) {
|
494 | e9b382b0 | Amit Shah | do_flush_queued_data(port, vq, vdev); |
495 | 9ed7b059 | Amit Shah | return;
|
496 | 9ed7b059 | Amit Shah | } |
497 | 98b19252 | Amit Shah | } |
498 | 98b19252 | Amit Shah | |
499 | 98b19252 | Amit Shah | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
500 | 98b19252 | Amit Shah | { |
501 | 98b19252 | Amit Shah | } |
502 | 98b19252 | Amit Shah | |
503 | 98b19252 | Amit Shah | static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
|
504 | 98b19252 | Amit Shah | { |
505 | 306eb457 | Amit Shah | VirtIOSerial *vser; |
506 | 306eb457 | Amit Shah | |
507 | 306eb457 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
508 | 306eb457 | Amit Shah | |
509 | 5e52e5f9 | Markus Armbruster | if (vser->bus.max_nr_ports > 1) { |
510 | ee4d45be | Michael S. Tsirkin | features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
|
511 | ee4d45be | Michael S. Tsirkin | } |
512 | 98b19252 | Amit Shah | return features;
|
513 | 98b19252 | Amit Shah | } |
514 | 98b19252 | Amit Shah | |
515 | 98b19252 | Amit Shah | /* Guest requested config info */
|
516 | 98b19252 | Amit Shah | static void get_config(VirtIODevice *vdev, uint8_t *config_data) |
517 | 98b19252 | Amit Shah | { |
518 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
519 | 98b19252 | Amit Shah | |
520 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
521 | 98b19252 | Amit Shah | memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); |
522 | 98b19252 | Amit Shah | } |
523 | 98b19252 | Amit Shah | |
524 | 98b19252 | Amit Shah | static void set_config(VirtIODevice *vdev, const uint8_t *config_data) |
525 | 98b19252 | Amit Shah | { |
526 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
527 | 98b19252 | Amit Shah | |
528 | 98b19252 | Amit Shah | memcpy(&config, config_data, sizeof(config));
|
529 | 98b19252 | Amit Shah | } |
530 | 98b19252 | Amit Shah | |
531 | 43997225 | Amit Shah | static void guest_reset(VirtIOSerial *vser) |
532 | 43997225 | Amit Shah | { |
533 | 43997225 | Amit Shah | VirtIOSerialPort *port; |
534 | 43997225 | Amit Shah | VirtIOSerialPortClass *vsc; |
535 | 43997225 | Amit Shah | |
536 | 43997225 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
537 | 43997225 | Amit Shah | vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); |
538 | 43997225 | Amit Shah | if (port->guest_connected) {
|
539 | 43997225 | Amit Shah | port->guest_connected = false;
|
540 | 43997225 | Amit Shah | |
541 | 43997225 | Amit Shah | if (vsc->guest_close)
|
542 | 43997225 | Amit Shah | vsc->guest_close(port); |
543 | 43997225 | Amit Shah | } |
544 | 43997225 | Amit Shah | } |
545 | 43997225 | Amit Shah | } |
546 | 43997225 | Amit Shah | |
547 | 62a9fbf7 | Alon Levy | static void set_status(VirtIODevice *vdev, uint8_t status) |
548 | 62a9fbf7 | Alon Levy | { |
549 | 62a9fbf7 | Alon Levy | VirtIOSerial *vser; |
550 | 62a9fbf7 | Alon Levy | VirtIOSerialPort *port; |
551 | 62a9fbf7 | Alon Levy | |
552 | 62a9fbf7 | Alon Levy | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
553 | 62a9fbf7 | Alon Levy | port = find_port_by_id(vser, 0);
|
554 | 62a9fbf7 | Alon Levy | |
555 | 62a9fbf7 | Alon Levy | if (port && !use_multiport(port->vser)
|
556 | 62a9fbf7 | Alon Levy | && (status & VIRTIO_CONFIG_S_DRIVER_OK)) { |
557 | 62a9fbf7 | Alon Levy | /*
|
558 | 62a9fbf7 | Alon Levy | * Non-multiport guests won't be able to tell us guest
|
559 | 62a9fbf7 | Alon Levy | * open/close status. Such guests can only have a port at id
|
560 | 62a9fbf7 | Alon Levy | * 0, so set guest_connected for such ports as soon as guest
|
561 | 62a9fbf7 | Alon Levy | * is up.
|
562 | 62a9fbf7 | Alon Levy | */
|
563 | 62a9fbf7 | Alon Levy | port->guest_connected = true;
|
564 | 62a9fbf7 | Alon Levy | } |
565 | 43997225 | Amit Shah | if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
566 | 43997225 | Amit Shah | guest_reset(vser); |
567 | 43997225 | Amit Shah | } |
568 | 43997225 | Amit Shah | } |
569 | 43997225 | Amit Shah | |
570 | 43997225 | Amit Shah | static void vser_reset(VirtIODevice *vdev) |
571 | 43997225 | Amit Shah | { |
572 | 43997225 | Amit Shah | VirtIOSerial *vser; |
573 | 43997225 | Amit Shah | |
574 | 43997225 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
575 | 43997225 | Amit Shah | guest_reset(vser); |
576 | 62a9fbf7 | Alon Levy | } |
577 | 62a9fbf7 | Alon Levy | |
578 | 98b19252 | Amit Shah | static void virtio_serial_save(QEMUFile *f, void *opaque) |
579 | 98b19252 | Amit Shah | { |
580 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
581 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
582 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
583 | 5c1c9bb2 | Alexey Kardashevskiy | unsigned int i, max_nr_ports; |
584 | 98b19252 | Amit Shah | |
585 | 98b19252 | Amit Shah | /* The virtio device */
|
586 | 98b19252 | Amit Shah | virtio_save(&s->vdev, f); |
587 | 98b19252 | Amit Shah | |
588 | 98b19252 | Amit Shah | /* The config space */
|
589 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.cols); |
590 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.rows); |
591 | 6663a195 | Amit Shah | |
592 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->config.max_nr_ports); |
593 | 055b889f | Amit Shah | |
594 | 055b889f | Amit Shah | /* The ports map */
|
595 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(s->config.max_nr_ports); |
596 | 5c1c9bb2 | Alexey Kardashevskiy | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
597 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->ports_map[i]); |
598 | 055b889f | Amit Shah | } |
599 | 6663a195 | Amit Shah | |
600 | 055b889f | Amit Shah | /* Ports */
|
601 | e245795b | Amit Shah | |
602 | 6663a195 | Amit Shah | nr_active_ports = 0;
|
603 | e245795b | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
604 | 6663a195 | Amit Shah | nr_active_ports++; |
605 | e245795b | Amit Shah | } |
606 | 6663a195 | Amit Shah | |
607 | 6663a195 | Amit Shah | qemu_put_be32s(f, &nr_active_ports); |
608 | 6663a195 | Amit Shah | |
609 | 6663a195 | Amit Shah | /*
|
610 | 6663a195 | Amit Shah | * Items in struct VirtIOSerialPort.
|
611 | 6663a195 | Amit Shah | */
|
612 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
613 | 37f95bf3 | Amit Shah | uint32_t elem_popped; |
614 | 37f95bf3 | Amit Shah | |
615 | 6663a195 | Amit Shah | qemu_put_be32s(f, &port->id); |
616 | 6663a195 | Amit Shah | qemu_put_byte(f, port->guest_connected); |
617 | 31abe21f | Amit Shah | qemu_put_byte(f, port->host_connected); |
618 | 37f95bf3 | Amit Shah | |
619 | 37f95bf3 | Amit Shah | elem_popped = 0;
|
620 | 37f95bf3 | Amit Shah | if (port->elem.out_num) {
|
621 | 37f95bf3 | Amit Shah | elem_popped = 1;
|
622 | 37f95bf3 | Amit Shah | } |
623 | 37f95bf3 | Amit Shah | qemu_put_be32s(f, &elem_popped); |
624 | 37f95bf3 | Amit Shah | if (elem_popped) {
|
625 | 37f95bf3 | Amit Shah | qemu_put_be32s(f, &port->iov_idx); |
626 | 37f95bf3 | Amit Shah | qemu_put_be64s(f, &port->iov_offset); |
627 | 37f95bf3 | Amit Shah | |
628 | 37f95bf3 | Amit Shah | qemu_put_buffer(f, (unsigned char *)&port->elem, |
629 | 37f95bf3 | Amit Shah | sizeof(port->elem));
|
630 | 37f95bf3 | Amit Shah | } |
631 | 6663a195 | Amit Shah | } |
632 | 98b19252 | Amit Shah | } |
633 | 98b19252 | Amit Shah | |
634 | 98b19252 | Amit Shah | static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) |
635 | 98b19252 | Amit Shah | { |
636 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
637 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
638 | f83ccb3e | Markus Armbruster | uint32_t max_nr_ports, nr_active_ports, ports_map; |
639 | 6663a195 | Amit Shah | unsigned int i; |
640 | 98b19252 | Amit Shah | |
641 | 37f95bf3 | Amit Shah | if (version_id > 3) { |
642 | 98b19252 | Amit Shah | return -EINVAL;
|
643 | 98b19252 | Amit Shah | } |
644 | 6663a195 | Amit Shah | |
645 | 98b19252 | Amit Shah | /* The virtio device */
|
646 | 98b19252 | Amit Shah | virtio_load(&s->vdev, f); |
647 | 98b19252 | Amit Shah | |
648 | 98b19252 | Amit Shah | if (version_id < 2) { |
649 | 98b19252 | Amit Shah | return 0; |
650 | 98b19252 | Amit Shah | } |
651 | 98b19252 | Amit Shah | |
652 | 98b19252 | Amit Shah | /* The config space */
|
653 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.cols); |
654 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.rows); |
655 | 295587f7 | Amit Shah | |
656 | 055b889f | Amit Shah | qemu_get_be32s(f, &max_nr_ports); |
657 | 5c1c9bb2 | Alexey Kardashevskiy | tswap32s(&max_nr_ports); |
658 | 5c1c9bb2 | Alexey Kardashevskiy | if (max_nr_ports > tswap32(s->config.max_nr_ports)) {
|
659 | 055b889f | Amit Shah | /* Source could have had more ports than us. Fail migration. */
|
660 | 295587f7 | Amit Shah | return -EINVAL;
|
661 | 295587f7 | Amit Shah | } |
662 | 98b19252 | Amit Shah | |
663 | 055b889f | Amit Shah | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
664 | f83ccb3e | Markus Armbruster | qemu_get_be32s(f, &ports_map); |
665 | 055b889f | Amit Shah | |
666 | f83ccb3e | Markus Armbruster | if (ports_map != s->ports_map[i]) {
|
667 | 055b889f | Amit Shah | /*
|
668 | 055b889f | Amit Shah | * Ports active on source and destination don't
|
669 | 055b889f | Amit Shah | * match. Fail migration.
|
670 | 055b889f | Amit Shah | */
|
671 | 055b889f | Amit Shah | return -EINVAL;
|
672 | 055b889f | Amit Shah | } |
673 | e245795b | Amit Shah | } |
674 | e245795b | Amit Shah | |
675 | 6663a195 | Amit Shah | qemu_get_be32s(f, &nr_active_ports); |
676 | 6663a195 | Amit Shah | |
677 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerialPort */
|
678 | 6663a195 | Amit Shah | for (i = 0; i < nr_active_ports; i++) { |
679 | 6663a195 | Amit Shah | uint32_t id; |
680 | 31abe21f | Amit Shah | bool host_connected;
|
681 | 6663a195 | Amit Shah | |
682 | 6663a195 | Amit Shah | id = qemu_get_be32(f); |
683 | 6663a195 | Amit Shah | port = find_port_by_id(s, id); |
684 | fbe0c559 | Michael S. Tsirkin | if (!port) {
|
685 | fbe0c559 | Michael S. Tsirkin | return -EINVAL;
|
686 | fbe0c559 | Michael S. Tsirkin | } |
687 | 6663a195 | Amit Shah | |
688 | 6663a195 | Amit Shah | port->guest_connected = qemu_get_byte(f); |
689 | 31abe21f | Amit Shah | host_connected = qemu_get_byte(f); |
690 | 31abe21f | Amit Shah | if (host_connected != port->host_connected) {
|
691 | 31abe21f | Amit Shah | /*
|
692 | 31abe21f | Amit Shah | * We have to let the guest know of the host connection
|
693 | 31abe21f | Amit Shah | * status change
|
694 | 31abe21f | Amit Shah | */
|
695 | 31abe21f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, |
696 | 31abe21f | Amit Shah | port->host_connected); |
697 | 31abe21f | Amit Shah | } |
698 | 37f95bf3 | Amit Shah | |
699 | 37f95bf3 | Amit Shah | if (version_id > 2) { |
700 | 37f95bf3 | Amit Shah | uint32_t elem_popped; |
701 | 37f95bf3 | Amit Shah | |
702 | 37f95bf3 | Amit Shah | qemu_get_be32s(f, &elem_popped); |
703 | 37f95bf3 | Amit Shah | if (elem_popped) {
|
704 | 37f95bf3 | Amit Shah | qemu_get_be32s(f, &port->iov_idx); |
705 | 37f95bf3 | Amit Shah | qemu_get_be64s(f, &port->iov_offset); |
706 | 37f95bf3 | Amit Shah | |
707 | 37f95bf3 | Amit Shah | qemu_get_buffer(f, (unsigned char *)&port->elem, |
708 | 37f95bf3 | Amit Shah | sizeof(port->elem));
|
709 | 37f95bf3 | Amit Shah | virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, |
710 | 37f95bf3 | Amit Shah | port->elem.in_num, 1);
|
711 | 37f95bf3 | Amit Shah | virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, |
712 | 37f95bf3 | Amit Shah | port->elem.out_num, 1);
|
713 | 37f95bf3 | Amit Shah | |
714 | 37f95bf3 | Amit Shah | /*
|
715 | 37f95bf3 | Amit Shah | * Port was throttled on source machine. Let's
|
716 | 37f95bf3 | Amit Shah | * unthrottle it here so data starts flowing again.
|
717 | 37f95bf3 | Amit Shah | */
|
718 | 37f95bf3 | Amit Shah | virtio_serial_throttle_port(port, false);
|
719 | 37f95bf3 | Amit Shah | } |
720 | 37f95bf3 | Amit Shah | } |
721 | 6663a195 | Amit Shah | } |
722 | 98b19252 | Amit Shah | return 0; |
723 | 98b19252 | Amit Shah | } |
724 | 98b19252 | Amit Shah | |
725 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
726 | 98b19252 | Amit Shah | |
727 | 98b19252 | Amit Shah | static struct BusInfo virtser_bus_info = { |
728 | 98b19252 | Amit Shah | .name = "virtio-serial-bus",
|
729 | 98b19252 | Amit Shah | .size = sizeof(VirtIOSerialBus),
|
730 | 98b19252 | Amit Shah | .print_dev = virtser_bus_dev_print, |
731 | d6cca4b0 | Markus Armbruster | .props = (Property[]) { |
732 | d6cca4b0 | Markus Armbruster | DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID),
|
733 | d6cca4b0 | Markus Armbruster | DEFINE_PROP_STRING("name", VirtIOSerialPort, name),
|
734 | d6cca4b0 | Markus Armbruster | DEFINE_PROP_END_OF_LIST() |
735 | d6cca4b0 | Markus Armbruster | } |
736 | 98b19252 | Amit Shah | }; |
737 | 98b19252 | Amit Shah | |
738 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
739 | 98b19252 | Amit Shah | { |
740 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
741 | 98b19252 | Amit Shah | |
742 | 021a1318 | Markus Armbruster | monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n",
|
743 | 021a1318 | Markus Armbruster | indent, "", port->id,
|
744 | 021a1318 | Markus Armbruster | port->guest_connected ? "on" : "off", |
745 | 021a1318 | Markus Armbruster | port->host_connected ? "on" : "off", |
746 | 021a1318 | Markus Armbruster | port->throttled ? "on" : "off"); |
747 | 98b19252 | Amit Shah | } |
748 | 98b19252 | Amit Shah | |
749 | 055b889f | Amit Shah | /* This function is only used if a port id is not provided by the user */
|
750 | 055b889f | Amit Shah | static uint32_t find_free_port_id(VirtIOSerial *vser)
|
751 | 055b889f | Amit Shah | { |
752 | 5c1c9bb2 | Alexey Kardashevskiy | unsigned int i, max_nr_ports; |
753 | 055b889f | Amit Shah | |
754 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(vser->config.max_nr_ports); |
755 | 5c1c9bb2 | Alexey Kardashevskiy | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
756 | 055b889f | Amit Shah | uint32_t map, bit; |
757 | 055b889f | Amit Shah | |
758 | 055b889f | Amit Shah | map = vser->ports_map[i]; |
759 | 055b889f | Amit Shah | bit = ffs(~map); |
760 | 055b889f | Amit Shah | if (bit) {
|
761 | 055b889f | Amit Shah | return (bit - 1) + i * 32; |
762 | 055b889f | Amit Shah | } |
763 | 055b889f | Amit Shah | } |
764 | 055b889f | Amit Shah | return VIRTIO_CONSOLE_BAD_ID;
|
765 | 055b889f | Amit Shah | } |
766 | 055b889f | Amit Shah | |
767 | 055b889f | Amit Shah | static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) |
768 | 055b889f | Amit Shah | { |
769 | 055b889f | Amit Shah | unsigned int i; |
770 | 055b889f | Amit Shah | |
771 | 055b889f | Amit Shah | i = port_id / 32;
|
772 | 055b889f | Amit Shah | vser->ports_map[i] |= 1U << (port_id % 32); |
773 | 055b889f | Amit Shah | } |
774 | 055b889f | Amit Shah | |
775 | 055b889f | Amit Shah | static void add_port(VirtIOSerial *vser, uint32_t port_id) |
776 | 055b889f | Amit Shah | { |
777 | 055b889f | Amit Shah | mark_port_added(vser, port_id); |
778 | 055b889f | Amit Shah | |
779 | 055b889f | Amit Shah | send_control_event(find_port_by_id(vser, port_id), |
780 | 055b889f | Amit Shah | VIRTIO_CONSOLE_PORT_ADD, 1);
|
781 | 055b889f | Amit Shah | } |
782 | 055b889f | Amit Shah | |
783 | 055b889f | Amit Shah | static void remove_port(VirtIOSerial *vser, uint32_t port_id) |
784 | 055b889f | Amit Shah | { |
785 | 9ed7b059 | Amit Shah | VirtIOSerialPort *port; |
786 | 055b889f | Amit Shah | unsigned int i; |
787 | 055b889f | Amit Shah | |
788 | 055b889f | Amit Shah | i = port_id / 32;
|
789 | 055b889f | Amit Shah | vser->ports_map[i] &= ~(1U << (port_id % 32)); |
790 | 055b889f | Amit Shah | |
791 | 9ed7b059 | Amit Shah | port = find_port_by_id(vser, port_id); |
792 | 9ed7b059 | Amit Shah | /* Flush out any unconsumed buffers first */
|
793 | 6bff8656 | Amit Shah | discard_vq_data(port->ovq, &port->vser->vdev); |
794 | 9ed7b059 | Amit Shah | |
795 | 9ed7b059 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
796 | 055b889f | Amit Shah | } |
797 | 055b889f | Amit Shah | |
798 | d307af79 | Anthony Liguori | static int virtser_port_qdev_init(DeviceState *qdev) |
799 | 98b19252 | Amit Shah | { |
800 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
801 | f82e35e3 | Anthony Liguori | VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); |
802 | 98b19252 | Amit Shah | VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); |
803 | 5c1c9bb2 | Alexey Kardashevskiy | int ret, max_nr_ports;
|
804 | 98b19252 | Amit Shah | bool plugging_port0;
|
805 | 98b19252 | Amit Shah | |
806 | 98b19252 | Amit Shah | port->vser = bus->vser; |
807 | 199646d8 | Alon Levy | port->bh = qemu_bh_new(flush_queued_data_bh, port); |
808 | 98b19252 | Amit Shah | |
809 | f82e35e3 | Anthony Liguori | assert(vsc->have_data); |
810 | 03ecd2c8 | Amit Shah | |
811 | 98b19252 | Amit Shah | /*
|
812 | 98b19252 | Amit Shah | * Is the first console port we're seeing? If so, put it up at
|
813 | 98b19252 | Amit Shah | * location 0. This is done for backward compatibility (old
|
814 | 98b19252 | Amit Shah | * kernel, new qemu).
|
815 | 98b19252 | Amit Shah | */
|
816 | f82e35e3 | Anthony Liguori | plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0);
|
817 | 98b19252 | Amit Shah | |
818 | 055b889f | Amit Shah | if (find_port_by_id(port->vser, port->id)) {
|
819 | 6daf194d | Markus Armbruster | error_report("virtio-serial-bus: A port already exists at id %u",
|
820 | 055b889f | Amit Shah | port->id); |
821 | 98b19252 | Amit Shah | return -1; |
822 | 98b19252 | Amit Shah | } |
823 | 98b19252 | Amit Shah | |
824 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
825 | 055b889f | Amit Shah | if (plugging_port0) {
|
826 | 055b889f | Amit Shah | port->id = 0;
|
827 | 055b889f | Amit Shah | } else {
|
828 | 055b889f | Amit Shah | port->id = find_free_port_id(port->vser); |
829 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
830 | 6daf194d | Markus Armbruster | error_report("virtio-serial-bus: Maximum port limit for this device reached");
|
831 | 055b889f | Amit Shah | return -1; |
832 | 055b889f | Amit Shah | } |
833 | 055b889f | Amit Shah | } |
834 | 055b889f | Amit Shah | } |
835 | 055b889f | Amit Shah | |
836 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(port->vser->config.max_nr_ports); |
837 | 5c1c9bb2 | Alexey Kardashevskiy | if (port->id >= max_nr_ports) {
|
838 | 6daf194d | Markus Armbruster | error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u",
|
839 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports - 1);
|
840 | 055b889f | Amit Shah | return -1; |
841 | 055b889f | Amit Shah | } |
842 | 055b889f | Amit Shah | |
843 | f82e35e3 | Anthony Liguori | ret = vsc->init(port); |
844 | 98b19252 | Amit Shah | if (ret) {
|
845 | 98b19252 | Amit Shah | return ret;
|
846 | 98b19252 | Amit Shah | } |
847 | 98b19252 | Amit Shah | |
848 | f1925dff | Amit Shah | port->elem.out_num = 0;
|
849 | f1925dff | Amit Shah | |
850 | 98b19252 | Amit Shah | QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); |
851 | 98b19252 | Amit Shah | port->ivq = port->vser->ivqs[port->id]; |
852 | 98b19252 | Amit Shah | port->ovq = port->vser->ovqs[port->id]; |
853 | 98b19252 | Amit Shah | |
854 | 055b889f | Amit Shah | add_port(port->vser, port->id); |
855 | 055b889f | Amit Shah | |
856 | 98b19252 | Amit Shah | /* Send an update to the guest about this new port added */
|
857 | 98b19252 | Amit Shah | virtio_notify_config(&port->vser->vdev); |
858 | 98b19252 | Amit Shah | |
859 | 98b19252 | Amit Shah | return ret;
|
860 | 98b19252 | Amit Shah | } |
861 | 98b19252 | Amit Shah | |
862 | 98b19252 | Amit Shah | static int virtser_port_qdev_exit(DeviceState *qdev) |
863 | 98b19252 | Amit Shah | { |
864 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
865 | f82e35e3 | Anthony Liguori | VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); |
866 | 98b19252 | Amit Shah | VirtIOSerial *vser = port->vser; |
867 | 98b19252 | Amit Shah | |
868 | 199646d8 | Alon Levy | qemu_bh_delete(port->bh); |
869 | 055b889f | Amit Shah | remove_port(port->vser, port->id); |
870 | f146ec9a | Amit Shah | |
871 | 98b19252 | Amit Shah | QTAILQ_REMOVE(&vser->ports, port, next); |
872 | 98b19252 | Amit Shah | |
873 | f82e35e3 | Anthony Liguori | if (vsc->exit) {
|
874 | f82e35e3 | Anthony Liguori | vsc->exit(port); |
875 | a15bb0d6 | Markus Armbruster | } |
876 | 98b19252 | Amit Shah | return 0; |
877 | 98b19252 | Amit Shah | } |
878 | 98b19252 | Amit Shah | |
879 | 6b331efb | Amit Shah | VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) |
880 | 98b19252 | Amit Shah | { |
881 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
882 | 98b19252 | Amit Shah | VirtIODevice *vdev; |
883 | 5ab4bb59 | Amit Shah | uint32_t i, max_supported_ports; |
884 | 98b19252 | Amit Shah | |
885 | 6b331efb | Amit Shah | if (!conf->max_virtserial_ports)
|
886 | 98b19252 | Amit Shah | return NULL; |
887 | 98b19252 | Amit Shah | |
888 | 5ab4bb59 | Amit Shah | /* Each port takes 2 queues, and one pair is for the control queue */
|
889 | 5ab4bb59 | Amit Shah | max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; |
890 | 5ab4bb59 | Amit Shah | |
891 | 6b331efb | Amit Shah | if (conf->max_virtserial_ports > max_supported_ports) {
|
892 | 5ab4bb59 | Amit Shah | error_report("maximum ports supported: %u", max_supported_ports);
|
893 | 5ab4bb59 | Amit Shah | return NULL; |
894 | 5ab4bb59 | Amit Shah | } |
895 | 5ab4bb59 | Amit Shah | |
896 | 98b19252 | Amit Shah | vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
|
897 | 98b19252 | Amit Shah | sizeof(struct virtio_console_config), |
898 | 98b19252 | Amit Shah | sizeof(VirtIOSerial));
|
899 | 98b19252 | Amit Shah | |
900 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
901 | 98b19252 | Amit Shah | |
902 | 98b19252 | Amit Shah | /* Spawn a new virtio-serial bus on which the ports will ride as devices */
|
903 | 5e52e5f9 | Markus Armbruster | qbus_create_inplace(&vser->bus.qbus, &virtser_bus_info, dev, NULL);
|
904 | 5e52e5f9 | Markus Armbruster | vser->bus.qbus.allow_hotplug = 1;
|
905 | 5e52e5f9 | Markus Armbruster | vser->bus.vser = vser; |
906 | 98b19252 | Amit Shah | QTAILQ_INIT(&vser->ports); |
907 | 98b19252 | Amit Shah | |
908 | 5e52e5f9 | Markus Armbruster | vser->bus.max_nr_ports = conf->max_virtserial_ports; |
909 | 7267c094 | Anthony Liguori | vser->ivqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
|
910 | 7267c094 | Anthony Liguori | vser->ovqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
|
911 | 98b19252 | Amit Shah | |
912 | 98b19252 | Amit Shah | /* Add a queue for host to guest transfers for port 0 (backward compat) */
|
913 | 98b19252 | Amit Shah | vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); |
914 | 98b19252 | Amit Shah | /* Add a queue for guest to host transfers for port 0 (backward compat) */
|
915 | 98b19252 | Amit Shah | vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); |
916 | 98b19252 | Amit Shah | |
917 | a01a9cb8 | Amit Shah | /* TODO: host to guest notifications can get dropped
|
918 | a01a9cb8 | Amit Shah | * if the queue fills up. Implement queueing in host,
|
919 | a01a9cb8 | Amit Shah | * this might also make it possible to reduce the control
|
920 | a01a9cb8 | Amit Shah | * queue size: as guest preposts buffers there,
|
921 | a01a9cb8 | Amit Shah | * this will save 4Kbyte of guest memory per entry. */
|
922 | a01a9cb8 | Amit Shah | |
923 | 98b19252 | Amit Shah | /* control queue: host to guest */
|
924 | a01a9cb8 | Amit Shah | vser->c_ivq = virtio_add_queue(vdev, 32, control_in);
|
925 | 98b19252 | Amit Shah | /* control queue: guest to host */
|
926 | a01a9cb8 | Amit Shah | vser->c_ovq = virtio_add_queue(vdev, 32, control_out);
|
927 | 98b19252 | Amit Shah | |
928 | 5e52e5f9 | Markus Armbruster | for (i = 1; i < vser->bus.max_nr_ports; i++) { |
929 | 98b19252 | Amit Shah | /* Add a per-port queue for host to guest transfers */
|
930 | 98b19252 | Amit Shah | vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
|
931 | 98b19252 | Amit Shah | /* Add a per-per queue for guest to host transfers */
|
932 | 98b19252 | Amit Shah | vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
|
933 | 98b19252 | Amit Shah | } |
934 | 98b19252 | Amit Shah | |
935 | 5c1c9bb2 | Alexey Kardashevskiy | vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports); |
936 | 7267c094 | Anthony Liguori | vser->ports_map = g_malloc0(((conf->max_virtserial_ports + 31) / 32) |
937 | a132a679 | Alon Levy | * sizeof(vser->ports_map[0])); |
938 | 98b19252 | Amit Shah | /*
|
939 | 98b19252 | Amit Shah | * Reserve location 0 for a console port for backward compat
|
940 | 98b19252 | Amit Shah | * (old kernel, new qemu)
|
941 | 98b19252 | Amit Shah | */
|
942 | 055b889f | Amit Shah | mark_port_added(vser, 0);
|
943 | 98b19252 | Amit Shah | |
944 | 98b19252 | Amit Shah | vser->vdev.get_features = get_features; |
945 | 98b19252 | Amit Shah | vser->vdev.get_config = get_config; |
946 | 98b19252 | Amit Shah | vser->vdev.set_config = set_config; |
947 | 62a9fbf7 | Alon Levy | vser->vdev.set_status = set_status; |
948 | 43997225 | Amit Shah | vser->vdev.reset = vser_reset; |
949 | 98b19252 | Amit Shah | |
950 | 8b53a865 | Amit Shah | vser->qdev = dev; |
951 | 8b53a865 | Amit Shah | |
952 | 98b19252 | Amit Shah | /*
|
953 | 98b19252 | Amit Shah | * Register for the savevm section with the virtio-console name
|
954 | 98b19252 | Amit Shah | * to preserve backward compat
|
955 | 98b19252 | Amit Shah | */
|
956 | 37f95bf3 | Amit Shah | register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, |
957 | 98b19252 | Amit Shah | virtio_serial_load, vser); |
958 | 98b19252 | Amit Shah | |
959 | 98b19252 | Amit Shah | return vdev;
|
960 | 98b19252 | Amit Shah | } |
961 | 8b53a865 | Amit Shah | |
962 | 8b53a865 | Amit Shah | void virtio_serial_exit(VirtIODevice *vdev)
|
963 | 8b53a865 | Amit Shah | { |
964 | 8b53a865 | Amit Shah | VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
965 | 8b53a865 | Amit Shah | |
966 | 8b53a865 | Amit Shah | unregister_savevm(vser->qdev, "virtio-console", vser);
|
967 | 8b53a865 | Amit Shah | |
968 | 7267c094 | Anthony Liguori | g_free(vser->ivqs); |
969 | 7267c094 | Anthony Liguori | g_free(vser->ovqs); |
970 | 7267c094 | Anthony Liguori | g_free(vser->ports_map); |
971 | 8b53a865 | Amit Shah | |
972 | 8b53a865 | Amit Shah | virtio_cleanup(vdev); |
973 | 8b53a865 | Amit Shah | } |
974 | f82e35e3 | Anthony Liguori | |
975 | 39bffca2 | Anthony Liguori | static void virtio_serial_port_class_init(ObjectClass *klass, void *data) |
976 | 39bffca2 | Anthony Liguori | { |
977 | 39bffca2 | Anthony Liguori | DeviceClass *k = DEVICE_CLASS(klass); |
978 | 39bffca2 | Anthony Liguori | k->init = virtser_port_qdev_init; |
979 | 39bffca2 | Anthony Liguori | k->bus_info = &virtser_bus_info; |
980 | 39bffca2 | Anthony Liguori | k->exit = virtser_port_qdev_exit; |
981 | 39bffca2 | Anthony Liguori | k->unplug = qdev_simple_unplug_cb; |
982 | 39bffca2 | Anthony Liguori | } |
983 | 39bffca2 | Anthony Liguori | |
984 | f82e35e3 | Anthony Liguori | static TypeInfo virtio_serial_port_type_info = {
|
985 | f82e35e3 | Anthony Liguori | .name = TYPE_VIRTIO_SERIAL_PORT, |
986 | f82e35e3 | Anthony Liguori | .parent = TYPE_DEVICE, |
987 | f82e35e3 | Anthony Liguori | .instance_size = sizeof(VirtIOSerialPort),
|
988 | f82e35e3 | Anthony Liguori | .abstract = true,
|
989 | f82e35e3 | Anthony Liguori | .class_size = sizeof(VirtIOSerialPortClass),
|
990 | 39bffca2 | Anthony Liguori | .class_init = virtio_serial_port_class_init, |
991 | f82e35e3 | Anthony Liguori | }; |
992 | f82e35e3 | Anthony Liguori | |
993 | 83f7d43a | Andreas Färber | static void virtio_serial_register_types(void) |
994 | f82e35e3 | Anthony Liguori | { |
995 | f82e35e3 | Anthony Liguori | type_register_static(&virtio_serial_port_type_info); |
996 | f82e35e3 | Anthony Liguori | } |
997 | f82e35e3 | Anthony Liguori | |
998 | 83f7d43a | Andreas Färber | type_init(virtio_serial_register_types) |