]>
Commit | Line | Data |
---|---|---|
185ecf40 SH |
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 <[email protected]> | |
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" | |
0d09e41a | 15 | #include "hw/virtio/dataplane/hostmem.h" |
185ecf40 SH |
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); | |
dfde4e6e | 67 | int i; |
185ecf40 SH |
68 | |
69 | qemu_mutex_lock(&hostmem->current_regions_lock); | |
dfde4e6e PB |
70 | for (i = 0; i < hostmem->num_current_regions; i++) { |
71 | memory_region_unref(hostmem->current_regions[i].mr); | |
72 | } | |
185ecf40 SH |
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, | |
052e87b0 | 97 | .size = int128_get64(section->size), |
185ecf40 | 98 | .readonly = section->readonly, |
dfde4e6e | 99 | .mr = section->mr, |
185ecf40 SH |
100 | }; |
101 | hostmem->num_new_regions++; | |
dfde4e6e PB |
102 | |
103 | memory_region_ref(section->mr); | |
185ecf40 SH |
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 | } |