41 |
41 |
VirtIOSerialBus *bus;
|
42 |
42 |
|
43 |
43 |
QTAILQ_HEAD(, VirtIOSerialPort) ports;
|
|
44 |
|
|
45 |
/* bitmap for identifying active ports */
|
|
46 |
uint32_t *ports_map;
|
|
47 |
|
44 |
48 |
struct virtio_console_config config;
|
45 |
49 |
};
|
46 |
50 |
|
... | ... | |
48 |
52 |
{
|
49 |
53 |
VirtIOSerialPort *port;
|
50 |
54 |
|
|
55 |
if (id == VIRTIO_CONSOLE_BAD_ID) {
|
|
56 |
return NULL;
|
|
57 |
}
|
|
58 |
|
51 |
59 |
QTAILQ_FOREACH(port, &vser->ports, next) {
|
52 |
60 |
if (port->id == id)
|
53 |
61 |
return port;
|
... | ... | |
208 |
216 |
size_t buffer_len;
|
209 |
217 |
|
210 |
218 |
gcpkt = buf;
|
211 |
|
port = find_port_by_id(vser, ldl_p(&gcpkt->id));
|
212 |
|
if (!port)
|
213 |
|
return;
|
214 |
219 |
|
215 |
220 |
cpkt.event = lduw_p(&gcpkt->event);
|
216 |
221 |
cpkt.value = lduw_p(&gcpkt->value);
|
217 |
222 |
|
|
223 |
port = find_port_by_id(vser, ldl_p(&gcpkt->id));
|
|
224 |
if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
|
|
225 |
return;
|
|
226 |
|
218 |
227 |
switch(cpkt.event) {
|
|
228 |
case VIRTIO_CONSOLE_DEVICE_READY:
|
|
229 |
/*
|
|
230 |
* The device is up, we can now tell the device about all the
|
|
231 |
* ports we have here.
|
|
232 |
*/
|
|
233 |
QTAILQ_FOREACH(port, &vser->ports, next) {
|
|
234 |
send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
|
|
235 |
}
|
|
236 |
break;
|
|
237 |
|
219 |
238 |
case VIRTIO_CONSOLE_PORT_READY:
|
220 |
239 |
/*
|
221 |
240 |
* Now that we know the guest asked for the port name, we're
|
... | ... | |
363 |
382 |
VirtIOSerial *s = opaque;
|
364 |
383 |
VirtIOSerialPort *port;
|
365 |
384 |
uint32_t nr_active_ports;
|
|
385 |
unsigned int i;
|
366 |
386 |
|
367 |
387 |
/* The virtio device */
|
368 |
388 |
virtio_save(&s->vdev, f);
|
... | ... | |
370 |
390 |
/* The config space */
|
371 |
391 |
qemu_put_be16s(f, &s->config.cols);
|
372 |
392 |
qemu_put_be16s(f, &s->config.rows);
|
373 |
|
qemu_put_be32s(f, &s->config.nr_ports);
|
374 |
393 |
|
375 |
|
/* Items in struct VirtIOSerial */
|
|
394 |
qemu_put_be32s(f, &s->config.max_nr_ports);
|
|
395 |
|
|
396 |
/* The ports map */
|
|
397 |
|
|
398 |
for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) {
|
|
399 |
qemu_put_be32s(f, &s->ports_map[i]);
|
|
400 |
}
|
376 |
401 |
|
377 |
|
qemu_put_be32s(f, &s->bus->max_nr_ports);
|
|
402 |
/* Ports */
|
378 |
403 |
|
379 |
|
/* Do this because we might have hot-unplugged some ports */
|
380 |
404 |
nr_active_ports = 0;
|
381 |
405 |
QTAILQ_FOREACH(port, &s->ports, next) {
|
382 |
406 |
nr_active_ports++;
|
... | ... | |
388 |
412 |
* Items in struct VirtIOSerialPort.
|
389 |
413 |
*/
|
390 |
414 |
QTAILQ_FOREACH(port, &s->ports, next) {
|
391 |
|
/*
|
392 |
|
* We put the port number because we may not have an active
|
393 |
|
* port at id 0 that's reserved for a console port, or in case
|
394 |
|
* of ports that might have gotten unplugged
|
395 |
|
*/
|
396 |
415 |
qemu_put_be32s(f, &port->id);
|
397 |
416 |
qemu_put_byte(f, port->guest_connected);
|
398 |
417 |
qemu_put_byte(f, port->host_connected);
|
... | ... | |
403 |
422 |
{
|
404 |
423 |
VirtIOSerial *s = opaque;
|
405 |
424 |
VirtIOSerialPort *port;
|
406 |
|
uint32_t max_nr_ports, nr_active_ports, nr_ports;
|
|
425 |
size_t ports_map_size;
|
|
426 |
uint32_t max_nr_ports, nr_active_ports, *ports_map;
|
407 |
427 |
unsigned int i;
|
408 |
428 |
|
409 |
429 |
if (version_id > 2) {
|
... | ... | |
420 |
440 |
/* The config space */
|
421 |
441 |
qemu_get_be16s(f, &s->config.cols);
|
422 |
442 |
qemu_get_be16s(f, &s->config.rows);
|
423 |
|
nr_ports = qemu_get_be32(f);
|
424 |
443 |
|
425 |
|
if (nr_ports != s->config.nr_ports) {
|
426 |
|
/*
|
427 |
|
* Source hot-plugged/unplugged ports and we don't have all of
|
428 |
|
* them here.
|
429 |
|
*
|
430 |
|
* Note: This condition cannot check for all hotplug/unplug
|
431 |
|
* events: eg, if one port was hot-plugged and one was
|
432 |
|
* unplugged, the nr_ports remains the same but the port id's
|
433 |
|
* would have changed and we won't catch it here. A later
|
434 |
|
* check for !find_port_by_id() will confirm if this happened.
|
435 |
|
*/
|
|
444 |
qemu_get_be32s(f, &max_nr_ports);
|
|
445 |
if (max_nr_ports > s->config.max_nr_ports) {
|
|
446 |
/* Source could have had more ports than us. Fail migration. */
|
436 |
447 |
return -EINVAL;
|
437 |
448 |
}
|
438 |
449 |
|
439 |
|
/* Items in struct VirtIOSerial */
|
|
450 |
ports_map_size = sizeof(uint32_t) * (max_nr_ports + 31) / 32;
|
|
451 |
ports_map = qemu_malloc(ports_map_size);
|
440 |
452 |
|
441 |
|
qemu_get_be32s(f, &max_nr_ports);
|
442 |
|
if (max_nr_ports > s->bus->max_nr_ports) {
|
443 |
|
/* Source could have more ports than us. Fail migration. */
|
444 |
|
return -EINVAL;
|
|
453 |
for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
|
|
454 |
qemu_get_be32s(f, &ports_map[i]);
|
|
455 |
|
|
456 |
if (ports_map[i] != s->ports_map[i]) {
|
|
457 |
/*
|
|
458 |
* Ports active on source and destination don't
|
|
459 |
* match. Fail migration.
|
|
460 |
*/
|
|
461 |
qemu_free(ports_map);
|
|
462 |
return -EINVAL;
|
|
463 |
}
|
445 |
464 |
}
|
|
465 |
qemu_free(ports_map);
|
446 |
466 |
|
447 |
467 |
qemu_get_be32s(f, &nr_active_ports);
|
448 |
468 |
|
... | ... | |
453 |
473 |
|
454 |
474 |
id = qemu_get_be32(f);
|
455 |
475 |
port = find_port_by_id(s, id);
|
456 |
|
if (!port) {
|
457 |
|
/*
|
458 |
|
* The requested port was hot-plugged on the source but we
|
459 |
|
* don't have it
|
460 |
|
*/
|
461 |
|
return -EINVAL;
|
462 |
|
}
|
463 |
476 |
|
464 |
477 |
port->guest_connected = qemu_get_byte(f);
|
465 |
478 |
host_connected = qemu_get_byte(f);
|
... | ... | |
472 |
485 |
port->host_connected);
|
473 |
486 |
}
|
474 |
487 |
}
|
475 |
|
|
476 |
488 |
return 0;
|
477 |
489 |
}
|
478 |
490 |
|
... | ... | |
507 |
519 |
indent, "", port->host_connected);
|
508 |
520 |
}
|
509 |
521 |
|
|
522 |
/* This function is only used if a port id is not provided by the user */
|
|
523 |
static uint32_t find_free_port_id(VirtIOSerial *vser)
|
|
524 |
{
|
|
525 |
unsigned int i;
|
|
526 |
|
|
527 |
for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) {
|
|
528 |
uint32_t map, bit;
|
|
529 |
|
|
530 |
map = vser->ports_map[i];
|
|
531 |
bit = ffs(~map);
|
|
532 |
if (bit) {
|
|
533 |
return (bit - 1) + i * 32;
|
|
534 |
}
|
|
535 |
}
|
|
536 |
return VIRTIO_CONSOLE_BAD_ID;
|
|
537 |
}
|
|
538 |
|
|
539 |
static void mark_port_added(VirtIOSerial *vser, uint32_t port_id)
|
|
540 |
{
|
|
541 |
unsigned int i;
|
|
542 |
|
|
543 |
i = port_id / 32;
|
|
544 |
vser->ports_map[i] |= 1U << (port_id % 32);
|
|
545 |
}
|
|
546 |
|
|
547 |
static void add_port(VirtIOSerial *vser, uint32_t port_id)
|
|
548 |
{
|
|
549 |
mark_port_added(vser, port_id);
|
|
550 |
|
|
551 |
send_control_event(find_port_by_id(vser, port_id),
|
|
552 |
VIRTIO_CONSOLE_PORT_ADD, 1);
|
|
553 |
}
|
|
554 |
|
|
555 |
static void remove_port(VirtIOSerial *vser, uint32_t port_id)
|
|
556 |
{
|
|
557 |
unsigned int i;
|
|
558 |
|
|
559 |
i = port_id / 32;
|
|
560 |
vser->ports_map[i] &= ~(1U << (port_id % 32));
|
|
561 |
|
|
562 |
send_control_event(find_port_by_id(vser, port_id),
|
|
563 |
VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
|
564 |
}
|
|
565 |
|
510 |
566 |
static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
511 |
567 |
{
|
512 |
568 |
VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev);
|
... | ... | |
525 |
581 |
*/
|
526 |
582 |
plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
|
527 |
583 |
|
528 |
|
if (port->vser->config.nr_ports == bus->max_nr_ports && !plugging_port0) {
|
529 |
|
error_report("virtio-serial-bus: Maximum device limit reached");
|
|
584 |
if (find_port_by_id(port->vser, port->id)) {
|
|
585 |
error_report("virtio-serial-bus: A port already exists at id %u\n",
|
|
586 |
port->id);
|
530 |
587 |
return -1;
|
531 |
588 |
}
|
532 |
|
dev->info = info;
|
533 |
589 |
|
|
590 |
if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
|
591 |
if (plugging_port0) {
|
|
592 |
port->id = 0;
|
|
593 |
} else {
|
|
594 |
port->id = find_free_port_id(port->vser);
|
|
595 |
if (port->id == VIRTIO_CONSOLE_BAD_ID) {
|
|
596 |
error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
|
|
597 |
return -1;
|
|
598 |
}
|
|
599 |
}
|
|
600 |
}
|
|
601 |
|
|
602 |
if (port->id >= port->vser->config.max_nr_ports) {
|
|
603 |
error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
|
|
604 |
port->vser->config.max_nr_ports - 1);
|
|
605 |
return -1;
|
|
606 |
}
|
|
607 |
|
|
608 |
dev->info = info;
|
534 |
609 |
ret = info->init(dev);
|
535 |
610 |
if (ret) {
|
536 |
611 |
return ret;
|
537 |
612 |
}
|
538 |
613 |
|
539 |
|
port->id = plugging_port0 ? 0 : port->vser->config.nr_ports++;
|
540 |
|
|
541 |
614 |
if (!use_multiport(port->vser)) {
|
542 |
615 |
/*
|
543 |
616 |
* Allow writes to guest in this case; we have no way of
|
... | ... | |
550 |
623 |
port->ivq = port->vser->ivqs[port->id];
|
551 |
624 |
port->ovq = port->vser->ovqs[port->id];
|
552 |
625 |
|
|
626 |
add_port(port->vser, port->id);
|
|
627 |
|
553 |
628 |
/* Send an update to the guest about this new port added */
|
554 |
629 |
virtio_notify_config(&port->vser->vdev);
|
555 |
630 |
|
... | ... | |
562 |
637 |
VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
|
563 |
638 |
VirtIOSerial *vser = port->vser;
|
564 |
639 |
|
565 |
|
send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
|
|
640 |
remove_port(port->vser, port->id);
|
566 |
641 |
|
567 |
|
/*
|
568 |
|
* Don't decrement nr_ports here; thus we keep a linearly
|
569 |
|
* increasing port id. Not utilising an id again saves us a couple
|
570 |
|
* of complications:
|
571 |
|
*
|
572 |
|
* - Not having to bother about sending the port id to the guest
|
573 |
|
* kernel on hotplug or on addition of new ports; the guest can
|
574 |
|
* also linearly increment the port number. This is preferable
|
575 |
|
* because the config space won't have the need to store a
|
576 |
|
* ports_map.
|
577 |
|
*
|
578 |
|
* - Extra state to be stored for all the "holes" that got created
|
579 |
|
* so that we keep filling in the ids from the least available
|
580 |
|
* index.
|
581 |
|
*
|
582 |
|
* When such a functionality is desired, a control message to add
|
583 |
|
* a port can be introduced.
|
584 |
|
*/
|
585 |
642 |
QTAILQ_REMOVE(&vser->ports, port, next);
|
586 |
643 |
|
587 |
644 |
if (port->info->exit)
|
... | ... | |
641 |
698 |
}
|
642 |
699 |
|
643 |
700 |
vser->config.max_nr_ports = max_nr_ports;
|
|
701 |
vser->ports_map = qemu_mallocz((max_nr_ports + 31) / 32);
|
644 |
702 |
/*
|
645 |
703 |
* Reserve location 0 for a console port for backward compat
|
646 |
704 |
* (old kernel, new qemu)
|
647 |
705 |
*/
|
648 |
|
vser->config.nr_ports = 1;
|
|
706 |
mark_port_added(vser, 0);
|
649 |
707 |
|
650 |
708 |
vser->vdev.get_features = get_features;
|
651 |
709 |
vser->vdev.get_config = get_config;
|