Merge tag 'v2.8.0'
[qemu/ar7.git] / target-i386 / hax-slot.c
bloba3d8e8b125967890ca99f0555de258727af1cba2
1 /*
2 ** HAX memory slot operations
3 **
4 ** Copyright (c) 2015-16 Intel Corporation
5 **
6 ** This software is licensed under the terms of the GNU General Public
7 ** License version 2, as published by the Free Software Foundation, and
8 ** may be copied, distributed, and modified under those terms.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
16 #include "qemu/osdep.h"
17 #include "cpu.h"
18 #include "exec/exec-all.h"
20 #include "target-i386/hax-slot.h"
21 #include "target-i386/hax-i386.h"
22 #include "qemu/queue.h"
24 #define DEBUG_HAX_SLOT 0
26 #define DPRINTF(fmt, ...) \
27 do { \
28 if (DEBUG_HAX_SLOT) { \
29 fprintf(stdout, fmt, ## __VA_ARGS__); \
30 } \
31 } while (0)
33 /**
34 * HAXSlot: describes a guest physical memory region and its mapping
36 * @start_pa: a guest physical address marking the start of the region; must be
37 * page-aligned
38 * @end_pa: a guest physical address marking the end of the region; must be
39 * page-aligned
40 * @hva_pa_delta: the host virtual address to which guest physical address 0 is
41 * mapped; in other words, for any guest physical address within
42 * the region (start_pa <= pa < end_pa), the corresponding host
43 * virtual address is calculated by host_va = pa + hva_pa_delta
44 * @flags: parameters for the mapping; must be non-negative
45 * @entry: additional fields for linking #HAXSlot instances together
47 typedef struct HAXSlot {
48 uint64_t start_pa;
49 uint64_t end_pa;
50 uint64_t hva_pa_delta;
51 int flags;
52 QTAILQ_ENTRY(HAXSlot) entry;
53 } HAXSlot;
55 /* A doubly-linked list (actually a tail queue) of all registered slots */
56 static QTAILQ_HEAD(HAXSlotListHead, HAXSlot) slot_list =
57 QTAILQ_HEAD_INITIALIZER(slot_list);
59 void hax_slot_init_registry(void)
61 HAXSlot *initial_slot;
63 g_assert(QTAILQ_EMPTY(&slot_list));
65 initial_slot = g_new0(HAXSlot, 1);
66 /* Implied: initial_slot->start_pa = 0; */
67 /* Ideally we want to set end_pa to 2^64, but that is too large for
68 * uint64_t. We don't need to support such a large guest physical address
69 * space anyway; (2^64 - TARGET_PAGE_SIZE) should be (more than) enough.
71 initial_slot->end_pa = TARGET_PAGE_MASK;
72 /* hva_pa_delta and flags are initialized with invalid values */
73 initial_slot->hva_pa_delta = ~TARGET_PAGE_MASK;
74 initial_slot->flags = -1;
75 QTAILQ_INSERT_TAIL(&slot_list, initial_slot, entry);
78 void hax_slot_free_registry(void)
80 DPRINTF("%s: Deleting all registered slots\n", __func__);
81 while (!QTAILQ_EMPTY(&slot_list)) {
82 HAXSlot *slot = QTAILQ_FIRST(&slot_list);
83 QTAILQ_REMOVE(&slot_list, slot, entry);
84 g_free(slot);
88 /**
89 * hax_slot_dump: dumps a slot to stdout (for debugging)
91 * @slot: the slot to dump
93 static void hax_slot_dump(HAXSlot *slot)
95 DPRINTF("[ start_pa=0x%016" PRIx64 ", end_pa=0x%016" PRIx64
96 ", hva_pa_delta=0x%016" PRIx64 ", flags=%d ]\n", slot->start_pa,
97 slot->end_pa, slot->hva_pa_delta, slot->flags);
101 * hax_slot_dump_list: dumps @slot_list to stdout (for debugging)
103 static void hax_slot_dump_list(void)
105 HAXSlot *slot;
106 int i = 0;
108 DPRINTF("**** BEGIN HAX SLOT LIST DUMP ****\n");
109 QTAILQ_FOREACH(slot, &slot_list, entry) {
110 DPRINTF("Slot %d:\n\t", i++);
111 hax_slot_dump(slot);
113 DPRINTF("**** END HAX SLOT LIST DUMP ****\n");
117 * hax_slot_find: locates the slot containing a guest physical address
119 * Traverses @slot_list, starting from @start_slot, and returns the slot which
120 * contains @pa. There should be one and only one such slot, because:
122 * 1) @slot_list is initialized with a slot which covers all valid @pa values.
123 * This coverage stays unchanged as new slots are inserted into @slot_list.
124 * 2) @slot_list does not contain overlapping slots.
126 * @start_slot: the first slot from which @slot_list is traversed and searched;
127 * must not be %NULL
128 * @pa: the guest physical address to locate; must not be less than the lower
129 * bound of @start_slot
131 static HAXSlot *hax_slot_find(HAXSlot *start_slot, uint64_t pa)
133 HAXSlot *slot;
135 g_assert(start_slot);
136 g_assert(start_slot->start_pa <= pa);
138 slot = start_slot;
139 do {
140 if (slot->end_pa > pa) {
141 return slot;
143 slot = QTAILQ_NEXT(slot, entry);
144 } while (slot);
146 /* Should never reach here */
147 g_assert_not_reached();
148 return NULL;
152 * hax_slot_split: splits a slot into two
154 * Shrinks @slot and creates a new slot from the vacated region. Returns the
155 * new slot.
157 * @slot: the slot to be split/shrinked
158 * @pa: the splitting point; must be page-aligned and within @slot
160 static HAXSlot *hax_slot_split(HAXSlot *slot, uint64_t pa)
162 HAXSlot *new_slot;
164 g_assert(slot);
165 g_assert(pa > slot->start_pa && pa < slot->end_pa);
166 g_assert(!(pa & ~TARGET_PAGE_MASK));
168 new_slot = g_new0(HAXSlot, 1);
169 new_slot->start_pa = pa;
170 new_slot->end_pa = slot->end_pa;
171 new_slot->hva_pa_delta = slot->hva_pa_delta;
172 new_slot->flags = slot->flags;
174 slot->end_pa = pa;
175 QTAILQ_INSERT_AFTER(&slot_list, slot, new_slot, entry);
176 return new_slot;
180 * hax_slot_can_merge: tests if two slots are compatible
182 * Two slots are considered compatible if they share the same memory mapping
183 * attributes. Compatible slots can be merged if they overlap or are adjacent.
185 * Returns %true if @slot1 and @slot2 are compatible.
187 * @slot1: one of the slots to be tested; must not be %NULL
188 * @slot2: the other slot to be tested; must not be %NULL
190 static bool hax_slot_can_merge(HAXSlot *slot1, HAXSlot *slot2)
192 g_assert(slot1 && slot2);
194 return slot1->hva_pa_delta == slot2->hva_pa_delta
195 && slot1->flags == slot2->flags;
199 * hax_slot_insert: inserts a slot into @slot_list, with the potential side
200 * effect of creating/updating memory mappings
202 * Causes memory mapping attributes of @slot to override those of overlapping
203 * slots (including partial slots) in @slot_list. For any slot whose mapping
204 * attributes have changed, performs an ioctl to enforce the new mapping.
206 * Aborts QEMU on error.
208 * @slot: the slot to be inserted
210 static void hax_slot_insert(HAXSlot *slot)
212 HAXSlot *low_slot, *high_slot;
213 HAXSlot *low_slot_prev, *high_slot_next;
214 HAXSlot *old_slot, *old_slot_next;
216 g_assert(!QTAILQ_EMPTY(&slot_list));
218 low_slot = hax_slot_find(QTAILQ_FIRST(&slot_list), slot->start_pa);
219 g_assert(low_slot);
220 low_slot_prev = QTAILQ_PREV(low_slot, HAXSlotListHead, entry);
222 /* Adjust slot and/or low_slot such that their lower bounds (start_pa)
223 * align.
225 if (hax_slot_can_merge(low_slot, slot)) {
226 slot->start_pa = low_slot->start_pa;
227 } else if (slot->start_pa == low_slot->start_pa && low_slot_prev
228 && hax_slot_can_merge(low_slot_prev, slot)) {
229 low_slot = low_slot_prev;
230 slot->start_pa = low_slot->start_pa;
231 } else if (slot->start_pa != low_slot->start_pa) {
232 /* low_slot->start_pa < slot->start_pa < low_slot->end_pa */
233 low_slot = hax_slot_split(low_slot, slot->start_pa);
234 g_assert(low_slot);
236 /* Now we have slot->start_pa == low_slot->start_pa */
238 high_slot = hax_slot_find(low_slot, slot->end_pa - 1);
239 g_assert(high_slot);
240 high_slot_next = QTAILQ_NEXT(high_slot, entry);
242 /* Adjust slot and/or high_slot such that their upper bounds (end_pa)
243 * align.
245 if (hax_slot_can_merge(slot, high_slot)) {
246 slot->end_pa = high_slot->end_pa;
247 } else if (slot->end_pa == high_slot->end_pa && high_slot_next
248 && hax_slot_can_merge(slot, high_slot_next)) {
249 high_slot = high_slot_next;
250 slot->end_pa = high_slot->end_pa;
251 } else if (slot->end_pa != high_slot->end_pa) {
252 /* high_slot->start_pa < slot->end_pa < high_slot->end_pa */
253 high_slot_next = hax_slot_split(high_slot, slot->end_pa);
254 g_assert(high_slot_next);
256 /* Now we have slot->end_pa == high_slot->end_pa */
258 /* We are ready for substitution: replace all slots between low_slot and
259 * high_slot (inclusive) with slot. */
261 /* Step 1: insert slot into the list, before low_slot */
262 QTAILQ_INSERT_BEFORE(low_slot, slot, entry);
264 /* Step 2: remove low_slot..high_slot, one by one */
265 for (old_slot = low_slot;
266 /* This condition always evaluates to 1. See:
267 * https://en.wikipedia.org/wiki/Comma_operator
269 old_slot_next = QTAILQ_NEXT(old_slot, entry), 1;
270 old_slot = old_slot_next) {
271 g_assert(old_slot);
273 QTAILQ_REMOVE(&slot_list, old_slot, entry);
274 if (!hax_slot_can_merge(slot, old_slot)) {
275 /* Mapping for guest memory region [old_slot->start_pa,
276 * old_slot->end_pa) has changed - must do ioctl. */
277 /* TODO: Further reduce the number of ioctl calls by preprocessing
278 * the low_slot..high_slot sublist and combining any two adjacent
279 * slots that are both incompatible with slot.
281 uint32_t size = old_slot->end_pa - old_slot->start_pa;
282 uint64_t host_va = old_slot->start_pa + slot->hva_pa_delta;
283 int err;
285 DPRINTF("%s: Doing ioctl (size=0x%08" PRIx32 ")\n", __func__, size);
286 /* Use the new host_va and flags */
287 err = hax_set_ram(old_slot->start_pa, size, host_va, slot->flags);
288 if (err) {
289 fprintf(stderr, "%s: Failed to set memory mapping (err=%d)\n",
290 __func__, err);
291 abort();
294 g_free(old_slot);
296 /* Exit the infinite loop following the removal of high_slot */
297 if (old_slot == high_slot) {
298 break;
303 void hax_slot_register(uint64_t start_pa, uint32_t size, uint64_t host_va,
304 int flags)
306 uint64_t end_pa = start_pa + size;
307 HAXSlot *slot;
309 g_assert(!(start_pa & ~TARGET_PAGE_MASK));
310 g_assert(!(end_pa & ~TARGET_PAGE_MASK));
311 g_assert(start_pa < end_pa);
312 g_assert(host_va);
313 g_assert(flags >= 0);
315 slot = g_malloc0(sizeof(*slot));
316 slot->start_pa = start_pa;
317 slot->end_pa = end_pa;
318 slot->hva_pa_delta = host_va - start_pa;
319 slot->flags = flags;
321 DPRINTF("%s: Inserting slot:\n\t", __func__);
322 hax_slot_dump(slot);
323 if (DEBUG_HAX_SLOT) {
324 hax_slot_dump_list();
327 hax_slot_insert(slot);
329 DPRINTF("%s: Done\n", __func__);
330 if (DEBUG_HAX_SLOT) {
331 hax_slot_dump_list();