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