root / hw / virtio-serial-bus.c @ 19d110ab
History | View | Annotate | Download (24.4 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 | 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 | 9ed7b059 | Amit Shah | void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) |
289 | 9ed7b059 | Amit Shah | { |
290 | 9ed7b059 | Amit Shah | if (!port) {
|
291 | 9ed7b059 | Amit Shah | return;
|
292 | 9ed7b059 | Amit Shah | } |
293 | 9ed7b059 | Amit Shah | |
294 | 9ed7b059 | Amit Shah | port->throttled = throttle; |
295 | 9ed7b059 | Amit Shah | if (throttle) {
|
296 | 9ed7b059 | Amit Shah | return;
|
297 | 9ed7b059 | Amit Shah | } |
298 | 9ed7b059 | Amit Shah | |
299 | 6bff8656 | Amit Shah | flush_queued_data(port); |
300 | 9ed7b059 | Amit Shah | } |
301 | 9ed7b059 | Amit Shah | |
302 | 98b19252 | Amit Shah | /* Guest wants to notify us of some event */
|
303 | e61da14d | Amit Shah | static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) |
304 | 98b19252 | Amit Shah | { |
305 | 98b19252 | Amit Shah | struct VirtIOSerialPort *port;
|
306 | 98b19252 | Amit Shah | struct virtio_console_control cpkt, *gcpkt;
|
307 | 160600fd | Amit Shah | uint8_t *buffer; |
308 | 160600fd | Amit Shah | size_t buffer_len; |
309 | 98b19252 | Amit Shah | |
310 | 98b19252 | Amit Shah | gcpkt = buf; |
311 | 98b19252 | Amit Shah | |
312 | e61da14d | Amit Shah | if (len < sizeof(cpkt)) { |
313 | e61da14d | Amit Shah | /* The guest sent an invalid control packet */
|
314 | e61da14d | Amit Shah | return;
|
315 | e61da14d | Amit Shah | } |
316 | e61da14d | Amit Shah | |
317 | 98b19252 | Amit Shah | cpkt.event = lduw_p(&gcpkt->event); |
318 | 98b19252 | Amit Shah | cpkt.value = lduw_p(&gcpkt->value); |
319 | 98b19252 | Amit Shah | |
320 | 055b889f | Amit Shah | port = find_port_by_id(vser, ldl_p(&gcpkt->id)); |
321 | 055b889f | Amit Shah | if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
|
322 | 055b889f | Amit Shah | return;
|
323 | 055b889f | Amit Shah | |
324 | 98b19252 | Amit Shah | switch(cpkt.event) {
|
325 | 055b889f | Amit Shah | case VIRTIO_CONSOLE_DEVICE_READY:
|
326 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
327 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding device %s\n",
|
328 | 4048c7c3 | Amit Shah | vser->bus->qbus.name); |
329 | 4048c7c3 | Amit Shah | break;
|
330 | 4048c7c3 | Amit Shah | } |
331 | 055b889f | Amit Shah | /*
|
332 | 055b889f | Amit Shah | * The device is up, we can now tell the device about all the
|
333 | 055b889f | Amit Shah | * ports we have here.
|
334 | 055b889f | Amit Shah | */
|
335 | 055b889f | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
336 | 055b889f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
|
337 | 055b889f | Amit Shah | } |
338 | 055b889f | Amit Shah | break;
|
339 | 055b889f | Amit Shah | |
340 | 98b19252 | Amit Shah | case VIRTIO_CONSOLE_PORT_READY:
|
341 | 4048c7c3 | Amit Shah | if (!cpkt.value) {
|
342 | 4048c7c3 | Amit Shah | error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n",
|
343 | 4048c7c3 | Amit Shah | port->id, vser->bus->qbus.name); |
344 | 4048c7c3 | Amit Shah | break;
|
345 | 4048c7c3 | Amit Shah | } |
346 | 98b19252 | Amit Shah | /*
|
347 | 98b19252 | Amit Shah | * Now that we know the guest asked for the port name, we're
|
348 | 98b19252 | Amit Shah | * sure the guest has initialised whatever state is necessary
|
349 | 98b19252 | Amit Shah | * for this port. Now's a good time to let the guest know if
|
350 | 98b19252 | Amit Shah | * this port is a console port so that the guest can hook it
|
351 | 98b19252 | Amit Shah | * up to hvc.
|
352 | 98b19252 | Amit Shah | */
|
353 | 98b19252 | Amit Shah | if (port->is_console) {
|
354 | 98b19252 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
|
355 | 98b19252 | Amit Shah | } |
356 | 6663a195 | Amit Shah | |
357 | 160600fd | Amit Shah | if (port->name) {
|
358 | 160600fd | Amit Shah | stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); |
359 | 160600fd | Amit Shah | stw_p(&cpkt.value, 1);
|
360 | 160600fd | Amit Shah | |
361 | 160600fd | Amit Shah | buffer_len = sizeof(cpkt) + strlen(port->name) + 1; |
362 | 160600fd | Amit Shah | buffer = qemu_malloc(buffer_len); |
363 | 160600fd | Amit Shah | |
364 | 160600fd | Amit Shah | memcpy(buffer, &cpkt, sizeof(cpkt));
|
365 | 160600fd | Amit Shah | memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
|
366 | 160600fd | Amit Shah | buffer[buffer_len - 1] = 0; |
367 | 160600fd | Amit Shah | |
368 | 160600fd | Amit Shah | send_control_msg(port, buffer, buffer_len); |
369 | 160600fd | Amit Shah | qemu_free(buffer); |
370 | 160600fd | Amit Shah | } |
371 | 160600fd | Amit Shah | |
372 | 6663a195 | Amit Shah | if (port->host_connected) {
|
373 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
374 | 6663a195 | Amit Shah | } |
375 | 6663a195 | Amit Shah | |
376 | 98b19252 | Amit Shah | /*
|
377 | 98b19252 | Amit Shah | * When the guest has asked us for this information it means
|
378 | 98b19252 | Amit Shah | * the guest is all setup and has its virtqueues
|
379 | 98b19252 | Amit Shah | * initialised. If some app is interested in knowing about
|
380 | 98b19252 | Amit Shah | * this event, let it know.
|
381 | 98b19252 | Amit Shah | */
|
382 | 98b19252 | Amit Shah | if (port->info->guest_ready) {
|
383 | 98b19252 | Amit Shah | port->info->guest_ready(port); |
384 | 98b19252 | Amit Shah | } |
385 | 98b19252 | Amit Shah | break;
|
386 | 6663a195 | Amit Shah | |
387 | 6663a195 | Amit Shah | case VIRTIO_CONSOLE_PORT_OPEN:
|
388 | 6663a195 | Amit Shah | port->guest_connected = cpkt.value; |
389 | 6663a195 | Amit Shah | if (cpkt.value && port->info->guest_open) {
|
390 | 6663a195 | Amit Shah | /* Send the guest opened notification if an app is interested */
|
391 | 6663a195 | Amit Shah | port->info->guest_open(port); |
392 | 6663a195 | Amit Shah | } |
393 | 6663a195 | Amit Shah | |
394 | 6663a195 | Amit Shah | if (!cpkt.value && port->info->guest_close) {
|
395 | 6663a195 | Amit Shah | /* Send the guest closed notification if an app is interested */
|
396 | 6663a195 | Amit Shah | port->info->guest_close(port); |
397 | 6663a195 | Amit Shah | } |
398 | 6663a195 | Amit Shah | break;
|
399 | 98b19252 | Amit Shah | } |
400 | 98b19252 | Amit Shah | } |
401 | 98b19252 | Amit Shah | |
402 | 98b19252 | Amit Shah | static void control_in(VirtIODevice *vdev, VirtQueue *vq) |
403 | 98b19252 | Amit Shah | { |
404 | 98b19252 | Amit Shah | } |
405 | 98b19252 | Amit Shah | |
406 | 98b19252 | Amit Shah | static void control_out(VirtIODevice *vdev, VirtQueue *vq) |
407 | 98b19252 | Amit Shah | { |
408 | 98b19252 | Amit Shah | VirtQueueElement elem; |
409 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
410 | e61da14d | Amit Shah | uint8_t *buf; |
411 | e61da14d | Amit Shah | size_t len; |
412 | 98b19252 | Amit Shah | |
413 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
414 | 98b19252 | Amit Shah | |
415 | e61da14d | Amit Shah | len = 0;
|
416 | e61da14d | Amit Shah | buf = NULL;
|
417 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
418 | e61da14d | Amit Shah | size_t cur_len, copied; |
419 | e61da14d | Amit Shah | |
420 | e61da14d | Amit Shah | cur_len = iov_size(elem.out_sg, elem.out_num); |
421 | e61da14d | Amit Shah | /*
|
422 | e61da14d | Amit Shah | * Allocate a new buf only if we didn't have one previously or
|
423 | e61da14d | Amit Shah | * if the size of the buf differs
|
424 | e61da14d | Amit Shah | */
|
425 | e61da14d | Amit Shah | if (cur_len > len) {
|
426 | e61da14d | Amit Shah | qemu_free(buf); |
427 | e61da14d | Amit Shah | |
428 | e61da14d | Amit Shah | buf = qemu_malloc(cur_len); |
429 | e61da14d | Amit Shah | len = cur_len; |
430 | e61da14d | Amit Shah | } |
431 | e61da14d | Amit Shah | copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len);
|
432 | e61da14d | Amit Shah | |
433 | e61da14d | Amit Shah | handle_control_message(vser, buf, copied); |
434 | 1e4476aa | Amit Shah | virtqueue_push(vq, &elem, 0);
|
435 | 98b19252 | Amit Shah | } |
436 | e61da14d | Amit Shah | qemu_free(buf); |
437 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
438 | 98b19252 | Amit Shah | } |
439 | 98b19252 | Amit Shah | |
440 | 98b19252 | Amit Shah | /* Guest wrote something to some port. */
|
441 | 98b19252 | Amit Shah | static void handle_output(VirtIODevice *vdev, VirtQueue *vq) |
442 | 98b19252 | Amit Shah | { |
443 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
444 | a69c7600 | Amit Shah | VirtIOSerialPort *port; |
445 | 98b19252 | Amit Shah | |
446 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
447 | a69c7600 | Amit Shah | port = find_port_by_vq(vser, vq); |
448 | 98b19252 | Amit Shah | |
449 | a69c7600 | Amit Shah | if (!port || !port->host_connected || !port->info->have_data) {
|
450 | 6bff8656 | Amit Shah | discard_vq_data(vq, vdev); |
451 | 6bff8656 | Amit Shah | return;
|
452 | 6bff8656 | Amit Shah | } |
453 | e9b382b0 | Amit Shah | |
454 | e9b382b0 | Amit Shah | if (!port->throttled) {
|
455 | e9b382b0 | Amit Shah | do_flush_queued_data(port, vq, vdev); |
456 | 9ed7b059 | Amit Shah | return;
|
457 | 9ed7b059 | Amit Shah | } |
458 | 98b19252 | Amit Shah | } |
459 | 98b19252 | Amit Shah | |
460 | 98b19252 | Amit Shah | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
461 | 98b19252 | Amit Shah | { |
462 | 98b19252 | Amit Shah | } |
463 | 98b19252 | Amit Shah | |
464 | 98b19252 | Amit Shah | static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
|
465 | 98b19252 | Amit Shah | { |
466 | 306eb457 | Amit Shah | VirtIOSerial *vser; |
467 | 306eb457 | Amit Shah | |
468 | 306eb457 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
469 | 306eb457 | Amit Shah | |
470 | ee4d45be | Michael S. Tsirkin | if (vser->bus->max_nr_ports > 1) { |
471 | ee4d45be | Michael S. Tsirkin | features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
|
472 | ee4d45be | Michael S. Tsirkin | } |
473 | 98b19252 | Amit Shah | return features;
|
474 | 98b19252 | Amit Shah | } |
475 | 98b19252 | Amit Shah | |
476 | 98b19252 | Amit Shah | /* Guest requested config info */
|
477 | 98b19252 | Amit Shah | static void get_config(VirtIODevice *vdev, uint8_t *config_data) |
478 | 98b19252 | Amit Shah | { |
479 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
480 | 98b19252 | Amit Shah | |
481 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
482 | 98b19252 | Amit Shah | memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); |
483 | 98b19252 | Amit Shah | } |
484 | 98b19252 | Amit Shah | |
485 | 98b19252 | Amit Shah | static void set_config(VirtIODevice *vdev, const uint8_t *config_data) |
486 | 98b19252 | Amit Shah | { |
487 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
488 | 98b19252 | Amit Shah | |
489 | 98b19252 | Amit Shah | memcpy(&config, config_data, sizeof(config));
|
490 | 98b19252 | Amit Shah | } |
491 | 98b19252 | Amit Shah | |
492 | 98b19252 | Amit Shah | static void virtio_serial_save(QEMUFile *f, void *opaque) |
493 | 98b19252 | Amit Shah | { |
494 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
495 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
496 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
497 | 5c1c9bb2 | Alexey Kardashevskiy | unsigned int i, max_nr_ports; |
498 | 98b19252 | Amit Shah | |
499 | 98b19252 | Amit Shah | /* The virtio device */
|
500 | 98b19252 | Amit Shah | virtio_save(&s->vdev, f); |
501 | 98b19252 | Amit Shah | |
502 | 98b19252 | Amit Shah | /* The config space */
|
503 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.cols); |
504 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.rows); |
505 | 6663a195 | Amit Shah | |
506 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->config.max_nr_ports); |
507 | 055b889f | Amit Shah | |
508 | 055b889f | Amit Shah | /* The ports map */
|
509 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(s->config.max_nr_ports); |
510 | 5c1c9bb2 | Alexey Kardashevskiy | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
511 | 055b889f | Amit Shah | qemu_put_be32s(f, &s->ports_map[i]); |
512 | 055b889f | Amit Shah | } |
513 | 6663a195 | Amit Shah | |
514 | 055b889f | Amit Shah | /* Ports */
|
515 | e245795b | Amit Shah | |
516 | 6663a195 | Amit Shah | nr_active_ports = 0;
|
517 | e245795b | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
518 | 6663a195 | Amit Shah | nr_active_ports++; |
519 | e245795b | Amit Shah | } |
520 | 6663a195 | Amit Shah | |
521 | 6663a195 | Amit Shah | qemu_put_be32s(f, &nr_active_ports); |
522 | 6663a195 | Amit Shah | |
523 | 6663a195 | Amit Shah | /*
|
524 | 6663a195 | Amit Shah | * Items in struct VirtIOSerialPort.
|
525 | 6663a195 | Amit Shah | */
|
526 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
527 | 37f95bf3 | Amit Shah | uint32_t elem_popped; |
528 | 37f95bf3 | Amit Shah | |
529 | 6663a195 | Amit Shah | qemu_put_be32s(f, &port->id); |
530 | 6663a195 | Amit Shah | qemu_put_byte(f, port->guest_connected); |
531 | 31abe21f | Amit Shah | qemu_put_byte(f, port->host_connected); |
532 | 37f95bf3 | Amit Shah | |
533 | 37f95bf3 | Amit Shah | elem_popped = 0;
|
534 | 37f95bf3 | Amit Shah | if (port->elem.out_num) {
|
535 | 37f95bf3 | Amit Shah | elem_popped = 1;
|
536 | 37f95bf3 | Amit Shah | } |
537 | 37f95bf3 | Amit Shah | qemu_put_be32s(f, &elem_popped); |
538 | 37f95bf3 | Amit Shah | if (elem_popped) {
|
539 | 37f95bf3 | Amit Shah | qemu_put_be32s(f, &port->iov_idx); |
540 | 37f95bf3 | Amit Shah | qemu_put_be64s(f, &port->iov_offset); |
541 | 37f95bf3 | Amit Shah | |
542 | 37f95bf3 | Amit Shah | qemu_put_buffer(f, (unsigned char *)&port->elem, |
543 | 37f95bf3 | Amit Shah | sizeof(port->elem));
|
544 | 37f95bf3 | Amit Shah | } |
545 | 6663a195 | Amit Shah | } |
546 | 98b19252 | Amit Shah | } |
547 | 98b19252 | Amit Shah | |
548 | 98b19252 | Amit Shah | static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) |
549 | 98b19252 | Amit Shah | { |
550 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
551 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
552 | f83ccb3e | Markus Armbruster | uint32_t max_nr_ports, nr_active_ports, ports_map; |
553 | 6663a195 | Amit Shah | unsigned int i; |
554 | 98b19252 | Amit Shah | |
555 | 37f95bf3 | Amit Shah | if (version_id > 3) { |
556 | 98b19252 | Amit Shah | return -EINVAL;
|
557 | 98b19252 | Amit Shah | } |
558 | 6663a195 | Amit Shah | |
559 | 98b19252 | Amit Shah | /* The virtio device */
|
560 | 98b19252 | Amit Shah | virtio_load(&s->vdev, f); |
561 | 98b19252 | Amit Shah | |
562 | 98b19252 | Amit Shah | if (version_id < 2) { |
563 | 98b19252 | Amit Shah | return 0; |
564 | 98b19252 | Amit Shah | } |
565 | 98b19252 | Amit Shah | |
566 | 98b19252 | Amit Shah | /* The config space */
|
567 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.cols); |
568 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.rows); |
569 | 295587f7 | Amit Shah | |
570 | 055b889f | Amit Shah | qemu_get_be32s(f, &max_nr_ports); |
571 | 5c1c9bb2 | Alexey Kardashevskiy | tswap32s(&max_nr_ports); |
572 | 5c1c9bb2 | Alexey Kardashevskiy | if (max_nr_ports > tswap32(s->config.max_nr_ports)) {
|
573 | 055b889f | Amit Shah | /* Source could have had more ports than us. Fail migration. */
|
574 | 295587f7 | Amit Shah | return -EINVAL;
|
575 | 295587f7 | Amit Shah | } |
576 | 98b19252 | Amit Shah | |
577 | 055b889f | Amit Shah | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
578 | f83ccb3e | Markus Armbruster | qemu_get_be32s(f, &ports_map); |
579 | 055b889f | Amit Shah | |
580 | f83ccb3e | Markus Armbruster | if (ports_map != s->ports_map[i]) {
|
581 | 055b889f | Amit Shah | /*
|
582 | 055b889f | Amit Shah | * Ports active on source and destination don't
|
583 | 055b889f | Amit Shah | * match. Fail migration.
|
584 | 055b889f | Amit Shah | */
|
585 | 055b889f | Amit Shah | return -EINVAL;
|
586 | 055b889f | Amit Shah | } |
587 | e245795b | Amit Shah | } |
588 | e245795b | Amit Shah | |
589 | 6663a195 | Amit Shah | qemu_get_be32s(f, &nr_active_ports); |
590 | 6663a195 | Amit Shah | |
591 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerialPort */
|
592 | 6663a195 | Amit Shah | for (i = 0; i < nr_active_ports; i++) { |
593 | 6663a195 | Amit Shah | uint32_t id; |
594 | 31abe21f | Amit Shah | bool host_connected;
|
595 | 6663a195 | Amit Shah | |
596 | 6663a195 | Amit Shah | id = qemu_get_be32(f); |
597 | 6663a195 | Amit Shah | port = find_port_by_id(s, id); |
598 | fbe0c559 | Michael S. Tsirkin | if (!port) {
|
599 | fbe0c559 | Michael S. Tsirkin | return -EINVAL;
|
600 | fbe0c559 | Michael S. Tsirkin | } |
601 | 6663a195 | Amit Shah | |
602 | 6663a195 | Amit Shah | port->guest_connected = qemu_get_byte(f); |
603 | 31abe21f | Amit Shah | host_connected = qemu_get_byte(f); |
604 | 31abe21f | Amit Shah | if (host_connected != port->host_connected) {
|
605 | 31abe21f | Amit Shah | /*
|
606 | 31abe21f | Amit Shah | * We have to let the guest know of the host connection
|
607 | 31abe21f | Amit Shah | * status change
|
608 | 31abe21f | Amit Shah | */
|
609 | 31abe21f | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, |
610 | 31abe21f | Amit Shah | port->host_connected); |
611 | 31abe21f | Amit Shah | } |
612 | 37f95bf3 | Amit Shah | |
613 | 37f95bf3 | Amit Shah | if (version_id > 2) { |
614 | 37f95bf3 | Amit Shah | uint32_t elem_popped; |
615 | 37f95bf3 | Amit Shah | |
616 | 37f95bf3 | Amit Shah | qemu_get_be32s(f, &elem_popped); |
617 | 37f95bf3 | Amit Shah | if (elem_popped) {
|
618 | 37f95bf3 | Amit Shah | qemu_get_be32s(f, &port->iov_idx); |
619 | 37f95bf3 | Amit Shah | qemu_get_be64s(f, &port->iov_offset); |
620 | 37f95bf3 | Amit Shah | |
621 | 37f95bf3 | Amit Shah | qemu_get_buffer(f, (unsigned char *)&port->elem, |
622 | 37f95bf3 | Amit Shah | sizeof(port->elem));
|
623 | 37f95bf3 | Amit Shah | virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, |
624 | 37f95bf3 | Amit Shah | port->elem.in_num, 1);
|
625 | 37f95bf3 | Amit Shah | virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, |
626 | 37f95bf3 | Amit Shah | port->elem.out_num, 1);
|
627 | 37f95bf3 | Amit Shah | |
628 | 37f95bf3 | Amit Shah | /*
|
629 | 37f95bf3 | Amit Shah | * Port was throttled on source machine. Let's
|
630 | 37f95bf3 | Amit Shah | * unthrottle it here so data starts flowing again.
|
631 | 37f95bf3 | Amit Shah | */
|
632 | 37f95bf3 | Amit Shah | virtio_serial_throttle_port(port, false);
|
633 | 37f95bf3 | Amit Shah | } |
634 | 37f95bf3 | Amit Shah | } |
635 | 6663a195 | Amit Shah | } |
636 | 98b19252 | Amit Shah | return 0; |
637 | 98b19252 | Amit Shah | } |
638 | 98b19252 | Amit Shah | |
639 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
640 | 98b19252 | Amit Shah | |
641 | 98b19252 | Amit Shah | static struct BusInfo virtser_bus_info = { |
642 | 98b19252 | Amit Shah | .name = "virtio-serial-bus",
|
643 | 98b19252 | Amit Shah | .size = sizeof(VirtIOSerialBus),
|
644 | 98b19252 | Amit Shah | .print_dev = virtser_bus_dev_print, |
645 | 98b19252 | Amit Shah | }; |
646 | 98b19252 | Amit Shah | |
647 | 98b19252 | Amit Shah | static VirtIOSerialBus *virtser_bus_new(DeviceState *dev)
|
648 | 98b19252 | Amit Shah | { |
649 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
650 | 98b19252 | Amit Shah | |
651 | 9ae84f0a | Amit Shah | bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL));
|
652 | 98b19252 | Amit Shah | bus->qbus.allow_hotplug = 1;
|
653 | 98b19252 | Amit Shah | |
654 | 98b19252 | Amit Shah | return bus;
|
655 | 98b19252 | Amit Shah | } |
656 | 98b19252 | Amit Shah | |
657 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
658 | 98b19252 | Amit Shah | { |
659 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
660 | 98b19252 | Amit Shah | |
661 | 98b19252 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
|
662 | 98b19252 | Amit Shah | indent, "", port->id);
|
663 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
|
664 | 6663a195 | Amit Shah | indent, "", port->guest_connected);
|
665 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
|
666 | 6663a195 | Amit Shah | indent, "", port->host_connected);
|
667 | 9ed7b059 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n",
|
668 | 9ed7b059 | Amit Shah | indent, "", port->throttled);
|
669 | 98b19252 | Amit Shah | } |
670 | 98b19252 | Amit Shah | |
671 | 055b889f | Amit Shah | /* This function is only used if a port id is not provided by the user */
|
672 | 055b889f | Amit Shah | static uint32_t find_free_port_id(VirtIOSerial *vser)
|
673 | 055b889f | Amit Shah | { |
674 | 5c1c9bb2 | Alexey Kardashevskiy | unsigned int i, max_nr_ports; |
675 | 055b889f | Amit Shah | |
676 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(vser->config.max_nr_ports); |
677 | 5c1c9bb2 | Alexey Kardashevskiy | for (i = 0; i < (max_nr_ports + 31) / 32; i++) { |
678 | 055b889f | Amit Shah | uint32_t map, bit; |
679 | 055b889f | Amit Shah | |
680 | 055b889f | Amit Shah | map = vser->ports_map[i]; |
681 | 055b889f | Amit Shah | bit = ffs(~map); |
682 | 055b889f | Amit Shah | if (bit) {
|
683 | 055b889f | Amit Shah | return (bit - 1) + i * 32; |
684 | 055b889f | Amit Shah | } |
685 | 055b889f | Amit Shah | } |
686 | 055b889f | Amit Shah | return VIRTIO_CONSOLE_BAD_ID;
|
687 | 055b889f | Amit Shah | } |
688 | 055b889f | Amit Shah | |
689 | 055b889f | Amit Shah | static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) |
690 | 055b889f | Amit Shah | { |
691 | 055b889f | Amit Shah | unsigned int i; |
692 | 055b889f | Amit Shah | |
693 | 055b889f | Amit Shah | i = port_id / 32;
|
694 | 055b889f | Amit Shah | vser->ports_map[i] |= 1U << (port_id % 32); |
695 | 055b889f | Amit Shah | } |
696 | 055b889f | Amit Shah | |
697 | 055b889f | Amit Shah | static void add_port(VirtIOSerial *vser, uint32_t port_id) |
698 | 055b889f | Amit Shah | { |
699 | 055b889f | Amit Shah | mark_port_added(vser, port_id); |
700 | 055b889f | Amit Shah | |
701 | 055b889f | Amit Shah | send_control_event(find_port_by_id(vser, port_id), |
702 | 055b889f | Amit Shah | VIRTIO_CONSOLE_PORT_ADD, 1);
|
703 | 055b889f | Amit Shah | } |
704 | 055b889f | Amit Shah | |
705 | 055b889f | Amit Shah | static void remove_port(VirtIOSerial *vser, uint32_t port_id) |
706 | 055b889f | Amit Shah | { |
707 | 9ed7b059 | Amit Shah | VirtIOSerialPort *port; |
708 | 055b889f | Amit Shah | unsigned int i; |
709 | 055b889f | Amit Shah | |
710 | 055b889f | Amit Shah | i = port_id / 32;
|
711 | 055b889f | Amit Shah | vser->ports_map[i] &= ~(1U << (port_id % 32)); |
712 | 055b889f | Amit Shah | |
713 | 9ed7b059 | Amit Shah | port = find_port_by_id(vser, port_id); |
714 | 9ed7b059 | Amit Shah | /* Flush out any unconsumed buffers first */
|
715 | 6bff8656 | Amit Shah | discard_vq_data(port->ovq, &port->vser->vdev); |
716 | 9ed7b059 | Amit Shah | |
717 | 9ed7b059 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
718 | 055b889f | Amit Shah | } |
719 | 055b889f | Amit Shah | |
720 | 98b19252 | Amit Shah | static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) |
721 | 98b19252 | Amit Shah | { |
722 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
723 | 98b19252 | Amit Shah | VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base); |
724 | 98b19252 | Amit Shah | VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); |
725 | 5c1c9bb2 | Alexey Kardashevskiy | int ret, max_nr_ports;
|
726 | 98b19252 | Amit Shah | bool plugging_port0;
|
727 | 98b19252 | Amit Shah | |
728 | 98b19252 | Amit Shah | port->vser = bus->vser; |
729 | 98b19252 | Amit Shah | |
730 | 98b19252 | Amit Shah | /*
|
731 | 98b19252 | Amit Shah | * Is the first console port we're seeing? If so, put it up at
|
732 | 98b19252 | Amit Shah | * location 0. This is done for backward compatibility (old
|
733 | 98b19252 | Amit Shah | * kernel, new qemu).
|
734 | 98b19252 | Amit Shah | */
|
735 | 98b19252 | Amit Shah | plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
|
736 | 98b19252 | Amit Shah | |
737 | 055b889f | Amit Shah | if (find_port_by_id(port->vser, port->id)) {
|
738 | 055b889f | Amit Shah | error_report("virtio-serial-bus: A port already exists at id %u\n",
|
739 | 055b889f | Amit Shah | port->id); |
740 | 98b19252 | Amit Shah | return -1; |
741 | 98b19252 | Amit Shah | } |
742 | 98b19252 | Amit Shah | |
743 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
744 | 055b889f | Amit Shah | if (plugging_port0) {
|
745 | 055b889f | Amit Shah | port->id = 0;
|
746 | 055b889f | Amit Shah | } else {
|
747 | 055b889f | Amit Shah | port->id = find_free_port_id(port->vser); |
748 | 055b889f | Amit Shah | if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
749 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
|
750 | 055b889f | Amit Shah | return -1; |
751 | 055b889f | Amit Shah | } |
752 | 055b889f | Amit Shah | } |
753 | 055b889f | Amit Shah | } |
754 | 055b889f | Amit Shah | |
755 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports = tswap32(port->vser->config.max_nr_ports); |
756 | 5c1c9bb2 | Alexey Kardashevskiy | if (port->id >= max_nr_ports) {
|
757 | 055b889f | Amit Shah | error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
|
758 | 5c1c9bb2 | Alexey Kardashevskiy | max_nr_ports - 1);
|
759 | 055b889f | Amit Shah | return -1; |
760 | 055b889f | Amit Shah | } |
761 | 055b889f | Amit Shah | |
762 | a43f9c90 | Gerd Hoffmann | port->info = info; |
763 | a43f9c90 | Gerd Hoffmann | ret = info->init(port); |
764 | 98b19252 | Amit Shah | if (ret) {
|
765 | 98b19252 | Amit Shah | return ret;
|
766 | 98b19252 | Amit Shah | } |
767 | 98b19252 | Amit Shah | |
768 | 6663a195 | Amit Shah | if (!use_multiport(port->vser)) {
|
769 | 6663a195 | Amit Shah | /*
|
770 | 6663a195 | Amit Shah | * Allow writes to guest in this case; we have no way of
|
771 | 6663a195 | Amit Shah | * knowing if a guest port is connected.
|
772 | 6663a195 | Amit Shah | */
|
773 | 6663a195 | Amit Shah | port->guest_connected = true;
|
774 | 6663a195 | Amit Shah | } |
775 | 6663a195 | Amit Shah | |
776 | f1925dff | Amit Shah | port->elem.out_num = 0;
|
777 | f1925dff | Amit Shah | |
778 | 98b19252 | Amit Shah | QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); |
779 | 98b19252 | Amit Shah | port->ivq = port->vser->ivqs[port->id]; |
780 | 98b19252 | Amit Shah | port->ovq = port->vser->ovqs[port->id]; |
781 | 98b19252 | Amit Shah | |
782 | 055b889f | Amit Shah | add_port(port->vser, port->id); |
783 | 055b889f | Amit Shah | |
784 | 98b19252 | Amit Shah | /* Send an update to the guest about this new port added */
|
785 | 98b19252 | Amit Shah | virtio_notify_config(&port->vser->vdev); |
786 | 98b19252 | Amit Shah | |
787 | 98b19252 | Amit Shah | return ret;
|
788 | 98b19252 | Amit Shah | } |
789 | 98b19252 | Amit Shah | |
790 | 98b19252 | Amit Shah | static int virtser_port_qdev_exit(DeviceState *qdev) |
791 | 98b19252 | Amit Shah | { |
792 | a43f9c90 | Gerd Hoffmann | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); |
793 | 98b19252 | Amit Shah | VirtIOSerial *vser = port->vser; |
794 | 98b19252 | Amit Shah | |
795 | 055b889f | Amit Shah | remove_port(port->vser, port->id); |
796 | f146ec9a | Amit Shah | |
797 | 98b19252 | Amit Shah | QTAILQ_REMOVE(&vser->ports, port, next); |
798 | 98b19252 | Amit Shah | |
799 | 98b19252 | Amit Shah | if (port->info->exit)
|
800 | a43f9c90 | Gerd Hoffmann | port->info->exit(port); |
801 | 98b19252 | Amit Shah | |
802 | 98b19252 | Amit Shah | return 0; |
803 | 98b19252 | Amit Shah | } |
804 | 98b19252 | Amit Shah | |
805 | 98b19252 | Amit Shah | void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info)
|
806 | 98b19252 | Amit Shah | { |
807 | 98b19252 | Amit Shah | info->qdev.init = virtser_port_qdev_init; |
808 | 98b19252 | Amit Shah | info->qdev.bus_info = &virtser_bus_info; |
809 | 98b19252 | Amit Shah | info->qdev.exit = virtser_port_qdev_exit; |
810 | 98b19252 | Amit Shah | info->qdev.unplug = qdev_simple_unplug_cb; |
811 | 98b19252 | Amit Shah | qdev_register(&info->qdev); |
812 | 98b19252 | Amit Shah | } |
813 | 98b19252 | Amit Shah | |
814 | 6b331efb | Amit Shah | VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) |
815 | 98b19252 | Amit Shah | { |
816 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
817 | 98b19252 | Amit Shah | VirtIODevice *vdev; |
818 | 5ab4bb59 | Amit Shah | uint32_t i, max_supported_ports; |
819 | 98b19252 | Amit Shah | |
820 | 6b331efb | Amit Shah | if (!conf->max_virtserial_ports)
|
821 | 98b19252 | Amit Shah | return NULL; |
822 | 98b19252 | Amit Shah | |
823 | 5ab4bb59 | Amit Shah | /* Each port takes 2 queues, and one pair is for the control queue */
|
824 | 5ab4bb59 | Amit Shah | max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; |
825 | 5ab4bb59 | Amit Shah | |
826 | 6b331efb | Amit Shah | if (conf->max_virtserial_ports > max_supported_ports) {
|
827 | 5ab4bb59 | Amit Shah | error_report("maximum ports supported: %u", max_supported_ports);
|
828 | 5ab4bb59 | Amit Shah | return NULL; |
829 | 5ab4bb59 | Amit Shah | } |
830 | 5ab4bb59 | Amit Shah | |
831 | 98b19252 | Amit Shah | vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
|
832 | 98b19252 | Amit Shah | sizeof(struct virtio_console_config), |
833 | 98b19252 | Amit Shah | sizeof(VirtIOSerial));
|
834 | 98b19252 | Amit Shah | |
835 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
836 | 98b19252 | Amit Shah | |
837 | 98b19252 | Amit Shah | /* Spawn a new virtio-serial bus on which the ports will ride as devices */
|
838 | 98b19252 | Amit Shah | vser->bus = virtser_bus_new(dev); |
839 | 98b19252 | Amit Shah | vser->bus->vser = vser; |
840 | 98b19252 | Amit Shah | QTAILQ_INIT(&vser->ports); |
841 | 98b19252 | Amit Shah | |
842 | 6b331efb | Amit Shah | vser->bus->max_nr_ports = conf->max_virtserial_ports; |
843 | 6b331efb | Amit Shah | vser->ivqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
|
844 | 6b331efb | Amit Shah | vser->ovqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
|
845 | 98b19252 | Amit Shah | |
846 | 98b19252 | Amit Shah | /* Add a queue for host to guest transfers for port 0 (backward compat) */
|
847 | 98b19252 | Amit Shah | vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); |
848 | 98b19252 | Amit Shah | /* Add a queue for guest to host transfers for port 0 (backward compat) */
|
849 | 98b19252 | Amit Shah | vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); |
850 | 98b19252 | Amit Shah | |
851 | a01a9cb8 | Amit Shah | /* TODO: host to guest notifications can get dropped
|
852 | a01a9cb8 | Amit Shah | * if the queue fills up. Implement queueing in host,
|
853 | a01a9cb8 | Amit Shah | * this might also make it possible to reduce the control
|
854 | a01a9cb8 | Amit Shah | * queue size: as guest preposts buffers there,
|
855 | a01a9cb8 | Amit Shah | * this will save 4Kbyte of guest memory per entry. */
|
856 | a01a9cb8 | Amit Shah | |
857 | 98b19252 | Amit Shah | /* control queue: host to guest */
|
858 | a01a9cb8 | Amit Shah | vser->c_ivq = virtio_add_queue(vdev, 32, control_in);
|
859 | 98b19252 | Amit Shah | /* control queue: guest to host */
|
860 | a01a9cb8 | Amit Shah | vser->c_ovq = virtio_add_queue(vdev, 32, control_out);
|
861 | 98b19252 | Amit Shah | |
862 | 98b19252 | Amit Shah | for (i = 1; i < vser->bus->max_nr_ports; i++) { |
863 | 98b19252 | Amit Shah | /* Add a per-port queue for host to guest transfers */
|
864 | 98b19252 | Amit Shah | vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
|
865 | 98b19252 | Amit Shah | /* Add a per-per queue for guest to host transfers */
|
866 | 98b19252 | Amit Shah | vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
|
867 | 98b19252 | Amit Shah | } |
868 | 98b19252 | Amit Shah | |
869 | 5c1c9bb2 | Alexey Kardashevskiy | vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports); |
870 | 6b331efb | Amit Shah | vser->ports_map = qemu_mallocz(((conf->max_virtserial_ports + 31) / 32) |
871 | a132a679 | Alon Levy | * sizeof(vser->ports_map[0])); |
872 | 98b19252 | Amit Shah | /*
|
873 | 98b19252 | Amit Shah | * Reserve location 0 for a console port for backward compat
|
874 | 98b19252 | Amit Shah | * (old kernel, new qemu)
|
875 | 98b19252 | Amit Shah | */
|
876 | 055b889f | Amit Shah | mark_port_added(vser, 0);
|
877 | 98b19252 | Amit Shah | |
878 | 98b19252 | Amit Shah | vser->vdev.get_features = get_features; |
879 | 98b19252 | Amit Shah | vser->vdev.get_config = get_config; |
880 | 98b19252 | Amit Shah | vser->vdev.set_config = set_config; |
881 | 98b19252 | Amit Shah | |
882 | 8b53a865 | Amit Shah | vser->qdev = dev; |
883 | 8b53a865 | Amit Shah | |
884 | 98b19252 | Amit Shah | /*
|
885 | 98b19252 | Amit Shah | * Register for the savevm section with the virtio-console name
|
886 | 98b19252 | Amit Shah | * to preserve backward compat
|
887 | 98b19252 | Amit Shah | */
|
888 | 37f95bf3 | Amit Shah | register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, |
889 | 98b19252 | Amit Shah | virtio_serial_load, vser); |
890 | 98b19252 | Amit Shah | |
891 | 98b19252 | Amit Shah | return vdev;
|
892 | 98b19252 | Amit Shah | } |
893 | 8b53a865 | Amit Shah | |
894 | 8b53a865 | Amit Shah | void virtio_serial_exit(VirtIODevice *vdev)
|
895 | 8b53a865 | Amit Shah | { |
896 | 8b53a865 | Amit Shah | VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
897 | 8b53a865 | Amit Shah | |
898 | 8b53a865 | Amit Shah | unregister_savevm(vser->qdev, "virtio-console", vser);
|
899 | 8b53a865 | Amit Shah | |
900 | 8b53a865 | Amit Shah | qemu_free(vser->ivqs); |
901 | 8b53a865 | Amit Shah | qemu_free(vser->ovqs); |
902 | 8b53a865 | Amit Shah | qemu_free(vser->ports_map); |
903 | 8b53a865 | Amit Shah | |
904 | 8b53a865 | Amit Shah | virtio_cleanup(vdev); |
905 | 8b53a865 | Amit Shah | } |