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