root / hw / virtio-serial-bus.c @ 81699d8a
History | View | Annotate | Download (21.6 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 | 98b19252 | Amit Shah | 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 | 9ed7b059 | Amit Shah | static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, |
117 | 9ed7b059 | Amit Shah | VirtIODevice *vdev, bool discard)
|
118 | a69c7600 | Amit Shah | { |
119 | a69c7600 | Amit Shah | VirtQueueElement elem; |
120 | a69c7600 | Amit Shah | |
121 | a69c7600 | Amit Shah | assert(port || discard); |
122 | fd11a78b | Amit Shah | assert(virtio_queue_ready(vq)); |
123 | a69c7600 | Amit Shah | |
124 | 9ed7b059 | Amit Shah | while ((discard || !port->throttled) && virtqueue_pop(vq, &elem)) {
|
125 | a69c7600 | Amit Shah | uint8_t *buf; |
126 | a69c7600 | Amit Shah | size_t ret, buf_size; |
127 | a69c7600 | Amit Shah | |
128 | a69c7600 | Amit Shah | if (!discard) {
|
129 | a69c7600 | Amit Shah | buf_size = iov_size(elem.out_sg, elem.out_num); |
130 | a69c7600 | Amit Shah | buf = qemu_malloc(buf_size); |
131 | a69c7600 | Amit Shah | ret = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, buf_size);
|
132 | a69c7600 | Amit Shah | |
133 | a69c7600 | Amit Shah | port->info->have_data(port, buf, ret); |
134 | a69c7600 | Amit Shah | qemu_free(buf); |
135 | a69c7600 | Amit Shah | } |
136 | a69c7600 | Amit Shah | virtqueue_push(vq, &elem, 0);
|
137 | a69c7600 | Amit Shah | } |
138 | a69c7600 | Amit Shah | virtio_notify(vdev, vq); |
139 | a69c7600 | Amit Shah | } |
140 | a69c7600 | Amit Shah | |
141 | 9ed7b059 | Amit Shah | static void flush_queued_data(VirtIOSerialPort *port, bool discard) |
142 | 9ed7b059 | Amit Shah | { |
143 | a1c59752 | Amit Shah | assert(port); |
144 | 9ed7b059 | Amit Shah | |
145 | 6b611d3a | Amit Shah | if (!virtio_queue_ready(port->ovq)) {
|
146 | 6b611d3a | Amit Shah | return;
|
147 | 6b611d3a | Amit Shah | } |
148 | 9ed7b059 | Amit Shah | do_flush_queued_data(port, port->ovq, &port->vser->vdev, discard); |
149 | 9ed7b059 | Amit Shah | } |
150 | 9ed7b059 | Amit Shah | |
151 | 98b19252 | Amit Shah | static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) |
152 | 98b19252 | Amit Shah | { |
153 | 98b19252 | Amit Shah | VirtQueueElement elem; |
154 | 98b19252 | Amit Shah | VirtQueue *vq; |
155 | 98b19252 | Amit Shah | struct virtio_console_control *cpkt;
|
156 | 98b19252 | Amit Shah | |
157 | 98b19252 | Amit Shah | vq = port->vser->c_ivq; |
158 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
159 | 98b19252 | Amit Shah | return 0; |
160 | 98b19252 | Amit Shah | } |
161 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
162 | 98b19252 | Amit Shah | return 0; |
163 | 98b19252 | Amit Shah | } |
164 | 98b19252 | Amit Shah | |
165 | 98b19252 | Amit Shah | cpkt = (struct virtio_console_control *)buf;
|
166 | 98b19252 | Amit Shah | stl_p(&cpkt->id, port->id); |
167 | 98b19252 | Amit Shah | memcpy(elem.in_sg[0].iov_base, buf, len);
|
168 | 98b19252 | Amit Shah | |
169 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
170 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
171 | 98b19252 | Amit Shah | return len;
|
172 | 98b19252 | Amit Shah | } |
173 | 98b19252 | Amit Shah | |
174 | 98b19252 | Amit Shah | static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
|
175 | 98b19252 | Amit Shah | uint16_t value) |
176 | 98b19252 | Amit Shah | { |
177 | 98b19252 | Amit Shah | struct virtio_console_control cpkt;
|
178 | 98b19252 | Amit Shah | |
179 | 98b19252 | Amit Shah | stw_p(&cpkt.event, event); |
180 | 98b19252 | Amit Shah | stw_p(&cpkt.value, value); |
181 | 98b19252 | Amit Shah | |
182 | 98b19252 | Amit Shah | return send_control_msg(port, &cpkt, sizeof(cpkt)); |
183 | 98b19252 | Amit Shah | } |
184 | 98b19252 | Amit Shah | |
185 | 98b19252 | Amit Shah | /* Functions for use inside qemu to open and read from/write to ports */
|
186 | 98b19252 | Amit Shah | int virtio_serial_open(VirtIOSerialPort *port)
|
187 | 98b19252 | Amit Shah | { |
188 | 6663a195 | Amit Shah | /* Don't allow opening an already-open port */
|
189 | 6663a195 | Amit Shah | if (port->host_connected) {
|
190 | 6663a195 | Amit Shah | return 0; |
191 | 6663a195 | Amit Shah | } |
192 | 6663a195 | Amit Shah | /* Send port open notification to the guest */
|
193 | 6663a195 | Amit Shah | port->host_connected = true;
|
194 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
195 | 6663a195 | Amit Shah | |
196 | 98b19252 | Amit Shah | return 0; |
197 | 98b19252 | Amit Shah | } |
198 | 98b19252 | Amit Shah | |
199 | 98b19252 | Amit Shah | int virtio_serial_close(VirtIOSerialPort *port)
|
200 | 98b19252 | Amit Shah | { |
201 | 6663a195 | Amit Shah | port->host_connected = false;
|
202 | 9ed7b059 | Amit Shah | /*
|
203 | 9ed7b059 | Amit Shah | * If there's any data the guest sent which the app didn't
|
204 | 9ed7b059 | Amit Shah | * consume, reset the throttling flag and discard the data.
|
205 | 9ed7b059 | Amit Shah | */
|
206 | 9ed7b059 | Amit Shah | port->throttled = false;
|
207 | 9ed7b059 | Amit Shah | flush_queued_data(port, true);
|
208 | 9ed7b059 | Amit Shah | |
209 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
|
210 | 6663a195 | Amit Shah | |
211 | 98b19252 | Amit Shah | return 0; |
212 | 98b19252 | Amit Shah | } |
213 | 98b19252 | Amit Shah | |
214 | 98b19252 | Amit Shah | /* Individual ports/apps call this function to write to the guest. */
|
215 | 98b19252 | Amit Shah | ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
|
216 | 98b19252 | Amit Shah | size_t size) |
217 | 98b19252 | Amit Shah | { |
218 | 6663a195 | Amit Shah | if (!port || !port->host_connected || !port->guest_connected) {
|
219 | 6663a195 | Amit Shah | return 0; |
220 | 6663a195 | Amit Shah | } |
221 | 98b19252 | Amit Shah | return write_to_port(port, buf, size);
|
222 | 98b19252 | Amit Shah | } |
223 | 98b19252 | Amit Shah | |
224 | 98b19252 | Amit Shah | /*
|
225 | 98b19252 | Amit Shah | * Readiness of the guest to accept data on a port.
|
226 | 98b19252 | Amit Shah | * Returns max. data the guest can receive
|
227 | 98b19252 | Amit Shah | */
|
228 | 98b19252 | Amit Shah | size_t virtio_serial_guest_ready(VirtIOSerialPort *port) |
229 | 98b19252 | Amit Shah | { |
230 | 98b19252 | Amit Shah | VirtQueue *vq = port->ivq; |
231 | 98b19252 | Amit Shah | |
232 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq) ||
|
233 | 98b19252 | Amit Shah | !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
234 | 98b19252 | Amit Shah | virtio_queue_empty(vq)) { |
235 | 98b19252 | Amit Shah | return 0; |
236 | 98b19252 | Amit Shah | } |
237 | 6663a195 | Amit Shah | if (use_multiport(port->vser) && !port->guest_connected) {
|
238 | 6663a195 | Amit Shah | return 0; |
239 | 6663a195 | Amit Shah | } |
240 | 98b19252 | Amit Shah | |
241 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 4096, 0)) { |
242 | 98b19252 | Amit Shah | return 4096; |
243 | 98b19252 | Amit Shah | } |
244 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 1, 0)) { |
245 | 98b19252 | Amit Shah | return 1; |
246 | 98b19252 | Amit Shah | } |
247 | 98b19252 | Amit Shah | return 0; |
248 | 98b19252 | Amit Shah | } |
249 | 98b19252 | Amit Shah | |
250 | 9ed7b059 | Amit Shah | void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) |
251 | 9ed7b059 | Amit Shah | { |
252 | 9ed7b059 | Amit Shah | if (!port) {
|
253 | 9ed7b059 | Amit Shah | return;
|
254 | 9ed7b059 | Amit Shah | } |
255 | 9ed7b059 | Amit Shah | |
256 | 9ed7b059 | Amit Shah | port->throttled = throttle; |
257 | 9ed7b059 | Amit Shah | if (throttle) {
|
258 | 9ed7b059 | Amit Shah | return;
|
259 | 9ed7b059 | Amit Shah | } |
260 | 9ed7b059 | Amit Shah | |
261 | 9ed7b059 | Amit Shah | flush_queued_data(port, false);
|
262 | 9ed7b059 | Amit Shah | } |
263 | 9ed7b059 | Amit Shah | |
264 | 98b19252 | Amit Shah | /* Guest wants to notify us of some event */
|
265 | e61da14d | Amit Shah | static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) |
266 | 98b19252 | Amit Shah | { |
267 | 98b19252 | Amit Shah | struct VirtIOSerialPort *port;
|
268 | 98b19252 | Amit Shah | struct virtio_console_control cpkt, *gcpkt;
|
269 | 160600fd | Amit Shah | uint8_t *buffer; |
270 | 160600fd | Amit Shah | size_t buffer_len; |
271 | 98b19252 | Amit Shah | |
272 | 98b19252 | Amit Shah | gcpkt = buf; |
273 | 98b19252 | Amit Shah | |
274 | e61da14d | Amit Shah | if (len < sizeof(cpkt)) { |
275 | e61da14d | Amit Shah | /* The guest sent an invalid control packet */
|
276 | e61da14d | Amit Shah | return;
|
277 | e61da14d | Amit Shah | } |
278 | e61da14d | Amit Shah | |
279 | 98b19252 | Amit Shah | cpkt.event = lduw_p(&gcpkt->event); |
280 | 98b19252 | Amit Shah | cpkt.value = lduw_p(&gcpkt->value); |
281 | 98b19252 | Amit Shah | |
282 | 055b889f | Amit Shah | port = find_port_by_id(vser, ldl_p(&gcpkt->id)); |
283 | 055b889f | Amit Shah | if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
|
284 | 055b889f | Amit Shah | return;
|
285 | 055b889f | Amit Shah | |
286 | 98b19252 | Amit Shah | switch(cpkt.event) {
|
287 | 055b889f | Amit Shah | case VIRTIO_CONSOLE_DEVICE_READY:
|
288 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
289 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding device %s\n",
|
290 | 4048c7c3 | Amit Shah | vser->bus->qbus.name); |
291 | 4048c7c3 | Amit Shah | break;
|
292 | 4048c7c3 | Amit Shah | } |
293 | 055b889f | Amit Shah | /*
|
294 | 055b889f | Amit Shah | * The device is up, we can now tell the device about all the
|
295 | 055b889f | Amit Shah | * ports we have here.
|
296 | 055b889f | Amit Shah | */
|
297 | 055b889f | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
298 | 055b889f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
|
299 | 055b889f | Amit Shah | } |
300 | 055b889f | Amit Shah | break;
|
301 | 055b889f | Amit Shah | |
302 | 98b19252 | Amit Shah | case VIRTIO_CONSOLE_PORT_READY:
|
303 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
304 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n",
|
305 | 4048c7c3 | Amit Shah | port->id, vser->bus->qbus.name); |
306 | 4048c7c3 | Amit Shah | break;
|
307 | 4048c7c3 | Amit Shah | } |
308 | 98b19252 | Amit Shah | /*
|
309 | 98b19252 | Amit Shah | * Now that we know the guest asked for the port name, we're
|
310 | 98b19252 | Amit Shah | * sure the guest has initialised whatever state is necessary
|
311 | 98b19252 | Amit Shah | * for this port. Now's a good time to let the guest know if
|
312 | 98b19252 | Amit Shah | * this port is a console port so that the guest can hook it
|
313 | 98b19252 | Amit Shah | * up to hvc.
|
314 | 98b19252 | Amit Shah | */
|
315 | 98b19252 | Amit Shah | if (port->is_console) {
|
316 | 98b19252 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
|
317 | 98b19252 | Amit Shah | } |
318 | 6663a195 | Amit Shah | |
319 | 160600fd | Amit Shah | if (port->name) {
|
320 | 160600fd | Amit Shah | stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); |
321 | 160600fd | Amit Shah | stw_p(&cpkt.value, 1);
|
322 | 160600fd | Amit Shah | |
323 | 160600fd | Amit Shah | buffer_len = sizeof(cpkt) + strlen(port->name) + 1; |
324 | 160600fd | Amit Shah | buffer = qemu_malloc(buffer_len); |
325 | 160600fd | Amit Shah | |
326 | 160600fd | Amit Shah | memcpy(buffer, &cpkt, sizeof(cpkt));
|
327 | 160600fd | Amit Shah | memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
|
328 | 160600fd | Amit Shah | buffer[buffer_len - 1] = 0; |
329 | 160600fd | Amit Shah | |
330 | 160600fd | Amit Shah | send_control_msg(port, buffer, buffer_len); |
331 | 160600fd | Amit Shah | qemu_free(buffer); |
332 | 160600fd | Amit Shah | } |
333 | 160600fd | Amit Shah | |
334 | 6663a195 | Amit Shah | if (port->host_connected) {
|
335 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
336 | 6663a195 | Amit Shah | } |
337 | 6663a195 | Amit Shah | |
338 | 98b19252 | Amit Shah | /*
|
339 | 98b19252 | Amit Shah | * When the guest has asked us for this information it means
|
340 | 98b19252 | Amit Shah | * the guest is all setup and has its virtqueues
|
341 | 98b19252 | Amit Shah | * initialised. If some app is interested in knowing about
|
342 | 98b19252 | Amit Shah | * this event, let it know.
|
343 | 98b19252 | Amit Shah | */
|
344 | 98b19252 | Amit Shah | if (port->info->guest_ready) {
|
345 | 98b19252 | Amit Shah | port->info->guest_ready(port); |
346 | 98b19252 | Amit Shah | } |
347 | 98b19252 | Amit Shah | break;
|
348 | 6663a195 | Amit Shah | |
349 | 6663a195 | Amit Shah | case VIRTIO_CONSOLE_PORT_OPEN:
|
350 | 6663a195 | Amit Shah | port->guest_connected = cpkt.value; |
351 | 6663a195 | Amit Shah | if (cpkt.value && port->info->guest_open) {
|
352 | 6663a195 | Amit Shah | /* Send the guest opened notification if an app is interested */
|
353 | 6663a195 | Amit Shah | port->info->guest_open(port); |
354 | 6663a195 | Amit Shah | } |
355 | 6663a195 | Amit Shah | |
356 | 6663a195 | Amit Shah | if (!cpkt.value && port->info->guest_close) {
|
357 | 6663a195 | Amit Shah | /* Send the guest closed notification if an app is interested */
|
358 | 6663a195 | Amit Shah | port->info->guest_close(port); |
359 | 6663a195 | Amit Shah | } |
360 | 6663a195 | Amit Shah | break;
|
361 | 98b19252 | Amit Shah | } |
362 | 98b19252 | Amit Shah | } |
363 | 98b19252 | Amit Shah | |
364 | 98b19252 | Amit Shah | static void control_in(VirtIODevice *vdev, VirtQueue *vq) |
365 | 98b19252 | Amit Shah | { |
366 | 98b19252 | Amit Shah | } |
367 | 98b19252 | Amit Shah | |
368 | 98b19252 | Amit Shah | static void control_out(VirtIODevice *vdev, VirtQueue *vq) |
369 | 98b19252 | Amit Shah | { |
370 | 98b19252 | Amit Shah | VirtQueueElement elem; |
371 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
372 | e61da14d | Amit Shah | uint8_t *buf; |
373 | e61da14d | Amit Shah | size_t len; |
374 | 98b19252 | Amit Shah | |
375 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
376 | 98b19252 | Amit Shah | |
377 | e61da14d | Amit Shah | len = 0;
|
378 | e61da14d | Amit Shah | buf = NULL;
|
379 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
380 | e61da14d | Amit Shah | size_t cur_len, copied; |
381 | e61da14d | Amit Shah | |
382 | e61da14d | Amit Shah | cur_len = iov_size(elem.out_sg, elem.out_num); |
383 | e61da14d | Amit Shah | /*
|
384 | e61da14d | Amit Shah | * Allocate a new buf only if we didn't have one previously or
|
385 | e61da14d | Amit Shah | * if the size of the buf differs
|
386 | e61da14d | Amit Shah | */
|
387 | e61da14d | Amit Shah | if (cur_len > len) {
|
388 | e61da14d | Amit Shah | qemu_free(buf); |
389 | e61da14d | Amit Shah | |
390 | e61da14d | Amit Shah | buf = qemu_malloc(cur_len); |
391 | e61da14d | Amit Shah | len = cur_len; |
392 | e61da14d | Amit Shah | } |
393 | e61da14d | Amit Shah | copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len);
|
394 | e61da14d | Amit Shah | |
395 | e61da14d | Amit Shah | handle_control_message(vser, buf, copied); |
396 | 1e4476aa | Amit Shah | virtqueue_push(vq, &elem, 0);
|
397 | 98b19252 | Amit Shah | } |
398 | e61da14d | Amit Shah | qemu_free(buf); |
399 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
400 | 98b19252 | Amit Shah | } |
401 | 98b19252 | Amit Shah | |
402 | 98b19252 | Amit Shah | /* Guest wrote something to some port. */
|
403 | 98b19252 | Amit Shah | static void handle_output(VirtIODevice *vdev, VirtQueue *vq) |
404 | 98b19252 | Amit Shah | { |
405 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
406 | a69c7600 | Amit Shah | VirtIOSerialPort *port; |
407 | a69c7600 | Amit Shah | bool discard;
|
408 | 98b19252 | Amit Shah | |
409 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
410 | a69c7600 | Amit Shah | port = find_port_by_vq(vser, vq); |
411 | 98b19252 | Amit Shah | |
412 | a69c7600 | Amit Shah | discard = false;
|
413 | a69c7600 | Amit Shah | if (!port || !port->host_connected || !port->info->have_data) {
|
414 | a69c7600 | Amit Shah | discard = true;
|
415 | 98b19252 | Amit Shah | } |
416 | a69c7600 | Amit Shah | |
417 | 9ed7b059 | Amit Shah | if (!discard && port->throttled) {
|
418 | 9ed7b059 | Amit Shah | return;
|
419 | 9ed7b059 | Amit Shah | } |
420 | 9ed7b059 | Amit Shah | |
421 | 9ed7b059 | Amit Shah | do_flush_queued_data(port, vq, vdev, discard); |
422 | 98b19252 | Amit Shah | } |
423 | 98b19252 | Amit Shah | |
424 | 98b19252 | Amit Shah | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
425 | 98b19252 | Amit Shah | { |
426 | 98b19252 | Amit Shah | } |
427 | 98b19252 | Amit Shah | |
428 | 98b19252 | Amit Shah | static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
|
429 | 98b19252 | Amit Shah | { |
430 | 306eb457 | Amit Shah | VirtIOSerial *vser; |
431 | 306eb457 | Amit Shah | |
432 | 306eb457 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
433 | 306eb457 | Amit Shah | |
434 | ee4d45be | Michael S. Tsirkin | if (vser->bus->max_nr_ports > 1) { |
435 | ee4d45be | Michael S. Tsirkin | features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
|
436 | ee4d45be | Michael S. Tsirkin | } |
437 | 98b19252 | Amit Shah | return features;
|
438 | 98b19252 | Amit Shah | } |
439 | 98b19252 | Amit Shah | |
440 | 98b19252 | Amit Shah | /* Guest requested config info */
|
441 | 98b19252 | Amit Shah | static void get_config(VirtIODevice *vdev, uint8_t *config_data) |
442 | 98b19252 | Amit Shah | { |
443 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
444 | 98b19252 | Amit Shah | |
445 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
446 | 98b19252 | Amit Shah | memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); |
447 | 98b19252 | Amit Shah | } |
448 | 98b19252 | Amit Shah | |
449 | 98b19252 | Amit Shah | static void set_config(VirtIODevice *vdev, const uint8_t *config_data) |
450 | 98b19252 | Amit Shah | { |
451 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
452 | 98b19252 | Amit Shah | |
453 | 98b19252 | Amit Shah | memcpy(&config, config_data, sizeof(config));
|
454 | 98b19252 | Amit Shah | } |
455 | 98b19252 | Amit Shah | |
456 | 98b19252 | Amit Shah | static void virtio_serial_save(QEMUFile *f, void *opaque) |
457 | 98b19252 | Amit Shah | { |
458 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
459 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
460 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
461 | 055b889f | Amit Shah | unsigned int i; |
462 | 98b19252 | Amit Shah | |
463 | 98b19252 | Amit Shah | /* The virtio device */
|
464 | 98b19252 | Amit Shah | virtio_save(&s->vdev, f); |
465 | 98b19252 | Amit Shah | |
466 | 98b19252 | Amit Shah | /* The config space */
|
467 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.cols); |
468 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.rows); |
469 | 6663a195 | Amit Shah | |
470 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->config.max_nr_ports); |
471 | 055b889f | Amit Shah | |
472 | 055b889f | Amit Shah | /* The ports map */
|
473 | 055b889f | Amit Shah | |
474 | 055b889f | Amit Shah | for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) { |
475 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->ports_map[i]); |
476 | 055b889f | Amit Shah | } |
477 | 6663a195 | Amit Shah | |
478 | 055b889f | Amit Shah | /* Ports */
|
479 | e245795b | Amit Shah | |
480 | 6663a195 | Amit Shah | nr_active_ports = 0;
|
481 | e245795b | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
482 | 6663a195 | Amit Shah | nr_active_ports++; |
483 | e245795b | Amit Shah | } |
484 | 6663a195 | Amit Shah | |
485 | 6663a195 | Amit Shah | qemu_put_be32s(f, &nr_active_ports); |
486 | 6663a195 | Amit Shah | |
487 | 6663a195 | Amit Shah | /*
|
488 | 6663a195 | Amit Shah | * Items in struct VirtIOSerialPort.
|
489 | 6663a195 | Amit Shah | */
|
490 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
491 | 6663a195 | Amit Shah | qemu_put_be32s(f, &port->id); |
492 | 6663a195 | Amit Shah | qemu_put_byte(f, port->guest_connected); |
493 | 31abe21f | Amit Shah | qemu_put_byte(f, port->host_connected); |
494 | 6663a195 | Amit Shah | } |
495 | 98b19252 | Amit Shah | } |
496 | 98b19252 | Amit Shah | |
497 | 98b19252 | Amit Shah | static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) |
498 | 98b19252 | Amit Shah | { |
499 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
500 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
501 | f83ccb3e | Markus Armbruster | uint32_t max_nr_ports, nr_active_ports, ports_map; |
502 | 6663a195 | Amit Shah | unsigned int i; |
503 | 98b19252 | Amit Shah | |
504 | 98b19252 | Amit Shah | if (version_id > 2) { |
505 | 98b19252 | Amit Shah | return -EINVAL;
|
506 | 98b19252 | Amit Shah | } |
507 | 6663a195 | Amit Shah | |
508 | 98b19252 | Amit Shah | /* The virtio device */
|
509 | 98b19252 | Amit Shah | virtio_load(&s->vdev, f); |
510 | 98b19252 | Amit Shah | |
511 | 98b19252 | Amit Shah | if (version_id < 2) { |
512 | 98b19252 | Amit Shah | return 0; |
513 | 98b19252 | Amit Shah | } |
514 | 98b19252 | Amit Shah | |
515 | 98b19252 | Amit Shah | /* The config space */
|
516 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.cols); |
517 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.rows); |
518 | 295587f7 | Amit Shah | |
519 | 055b889f | Amit Shah | qemu_get_be32s(f, &max_nr_ports); |
520 | 055b889f | Amit Shah | if (max_nr_ports > s->config.max_nr_ports) {
|
521 | 055b889f | Amit Shah | /* Source could have had more ports than us. Fail migration. */
|
522 | 295587f7 | Amit Shah | return -EINVAL;
|
523 | 295587f7 | Amit Shah | } |
524 | 98b19252 | Amit Shah | |
525 | 055b889f | Amit Shah | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
526 | f83ccb3e | Markus Armbruster | qemu_get_be32s(f, &ports_map); |
527 | 055b889f | Amit Shah | |
528 | f83ccb3e | Markus Armbruster | if (ports_map != s->ports_map[i]) {
|
529 | 055b889f | Amit Shah | /*
|
530 | 055b889f | Amit Shah | * Ports active on source and destination don't
|
531 | 055b889f | Amit Shah | * match. Fail migration.
|
532 | 055b889f | Amit Shah | */
|
533 | 055b889f | Amit Shah | return -EINVAL;
|
534 | 055b889f | Amit Shah | } |
535 | e245795b | Amit Shah | } |
536 | e245795b | Amit Shah | |
537 | 6663a195 | Amit Shah | qemu_get_be32s(f, &nr_active_ports); |
538 | 6663a195 | Amit Shah | |
539 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerialPort */
|
540 | 6663a195 | Amit Shah | for (i = 0; i < nr_active_ports; i++) { |
541 | 6663a195 | Amit Shah | uint32_t id; |
542 | 31abe21f | Amit Shah | bool host_connected;
|
543 | 6663a195 | Amit Shah | |
544 | 6663a195 | Amit Shah | id = qemu_get_be32(f); |
545 | 6663a195 | Amit Shah | port = find_port_by_id(s, id); |
546 | 6663a195 | Amit Shah | |
547 | 6663a195 | Amit Shah | port->guest_connected = qemu_get_byte(f); |
548 | 31abe21f | Amit Shah | host_connected = qemu_get_byte(f); |
549 | 31abe21f | Amit Shah | if (host_connected != port->host_connected) {
|
550 | 31abe21f | Amit Shah | /*
|
551 | 31abe21f | Amit Shah | * We have to let the guest know of the host connection
|
552 | 31abe21f | Amit Shah | * status change
|
553 | 31abe21f | Amit Shah | */
|
554 | 31abe21f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, |
555 | 31abe21f | Amit Shah | port->host_connected); |
556 | 31abe21f | Amit Shah | } |
557 | 6663a195 | Amit Shah | } |
558 | 98b19252 | Amit Shah | return 0; |
559 | 98b19252 | Amit Shah | } |
560 | 98b19252 | Amit Shah | |
561 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
562 | 98b19252 | Amit Shah | |
563 | 98b19252 | Amit Shah | static struct BusInfo virtser_bus_info = { |
564 | 98b19252 | Amit Shah | .name = "virtio-serial-bus",
|
565 | 98b19252 | Amit Shah | .size = sizeof(VirtIOSerialBus),
|
566 | 98b19252 | Amit Shah | .print_dev = virtser_bus_dev_print, |
567 | 98b19252 | Amit Shah | }; |
568 | 98b19252 | Amit Shah | |
569 | 98b19252 | Amit Shah | static VirtIOSerialBus *virtser_bus_new(DeviceState *dev)
|
570 | 98b19252 | Amit Shah | { |
571 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
572 | 98b19252 | Amit Shah | |
573 | 9ae84f0a | Amit Shah | bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL));
|
574 | 98b19252 | Amit Shah | bus->qbus.allow_hotplug = 1;
|
575 | 98b19252 | Amit Shah | |
576 | 98b19252 | Amit Shah | return bus;
|
577 | 98b19252 | Amit Shah | } |
578 | 98b19252 | Amit Shah | |
579 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
580 | 98b19252 | Amit Shah | { |
581 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
582 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
583 | 98b19252 | Amit Shah | |
584 | 98b19252 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
|
585 | 98b19252 | Amit Shah | indent, "", port->id);
|
586 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
|
587 | 6663a195 | Amit Shah | indent, "", port->guest_connected);
|
588 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
|
589 | 6663a195 | Amit Shah | indent, "", port->host_connected);
|
590 | 9ed7b059 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n",
|
591 | 9ed7b059 | Amit Shah | indent, "", port->throttled);
|
592 | 98b19252 | Amit Shah | } |
593 | 98b19252 | Amit Shah | |
594 | 055b889f | Amit Shah | /* This function is only used if a port id is not provided by the user */
|
595 | 055b889f | Amit Shah | static uint32_t find_free_port_id(VirtIOSerial *vser)
|
596 | 055b889f | Amit Shah | { |
597 | 055b889f | Amit Shah | unsigned int i; |
598 | 055b889f | Amit Shah | |
599 | 055b889f | Amit Shah | for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) { |
600 | 055b889f | Amit Shah | uint32_t map, bit; |
601 | 055b889f | Amit Shah | |
602 | 055b889f | Amit Shah | map = vser->ports_map[i]; |
603 | 055b889f | Amit Shah | bit = ffs(~map); |
604 | 055b889f | Amit Shah | if (bit) {
|
605 | 055b889f | Amit Shah | return (bit - 1) + i * 32; |
606 | 055b889f | Amit Shah | } |
607 | 055b889f | Amit Shah | } |
608 | 055b889f | Amit Shah | return VIRTIO_CONSOLE_BAD_ID;
|
609 | 055b889f | Amit Shah | } |
610 | 055b889f | Amit Shah | |
611 | 055b889f | Amit Shah | static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) |
612 | 055b889f | Amit Shah | { |
613 | 055b889f | Amit Shah | unsigned int i; |
614 | 055b889f | Amit Shah | |
615 | 055b889f | Amit Shah | i = port_id / 32;
|
616 | 055b889f | Amit Shah | vser->ports_map[i] |= 1U << (port_id % 32); |
617 | 055b889f | Amit Shah | } |
618 | 055b889f | Amit Shah | |
619 | 055b889f | Amit Shah | static void add_port(VirtIOSerial *vser, uint32_t port_id) |
620 | 055b889f | Amit Shah | { |
621 | 055b889f | Amit Shah | mark_port_added(vser, port_id); |
622 | 055b889f | Amit Shah | |
623 | 055b889f | Amit Shah | send_control_event(find_port_by_id(vser, port_id), |
624 | 055b889f | Amit Shah | VIRTIO_CONSOLE_PORT_ADD, 1);
|
625 | 055b889f | Amit Shah | } |
626 | 055b889f | Amit Shah | |
627 | 055b889f | Amit Shah | static void remove_port(VirtIOSerial *vser, uint32_t port_id) |
628 | 055b889f | Amit Shah | { |
629 | 9ed7b059 | Amit Shah | VirtIOSerialPort *port; |
630 | 055b889f | Amit Shah | unsigned int i; |
631 | 055b889f | Amit Shah | |
632 | 055b889f | Amit Shah | i = port_id / 32;
|
633 | 055b889f | Amit Shah | vser->ports_map[i] &= ~(1U << (port_id % 32)); |
634 | 055b889f | Amit Shah | |
635 | 9ed7b059 | Amit Shah | port = find_port_by_id(vser, port_id); |
636 | 9ed7b059 | Amit Shah | /* Flush out any unconsumed buffers first */
|
637 | 9ed7b059 | Amit Shah | flush_queued_data(port, true);
|
638 | 9ed7b059 | Amit Shah | |
639 | 9ed7b059 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
640 | 055b889f | Amit Shah | } |
641 | 055b889f | Amit Shah | |
642 | 98b19252 | Amit Shah | static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) |
643 | 98b19252 | Amit Shah | { |
644 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
645 | 98b19252 | Amit Shah | VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base); |
646 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
647 | 98b19252 | Amit Shah | VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); |
648 | 98b19252 | Amit Shah | int ret;
|
649 | 98b19252 | Amit Shah | bool plugging_port0;
|
650 | 98b19252 | Amit Shah | |
651 | 98b19252 | Amit Shah | port->vser = bus->vser; |
652 | 98b19252 | Amit Shah | |
653 | 98b19252 | Amit Shah | /*
|
654 | 98b19252 | Amit Shah | * Is the first console port we're seeing? If so, put it up at
|
655 | 98b19252 | Amit Shah | * location 0. This is done for backward compatibility (old
|
656 | 98b19252 | Amit Shah | * kernel, new qemu).
|
657 | 98b19252 | Amit Shah | */
|
658 | 98b19252 | Amit Shah | plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
|
659 | 98b19252 | Amit Shah | |
660 | 055b889f | Amit Shah | if (find_port_by_id(port->vser, port->id)) {
|
661 | 055b889f | Amit Shah | error_report("virtio-serial-bus: A port already exists at id %u\n",
|
662 | 055b889f | Amit Shah | port->id); |
663 | 98b19252 | Amit Shah | return -1; |
664 | 98b19252 | Amit Shah | } |
665 | 98b19252 | Amit Shah | |
666 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
667 | 055b889f | Amit Shah | if (plugging_port0) {
|
668 | 055b889f | Amit Shah | port->id = 0;
|
669 | 055b889f | Amit Shah | } else {
|
670 | 055b889f | Amit Shah | port->id = find_free_port_id(port->vser); |
671 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
672 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
|
673 | 055b889f | Amit Shah | return -1; |
674 | 055b889f | Amit Shah | } |
675 | 055b889f | Amit Shah | } |
676 | 055b889f | Amit Shah | } |
677 | 055b889f | Amit Shah | |
678 | 055b889f | Amit Shah | if (port->id >= port->vser->config.max_nr_ports) {
|
679 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
|
680 | 055b889f | Amit Shah | port->vser->config.max_nr_ports - 1);
|
681 | 055b889f | Amit Shah | return -1; |
682 | 055b889f | Amit Shah | } |
683 | 055b889f | Amit Shah | |
684 | 055b889f | Amit Shah | dev->info = info; |
685 | 98b19252 | Amit Shah | ret = info->init(dev); |
686 | 98b19252 | Amit Shah | if (ret) {
|
687 | 98b19252 | Amit Shah | return ret;
|
688 | 98b19252 | Amit Shah | } |
689 | 98b19252 | Amit Shah | |
690 | 6663a195 | Amit Shah | if (!use_multiport(port->vser)) {
|
691 | 6663a195 | Amit Shah | /*
|
692 | 6663a195 | Amit Shah | * Allow writes to guest in this case; we have no way of
|
693 | 6663a195 | Amit Shah | * knowing if a guest port is connected.
|
694 | 6663a195 | Amit Shah | */
|
695 | 6663a195 | Amit Shah | port->guest_connected = true;
|
696 | 6663a195 | Amit Shah | } |
697 | 6663a195 | Amit Shah | |
698 | 98b19252 | Amit Shah | QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); |
699 | 98b19252 | Amit Shah | port->ivq = port->vser->ivqs[port->id]; |
700 | 98b19252 | Amit Shah | port->ovq = port->vser->ovqs[port->id]; |
701 | 98b19252 | Amit Shah | |
702 | 055b889f | Amit Shah | add_port(port->vser, port->id); |
703 | 055b889f | Amit Shah | |
704 | 98b19252 | Amit Shah | /* Send an update to the guest about this new port added */
|
705 | 98b19252 | Amit Shah | virtio_notify_config(&port->vser->vdev); |
706 | 98b19252 | Amit Shah | |
707 | 98b19252 | Amit Shah | return ret;
|
708 | 98b19252 | Amit Shah | } |
709 | 98b19252 | Amit Shah | |
710 | 98b19252 | Amit Shah | static int virtser_port_qdev_exit(DeviceState *qdev) |
711 | 98b19252 | Amit Shah | { |
712 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
713 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
714 | 98b19252 | Amit Shah | VirtIOSerial *vser = port->vser; |
715 | 98b19252 | Amit Shah | |
716 | 055b889f | Amit Shah | remove_port(port->vser, port->id); |
717 | f146ec9a | Amit Shah | |
718 | 98b19252 | Amit Shah | QTAILQ_REMOVE(&vser->ports, port, next); |
719 | 98b19252 | Amit Shah | |
720 | 98b19252 | Amit Shah | if (port->info->exit)
|
721 | 98b19252 | Amit Shah | port->info->exit(dev); |
722 | 98b19252 | Amit Shah | |
723 | 98b19252 | Amit Shah | return 0; |
724 | 98b19252 | Amit Shah | } |
725 | 98b19252 | Amit Shah | |
726 | 98b19252 | Amit Shah | void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info)
|
727 | 98b19252 | Amit Shah | { |
728 | 98b19252 | Amit Shah | info->qdev.init = virtser_port_qdev_init; |
729 | 98b19252 | Amit Shah | info->qdev.bus_info = &virtser_bus_info; |
730 | 98b19252 | Amit Shah | info->qdev.exit = virtser_port_qdev_exit; |
731 | 98b19252 | Amit Shah | info->qdev.unplug = qdev_simple_unplug_cb; |
732 | 98b19252 | Amit Shah | qdev_register(&info->qdev); |
733 | 98b19252 | Amit Shah | } |
734 | 98b19252 | Amit Shah | |
735 | 98b19252 | Amit Shah | VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) |
736 | 98b19252 | Amit Shah | { |
737 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
738 | 98b19252 | Amit Shah | VirtIODevice *vdev; |
739 | 5ab4bb59 | Amit Shah | uint32_t i, max_supported_ports; |
740 | 98b19252 | Amit Shah | |
741 | 98b19252 | Amit Shah | if (!max_nr_ports)
|
742 | 98b19252 | Amit Shah | return NULL; |
743 | 98b19252 | Amit Shah | |
744 | 5ab4bb59 | Amit Shah | /* Each port takes 2 queues, and one pair is for the control queue */
|
745 | 5ab4bb59 | Amit Shah | max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; |
746 | 5ab4bb59 | Amit Shah | |
747 | 5ab4bb59 | Amit Shah | if (max_nr_ports > max_supported_ports) {
|
748 | 5ab4bb59 | Amit Shah | error_report("maximum ports supported: %u", max_supported_ports);
|
749 | 5ab4bb59 | Amit Shah | return NULL; |
750 | 5ab4bb59 | Amit Shah | } |
751 | 5ab4bb59 | Amit Shah | |
752 | 98b19252 | Amit Shah | vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
|
753 | 98b19252 | Amit Shah | sizeof(struct virtio_console_config), |
754 | 98b19252 | Amit Shah | sizeof(VirtIOSerial));
|
755 | 98b19252 | Amit Shah | |
756 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
757 | 98b19252 | Amit Shah | |
758 | 98b19252 | Amit Shah | /* Spawn a new virtio-serial bus on which the ports will ride as devices */
|
759 | 98b19252 | Amit Shah | vser->bus = virtser_bus_new(dev); |
760 | 98b19252 | Amit Shah | vser->bus->vser = vser; |
761 | 98b19252 | Amit Shah | QTAILQ_INIT(&vser->ports); |
762 | 98b19252 | Amit Shah | |
763 | 98b19252 | Amit Shah | vser->bus->max_nr_ports = max_nr_ports; |
764 | 98b19252 | Amit Shah | vser->ivqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
765 | 98b19252 | Amit Shah | vser->ovqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
766 | 98b19252 | Amit Shah | |
767 | 98b19252 | Amit Shah | /* Add a queue for host to guest transfers for port 0 (backward compat) */
|
768 | 98b19252 | Amit Shah | vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); |
769 | 98b19252 | Amit Shah | /* Add a queue for guest to host transfers for port 0 (backward compat) */
|
770 | 98b19252 | Amit Shah | vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); |
771 | 98b19252 | Amit Shah | |
772 | 98b19252 | Amit Shah | /* control queue: host to guest */
|
773 | 98b19252 | Amit Shah | vser->c_ivq = virtio_add_queue(vdev, 16, control_in);
|
774 | 98b19252 | Amit Shah | /* control queue: guest to host */
|
775 | 98b19252 | Amit Shah | vser->c_ovq = virtio_add_queue(vdev, 16, control_out);
|
776 | 98b19252 | Amit Shah | |
777 | 98b19252 | Amit Shah | for (i = 1; i < vser->bus->max_nr_ports; i++) { |
778 | 98b19252 | Amit Shah | /* Add a per-port queue for host to guest transfers */
|
779 | 98b19252 | Amit Shah | vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
|
780 | 98b19252 | Amit Shah | /* Add a per-per queue for guest to host transfers */
|
781 | 98b19252 | Amit Shah | vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
|
782 | 98b19252 | Amit Shah | } |
783 | 98b19252 | Amit Shah | |
784 | 98b19252 | Amit Shah | vser->config.max_nr_ports = max_nr_ports; |
785 | a132a679 | Alon Levy | vser->ports_map = qemu_mallocz(((max_nr_ports + 31) / 32) |
786 | a132a679 | Alon Levy | * sizeof(vser->ports_map[0])); |
787 | 98b19252 | Amit Shah | /*
|
788 | 98b19252 | Amit Shah | * Reserve location 0 for a console port for backward compat
|
789 | 98b19252 | Amit Shah | * (old kernel, new qemu)
|
790 | 98b19252 | Amit Shah | */
|
791 | 055b889f | Amit Shah | mark_port_added(vser, 0);
|
792 | 98b19252 | Amit Shah | |
793 | 98b19252 | Amit Shah | vser->vdev.get_features = get_features; |
794 | 98b19252 | Amit Shah | vser->vdev.get_config = get_config; |
795 | 98b19252 | Amit Shah | vser->vdev.set_config = set_config; |
796 | 98b19252 | Amit Shah | |
797 | 8b53a865 | Amit Shah | vser->qdev = dev; |
798 | 8b53a865 | Amit Shah | |
799 | 98b19252 | Amit Shah | /*
|
800 | 98b19252 | Amit Shah | * Register for the savevm section with the virtio-console name
|
801 | 98b19252 | Amit Shah | * to preserve backward compat
|
802 | 98b19252 | Amit Shah | */
|
803 | 0be71e32 | Alex Williamson | register_savevm(dev, "virtio-console", -1, 2, virtio_serial_save, |
804 | 98b19252 | Amit Shah | virtio_serial_load, vser); |
805 | 98b19252 | Amit Shah | |
806 | 98b19252 | Amit Shah | return vdev;
|
807 | 98b19252 | Amit Shah | } |
808 | 8b53a865 | Amit Shah | |
809 | 8b53a865 | Amit Shah | void virtio_serial_exit(VirtIODevice *vdev)
|
810 | 8b53a865 | Amit Shah | { |
811 | 8b53a865 | Amit Shah | VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
812 | 8b53a865 | Amit Shah | |
813 | 8b53a865 | Amit Shah | unregister_savevm(vser->qdev, "virtio-console", vser);
|
814 | 8b53a865 | Amit Shah | |
815 | 8b53a865 | Amit Shah | qemu_free(vser->ivqs); |
816 | 8b53a865 | Amit Shah | qemu_free(vser->ovqs); |
817 | 8b53a865 | Amit Shah | qemu_free(vser->ports_map); |
818 | 8b53a865 | Amit Shah | |
819 | 8b53a865 | Amit Shah | virtio_cleanup(vdev); |
820 | 8b53a865 | Amit Shah | } |