aboutsummaryrefslogtreecommitdiff
path: root/hw/dataplane/hostmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/dataplane/hostmem.c')
-rw-r--r--hw/dataplane/hostmem.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/hw/dataplane/hostmem.c b/hw/dataplane/hostmem.c
new file mode 100644
index 0000000000..380537e06d
--- /dev/null
+++ b/hw/dataplane/hostmem.c
@@ -0,0 +1,176 @@
+/*
+ * Thread-safe guest to host memory mapping
+ *
+ * Copyright 2012 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "exec/address-spaces.h"
+#include "hostmem.h"
+
+static int hostmem_lookup_cmp(const void *phys_, const void *region_)
+{
+ hwaddr phys = *(const hwaddr *)phys_;
+ const HostMemRegion *region = region_;
+
+ if (phys < region->guest_addr) {
+ return -1;
+ } else if (phys >= region->guest_addr + region->size) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Map guest physical address to host pointer
+ */
+void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write)
+{
+ HostMemRegion *region;
+ void *host_addr = NULL;
+ hwaddr offset_within_region;
+
+ qemu_mutex_lock(&hostmem->current_regions_lock);
+ region = bsearch(&phys, hostmem->current_regions,
+ hostmem->num_current_regions,
+ sizeof(hostmem->current_regions[0]),
+ hostmem_lookup_cmp);
+ if (!region) {
+ goto out;
+ }
+ if (is_write && region->readonly) {
+ goto out;
+ }
+ offset_within_region = phys - region->guest_addr;
+ if (len <= region->size - offset_within_region) {
+ host_addr = region->host_addr + offset_within_region;
+ }
+out:
+ qemu_mutex_unlock(&hostmem->current_regions_lock);
+
+ return host_addr;
+}
+
+/**
+ * Install new regions list
+ */
+static void hostmem_listener_commit(MemoryListener *listener)
+{
+ HostMem *hostmem = container_of(listener, HostMem, listener);
+
+ qemu_mutex_lock(&hostmem->current_regions_lock);
+ g_free(hostmem->current_regions);
+ hostmem->current_regions = hostmem->new_regions;
+ hostmem->num_current_regions = hostmem->num_new_regions;
+ qemu_mutex_unlock(&hostmem->current_regions_lock);
+
+ /* Reset new regions list */
+ hostmem->new_regions = NULL;
+ hostmem->num_new_regions = 0;
+}
+
+/**
+ * Add a MemoryRegionSection to the new regions list
+ */
+static void hostmem_append_new_region(HostMem *hostmem,
+ MemoryRegionSection *section)
+{
+ void *ram_ptr = memory_region_get_ram_ptr(section->mr);
+ size_t num = hostmem->num_new_regions;
+ size_t new_size = (num + 1) * sizeof(hostmem->new_regions[0]);
+
+ hostmem->new_regions = g_realloc(hostmem->new_regions, new_size);
+ hostmem->new_regions[num] = (HostMemRegion){
+ .host_addr = ram_ptr + section->offset_within_region,
+ .guest_addr = section->offset_within_address_space,
+ .size = section->size,
+ .readonly = section->readonly,
+ };
+ hostmem->num_new_regions++;
+}
+
+static void hostmem_listener_append_region(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ HostMem *hostmem = container_of(listener, HostMem, listener);
+
+ /* Ignore non-RAM regions, we may not be able to map them */
+ if (!memory_region_is_ram(section->mr)) {
+ return;
+ }
+
+ /* Ignore regions with dirty logging, we cannot mark them dirty */
+ if (memory_region_is_logging(section->mr)) {
+ return;
+ }
+
+ hostmem_append_new_region(hostmem, section);
+}
+
+/* We don't implement most MemoryListener callbacks, use these nop stubs */
+static void hostmem_listener_dummy(MemoryListener *listener)
+{
+}
+
+static void hostmem_listener_section_dummy(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+}
+
+static void hostmem_listener_eventfd_dummy(MemoryListener *listener,
+ MemoryRegionSection *section,
+ bool match_data, uint64_t data,
+ EventNotifier *e)
+{
+}
+
+static void hostmem_listener_coalesced_mmio_dummy(MemoryListener *listener,
+ MemoryRegionSection *section,
+ hwaddr addr, hwaddr len)
+{
+}
+
+void hostmem_init(HostMem *hostmem)
+{
+ memset(hostmem, 0, sizeof(*hostmem));
+
+ qemu_mutex_init(&hostmem->current_regions_lock);
+
+ hostmem->listener = (MemoryListener){
+ .begin = hostmem_listener_dummy,
+ .commit = hostmem_listener_commit,
+ .region_add = hostmem_listener_append_region,
+ .region_del = hostmem_listener_section_dummy,
+ .region_nop = hostmem_listener_append_region,
+ .log_start = hostmem_listener_section_dummy,
+ .log_stop = hostmem_listener_section_dummy,
+ .log_sync = hostmem_listener_section_dummy,
+ .log_global_start = hostmem_listener_dummy,
+ .log_global_stop = hostmem_listener_dummy,
+ .eventfd_add = hostmem_listener_eventfd_dummy,
+ .eventfd_del = hostmem_listener_eventfd_dummy,
+ .coalesced_mmio_add = hostmem_listener_coalesced_mmio_dummy,
+ .coalesced_mmio_del = hostmem_listener_coalesced_mmio_dummy,
+ .priority = 10,
+ };
+
+ memory_listener_register(&hostmem->listener, &address_space_memory);
+ if (hostmem->num_new_regions > 0) {
+ hostmem_listener_commit(&hostmem->listener);
+ }
+}
+
+void hostmem_finalize(HostMem *hostmem)
+{
+ memory_listener_unregister(&hostmem->listener);
+ g_free(hostmem->new_regions);
+ g_free(hostmem->current_regions);
+ qemu_mutex_destroy(&hostmem->current_regions_lock);
+}