/* * Thread-safe guest to host memory mapping * * Copyright 2012 Red Hat, Inc. and/or its affiliates * * Authors: * Stefan Hajnoczi * * 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 "hw/virtio/dataplane/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); int i; qemu_mutex_lock(&hostmem->current_regions_lock); for (i = 0; i < hostmem->num_current_regions; i++) { memory_region_unref(hostmem->current_regions[i].mr); } 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 = int128_get64(section->size), .readonly = section->readonly, .mr = section->mr, }; hostmem->num_new_regions++; memory_region_ref(section->mr); } 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); }