master
c 299 lines 8.46 KB
Raw
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 "qapi/error.h"
14 #include "qapi/qapi-visit-common.h"
15 #include "accel/accel-ops.h"
16 #include "exec/cpu-common.h"
17 #include "system/address-spaces.h"
18 #include "system/memory.h"
19 #include "system/hvf.h"
20 #include "system/hvf_int.h"
21 #include "hw/core/cpu.h"
22 #include "hw/core/boards.h"
23 #include "trace.h"
24
25 bool hvf_allowed;
26 bool hvf_kernel_irqchip;
27 bool hvf_nested_virt;
28 static bool hvf_kernel_irqchip_override;
29
30 void hvf_nested_virt_enable(bool nested_virt)
31 {
32 hvf_nested_virt = nested_virt;
33 }
34
35 const char *hvf_return_string(hv_return_t ret)
36 {
37 switch (ret) {
38 case HV_SUCCESS: return "HV_SUCCESS";
39 case HV_ERROR: return "HV_ERROR";
40 case HV_BUSY: return "HV_BUSY";
41 case HV_BAD_ARGUMENT: return "HV_BAD_ARGUMENT";
42 case HV_NO_RESOURCES: return "HV_NO_RESOURCES";
43 case HV_NO_DEVICE: return "HV_NO_DEVICE";
44 case HV_UNSUPPORTED: return "HV_UNSUPPORTED";
45 case HV_DENIED: return "HV_DENIED";
46 default: return "[unknown hv_return value]";
47 }
48 }
49
50 void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
51 const char *exp)
52 {
53 if (ret == HV_SUCCESS) {
54 return;
55 }
56
57 error_report("Error: %s = %s (0x%x, at %s:%u)",
58 exp, hvf_return_string(ret), ret, file, line);
59
60 abort();
61 }
62
63 static void do_hv_vm_protect(hwaddr start, size_t size,
64 hv_memory_flags_t flags)
65 {
66 intptr_t page_mask = qemu_real_host_page_mask();
67 hv_return_t ret;
68
69 trace_hvf_vm_protect(start, size, flags,
70 flags & HV_MEMORY_READ ? 'R' : '-',
71 flags & HV_MEMORY_WRITE ? 'W' : '-',
72 flags & HV_MEMORY_EXEC ? 'X' : '-');
73 g_assert(!((uintptr_t)start & ~page_mask));
74 g_assert(!(size & ~page_mask));
75
76 ret = hv_vm_protect(start, size, flags);
77 assert_hvf_ok(ret);
78 }
79
80 void hvf_protect_clean_range(hwaddr addr, size_t size)
81 {
82 do_hv_vm_protect(addr, size, HV_MEMORY_READ | HV_MEMORY_EXEC);
83 }
84
85 void hvf_unprotect_dirty_range(hwaddr addr, size_t size)
86 {
87 do_hv_vm_protect(addr, size,
88 HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
89 }
90
91 static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
92 {
93 MemoryRegion *area = section->mr;
94 bool writable = !area->readonly && !area->rom_device;
95 hv_memory_flags_t flags;
96 uint64_t page_size = qemu_real_host_page_size();
97 uint64_t gpa = section->offset_within_address_space;
98 uint64_t size = int128_get64(section->size);
99 hv_return_t ret;
100 void *mem;
101
102 if (!memory_region_is_ram(area)) {
103 if (writable) {
104 return;
105 } else if (!memory_region_is_romd(area)) {
106 /*
107 * If the memory device is not in romd_mode, then we actually want
108 * to remove the hvf memory slot so all accesses will trap.
109 */
110 add = false;
111 }
112 }
113
114 if (!QEMU_IS_ALIGNED(size, page_size) ||
115 !QEMU_IS_ALIGNED(gpa, page_size)) {
116 /* Not page aligned, so we can not map as RAM */
117 add = false;
118 }
119
120 if (!add) {
121 trace_hvf_vm_unmap(gpa, size);
122 ret = hv_vm_unmap(gpa, size);
123 assert_hvf_ok(ret);
124 return;
125 }
126
127 flags = HV_MEMORY_READ | HV_MEMORY_EXEC | (writable ? HV_MEMORY_WRITE : 0);
128 mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
129
130 trace_hvf_vm_map(gpa, size, mem, flags,
131 flags & HV_MEMORY_READ ? 'R' : '-',
132 flags & HV_MEMORY_WRITE ? 'W' : '-',
133 flags & HV_MEMORY_EXEC ? 'X' : '-');
134 ret = hv_vm_map(mem, gpa, size, flags);
135 assert_hvf_ok(ret);
136 }
137
138 static void hvf_log_start(MemoryListener *listener,
139 MemoryRegionSection *section, int old, int new)
140 {
141 assert(new != 0);
142 if (old == 0) {
143 hvf_protect_clean_range(section->offset_within_address_space,
144 int128_get64(section->size));
145 }
146 }
147
148 static void hvf_log_stop(MemoryListener *listener,
149 MemoryRegionSection *section, int old, int new)
150 {
151 assert(old != 0);
152 if (new == 0) {
153 hvf_unprotect_dirty_range(section->offset_within_address_space,
154 int128_get64(section->size));
155 }
156 }
157
158 static void hvf_log_clear(MemoryListener *listener,
159 MemoryRegionSection *section)
160 {
161 /*
162 * The dirty page bits within section are being cleared.
163 * Some number of those pages may have been dirtied and
164 * the write permission enabled. Reset the range read-only.
165 */
166 hvf_protect_clean_range(section->offset_within_address_space,
167 int128_get64(section->size));
168 }
169
170 static void hvf_region_add(MemoryListener *listener,
171 MemoryRegionSection *section)
172 {
173 hvf_set_phys_mem(section, true);
174 }
175
176 static void hvf_region_del(MemoryListener *listener,
177 MemoryRegionSection *section)
178 {
179 hvf_set_phys_mem(section, false);
180 }
181
182 static MemoryListener hvf_memory_listener = {
183 .name = "hvf",
184 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
185 .region_add = hvf_region_add,
186 .region_del = hvf_region_del,
187 .log_start = hvf_log_start,
188 .log_stop = hvf_log_stop,
189 .log_clear = hvf_log_clear,
190 };
191
192 static int hvf_accel_init(AccelState *as, MachineState *ms)
193 {
194 hv_return_t ret;
195 HVFState *s = HVF_STATE(as);
196 int pa_range = 36;
197 MachineClass *mc = MACHINE_GET_CLASS(ms);
198
199
200 if (mc->get_physical_address_range) {
201 pa_range = mc->get_physical_address_range(ms,
202 hvf_arch_get_default_ipa_bit_size(), hvf_arch_get_max_ipa_bit_size());
203 if (pa_range < 0) {
204 return -EINVAL;
205 }
206 }
207
208 if (mc->get_kernel_irqchip_default) {
209 bool kernel_irqchip_default = mc->get_kernel_irqchip_default(ms);
210 if (!hvf_kernel_irqchip_override) {
211 hvf_kernel_irqchip = kernel_irqchip_default;
212 }
213 }
214
215 ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
216 if (ret == HV_DENIED) {
217 error_report("Could not access HVF. Is the executable signed"
218 " with com.apple.security.hypervisor entitlement?");
219 exit(1);
220 }
221 assert_hvf_ok(ret);
222
223 as->gdbstub.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ;
224
225 QTAILQ_INIT(&s->hvf_sw_breakpoints);
226
227 hvf_state = s;
228 memory_listener_register(&hvf_memory_listener, &address_space_memory);
229
230 return hvf_arch_init();
231 }
232
233 static void hvf_set_kernel_irqchip(Object *obj, Visitor *v,
234 const char *name, void *opaque,
235 Error **errp)
236 {
237 OnOffSplit mode;
238
239 hvf_kernel_irqchip_override = true;
240 if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
241 return;
242 }
243
244 switch (mode) {
245 case ON_OFF_SPLIT_ON:
246 #ifdef HOST_X86_64
247 /* macOS 12 onwards exposes an HVF virtual APIC. */
248 error_setg(errp, "HVF: kernel irqchip is not currently implemented for x86.");
249 break;
250 #else
251 hvf_kernel_irqchip = true;
252 break;
253 #endif
254
255 case ON_OFF_SPLIT_OFF:
256 hvf_kernel_irqchip = false;
257 break;
258
259 case ON_OFF_SPLIT_SPLIT:
260 error_setg(errp, "HVF: split irqchip is not supported on HVF.");
261 break;
262
263 default:
264 /*
265 * The value was checked in visit_type_OnOffSplit() above. If
266 * we get here, then something is wrong in QEMU.
267 */
268 abort();
269 }
270 }
271
272 static void hvf_accel_class_init(ObjectClass *oc, const void *data)
273 {
274 AccelClass *ac = ACCEL_CLASS(oc);
275 ac->name = "HVF";
276 ac->init_machine = hvf_accel_init;
277 ac->allowed = &hvf_allowed;
278 hvf_kernel_irqchip_override = false;
279 hvf_kernel_irqchip = false;
280 object_class_property_add(oc, "kernel-irqchip", "on|off|split",
281 NULL, hvf_set_kernel_irqchip,
282 NULL, NULL);
283 object_class_property_set_description(oc, "kernel-irqchip",
284 "Configure HVF irqchip");
285 }
286
287 static const TypeInfo hvf_accel_type = {
288 .name = TYPE_HVF_ACCEL,
289 .parent = TYPE_ACCEL,
290 .instance_size = sizeof(HVFState),
291 .class_init = hvf_accel_class_init,
292 };
293
294 static void hvf_type_init(void)
295 {
296 type_register_static(&hvf_accel_type);
297 }
298
299 type_init(hvf_type_init);