|
@@ -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);
|
|
|
|
+}
|