]>
Commit | Line | Data |
---|---|---|
c746b74a JR |
1 | /* |
2 | * Copyright © 2018, 2021 Oracle and/or its affiliates. | |
3 | * | |
4 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
5 | * See the COPYING file in the top-level directory. | |
6 | * | |
7 | */ | |
8 | ||
9 | #include "qemu/osdep.h" | |
10 | #include "qemu-common.h" | |
11 | ||
12 | #include "qemu/compiler.h" | |
13 | #include "qemu/int128.h" | |
14 | #include "qemu/range.h" | |
15 | #include "exec/memory.h" | |
16 | #include "exec/cpu-common.h" | |
c746b74a | 17 | #include "exec/ram_addr.h" |
c746b74a JR |
18 | #include "qapi/error.h" |
19 | #include "hw/remote/mpqemu-link.h" | |
20 | #include "hw/remote/proxy-memory-listener.h" | |
21 | ||
22 | /* | |
23 | * TODO: get_fd_from_hostaddr(), proxy_mrs_can_merge() and | |
24 | * proxy_memory_listener_commit() defined below perform tasks similar to the | |
25 | * functions defined in vhost-user.c. These functions are good candidates | |
26 | * for refactoring. | |
27 | * | |
28 | */ | |
29 | ||
30 | static void proxy_memory_listener_reset(MemoryListener *listener) | |
31 | { | |
32 | ProxyMemoryListener *proxy_listener = container_of(listener, | |
33 | ProxyMemoryListener, | |
34 | listener); | |
35 | int mrs; | |
36 | ||
37 | for (mrs = 0; mrs < proxy_listener->n_mr_sections; mrs++) { | |
38 | memory_region_unref(proxy_listener->mr_sections[mrs].mr); | |
39 | } | |
40 | ||
41 | g_free(proxy_listener->mr_sections); | |
42 | proxy_listener->mr_sections = NULL; | |
43 | proxy_listener->n_mr_sections = 0; | |
44 | } | |
45 | ||
46 | static int get_fd_from_hostaddr(uint64_t host, ram_addr_t *offset) | |
47 | { | |
48 | MemoryRegion *mr; | |
49 | ram_addr_t off; | |
50 | ||
51 | /** | |
52 | * Assumes that the host address is a valid address as it's | |
53 | * coming from the MemoryListener system. In the case host | |
54 | * address is not valid, the following call would return | |
55 | * the default subregion of "system_memory" region, and | |
56 | * not NULL. So it's not possible to check for NULL here. | |
57 | */ | |
58 | mr = memory_region_from_host((void *)(uintptr_t)host, &off); | |
59 | ||
60 | if (offset) { | |
61 | *offset = off; | |
62 | } | |
63 | ||
64 | return memory_region_get_fd(mr); | |
65 | } | |
66 | ||
67 | static bool proxy_mrs_can_merge(uint64_t host, uint64_t prev_host, size_t size) | |
68 | { | |
69 | if (((prev_host + size) != host)) { | |
70 | return false; | |
71 | } | |
72 | ||
73 | if (get_fd_from_hostaddr(host, NULL) != | |
74 | get_fd_from_hostaddr(prev_host, NULL)) { | |
75 | return false; | |
76 | } | |
77 | ||
78 | return true; | |
79 | } | |
80 | ||
81 | static bool try_merge(ProxyMemoryListener *proxy_listener, | |
82 | MemoryRegionSection *section) | |
83 | { | |
84 | uint64_t mrs_size, mrs_gpa, mrs_page; | |
85 | MemoryRegionSection *prev_sec; | |
86 | bool merged = false; | |
87 | uintptr_t mrs_host; | |
88 | RAMBlock *mrs_rb; | |
89 | ||
90 | if (!proxy_listener->n_mr_sections) { | |
91 | return false; | |
92 | } | |
93 | ||
94 | mrs_rb = section->mr->ram_block; | |
95 | mrs_page = (uint64_t)qemu_ram_pagesize(mrs_rb); | |
96 | mrs_size = int128_get64(section->size); | |
97 | mrs_gpa = section->offset_within_address_space; | |
98 | mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) + | |
99 | section->offset_within_region; | |
100 | ||
101 | if (get_fd_from_hostaddr(mrs_host, NULL) < 0) { | |
102 | return true; | |
103 | } | |
104 | ||
105 | mrs_host = mrs_host & ~(mrs_page - 1); | |
106 | mrs_gpa = mrs_gpa & ~(mrs_page - 1); | |
107 | mrs_size = ROUND_UP(mrs_size, mrs_page); | |
108 | ||
109 | prev_sec = proxy_listener->mr_sections + | |
110 | (proxy_listener->n_mr_sections - 1); | |
111 | uint64_t prev_gpa_start = prev_sec->offset_within_address_space; | |
112 | uint64_t prev_size = int128_get64(prev_sec->size); | |
113 | uint64_t prev_gpa_end = range_get_last(prev_gpa_start, prev_size); | |
114 | uint64_t prev_host_start = | |
115 | (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr) + | |
116 | prev_sec->offset_within_region; | |
117 | uint64_t prev_host_end = range_get_last(prev_host_start, prev_size); | |
118 | ||
119 | if (mrs_gpa <= (prev_gpa_end + 1)) { | |
120 | g_assert(mrs_gpa > prev_gpa_start); | |
121 | ||
122 | if ((section->mr == prev_sec->mr) && | |
123 | proxy_mrs_can_merge(mrs_host, prev_host_start, | |
124 | (mrs_gpa - prev_gpa_start))) { | |
125 | uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size); | |
126 | merged = true; | |
127 | prev_sec->offset_within_address_space = | |
128 | MIN(prev_gpa_start, mrs_gpa); | |
129 | prev_sec->offset_within_region = | |
130 | MIN(prev_host_start, mrs_host) - | |
131 | (uintptr_t)memory_region_get_ram_ptr(prev_sec->mr); | |
132 | prev_sec->size = int128_make64(max_end - MIN(prev_host_start, | |
133 | mrs_host)); | |
134 | } | |
135 | } | |
136 | ||
137 | return merged; | |
138 | } | |
139 | ||
140 | static void proxy_memory_listener_region_addnop(MemoryListener *listener, | |
141 | MemoryRegionSection *section) | |
142 | { | |
143 | ProxyMemoryListener *proxy_listener = container_of(listener, | |
144 | ProxyMemoryListener, | |
145 | listener); | |
146 | ||
147 | if (!memory_region_is_ram(section->mr) || | |
148 | memory_region_is_rom(section->mr)) { | |
149 | return; | |
150 | } | |
151 | ||
152 | if (try_merge(proxy_listener, section)) { | |
153 | return; | |
154 | } | |
155 | ||
156 | ++proxy_listener->n_mr_sections; | |
157 | proxy_listener->mr_sections = g_renew(MemoryRegionSection, | |
158 | proxy_listener->mr_sections, | |
159 | proxy_listener->n_mr_sections); | |
160 | proxy_listener->mr_sections[proxy_listener->n_mr_sections - 1] = *section; | |
161 | proxy_listener->mr_sections[proxy_listener->n_mr_sections - 1].fv = NULL; | |
162 | memory_region_ref(section->mr); | |
163 | } | |
164 | ||
165 | static void proxy_memory_listener_commit(MemoryListener *listener) | |
166 | { | |
167 | ProxyMemoryListener *proxy_listener = container_of(listener, | |
168 | ProxyMemoryListener, | |
169 | listener); | |
170 | MPQemuMsg msg; | |
171 | MemoryRegionSection *section; | |
172 | ram_addr_t offset; | |
173 | uintptr_t host_addr; | |
174 | int region; | |
175 | Error *local_err = NULL; | |
176 | ||
177 | memset(&msg, 0, sizeof(MPQemuMsg)); | |
178 | ||
179 | msg.cmd = MPQEMU_CMD_SYNC_SYSMEM; | |
180 | msg.num_fds = proxy_listener->n_mr_sections; | |
181 | msg.size = sizeof(SyncSysmemMsg); | |
182 | if (msg.num_fds > REMOTE_MAX_FDS) { | |
183 | error_report("Number of fds is more than %d", REMOTE_MAX_FDS); | |
184 | return; | |
185 | } | |
186 | ||
187 | for (region = 0; region < proxy_listener->n_mr_sections; region++) { | |
188 | section = &proxy_listener->mr_sections[region]; | |
189 | msg.data.sync_sysmem.gpas[region] = | |
190 | section->offset_within_address_space; | |
191 | msg.data.sync_sysmem.sizes[region] = int128_get64(section->size); | |
192 | host_addr = (uintptr_t)memory_region_get_ram_ptr(section->mr) + | |
193 | section->offset_within_region; | |
194 | msg.fds[region] = get_fd_from_hostaddr(host_addr, &offset); | |
195 | msg.data.sync_sysmem.offsets[region] = offset; | |
196 | } | |
197 | if (!mpqemu_msg_send(&msg, proxy_listener->ioc, &local_err)) { | |
198 | error_report_err(local_err); | |
199 | } | |
200 | } | |
201 | ||
202 | void proxy_memory_listener_deconfigure(ProxyMemoryListener *proxy_listener) | |
203 | { | |
204 | memory_listener_unregister(&proxy_listener->listener); | |
205 | ||
206 | proxy_memory_listener_reset(&proxy_listener->listener); | |
207 | } | |
208 | ||
209 | void proxy_memory_listener_configure(ProxyMemoryListener *proxy_listener, | |
210 | QIOChannel *ioc) | |
211 | { | |
212 | proxy_listener->n_mr_sections = 0; | |
213 | proxy_listener->mr_sections = NULL; | |
214 | ||
215 | proxy_listener->ioc = ioc; | |
216 | ||
217 | proxy_listener->listener.begin = proxy_memory_listener_reset; | |
218 | proxy_listener->listener.commit = proxy_memory_listener_commit; | |
219 | proxy_listener->listener.region_add = proxy_memory_listener_region_addnop; | |
220 | proxy_listener->listener.region_nop = proxy_memory_listener_region_addnop; | |
221 | proxy_listener->listener.priority = 10; | |
142518bd | 222 | proxy_listener->listener.name = "proxy"; |
c746b74a JR |
223 | |
224 | memory_listener_register(&proxy_listener->listener, | |
225 | &address_space_memory); | |
226 | } |