1
2
3
4
5
6
7
8
9
10
11#include "qemu/osdep.h"
12#include "cpu.h"
13#include "exec/address-spaces.h"
14#include "qemu/error-report.h"
15
16#include "target/i386/hax-i386.h"
17#include "qemu/queue.h"
18
19#define DEBUG_HAX_MEM 0
20
21#define DPRINTF(fmt, ...) \
22 do { \
23 if (DEBUG_HAX_MEM) { \
24 fprintf(stdout, fmt, ## __VA_ARGS__); \
25 } \
26 } while (0)
27
28
29
30
31
32
33
34
35
36
37
38
39typedef struct HAXMapping {
40 uint64_t start_pa;
41 uint32_t size;
42 uint64_t host_va;
43 int flags;
44 QTAILQ_ENTRY(HAXMapping) entry;
45} HAXMapping;
46
47
48
49
50
51
52
53
54
55
56
57
58
59static QTAILQ_HEAD(, HAXMapping) mappings =
60 QTAILQ_HEAD_INITIALIZER(mappings);
61
62
63
64
65static void hax_mapping_dump_list(void)
66{
67 HAXMapping *entry;
68
69 DPRINTF("%s updates:\n", __func__);
70 QTAILQ_FOREACH(entry, &mappings, entry) {
71 DPRINTF("\t%c 0x%016" PRIx64 "->0x%016" PRIx64 " VA 0x%016" PRIx64
72 "%s\n", entry->flags & HAX_RAM_INFO_INVALID ? '-' : '+',
73 entry->start_pa, entry->start_pa + entry->size, entry->host_va,
74 entry->flags & HAX_RAM_INFO_ROM ? " ROM" : "");
75 }
76}
77
78static void hax_insert_mapping_before(HAXMapping *next, uint64_t start_pa,
79 uint32_t size, uint64_t host_va,
80 uint8_t flags)
81{
82 HAXMapping *entry;
83
84 entry = g_malloc0(sizeof(*entry));
85 entry->start_pa = start_pa;
86 entry->size = size;
87 entry->host_va = host_va;
88 entry->flags = flags;
89 if (!next) {
90 QTAILQ_INSERT_TAIL(&mappings, entry, entry);
91 } else {
92 QTAILQ_INSERT_BEFORE(next, entry, entry);
93 }
94}
95
96static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va,
97 uint8_t flags)
98{
99
100 bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID;
101
102 return (entry->host_va == host_va) && nop_flags;
103}
104
105static void hax_update_mapping(uint64_t start_pa, uint32_t size,
106 uint64_t host_va, uint8_t flags)
107{
108 uint64_t end_pa = start_pa + size;
109 HAXMapping *entry, *next;
110
111 QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
112 uint32_t chunk_sz;
113 if (start_pa >= entry->start_pa + entry->size) {
114 continue;
115 }
116 if (start_pa < entry->start_pa) {
117 chunk_sz = end_pa <= entry->start_pa ? size
118 : entry->start_pa - start_pa;
119 hax_insert_mapping_before(entry, start_pa, chunk_sz,
120 host_va, flags);
121 start_pa += chunk_sz;
122 host_va += chunk_sz;
123 size -= chunk_sz;
124 } else if (start_pa > entry->start_pa) {
125
126 chunk_sz = start_pa - entry->start_pa;
127 hax_insert_mapping_before(entry, entry->start_pa, chunk_sz,
128 entry->host_va, entry->flags);
129 entry->start_pa += chunk_sz;
130 entry->host_va += chunk_sz;
131 entry->size -= chunk_sz;
132 }
133
134 chunk_sz = MIN(size, entry->size);
135 if (chunk_sz) {
136 bool nop = hax_mapping_is_opposite(entry, host_va, flags);
137 bool partial = chunk_sz < entry->size;
138 if (partial) {
139
140 entry->start_pa += chunk_sz;
141 entry->host_va += chunk_sz;
142 entry->size -= chunk_sz;
143 if (!nop) {
144 hax_insert_mapping_before(entry, start_pa, chunk_sz,
145 host_va, flags);
146 }
147 } else {
148 if (nop) {
149 QTAILQ_REMOVE(&mappings, entry, entry);
150 g_free(entry);
151 } else {
152 entry->host_va = host_va;
153 entry->flags = flags;
154 }
155 }
156 start_pa += chunk_sz;
157 host_va += chunk_sz;
158 size -= chunk_sz;
159 }
160 if (!size) {
161 break;
162 }
163 }
164 if (size) {
165 hax_insert_mapping_before(NULL, start_pa, size, host_va, flags);
166 }
167}
168
169static void hax_process_section(MemoryRegionSection *section, uint8_t flags)
170{
171 MemoryRegion *mr = section->mr;
172 hwaddr start_pa = section->offset_within_address_space;
173 ram_addr_t size = int128_get64(section->size);
174 unsigned int delta;
175 uint64_t host_va;
176 uint32_t max_mapping_size;
177
178
179 if (!memory_region_is_ram(mr)) {
180 if (memory_region_is_romd(mr)) {
181
182 warn_report("Ignoring ROMD region 0x%016" PRIx64 "->0x%016" PRIx64,
183 start_pa, start_pa + size);
184 }
185 return;
186 }
187
188
189
190
191 delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
192 delta &= ~qemu_real_host_page_mask;
193 if (delta > size) {
194 return;
195 }
196 start_pa += delta;
197 size -= delta;
198 size &= qemu_real_host_page_mask;
199 if (!size || (start_pa & ~qemu_real_host_page_mask)) {
200 return;
201 }
202
203 host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
204 + section->offset_within_region + delta;
205 if (memory_region_is_rom(section->mr)) {
206 flags |= HAX_RAM_INFO_ROM;
207 }
208
209
210
211
212
213
214
215
216
217 max_mapping_size = UINT32_MAX & qemu_real_host_page_mask;
218 while (size > max_mapping_size) {
219 hax_update_mapping(start_pa, max_mapping_size, host_va, flags);
220 start_pa += max_mapping_size;
221 size -= max_mapping_size;
222 host_va += max_mapping_size;
223 }
224
225 hax_update_mapping(start_pa, (uint32_t)size, host_va, flags);
226}
227
228static void hax_region_add(MemoryListener *listener,
229 MemoryRegionSection *section)
230{
231 memory_region_ref(section->mr);
232 hax_process_section(section, 0);
233}
234
235static void hax_region_del(MemoryListener *listener,
236 MemoryRegionSection *section)
237{
238 hax_process_section(section, HAX_RAM_INFO_INVALID);
239 memory_region_unref(section->mr);
240}
241
242static void hax_transaction_begin(MemoryListener *listener)
243{
244 g_assert(QTAILQ_EMPTY(&mappings));
245}
246
247static void hax_transaction_commit(MemoryListener *listener)
248{
249 if (!QTAILQ_EMPTY(&mappings)) {
250 HAXMapping *entry, *next;
251
252 if (DEBUG_HAX_MEM) {
253 hax_mapping_dump_list();
254 }
255 QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
256 if (entry->flags & HAX_RAM_INFO_INVALID) {
257
258 entry->flags = HAX_RAM_INFO_INVALID;
259 entry->host_va = 0;
260 }
261 if (hax_set_ram(entry->start_pa, entry->size,
262 entry->host_va, entry->flags)) {
263 fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%"
264 PRIx32 " flags %02x\n", __func__, entry->start_pa,
265 entry->size, entry->flags);
266 }
267 QTAILQ_REMOVE(&mappings, entry, entry);
268 g_free(entry);
269 }
270 }
271}
272
273
274static void hax_log_sync(MemoryListener *listener,
275 MemoryRegionSection *section)
276{
277 MemoryRegion *mr = section->mr;
278
279 if (!memory_region_is_ram(mr)) {
280
281 return;
282 }
283
284 memory_region_set_dirty(mr, 0, int128_get64(section->size));
285}
286
287static MemoryListener hax_memory_listener = {
288 .begin = hax_transaction_begin,
289 .commit = hax_transaction_commit,
290 .region_add = hax_region_add,
291 .region_del = hax_region_del,
292 .log_sync = hax_log_sync,
293 .priority = 10,
294};
295
296static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
297{
298
299
300
301
302
303
304
305
306
307 if (hax_populate_ram((uint64_t)(uintptr_t)host, size) < 0) {
308 fprintf(stderr, "HAX failed to populate RAM\n");
309 abort();
310 }
311}
312
313static struct RAMBlockNotifier hax_ram_notifier = {
314 .ram_block_added = hax_ram_block_added,
315};
316
317void hax_memory_init(void)
318{
319 ram_block_notifier_add(&hax_ram_notifier);
320 memory_listener_register(&hax_memory_listener, &address_space_memory);
321}
322