2 ** HAX memory slot operations
4 ** Copyright (c) 2015-16 Intel Corporation
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.
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"
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, ...) \
28 if (DEBUG_HAX_SLOT) { \
29 fprintf(stdout, fmt, ## __VA_ARGS__); \
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
38 * @end_pa: a guest physical address marking the end of the region; must be
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
{
50 uint64_t hva_pa_delta
;
52 QTAILQ_ENTRY(HAXSlot
) entry
;
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
);
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)
108 DPRINTF("**** BEGIN HAX SLOT LIST DUMP ****\n");
109 QTAILQ_FOREACH(slot
, &slot_list
, entry
) {
110 DPRINTF("Slot %d:\n\t", i
++);
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;
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
)
135 g_assert(start_slot
);
136 g_assert(start_slot
->start_pa
<= pa
);
140 if (slot
->end_pa
> pa
) {
143 slot
= QTAILQ_NEXT(slot
, entry
);
146 /* Should never reach here */
147 g_assert_not_reached();
152 * hax_slot_split: splits a slot into two
154 * Shrinks @slot and creates a new slot from the vacated region. Returns the
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
)
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
;
175 QTAILQ_INSERT_AFTER(&slot_list
, slot
, new_slot
, entry
);
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
);
220 low_slot_prev
= QTAILQ_PREV(low_slot
, HAXSlotListHead
, entry
);
222 /* Adjust slot and/or low_slot such that their lower bounds (start_pa)
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
);
236 /* Now we have slot->start_pa == low_slot->start_pa */
238 high_slot
= hax_slot_find(low_slot
, slot
->end_pa
- 1);
240 high_slot_next
= QTAILQ_NEXT(high_slot
, entry
);
242 /* Adjust slot and/or high_slot such that their upper bounds (end_pa)
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
) {
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
;
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
);
289 fprintf(stderr
, "%s: Failed to set memory mapping (err=%d)\n",
296 /* Exit the infinite loop following the removal of high_slot */
297 if (old_slot
== high_slot
) {
303 void hax_slot_register(uint64_t start_pa
, uint32_t size
, uint64_t host_va
,
306 uint64_t end_pa
= start_pa
+ size
;
309 g_assert(!(start_pa
& ~TARGET_PAGE_MASK
));
310 g_assert(!(end_pa
& ~TARGET_PAGE_MASK
));
311 g_assert(start_pa
< end_pa
);
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
;
321 DPRINTF("%s: Inserting slot:\n\t", __func__
);
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();