aboutsummaryrefslogtreecommitdiff
path: root/hw/misc/ivshmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/misc/ivshmem.c')
-rw-r--r--hw/misc/ivshmem.c118
1 files changed, 85 insertions, 33 deletions
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index bd9d7182af..5d272c84e9 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -24,10 +24,12 @@
#include "migration/migration.h"
#include "qapi/qmp/qerror.h"
#include "qemu/event_notifier.h"
+#include "qemu/fifo8.h"
#include "sysemu/char.h"
#include <sys/mman.h>
#include <sys/types.h>
+#include <limits.h>
#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
#define PCI_DEVICE_ID_IVSHMEM 0x1110
@@ -73,6 +75,7 @@ typedef struct IVShmemState {
CharDriverState **eventfd_chr;
CharDriverState *server_chr;
+ Fifo8 incoming_fifo;
MemoryRegion ivshmem_mmio;
/* We might need to register the BAR before we actually have the memory.
@@ -130,7 +133,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val)
/* don't print ISR resets */
if (isr) {
IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
- isr ? 1 : 0, s->intrstatus, s->intrmask);
+ isr ? 1 : 0, s->intrstatus, s->intrmask);
}
pci_set_irq(d, (isr != 0));
@@ -297,8 +300,8 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *
chr = qemu_chr_open_eventfd(eventfd);
if (chr == NULL) {
- fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
- exit(-1);
+ error_report("creating eventfd for eventfd %d failed", eventfd);
+ exit(1);
}
qemu_chr_fe_claim_no_fail(chr);
@@ -325,16 +328,15 @@ static int check_shm_size(IVShmemState *s, int fd) {
struct stat buf;
if (fstat(fd, &buf) < 0) {
- fprintf(stderr, "ivshmem: exiting: fstat on fd %d failed: %s\n",
- fd, strerror(errno));
+ error_report("exiting: fstat on fd %d failed: %s",
+ fd, strerror(errno));
return -1;
}
if (s->ivshmem_size > buf.st_size) {
- fprintf(stderr,
- "IVSHMEM ERROR: Requested memory size greater"
- " than shared object size (%" PRIu64 " > %" PRIu64")\n",
- s->ivshmem_size, (uint64_t)buf.st_size);
+ error_report("Requested memory size greater"
+ " than shared object size (%" PRIu64 " > %" PRIu64")",
+ s->ivshmem_size, (uint64_t)buf.st_size);
return -1;
} else {
return 0;
@@ -387,6 +389,9 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
return;
}
+ if (posn < 0 || posn >= s->nb_peers) {
+ return;
+ }
guest_curr_max = s->peers[posn].nb_eventfds;
@@ -405,14 +410,24 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
/* this function increase the dynamic storage need to store data about other
* guests */
-static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
+static int increase_dynamic_storage(IVShmemState *s, int new_min_size)
+{
int j, old_nb_alloc;
+ /* check for integer overflow */
+ if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) {
+ return -1;
+ }
+
old_nb_alloc = s->nb_peers;
- while (new_min_size >= s->nb_peers)
- s->nb_peers = s->nb_peers * 2;
+ if (new_min_size >= s->nb_peers) {
+ /* +1 because #new_min_size is used as last array index */
+ s->nb_peers = new_min_size + 1;
+ } else {
+ return 0;
+ }
IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
@@ -422,23 +437,57 @@ static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
s->peers[j].eventfds = NULL;
s->peers[j].nb_eventfds = 0;
}
+
+ return 0;
}
-static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
+static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
{
IVShmemState *s = opaque;
int incoming_fd, tmp_fd;
int guest_max_eventfd;
long incoming_posn;
- memcpy(&incoming_posn, buf, sizeof(long));
+ if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) {
+ memcpy(&incoming_posn, buf, size);
+ } else {
+ const uint8_t *p;
+ uint32_t num;
+
+ IVSHMEM_DPRINTF("short read of %d bytes\n", size);
+ num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
+ fifo8_push_all(&s->incoming_fifo, buf, num);
+ if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) {
+ return;
+ }
+ size -= num;
+ buf += num;
+ p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num);
+ g_assert(num == sizeof(incoming_posn));
+ memcpy(&incoming_posn, p, sizeof(incoming_posn));
+ if (size > 0) {
+ fifo8_push_all(&s->incoming_fifo, buf, size);
+ }
+ }
+
+ if (incoming_posn < -1) {
+ IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
+ return;
+ }
+
/* pick off s->server_chr->msgfd and store it, posn should accompany msg */
tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
/* make sure we have enough space for this guest */
if (incoming_posn >= s->nb_peers) {
- increase_dynamic_storage(s, incoming_posn);
+ if (increase_dynamic_storage(s, incoming_posn) < 0) {
+ error_report("increase_dynamic_storage() failed");
+ if (tmp_fd != -1) {
+ close(tmp_fd);
+ }
+ return;
+ }
}
if (tmp_fd == -1) {
@@ -460,8 +509,8 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
incoming_fd = dup(tmp_fd);
if (incoming_fd == -1) {
- fprintf(stderr, "could not allocate file descriptor %s\n",
- strerror(errno));
+ error_report("could not allocate file descriptor %s", strerror(errno));
+ close(tmp_fd);
return;
}
@@ -473,7 +522,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
s->max_peer = 0;
if (check_shm_size(s, incoming_fd) == -1) {
- exit(-1);
+ exit(1);
}
/* mmap the region and map into the BAR2 */
@@ -484,7 +533,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
vmstate_register_ram(&s->ivshmem, DEVICE(s));
IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n",
- map_ptr, s->ivshmem_size);
+ map_ptr, s->ivshmem_size);
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
@@ -505,7 +554,7 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
/* this is an eventfd for a particular guest VM */
IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
- guest_max_eventfd, incoming_fd);
+ guest_max_eventfd, incoming_fd);
event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
incoming_fd);
@@ -567,13 +616,13 @@ static uint64_t ivshmem_get_size(IVShmemState * s) {
value <<= 30;
break;
default:
- fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg);
+ error_report("invalid ram size: %s", s->sizearg);
exit(1);
}
/* BARs must be a power of 2 */
if (!is_power_of_two(value)) {
- fprintf(stderr, "ivshmem: size must be power of 2\n");
+ error_report("size must be power of 2");
exit(1);
}
@@ -625,7 +674,7 @@ static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
}
if (proxy->role_val == IVSHMEM_PEER) {
- fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n");
+ error_report("'peer' devices are not migratable");
return -EINVAL;
}
@@ -663,13 +712,15 @@ static int pci_ivshmem_init(PCIDevice *dev)
s->ivshmem_size = ivshmem_get_size(s);
}
+ fifo8_create(&s->incoming_fifo, sizeof(long));
+
register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
dev);
/* IRQFD requires MSI */
if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
!ivshmem_has_feature(s, IVSHMEM_MSI)) {
- fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n");
+ error_report("ioeventfd/irqfd requires MSI");
exit(1);
}
@@ -680,7 +731,7 @@ static int pci_ivshmem_init(PCIDevice *dev)
} else if (strncmp(s->role, "master", 7) == 0) {
s->role_val = IVSHMEM_MASTER;
} else {
- fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n");
+ error_report("'role' must be 'peer' or 'master'");
exit(1);
}
} else {
@@ -720,12 +771,12 @@ static int pci_ivshmem_init(PCIDevice *dev)
* to the ivshmem server to receive the memory region */
if (s->shmobj != NULL) {
- fprintf(stderr, "WARNING: do not specify both 'chardev' "
- "and 'shm' with ivshmem\n");
+ error_report("WARNING: do not specify both 'chardev' "
+ "and 'shm' with ivshmem");
}
IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
- s->server_chr->filename);
+ s->server_chr->filename);
if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
ivshmem_setup_msi(s);
@@ -749,7 +800,7 @@ static int pci_ivshmem_init(PCIDevice *dev)
int fd;
if (s->shmobj == NULL) {
- fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n");
+ error_report("Must specify 'chardev' or 'shm' to ivshmem");
exit(1);
}
@@ -761,18 +812,18 @@ static int pci_ivshmem_init(PCIDevice *dev)
S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
/* truncate file to length PCI device's memory */
if (ftruncate(fd, s->ivshmem_size) != 0) {
- fprintf(stderr, "ivshmem: could not truncate shared file\n");
+ error_report("could not truncate shared file");
}
} else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
- fprintf(stderr, "ivshmem: could not open shared file\n");
- exit(-1);
+ error_report("could not open shared file");
+ exit(1);
}
if (check_shm_size(s, fd) == -1) {
- exit(-1);
+ exit(1);
}
create_shared_memory_BAR(s, fd);
@@ -796,6 +847,7 @@ static void pci_ivshmem_uninit(PCIDevice *dev)
memory_region_del_subregion(&s->bar, &s->ivshmem);
vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
unregister_savevm(DEVICE(dev), "ivshmem", s);
+ fifo8_destroy(&s->incoming_fifo);
}
static Property ivshmem_properties[] = {