]> Git Repo - qemu.git/blob - target/i386/hax-mem.c
Merge remote-tracking branch 'armbru/tags/pull-qapi-2017-05-04-v3' into staging
[qemu.git] / target / i386 / hax-mem.c
1 /*
2  * HAX memory mapping operations
3  *
4  * Copyright (c) 2015-16 Intel Corporation
5  * Copyright 2016 Google, Inc.
6  *
7  * This work is licensed under the terms of the GNU GPL, version 2.  See
8  * the COPYING file in the top-level directory.
9  */
10
11 #include "qemu/osdep.h"
12 #include "cpu.h"
13 #include "exec/address-spaces.h"
14 #include "exec/exec-all.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  * HAXMapping: describes a pending guest physical memory mapping
30  *
31  * @start_pa: a guest physical address marking the start of the region; must be
32  *            page-aligned
33  * @size: a guest physical address marking the end of the region; must be
34  *          page-aligned
35  * @host_va: the host virtual address of the start of the mapping
36  * @flags: mapping parameters e.g. HAX_RAM_INFO_ROM or HAX_RAM_INFO_INVALID
37  * @entry: additional fields for linking #HAXMapping instances together
38  */
39 typedef 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  * A doubly-linked list (actually a tail queue) of the pending page mappings
49  * for the ongoing memory transaction.
50  *
51  * It is used to optimize the number of page mapping updates done through the
52  * kernel module. For example, it's effective when a driver is digging an MMIO
53  * hole inside an existing memory mapping. It will get a deletion of the whole
54  * region, then the addition of the 2 remaining RAM areas around the hole and
55  * finally the memory transaction commit. During the commit, it will effectively
56  * send to the kernel only the removal of the pages from the MMIO hole after
57  * having computed locally the result of the deletion and additions.
58  */
59 static QTAILQ_HEAD(HAXMappingListHead, HAXMapping) mappings =
60     QTAILQ_HEAD_INITIALIZER(mappings);
61
62 /**
63  * hax_mapping_dump_list: dumps @mappings to stdout (for debugging)
64  */
65 static 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
78 static 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
96 static bool hax_mapping_is_opposite(HAXMapping *entry, uint64_t host_va,
97                                     uint8_t flags)
98 {
99     /* removed then added without change for the read-only flag */
100     bool nop_flags = (entry->flags ^ flags) == HAX_RAM_INFO_INVALID;
101
102     return (entry->host_va == host_va) && nop_flags;
103 }
104
105 static 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             /* split the existing chunk at start_pa */
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         /* now start_pa == entry->start_pa */
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                 /* remove the beginning of the existing chunk */
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 { /* affects the full mapping entry */
148                 if (nop) { /* no change to this mapping, remove it */
149                     QTAILQ_REMOVE(&mappings, entry, entry);
150                     g_free(entry);
151                 } else { /* update mapping properties */
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) { /* we are done */
161             break;
162         }
163     }
164     if (size) { /* add the leftover */
165         hax_insert_mapping_before(NULL, start_pa, size, host_va, flags);
166     }
167 }
168
169 static 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
177     /* We only care about RAM and ROM regions */
178     if (!memory_region_is_ram(mr)) {
179         if (memory_region_is_romd(mr)) {
180             /* HAXM kernel module does not support ROMD yet  */
181             fprintf(stderr, "%s: Warning: Ignoring ROMD region 0x%016" PRIx64
182                     "->0x%016" PRIx64 "\n", __func__, start_pa,
183                     start_pa + size);
184         }
185         return;
186     }
187
188     /* Adjust start_pa and size so that they are page-aligned. (Cf
189      * kvm_set_phys_mem() in kvm-all.c).
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     /* the kernel module interface uses 32-bit sizes (but we could split...) */
210     g_assert(size <= UINT32_MAX);
211
212     hax_update_mapping(start_pa, size, host_va, flags);
213 }
214
215 static void hax_region_add(MemoryListener *listener,
216                            MemoryRegionSection *section)
217 {
218     memory_region_ref(section->mr);
219     hax_process_section(section, 0);
220 }
221
222 static void hax_region_del(MemoryListener *listener,
223                            MemoryRegionSection *section)
224 {
225     hax_process_section(section, HAX_RAM_INFO_INVALID);
226     memory_region_unref(section->mr);
227 }
228
229 static void hax_transaction_begin(MemoryListener *listener)
230 {
231     g_assert(QTAILQ_EMPTY(&mappings));
232 }
233
234 static void hax_transaction_commit(MemoryListener *listener)
235 {
236     if (!QTAILQ_EMPTY(&mappings)) {
237         HAXMapping *entry, *next;
238
239         if (DEBUG_HAX_MEM) {
240             hax_mapping_dump_list();
241         }
242         QTAILQ_FOREACH_SAFE(entry, &mappings, entry, next) {
243             if (entry->flags & HAX_RAM_INFO_INVALID) {
244                 /* for unmapping, put the values expected by the kernel */
245                 entry->flags = HAX_RAM_INFO_INVALID;
246                 entry->host_va = 0;
247             }
248             if (hax_set_ram(entry->start_pa, entry->size,
249                             entry->host_va, entry->flags)) {
250                 fprintf(stderr, "%s: Failed mapping @0x%016" PRIx64 "+0x%"
251                         PRIx32 " flags %02x\n", __func__, entry->start_pa,
252                         entry->size, entry->flags);
253             }
254             QTAILQ_REMOVE(&mappings, entry, entry);
255             g_free(entry);
256         }
257     }
258 }
259
260 /* currently we fake the dirty bitmap sync, always dirty */
261 static void hax_log_sync(MemoryListener *listener,
262                          MemoryRegionSection *section)
263 {
264     MemoryRegion *mr = section->mr;
265
266     if (!memory_region_is_ram(mr)) {
267         /* Skip MMIO regions */
268         return;
269     }
270
271     memory_region_set_dirty(mr, 0, int128_get64(section->size));
272 }
273
274 static MemoryListener hax_memory_listener = {
275     .begin = hax_transaction_begin,
276     .commit = hax_transaction_commit,
277     .region_add = hax_region_add,
278     .region_del = hax_region_del,
279     .log_sync = hax_log_sync,
280     .priority = 10,
281 };
282
283 static void hax_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
284 {
285     /*
286      * In HAX, QEMU allocates the virtual address, and HAX kernel
287      * populates the memory with physical memory. Currently we have no
288      * paging, so user should make sure enough free memory in advance.
289      */
290     if (hax_populate_ram((uint64_t)(uintptr_t)host, size) < 0) {
291         fprintf(stderr, "HAX failed to populate RAM");
292         abort();
293     }
294 }
295
296 static struct RAMBlockNotifier hax_ram_notifier = {
297     .ram_block_added = hax_ram_block_added,
298 };
299
300 void hax_memory_init(void)
301 {
302     ram_block_notifier_add(&hax_ram_notifier);
303     memory_listener_register(&hax_memory_listener, &address_space_memory);
304 }
This page took 0.040172 seconds and 4 git commands to generate.