]> git.proxmox.com Git - qemu.git/blob - hw/virtio/dataplane/hostmem.c
Merge remote-tracking branch 'stefanha/block' into staging
[qemu.git] / hw / virtio / dataplane / hostmem.c
1 /*
2 * Thread-safe guest to host memory mapping
3 *
4 * Copyright 2012 Red Hat, Inc. and/or its affiliates
5 *
6 * Authors:
7 * Stefan Hajnoczi <stefanha@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 *
12 */
13
14 #include "exec/address-spaces.h"
15 #include "hw/virtio/dataplane/hostmem.h"
16
17 static int hostmem_lookup_cmp(const void *phys_, const void *region_)
18 {
19 hwaddr phys = *(const hwaddr *)phys_;
20 const HostMemRegion *region = region_;
21
22 if (phys < region->guest_addr) {
23 return -1;
24 } else if (phys >= region->guest_addr + region->size) {
25 return 1;
26 } else {
27 return 0;
28 }
29 }
30
31 /**
32 * Map guest physical address to host pointer
33 */
34 void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write)
35 {
36 HostMemRegion *region;
37 void *host_addr = NULL;
38 hwaddr offset_within_region;
39
40 qemu_mutex_lock(&hostmem->current_regions_lock);
41 region = bsearch(&phys, hostmem->current_regions,
42 hostmem->num_current_regions,
43 sizeof(hostmem->current_regions[0]),
44 hostmem_lookup_cmp);
45 if (!region) {
46 goto out;
47 }
48 if (is_write && region->readonly) {
49 goto out;
50 }
51 offset_within_region = phys - region->guest_addr;
52 if (len <= region->size - offset_within_region) {
53 host_addr = region->host_addr + offset_within_region;
54 }
55 out:
56 qemu_mutex_unlock(&hostmem->current_regions_lock);
57
58 return host_addr;
59 }
60
61 /**
62 * Install new regions list
63 */
64 static void hostmem_listener_commit(MemoryListener *listener)
65 {
66 HostMem *hostmem = container_of(listener, HostMem, listener);
67 int i;
68
69 qemu_mutex_lock(&hostmem->current_regions_lock);
70 for (i = 0; i < hostmem->num_current_regions; i++) {
71 memory_region_unref(hostmem->current_regions[i].mr);
72 }
73 g_free(hostmem->current_regions);
74 hostmem->current_regions = hostmem->new_regions;
75 hostmem->num_current_regions = hostmem->num_new_regions;
76 qemu_mutex_unlock(&hostmem->current_regions_lock);
77
78 /* Reset new regions list */
79 hostmem->new_regions = NULL;
80 hostmem->num_new_regions = 0;
81 }
82
83 /**
84 * Add a MemoryRegionSection to the new regions list
85 */
86 static void hostmem_append_new_region(HostMem *hostmem,
87 MemoryRegionSection *section)
88 {
89 void *ram_ptr = memory_region_get_ram_ptr(section->mr);
90 size_t num = hostmem->num_new_regions;
91 size_t new_size = (num + 1) * sizeof(hostmem->new_regions[0]);
92
93 hostmem->new_regions = g_realloc(hostmem->new_regions, new_size);
94 hostmem->new_regions[num] = (HostMemRegion){
95 .host_addr = ram_ptr + section->offset_within_region,
96 .guest_addr = section->offset_within_address_space,
97 .size = int128_get64(section->size),
98 .readonly = section->readonly,
99 .mr = section->mr,
100 };
101 hostmem->num_new_regions++;
102
103 memory_region_ref(section->mr);
104 }
105
106 static void hostmem_listener_append_region(MemoryListener *listener,
107 MemoryRegionSection *section)
108 {
109 HostMem *hostmem = container_of(listener, HostMem, listener);
110
111 /* Ignore non-RAM regions, we may not be able to map them */
112 if (!memory_region_is_ram(section->mr)) {
113 return;
114 }
115
116 /* Ignore regions with dirty logging, we cannot mark them dirty */
117 if (memory_region_is_logging(section->mr)) {
118 return;
119 }
120
121 hostmem_append_new_region(hostmem, section);
122 }
123
124 /* We don't implement most MemoryListener callbacks, use these nop stubs */
125 static void hostmem_listener_dummy(MemoryListener *listener)
126 {
127 }
128
129 static void hostmem_listener_section_dummy(MemoryListener *listener,
130 MemoryRegionSection *section)
131 {
132 }
133
134 static void hostmem_listener_eventfd_dummy(MemoryListener *listener,
135 MemoryRegionSection *section,
136 bool match_data, uint64_t data,
137 EventNotifier *e)
138 {
139 }
140
141 static void hostmem_listener_coalesced_mmio_dummy(MemoryListener *listener,
142 MemoryRegionSection *section,
143 hwaddr addr, hwaddr len)
144 {
145 }
146
147 void hostmem_init(HostMem *hostmem)
148 {
149 memset(hostmem, 0, sizeof(*hostmem));
150
151 qemu_mutex_init(&hostmem->current_regions_lock);
152
153 hostmem->listener = (MemoryListener){
154 .begin = hostmem_listener_dummy,
155 .commit = hostmem_listener_commit,
156 .region_add = hostmem_listener_append_region,
157 .region_del = hostmem_listener_section_dummy,
158 .region_nop = hostmem_listener_append_region,
159 .log_start = hostmem_listener_section_dummy,
160 .log_stop = hostmem_listener_section_dummy,
161 .log_sync = hostmem_listener_section_dummy,
162 .log_global_start = hostmem_listener_dummy,
163 .log_global_stop = hostmem_listener_dummy,
164 .eventfd_add = hostmem_listener_eventfd_dummy,
165 .eventfd_del = hostmem_listener_eventfd_dummy,
166 .coalesced_mmio_add = hostmem_listener_coalesced_mmio_dummy,
167 .coalesced_mmio_del = hostmem_listener_coalesced_mmio_dummy,
168 .priority = 10,
169 };
170
171 memory_listener_register(&hostmem->listener, &address_space_memory);
172 if (hostmem->num_new_regions > 0) {
173 hostmem_listener_commit(&hostmem->listener);
174 }
175 }
176
177 void hostmem_finalize(HostMem *hostmem)
178 {
179 memory_listener_unregister(&hostmem->listener);
180 g_free(hostmem->new_regions);
181 g_free(hostmem->current_regions);
182 qemu_mutex_destroy(&hostmem->current_regions_lock);
183 }