root / hw / virtio-serial-bus.c @ 452efba6
History | View | Annotate | Download (16.9 kB)
1 | 98b19252 | Amit Shah | /*
|
---|---|---|---|
2 | 98b19252 | Amit Shah | * A bus for connecting virtio serial and console ports
|
3 | 98b19252 | Amit Shah | *
|
4 | 98b19252 | Amit Shah | * Copyright (C) 2009 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 | 98b19252 | Amit Shah | #include "monitor.h" |
19 | 98b19252 | Amit Shah | #include "qemu-queue.h" |
20 | 98b19252 | Amit Shah | #include "sysbus.h" |
21 | 98b19252 | Amit Shah | #include "virtio-serial.h" |
22 | 98b19252 | Amit Shah | |
23 | 98b19252 | Amit Shah | /* The virtio-serial bus on top of which the ports will ride as devices */
|
24 | 98b19252 | Amit Shah | struct VirtIOSerialBus {
|
25 | 98b19252 | Amit Shah | BusState qbus; |
26 | 98b19252 | Amit Shah | |
27 | 98b19252 | Amit Shah | /* This is the parent device that provides the bus for ports. */
|
28 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
29 | 98b19252 | Amit Shah | |
30 | 98b19252 | Amit Shah | /* The maximum number of ports that can ride on top of this bus */
|
31 | 98b19252 | Amit Shah | uint32_t max_nr_ports; |
32 | 98b19252 | Amit Shah | }; |
33 | 98b19252 | Amit Shah | |
34 | 98b19252 | Amit Shah | struct VirtIOSerial {
|
35 | 98b19252 | Amit Shah | VirtIODevice vdev; |
36 | 98b19252 | Amit Shah | |
37 | 98b19252 | Amit Shah | VirtQueue *c_ivq, *c_ovq; |
38 | 98b19252 | Amit Shah | /* Arrays of ivqs and ovqs: one per port */
|
39 | 98b19252 | Amit Shah | VirtQueue **ivqs, **ovqs; |
40 | 98b19252 | Amit Shah | |
41 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
42 | 98b19252 | Amit Shah | |
43 | 98b19252 | Amit Shah | QTAILQ_HEAD(, VirtIOSerialPort) ports; |
44 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
45 | 98b19252 | Amit Shah | }; |
46 | 98b19252 | Amit Shah | |
47 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id)
|
48 | 98b19252 | Amit Shah | { |
49 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
50 | 98b19252 | Amit Shah | |
51 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
52 | 98b19252 | Amit Shah | if (port->id == id)
|
53 | 98b19252 | Amit Shah | return port;
|
54 | 98b19252 | Amit Shah | } |
55 | 98b19252 | Amit Shah | return NULL; |
56 | 98b19252 | Amit Shah | } |
57 | 98b19252 | Amit Shah | |
58 | 98b19252 | Amit Shah | static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq)
|
59 | 98b19252 | Amit Shah | { |
60 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
61 | 98b19252 | Amit Shah | |
62 | 98b19252 | Amit Shah | QTAILQ_FOREACH(port, &vser->ports, next) { |
63 | 98b19252 | Amit Shah | if (port->ivq == vq || port->ovq == vq)
|
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 | 6663a195 | Amit Shah | static bool use_multiport(VirtIOSerial *vser) |
70 | 6663a195 | Amit Shah | { |
71 | 6663a195 | Amit Shah | return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); |
72 | 6663a195 | Amit Shah | } |
73 | 6663a195 | Amit Shah | |
74 | 98b19252 | Amit Shah | static size_t write_to_port(VirtIOSerialPort *port,
|
75 | 98b19252 | Amit Shah | const uint8_t *buf, size_t size)
|
76 | 98b19252 | Amit Shah | { |
77 | 98b19252 | Amit Shah | VirtQueueElement elem; |
78 | 98b19252 | Amit Shah | VirtQueue *vq; |
79 | 98b19252 | Amit Shah | size_t offset = 0;
|
80 | 98b19252 | Amit Shah | size_t len = 0;
|
81 | 98b19252 | Amit Shah | |
82 | 98b19252 | Amit Shah | vq = port->ivq; |
83 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
84 | 98b19252 | Amit Shah | return 0; |
85 | 98b19252 | Amit Shah | } |
86 | 98b19252 | Amit Shah | if (!size) {
|
87 | 98b19252 | Amit Shah | return 0; |
88 | 98b19252 | Amit Shah | } |
89 | 98b19252 | Amit Shah | |
90 | 98b19252 | Amit Shah | while (offset < size) {
|
91 | 98b19252 | Amit Shah | int i;
|
92 | 98b19252 | Amit Shah | |
93 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
94 | 98b19252 | Amit Shah | break;
|
95 | 98b19252 | Amit Shah | } |
96 | 98b19252 | Amit Shah | |
97 | 98b19252 | Amit Shah | for (i = 0; offset < size && i < elem.in_num; i++) { |
98 | 98b19252 | Amit Shah | len = MIN(elem.in_sg[i].iov_len, size - offset); |
99 | 98b19252 | Amit Shah | |
100 | 98b19252 | Amit Shah | memcpy(elem.in_sg[i].iov_base, buf + offset, len); |
101 | 98b19252 | Amit Shah | offset += len; |
102 | 98b19252 | Amit Shah | } |
103 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
104 | 98b19252 | Amit Shah | } |
105 | 98b19252 | Amit Shah | |
106 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
107 | 98b19252 | Amit Shah | return offset;
|
108 | 98b19252 | Amit Shah | } |
109 | 98b19252 | Amit Shah | |
110 | 98b19252 | Amit Shah | static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) |
111 | 98b19252 | Amit Shah | { |
112 | 98b19252 | Amit Shah | VirtQueueElement elem; |
113 | 98b19252 | Amit Shah | VirtQueue *vq; |
114 | 98b19252 | Amit Shah | struct virtio_console_control *cpkt;
|
115 | 98b19252 | Amit Shah | |
116 | 98b19252 | Amit Shah | vq = port->vser->c_ivq; |
117 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq)) {
|
118 | 98b19252 | Amit Shah | return 0; |
119 | 98b19252 | Amit Shah | } |
120 | 98b19252 | Amit Shah | if (!virtqueue_pop(vq, &elem)) {
|
121 | 98b19252 | Amit Shah | return 0; |
122 | 98b19252 | Amit Shah | } |
123 | 98b19252 | Amit Shah | |
124 | 98b19252 | Amit Shah | cpkt = (struct virtio_console_control *)buf;
|
125 | 98b19252 | Amit Shah | stl_p(&cpkt->id, port->id); |
126 | 98b19252 | Amit Shah | memcpy(elem.in_sg[0].iov_base, buf, len);
|
127 | 98b19252 | Amit Shah | |
128 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, len); |
129 | 98b19252 | Amit Shah | virtio_notify(&port->vser->vdev, vq); |
130 | 98b19252 | Amit Shah | return len;
|
131 | 98b19252 | Amit Shah | } |
132 | 98b19252 | Amit Shah | |
133 | 98b19252 | Amit Shah | static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
|
134 | 98b19252 | Amit Shah | uint16_t value) |
135 | 98b19252 | Amit Shah | { |
136 | 98b19252 | Amit Shah | struct virtio_console_control cpkt;
|
137 | 98b19252 | Amit Shah | |
138 | 98b19252 | Amit Shah | stw_p(&cpkt.event, event); |
139 | 98b19252 | Amit Shah | stw_p(&cpkt.value, value); |
140 | 98b19252 | Amit Shah | |
141 | 98b19252 | Amit Shah | return send_control_msg(port, &cpkt, sizeof(cpkt)); |
142 | 98b19252 | Amit Shah | } |
143 | 98b19252 | Amit Shah | |
144 | 98b19252 | Amit Shah | /* Functions for use inside qemu to open and read from/write to ports */
|
145 | 98b19252 | Amit Shah | int virtio_serial_open(VirtIOSerialPort *port)
|
146 | 98b19252 | Amit Shah | { |
147 | 6663a195 | Amit Shah | /* Don't allow opening an already-open port */
|
148 | 6663a195 | Amit Shah | if (port->host_connected) {
|
149 | 6663a195 | Amit Shah | return 0; |
150 | 6663a195 | Amit Shah | } |
151 | 6663a195 | Amit Shah | /* Send port open notification to the guest */
|
152 | 6663a195 | Amit Shah | port->host_connected = true;
|
153 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
154 | 6663a195 | Amit Shah | |
155 | 98b19252 | Amit Shah | return 0; |
156 | 98b19252 | Amit Shah | } |
157 | 98b19252 | Amit Shah | |
158 | 98b19252 | Amit Shah | int virtio_serial_close(VirtIOSerialPort *port)
|
159 | 98b19252 | Amit Shah | { |
160 | 6663a195 | Amit Shah | port->host_connected = false;
|
161 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
|
162 | 6663a195 | Amit Shah | |
163 | 98b19252 | Amit Shah | return 0; |
164 | 98b19252 | Amit Shah | } |
165 | 98b19252 | Amit Shah | |
166 | 98b19252 | Amit Shah | /* Individual ports/apps call this function to write to the guest. */
|
167 | 98b19252 | Amit Shah | ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
|
168 | 98b19252 | Amit Shah | size_t size) |
169 | 98b19252 | Amit Shah | { |
170 | 6663a195 | Amit Shah | if (!port || !port->host_connected || !port->guest_connected) {
|
171 | 6663a195 | Amit Shah | return 0; |
172 | 6663a195 | Amit Shah | } |
173 | 98b19252 | Amit Shah | return write_to_port(port, buf, size);
|
174 | 98b19252 | Amit Shah | } |
175 | 98b19252 | Amit Shah | |
176 | 98b19252 | Amit Shah | /*
|
177 | 98b19252 | Amit Shah | * Readiness of the guest to accept data on a port.
|
178 | 98b19252 | Amit Shah | * Returns max. data the guest can receive
|
179 | 98b19252 | Amit Shah | */
|
180 | 98b19252 | Amit Shah | size_t virtio_serial_guest_ready(VirtIOSerialPort *port) |
181 | 98b19252 | Amit Shah | { |
182 | 98b19252 | Amit Shah | VirtQueue *vq = port->ivq; |
183 | 98b19252 | Amit Shah | |
184 | 98b19252 | Amit Shah | if (!virtio_queue_ready(vq) ||
|
185 | 98b19252 | Amit Shah | !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || |
186 | 98b19252 | Amit Shah | virtio_queue_empty(vq)) { |
187 | 98b19252 | Amit Shah | return 0; |
188 | 98b19252 | Amit Shah | } |
189 | 6663a195 | Amit Shah | if (use_multiport(port->vser) && !port->guest_connected) {
|
190 | 6663a195 | Amit Shah | return 0; |
191 | 6663a195 | Amit Shah | } |
192 | 98b19252 | Amit Shah | |
193 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 4096, 0)) { |
194 | 98b19252 | Amit Shah | return 4096; |
195 | 98b19252 | Amit Shah | } |
196 | 98b19252 | Amit Shah | if (virtqueue_avail_bytes(vq, 1, 0)) { |
197 | 98b19252 | Amit Shah | return 1; |
198 | 98b19252 | Amit Shah | } |
199 | 98b19252 | Amit Shah | return 0; |
200 | 98b19252 | Amit Shah | } |
201 | 98b19252 | Amit Shah | |
202 | 98b19252 | Amit Shah | /* Guest wants to notify us of some event */
|
203 | 98b19252 | Amit Shah | static void handle_control_message(VirtIOSerial *vser, void *buf) |
204 | 98b19252 | Amit Shah | { |
205 | 98b19252 | Amit Shah | struct VirtIOSerialPort *port;
|
206 | 98b19252 | Amit Shah | struct virtio_console_control cpkt, *gcpkt;
|
207 | 160600fd | Amit Shah | uint8_t *buffer; |
208 | 160600fd | Amit Shah | size_t buffer_len; |
209 | 98b19252 | Amit Shah | |
210 | 98b19252 | Amit Shah | gcpkt = buf; |
211 | 98b19252 | Amit Shah | port = find_port_by_id(vser, ldl_p(&gcpkt->id)); |
212 | 98b19252 | Amit Shah | if (!port)
|
213 | 98b19252 | Amit Shah | return;
|
214 | 98b19252 | Amit Shah | |
215 | 98b19252 | Amit Shah | cpkt.event = lduw_p(&gcpkt->event); |
216 | 98b19252 | Amit Shah | cpkt.value = lduw_p(&gcpkt->value); |
217 | 98b19252 | Amit Shah | |
218 | 98b19252 | Amit Shah | switch(cpkt.event) {
|
219 | 98b19252 | Amit Shah | case VIRTIO_CONSOLE_PORT_READY:
|
220 | 98b19252 | Amit Shah | /*
|
221 | 98b19252 | Amit Shah | * Now that we know the guest asked for the port name, we're
|
222 | 98b19252 | Amit Shah | * sure the guest has initialised whatever state is necessary
|
223 | 98b19252 | Amit Shah | * for this port. Now's a good time to let the guest know if
|
224 | 98b19252 | Amit Shah | * this port is a console port so that the guest can hook it
|
225 | 98b19252 | Amit Shah | * up to hvc.
|
226 | 98b19252 | Amit Shah | */
|
227 | 98b19252 | Amit Shah | if (port->is_console) {
|
228 | 98b19252 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
|
229 | 98b19252 | Amit Shah | } |
230 | 6663a195 | Amit Shah | |
231 | 160600fd | Amit Shah | if (port->name) {
|
232 | 160600fd | Amit Shah | stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); |
233 | 160600fd | Amit Shah | stw_p(&cpkt.value, 1);
|
234 | 160600fd | Amit Shah | |
235 | 160600fd | Amit Shah | buffer_len = sizeof(cpkt) + strlen(port->name) + 1; |
236 | 160600fd | Amit Shah | buffer = qemu_malloc(buffer_len); |
237 | 160600fd | Amit Shah | |
238 | 160600fd | Amit Shah | memcpy(buffer, &cpkt, sizeof(cpkt));
|
239 | 160600fd | Amit Shah | memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
|
240 | 160600fd | Amit Shah | buffer[buffer_len - 1] = 0; |
241 | 160600fd | Amit Shah | |
242 | 160600fd | Amit Shah | send_control_msg(port, buffer, buffer_len); |
243 | 160600fd | Amit Shah | qemu_free(buffer); |
244 | 160600fd | Amit Shah | } |
245 | 160600fd | Amit Shah | |
246 | 6663a195 | Amit Shah | if (port->host_connected) {
|
247 | 6663a195 | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
248 | 6663a195 | Amit Shah | } |
249 | 6663a195 | Amit Shah | |
250 | 98b19252 | Amit Shah | /*
|
251 | 98b19252 | Amit Shah | * When the guest has asked us for this information it means
|
252 | 98b19252 | Amit Shah | * the guest is all setup and has its virtqueues
|
253 | 98b19252 | Amit Shah | * initialised. If some app is interested in knowing about
|
254 | 98b19252 | Amit Shah | * this event, let it know.
|
255 | 98b19252 | Amit Shah | */
|
256 | 98b19252 | Amit Shah | if (port->info->guest_ready) {
|
257 | 98b19252 | Amit Shah | port->info->guest_ready(port); |
258 | 98b19252 | Amit Shah | } |
259 | 98b19252 | Amit Shah | break;
|
260 | 6663a195 | Amit Shah | |
261 | 6663a195 | Amit Shah | case VIRTIO_CONSOLE_PORT_OPEN:
|
262 | 6663a195 | Amit Shah | port->guest_connected = cpkt.value; |
263 | 6663a195 | Amit Shah | if (cpkt.value && port->info->guest_open) {
|
264 | 6663a195 | Amit Shah | /* Send the guest opened notification if an app is interested */
|
265 | 6663a195 | Amit Shah | port->info->guest_open(port); |
266 | 6663a195 | Amit Shah | } |
267 | 6663a195 | Amit Shah | |
268 | 6663a195 | Amit Shah | if (!cpkt.value && port->info->guest_close) {
|
269 | 6663a195 | Amit Shah | /* Send the guest closed notification if an app is interested */
|
270 | 6663a195 | Amit Shah | port->info->guest_close(port); |
271 | 6663a195 | Amit Shah | } |
272 | 6663a195 | Amit Shah | break;
|
273 | 98b19252 | Amit Shah | } |
274 | 98b19252 | Amit Shah | } |
275 | 98b19252 | Amit Shah | |
276 | 98b19252 | Amit Shah | static void control_in(VirtIODevice *vdev, VirtQueue *vq) |
277 | 98b19252 | Amit Shah | { |
278 | 98b19252 | Amit Shah | } |
279 | 98b19252 | Amit Shah | |
280 | 98b19252 | Amit Shah | static void control_out(VirtIODevice *vdev, VirtQueue *vq) |
281 | 98b19252 | Amit Shah | { |
282 | 98b19252 | Amit Shah | VirtQueueElement elem; |
283 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
284 | 98b19252 | Amit Shah | |
285 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
286 | 98b19252 | Amit Shah | |
287 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
288 | 98b19252 | Amit Shah | handle_control_message(vser, elem.out_sg[0].iov_base);
|
289 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, elem.out_sg[0].iov_len);
|
290 | 98b19252 | Amit Shah | } |
291 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
292 | 98b19252 | Amit Shah | } |
293 | 98b19252 | Amit Shah | |
294 | 98b19252 | Amit Shah | /* Guest wrote something to some port. */
|
295 | 98b19252 | Amit Shah | static void handle_output(VirtIODevice *vdev, VirtQueue *vq) |
296 | 98b19252 | Amit Shah | { |
297 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
298 | 98b19252 | Amit Shah | VirtQueueElement elem; |
299 | 98b19252 | Amit Shah | |
300 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
301 | 98b19252 | Amit Shah | |
302 | 98b19252 | Amit Shah | while (virtqueue_pop(vq, &elem)) {
|
303 | 98b19252 | Amit Shah | VirtIOSerialPort *port; |
304 | 98b19252 | Amit Shah | size_t ret; |
305 | 98b19252 | Amit Shah | |
306 | 98b19252 | Amit Shah | port = find_port_by_vq(vser, vq); |
307 | 98b19252 | Amit Shah | if (!port) {
|
308 | 98b19252 | Amit Shah | ret = 0;
|
309 | 98b19252 | Amit Shah | goto next_buf;
|
310 | 98b19252 | Amit Shah | } |
311 | 98b19252 | Amit Shah | |
312 | 98b19252 | Amit Shah | /*
|
313 | 98b19252 | Amit Shah | * A port may not have any handler registered for consuming the
|
314 | 98b19252 | Amit Shah | * data that the guest sends or it may not have a chardev associated
|
315 | 98b19252 | Amit Shah | * with it. Just ignore the data in that case.
|
316 | 98b19252 | Amit Shah | */
|
317 | 98b19252 | Amit Shah | if (!port->info->have_data) {
|
318 | 98b19252 | Amit Shah | ret = 0;
|
319 | 98b19252 | Amit Shah | goto next_buf;
|
320 | 98b19252 | Amit Shah | } |
321 | 98b19252 | Amit Shah | |
322 | 98b19252 | Amit Shah | /* The guest always sends only one sg */
|
323 | 98b19252 | Amit Shah | ret = port->info->have_data(port, elem.out_sg[0].iov_base,
|
324 | 98b19252 | Amit Shah | elem.out_sg[0].iov_len);
|
325 | 98b19252 | Amit Shah | |
326 | 98b19252 | Amit Shah | next_buf:
|
327 | 98b19252 | Amit Shah | virtqueue_push(vq, &elem, ret); |
328 | 98b19252 | Amit Shah | } |
329 | 98b19252 | Amit Shah | virtio_notify(vdev, vq); |
330 | 98b19252 | Amit Shah | } |
331 | 98b19252 | Amit Shah | |
332 | 98b19252 | Amit Shah | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
333 | 98b19252 | Amit Shah | { |
334 | 98b19252 | Amit Shah | } |
335 | 98b19252 | Amit Shah | |
336 | 98b19252 | Amit Shah | static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
|
337 | 98b19252 | Amit Shah | { |
338 | 98b19252 | Amit Shah | features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
|
339 | 98b19252 | Amit Shah | |
340 | 98b19252 | Amit Shah | return features;
|
341 | 98b19252 | Amit Shah | } |
342 | 98b19252 | Amit Shah | |
343 | 98b19252 | Amit Shah | /* Guest requested config info */
|
344 | 98b19252 | Amit Shah | static void get_config(VirtIODevice *vdev, uint8_t *config_data) |
345 | 98b19252 | Amit Shah | { |
346 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
347 | 98b19252 | Amit Shah | |
348 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
349 | 98b19252 | Amit Shah | memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); |
350 | 98b19252 | Amit Shah | } |
351 | 98b19252 | Amit Shah | |
352 | 98b19252 | Amit Shah | static void set_config(VirtIODevice *vdev, const uint8_t *config_data) |
353 | 98b19252 | Amit Shah | { |
354 | 98b19252 | Amit Shah | struct virtio_console_config config;
|
355 | 98b19252 | Amit Shah | |
356 | 98b19252 | Amit Shah | memcpy(&config, config_data, sizeof(config));
|
357 | 98b19252 | Amit Shah | } |
358 | 98b19252 | Amit Shah | |
359 | 98b19252 | Amit Shah | static void virtio_serial_save(QEMUFile *f, void *opaque) |
360 | 98b19252 | Amit Shah | { |
361 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
362 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
363 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
364 | 98b19252 | Amit Shah | |
365 | 98b19252 | Amit Shah | /* The virtio device */
|
366 | 98b19252 | Amit Shah | virtio_save(&s->vdev, f); |
367 | 98b19252 | Amit Shah | |
368 | 98b19252 | Amit Shah | /* The config space */
|
369 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.cols); |
370 | 98b19252 | Amit Shah | qemu_put_be16s(f, &s->config.rows); |
371 | 98b19252 | Amit Shah | qemu_put_be32s(f, &s->config.nr_ports); |
372 | 6663a195 | Amit Shah | |
373 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerial */
|
374 | 6663a195 | Amit Shah | |
375 | 6663a195 | Amit Shah | /* Do this because we might have hot-unplugged some ports */
|
376 | 6663a195 | Amit Shah | nr_active_ports = 0;
|
377 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) |
378 | 6663a195 | Amit Shah | nr_active_ports++; |
379 | 6663a195 | Amit Shah | |
380 | 6663a195 | Amit Shah | qemu_put_be32s(f, &nr_active_ports); |
381 | 6663a195 | Amit Shah | |
382 | 6663a195 | Amit Shah | /*
|
383 | 6663a195 | Amit Shah | * Items in struct VirtIOSerialPort.
|
384 | 6663a195 | Amit Shah | */
|
385 | 6663a195 | Amit Shah | QTAILQ_FOREACH(port, &s->ports, next) { |
386 | 6663a195 | Amit Shah | /*
|
387 | 6663a195 | Amit Shah | * We put the port number because we may not have an active
|
388 | 6663a195 | Amit Shah | * port at id 0 that's reserved for a console port, or in case
|
389 | 6663a195 | Amit Shah | * of ports that might have gotten unplugged
|
390 | 6663a195 | Amit Shah | */
|
391 | 6663a195 | Amit Shah | qemu_put_be32s(f, &port->id); |
392 | 6663a195 | Amit Shah | qemu_put_byte(f, port->guest_connected); |
393 | 6663a195 | Amit Shah | } |
394 | 98b19252 | Amit Shah | } |
395 | 98b19252 | Amit Shah | |
396 | 98b19252 | Amit Shah | static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) |
397 | 98b19252 | Amit Shah | { |
398 | 98b19252 | Amit Shah | VirtIOSerial *s = opaque; |
399 | 6663a195 | Amit Shah | VirtIOSerialPort *port; |
400 | 6663a195 | Amit Shah | uint32_t nr_active_ports; |
401 | 6663a195 | Amit Shah | unsigned int i; |
402 | 98b19252 | Amit Shah | |
403 | 98b19252 | Amit Shah | if (version_id > 2) { |
404 | 98b19252 | Amit Shah | return -EINVAL;
|
405 | 98b19252 | Amit Shah | } |
406 | 6663a195 | Amit Shah | |
407 | 98b19252 | Amit Shah | /* The virtio device */
|
408 | 98b19252 | Amit Shah | virtio_load(&s->vdev, f); |
409 | 98b19252 | Amit Shah | |
410 | 98b19252 | Amit Shah | if (version_id < 2) { |
411 | 98b19252 | Amit Shah | return 0; |
412 | 98b19252 | Amit Shah | } |
413 | 98b19252 | Amit Shah | |
414 | 98b19252 | Amit Shah | /* The config space */
|
415 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.cols); |
416 | 98b19252 | Amit Shah | qemu_get_be16s(f, &s->config.rows); |
417 | 98b19252 | Amit Shah | s->config.nr_ports = qemu_get_be32(f); |
418 | 98b19252 | Amit Shah | |
419 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerial */
|
420 | 6663a195 | Amit Shah | |
421 | 6663a195 | Amit Shah | qemu_get_be32s(f, &nr_active_ports); |
422 | 6663a195 | Amit Shah | |
423 | 6663a195 | Amit Shah | /* Items in struct VirtIOSerialPort */
|
424 | 6663a195 | Amit Shah | for (i = 0; i < nr_active_ports; i++) { |
425 | 6663a195 | Amit Shah | uint32_t id; |
426 | 6663a195 | Amit Shah | |
427 | 6663a195 | Amit Shah | id = qemu_get_be32(f); |
428 | 6663a195 | Amit Shah | port = find_port_by_id(s, id); |
429 | 6663a195 | Amit Shah | |
430 | 6663a195 | Amit Shah | port->guest_connected = qemu_get_byte(f); |
431 | 6663a195 | Amit Shah | } |
432 | 6663a195 | Amit Shah | |
433 | 98b19252 | Amit Shah | return 0; |
434 | 98b19252 | Amit Shah | } |
435 | 98b19252 | Amit Shah | |
436 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); |
437 | 98b19252 | Amit Shah | |
438 | 98b19252 | Amit Shah | static struct BusInfo virtser_bus_info = { |
439 | 98b19252 | Amit Shah | .name = "virtio-serial-bus",
|
440 | 98b19252 | Amit Shah | .size = sizeof(VirtIOSerialBus),
|
441 | 98b19252 | Amit Shah | .print_dev = virtser_bus_dev_print, |
442 | 98b19252 | Amit Shah | }; |
443 | 98b19252 | Amit Shah | |
444 | 98b19252 | Amit Shah | static VirtIOSerialBus *virtser_bus_new(DeviceState *dev)
|
445 | 98b19252 | Amit Shah | { |
446 | 98b19252 | Amit Shah | VirtIOSerialBus *bus; |
447 | 98b19252 | Amit Shah | |
448 | 98b19252 | Amit Shah | bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, |
449 | 98b19252 | Amit Shah | "virtio-serial-bus"));
|
450 | 98b19252 | Amit Shah | bus->qbus.allow_hotplug = 1;
|
451 | 98b19252 | Amit Shah | |
452 | 98b19252 | Amit Shah | return bus;
|
453 | 98b19252 | Amit Shah | } |
454 | 98b19252 | Amit Shah | |
455 | 98b19252 | Amit Shah | static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) |
456 | 98b19252 | Amit Shah | { |
457 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
458 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
459 | 98b19252 | Amit Shah | |
460 | 98b19252 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
|
461 | 98b19252 | Amit Shah | indent, "", port->id);
|
462 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
|
463 | 6663a195 | Amit Shah | indent, "", port->guest_connected);
|
464 | 6663a195 | Amit Shah | monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
|
465 | 6663a195 | Amit Shah | indent, "", port->host_connected);
|
466 | 98b19252 | Amit Shah | } |
467 | 98b19252 | Amit Shah | |
468 | 98b19252 | Amit Shah | static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) |
469 | 98b19252 | Amit Shah | { |
470 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
471 | 98b19252 | Amit Shah | VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base); |
472 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
473 | 98b19252 | Amit Shah | VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); |
474 | 98b19252 | Amit Shah | int ret;
|
475 | 98b19252 | Amit Shah | bool plugging_port0;
|
476 | 98b19252 | Amit Shah | |
477 | 98b19252 | Amit Shah | port->vser = bus->vser; |
478 | 98b19252 | Amit Shah | |
479 | 98b19252 | Amit Shah | /*
|
480 | 98b19252 | Amit Shah | * Is the first console port we're seeing? If so, put it up at
|
481 | 98b19252 | Amit Shah | * location 0. This is done for backward compatibility (old
|
482 | 98b19252 | Amit Shah | * kernel, new qemu).
|
483 | 98b19252 | Amit Shah | */
|
484 | 98b19252 | Amit Shah | plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
|
485 | 98b19252 | Amit Shah | |
486 | 98b19252 | Amit Shah | if (port->vser->config.nr_ports == bus->max_nr_ports && !plugging_port0) {
|
487 | 98b19252 | Amit Shah | qemu_error("virtio-serial-bus: Maximum device limit reached\n");
|
488 | 98b19252 | Amit Shah | return -1; |
489 | 98b19252 | Amit Shah | } |
490 | 98b19252 | Amit Shah | dev->info = info; |
491 | 98b19252 | Amit Shah | |
492 | 98b19252 | Amit Shah | ret = info->init(dev); |
493 | 98b19252 | Amit Shah | if (ret) {
|
494 | 98b19252 | Amit Shah | return ret;
|
495 | 98b19252 | Amit Shah | } |
496 | 98b19252 | Amit Shah | |
497 | 98b19252 | Amit Shah | port->id = plugging_port0 ? 0 : port->vser->config.nr_ports++;
|
498 | 98b19252 | Amit Shah | |
499 | 6663a195 | Amit Shah | if (!use_multiport(port->vser)) {
|
500 | 6663a195 | Amit Shah | /*
|
501 | 6663a195 | Amit Shah | * Allow writes to guest in this case; we have no way of
|
502 | 6663a195 | Amit Shah | * knowing if a guest port is connected.
|
503 | 6663a195 | Amit Shah | */
|
504 | 6663a195 | Amit Shah | port->guest_connected = true;
|
505 | 6663a195 | Amit Shah | } |
506 | 6663a195 | Amit Shah | |
507 | 98b19252 | Amit Shah | QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); |
508 | 98b19252 | Amit Shah | port->ivq = port->vser->ivqs[port->id]; |
509 | 98b19252 | Amit Shah | port->ovq = port->vser->ovqs[port->id]; |
510 | 98b19252 | Amit Shah | |
511 | 98b19252 | Amit Shah | /* Send an update to the guest about this new port added */
|
512 | 98b19252 | Amit Shah | virtio_notify_config(&port->vser->vdev); |
513 | 98b19252 | Amit Shah | |
514 | 98b19252 | Amit Shah | return ret;
|
515 | 98b19252 | Amit Shah | } |
516 | 98b19252 | Amit Shah | |
517 | 98b19252 | Amit Shah | static int virtser_port_qdev_exit(DeviceState *qdev) |
518 | 98b19252 | Amit Shah | { |
519 | 98b19252 | Amit Shah | VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); |
520 | 98b19252 | Amit Shah | VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); |
521 | 98b19252 | Amit Shah | VirtIOSerial *vser = port->vser; |
522 | 98b19252 | Amit Shah | |
523 | f146ec9a | Amit Shah | send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
524 | f146ec9a | Amit Shah | |
525 | 98b19252 | Amit Shah | /*
|
526 | 98b19252 | Amit Shah | * Don't decrement nr_ports here; thus we keep a linearly
|
527 | 98b19252 | Amit Shah | * increasing port id. Not utilising an id again saves us a couple
|
528 | 98b19252 | Amit Shah | * of complications:
|
529 | 98b19252 | Amit Shah | *
|
530 | 98b19252 | Amit Shah | * - Not having to bother about sending the port id to the guest
|
531 | 98b19252 | Amit Shah | * kernel on hotplug or on addition of new ports; the guest can
|
532 | 98b19252 | Amit Shah | * also linearly increment the port number. This is preferable
|
533 | 98b19252 | Amit Shah | * because the config space won't have the need to store a
|
534 | 98b19252 | Amit Shah | * ports_map.
|
535 | 98b19252 | Amit Shah | *
|
536 | 98b19252 | Amit Shah | * - Extra state to be stored for all the "holes" that got created
|
537 | 98b19252 | Amit Shah | * so that we keep filling in the ids from the least available
|
538 | 98b19252 | Amit Shah | * index.
|
539 | 98b19252 | Amit Shah | *
|
540 | 98b19252 | Amit Shah | * When such a functionality is desired, a control message to add
|
541 | 98b19252 | Amit Shah | * a port can be introduced.
|
542 | 98b19252 | Amit Shah | */
|
543 | 98b19252 | Amit Shah | QTAILQ_REMOVE(&vser->ports, port, next); |
544 | 98b19252 | Amit Shah | |
545 | 98b19252 | Amit Shah | if (port->info->exit)
|
546 | 98b19252 | Amit Shah | port->info->exit(dev); |
547 | 98b19252 | Amit Shah | |
548 | 98b19252 | Amit Shah | return 0; |
549 | 98b19252 | Amit Shah | } |
550 | 98b19252 | Amit Shah | |
551 | 98b19252 | Amit Shah | void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info)
|
552 | 98b19252 | Amit Shah | { |
553 | 98b19252 | Amit Shah | info->qdev.init = virtser_port_qdev_init; |
554 | 98b19252 | Amit Shah | info->qdev.bus_info = &virtser_bus_info; |
555 | 98b19252 | Amit Shah | info->qdev.exit = virtser_port_qdev_exit; |
556 | 98b19252 | Amit Shah | info->qdev.unplug = qdev_simple_unplug_cb; |
557 | 98b19252 | Amit Shah | qdev_register(&info->qdev); |
558 | 98b19252 | Amit Shah | } |
559 | 98b19252 | Amit Shah | |
560 | 98b19252 | Amit Shah | VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) |
561 | 98b19252 | Amit Shah | { |
562 | 98b19252 | Amit Shah | VirtIOSerial *vser; |
563 | 98b19252 | Amit Shah | VirtIODevice *vdev; |
564 | 98b19252 | Amit Shah | uint32_t i; |
565 | 98b19252 | Amit Shah | |
566 | 98b19252 | Amit Shah | if (!max_nr_ports)
|
567 | 98b19252 | Amit Shah | return NULL; |
568 | 98b19252 | Amit Shah | |
569 | 98b19252 | Amit Shah | vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
|
570 | 98b19252 | Amit Shah | sizeof(struct virtio_console_config), |
571 | 98b19252 | Amit Shah | sizeof(VirtIOSerial));
|
572 | 98b19252 | Amit Shah | |
573 | 98b19252 | Amit Shah | vser = DO_UPCAST(VirtIOSerial, vdev, vdev); |
574 | 98b19252 | Amit Shah | |
575 | 98b19252 | Amit Shah | /* Spawn a new virtio-serial bus on which the ports will ride as devices */
|
576 | 98b19252 | Amit Shah | vser->bus = virtser_bus_new(dev); |
577 | 98b19252 | Amit Shah | vser->bus->vser = vser; |
578 | 98b19252 | Amit Shah | QTAILQ_INIT(&vser->ports); |
579 | 98b19252 | Amit Shah | |
580 | 98b19252 | Amit Shah | vser->bus->max_nr_ports = max_nr_ports; |
581 | 98b19252 | Amit Shah | vser->ivqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
582 | 98b19252 | Amit Shah | vser->ovqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
|
583 | 98b19252 | Amit Shah | |
584 | 98b19252 | Amit Shah | /* Add a queue for host to guest transfers for port 0 (backward compat) */
|
585 | 98b19252 | Amit Shah | vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); |
586 | 98b19252 | Amit Shah | /* Add a queue for guest to host transfers for port 0 (backward compat) */
|
587 | 98b19252 | Amit Shah | vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); |
588 | 98b19252 | Amit Shah | |
589 | 98b19252 | Amit Shah | /* control queue: host to guest */
|
590 | 98b19252 | Amit Shah | vser->c_ivq = virtio_add_queue(vdev, 16, control_in);
|
591 | 98b19252 | Amit Shah | /* control queue: guest to host */
|
592 | 98b19252 | Amit Shah | vser->c_ovq = virtio_add_queue(vdev, 16, control_out);
|
593 | 98b19252 | Amit Shah | |
594 | 98b19252 | Amit Shah | for (i = 1; i < vser->bus->max_nr_ports; i++) { |
595 | 98b19252 | Amit Shah | /* Add a per-port queue for host to guest transfers */
|
596 | 98b19252 | Amit Shah | vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
|
597 | 98b19252 | Amit Shah | /* Add a per-per queue for guest to host transfers */
|
598 | 98b19252 | Amit Shah | vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
|
599 | 98b19252 | Amit Shah | } |
600 | 98b19252 | Amit Shah | |
601 | 98b19252 | Amit Shah | vser->config.max_nr_ports = max_nr_ports; |
602 | 98b19252 | Amit Shah | /*
|
603 | 98b19252 | Amit Shah | * Reserve location 0 for a console port for backward compat
|
604 | 98b19252 | Amit Shah | * (old kernel, new qemu)
|
605 | 98b19252 | Amit Shah | */
|
606 | 98b19252 | Amit Shah | vser->config.nr_ports = 1;
|
607 | 98b19252 | Amit Shah | |
608 | 98b19252 | Amit Shah | vser->vdev.get_features = get_features; |
609 | 98b19252 | Amit Shah | vser->vdev.get_config = get_config; |
610 | 98b19252 | Amit Shah | vser->vdev.set_config = set_config; |
611 | 98b19252 | Amit Shah | |
612 | 98b19252 | Amit Shah | /*
|
613 | 98b19252 | Amit Shah | * Register for the savevm section with the virtio-console name
|
614 | 98b19252 | Amit Shah | * to preserve backward compat
|
615 | 98b19252 | Amit Shah | */
|
616 | 98b19252 | Amit Shah | register_savevm("virtio-console", -1, 2, virtio_serial_save, |
617 | 98b19252 | Amit Shah | virtio_serial_load, vser); |
618 | 98b19252 | Amit Shah | |
619 | 98b19252 | Amit Shah | return vdev;
|
620 | 98b19252 | Amit Shah | } |