1 /*
2 * QEMU Hypervisor.framework support
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2. See
5 * the COPYING file in the top-level directory.
6 *
7 * Contributions after 2012-01-13 are licensed under the terms of the
8 * GNU GPL, version 2 or (at your option) any later version.
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu/error-report.h"
13 #include "accel/accel-ops.h"
14 #include "system/address-spaces.h"
15 #include "system/memory.h"
16 #include "system/hvf.h"
17 #include "system/hvf_int.h"
18 #include "hw/core/cpu.h"
19 #include "hw/boards.h"
20 #include "trace.h"
21
22 bool hvf_allowed;
23
24 struct mac_slot {
25 int present;
26 uint64_t size;
27 uint64_t gpa_start;
28 uint64_t gva;
29 };
30
31 struct mac_slot mac_slots[32];
32
hvf_return_string(hv_return_t ret)33 const char *hvf_return_string(hv_return_t ret)
34 {
35 switch (ret) {
36 case HV_SUCCESS: return "HV_SUCCESS";
37 case HV_ERROR: return "HV_ERROR";
38 case HV_BUSY: return "HV_BUSY";
39 case HV_BAD_ARGUMENT: return "HV_BAD_ARGUMENT";
40 case HV_NO_RESOURCES: return "HV_NO_RESOURCES";
41 case HV_NO_DEVICE: return "HV_NO_DEVICE";
42 case HV_UNSUPPORTED: return "HV_UNSUPPORTED";
43 case HV_DENIED: return "HV_DENIED";
44 default: return "[unknown hv_return value]";
45 }
46 }
47
assert_hvf_ok_impl(hv_return_t ret,const char * file,unsigned int line,const char * exp)48 void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
49 const char *exp)
50 {
51 if (ret == HV_SUCCESS) {
52 return;
53 }
54
55 error_report("Error: %s = %s (0x%x, at %s:%u)",
56 exp, hvf_return_string(ret), ret, file, line);
57
58 abort();
59 }
60
do_hvf_set_memory(hvf_slot * slot,hv_memory_flags_t flags)61 static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
62 {
63 struct mac_slot *macslot;
64 hv_return_t ret;
65
66 macslot = &mac_slots[slot->slot_id];
67
68 if (macslot->present) {
69 if (macslot->size != slot->size) {
70 macslot->present = 0;
71 trace_hvf_vm_unmap(macslot->gpa_start, macslot->size);
72 ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
73 assert_hvf_ok(ret);
74 }
75 }
76
77 if (!slot->size) {
78 return 0;
79 }
80
81 macslot->present = 1;
82 macslot->gpa_start = slot->start;
83 macslot->size = slot->size;
84 trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags,
85 flags & HV_MEMORY_READ ? 'R' : '-',
86 flags & HV_MEMORY_WRITE ? 'W' : '-',
87 flags & HV_MEMORY_EXEC ? 'X' : '-');
88 ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
89 assert_hvf_ok(ret);
90 return 0;
91 }
92
hvf_set_phys_mem(MemoryRegionSection * section,bool add)93 static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
94 {
95 hvf_slot *mem;
96 MemoryRegion *area = section->mr;
97 bool writable = !area->readonly && !area->rom_device;
98 hv_memory_flags_t flags;
99 uint64_t page_size = qemu_real_host_page_size();
100
101 if (!memory_region_is_ram(area)) {
102 if (writable) {
103 return;
104 } else if (!memory_region_is_romd(area)) {
105 /*
106 * If the memory device is not in romd_mode, then we actually want
107 * to remove the hvf memory slot so all accesses will trap.
108 */
109 add = false;
110 }
111 }
112
113 if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
114 !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
115 /* Not page aligned, so we can not map as RAM */
116 add = false;
117 }
118
119 mem = hvf_find_overlap_slot(
120 section->offset_within_address_space,
121 int128_get64(section->size));
122
123 if (mem && add) {
124 if (mem->size == int128_get64(section->size) &&
125 mem->start == section->offset_within_address_space &&
126 mem->mem == (memory_region_get_ram_ptr(area) +
127 section->offset_within_region)) {
128 return; /* Same region was attempted to register, go away. */
129 }
130 }
131
132 /* Region needs to be reset. set the size to 0 and remap it. */
133 if (mem) {
134 mem->size = 0;
135 if (do_hvf_set_memory(mem, 0)) {
136 error_report("Failed to reset overlapping slot");
137 abort();
138 }
139 }
140
141 if (!add) {
142 return;
143 }
144
145 if (area->readonly ||
146 (!memory_region_is_ram(area) && memory_region_is_romd(area))) {
147 flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
148 } else {
149 flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
150 }
151
152 /* Now make a new slot. */
153 int x;
154
155 for (x = 0; x < hvf_state->num_slots; ++x) {
156 mem = &hvf_state->slots[x];
157 if (!mem->size) {
158 break;
159 }
160 }
161
162 if (x == hvf_state->num_slots) {
163 error_report("No free slots");
164 abort();
165 }
166
167 mem->size = int128_get64(section->size);
168 mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
169 mem->start = section->offset_within_address_space;
170 mem->region = area;
171
172 if (do_hvf_set_memory(mem, flags)) {
173 error_report("Error registering new memory slot");
174 abort();
175 }
176 }
177
hvf_set_dirty_tracking(MemoryRegionSection * section,bool on)178 static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
179 {
180 hvf_slot *slot;
181
182 slot = hvf_find_overlap_slot(
183 section->offset_within_address_space,
184 int128_get64(section->size));
185
186 /* protect region against writes; begin tracking it */
187 if (on) {
188 slot->flags |= HVF_SLOT_LOG;
189 hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
190 HV_MEMORY_READ | HV_MEMORY_EXEC);
191 /* stop tracking region*/
192 } else {
193 slot->flags &= ~HVF_SLOT_LOG;
194 hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
195 HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
196 }
197 }
198
hvf_log_start(MemoryListener * listener,MemoryRegionSection * section,int old,int new)199 static void hvf_log_start(MemoryListener *listener,
200 MemoryRegionSection *section, int old, int new)
201 {
202 if (old != 0) {
203 return;
204 }
205
206 hvf_set_dirty_tracking(section, 1);
207 }
208
hvf_log_stop(MemoryListener * listener,MemoryRegionSection * section,int old,int new)209 static void hvf_log_stop(MemoryListener *listener,
210 MemoryRegionSection *section, int old, int new)
211 {
212 if (new != 0) {
213 return;
214 }
215
216 hvf_set_dirty_tracking(section, 0);
217 }
218
hvf_log_sync(MemoryListener * listener,MemoryRegionSection * section)219 static void hvf_log_sync(MemoryListener *listener,
220 MemoryRegionSection *section)
221 {
222 /*
223 * sync of dirty pages is handled elsewhere; just make sure we keep
224 * tracking the region.
225 */
226 hvf_set_dirty_tracking(section, 1);
227 }
228
hvf_region_add(MemoryListener * listener,MemoryRegionSection * section)229 static void hvf_region_add(MemoryListener *listener,
230 MemoryRegionSection *section)
231 {
232 hvf_set_phys_mem(section, true);
233 }
234
hvf_region_del(MemoryListener * listener,MemoryRegionSection * section)235 static void hvf_region_del(MemoryListener *listener,
236 MemoryRegionSection *section)
237 {
238 hvf_set_phys_mem(section, false);
239 }
240
241 static MemoryListener hvf_memory_listener = {
242 .name = "hvf",
243 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
244 .region_add = hvf_region_add,
245 .region_del = hvf_region_del,
246 .log_start = hvf_log_start,
247 .log_stop = hvf_log_stop,
248 .log_sync = hvf_log_sync,
249 };
250
hvf_accel_init(AccelState * as,MachineState * ms)251 static int hvf_accel_init(AccelState *as, MachineState *ms)
252 {
253 int x;
254 hv_return_t ret;
255 HVFState *s = HVF_STATE(as);
256 int pa_range = 36;
257 MachineClass *mc = MACHINE_GET_CLASS(ms);
258
259 if (mc->hvf_get_physical_address_range) {
260 pa_range = mc->hvf_get_physical_address_range(ms);
261 if (pa_range < 0) {
262 return -EINVAL;
263 }
264 }
265
266 ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
267 if (ret == HV_DENIED) {
268 error_report("Could not access HVF. Is the executable signed"
269 " with com.apple.security.hypervisor entitlement?");
270 exit(1);
271 }
272 assert_hvf_ok(ret);
273
274 s->num_slots = ARRAY_SIZE(s->slots);
275 for (x = 0; x < s->num_slots; ++x) {
276 s->slots[x].size = 0;
277 s->slots[x].slot_id = x;
278 }
279
280 QTAILQ_INIT(&s->hvf_sw_breakpoints);
281
282 hvf_state = s;
283 memory_listener_register(&hvf_memory_listener, &address_space_memory);
284
285 return hvf_arch_init();
286 }
287
hvf_gdbstub_sstep_flags(AccelState * as)288 static int hvf_gdbstub_sstep_flags(AccelState *as)
289 {
290 return SSTEP_ENABLE | SSTEP_NOIRQ;
291 }
292
hvf_accel_class_init(ObjectClass * oc,const void * data)293 static void hvf_accel_class_init(ObjectClass *oc, const void *data)
294 {
295 AccelClass *ac = ACCEL_CLASS(oc);
296 ac->name = "HVF";
297 ac->init_machine = hvf_accel_init;
298 ac->allowed = &hvf_allowed;
299 ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
300 }
301
302 static const TypeInfo hvf_accel_type = {
303 .name = TYPE_HVF_ACCEL,
304 .parent = TYPE_ACCEL,
305 .instance_size = sizeof(HVFState),
306 .class_init = hvf_accel_class_init,
307 };
308
hvf_type_init(void)309 static void hvf_type_init(void)
310 {
311 type_register_static(&hvf_accel_type);
312 }
313
314 type_init(hvf_type_init);
315