]>
Commit | Line | Data |
---|---|---|
47c1c8c1 VP |
1 | /* |
2 | * QEMU HAX support | |
3 | * | |
4 | * Copyright IBM, Corp. 2008 | |
5 | * Red Hat, Inc. 2008 | |
6 | * | |
7 | * Authors: | |
8 | * Anthony Liguori <aliguori@us.ibm.com> | |
9 | * Glauber Costa <gcosta@redhat.com> | |
10 | * | |
11 | * Copyright (c) 2011 Intel Corporation | |
12 | * Written by: | |
13 | * Jiang Yunhong<yunhong.jiang@intel.com> | |
14 | * Xin Xiaohui<xiaohui.xin@intel.com> | |
15 | * Zhang Xiantao<xiantao.zhang@intel.com> | |
16 | * | |
17 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
18 | * See the COPYING file in the top-level directory. | |
19 | * | |
20 | */ | |
21 | ||
22 | /* | |
23 | * HAX common code for both windows and darwin | |
24 | */ | |
25 | ||
26 | #include "qemu/osdep.h" | |
27 | #include "cpu.h" | |
28 | #include "exec/address-spaces.h" | |
47c1c8c1 VP |
29 | |
30 | #include "qemu-common.h" | |
47c1c8c1 VP |
31 | #include "hax-i386.h" |
32 | #include "sysemu/accel.h" | |
33 | #include "sysemu/sysemu.h" | |
34 | #include "qemu/main-loop.h" | |
35 | #include "hw/boards.h" | |
36 | ||
37 | #define DEBUG_HAX 0 | |
38 | ||
39 | #define DPRINTF(fmt, ...) \ | |
40 | do { \ | |
41 | if (DEBUG_HAX) { \ | |
42 | fprintf(stdout, fmt, ## __VA_ARGS__); \ | |
43 | } \ | |
44 | } while (0) | |
45 | ||
46 | /* Current version */ | |
47 | const uint32_t hax_cur_version = 0x4; /* API v4: unmapping and MMIO moves */ | |
48 | /* Minimum HAX kernel version */ | |
49 | const uint32_t hax_min_version = 0x4; /* API v4: supports unmapping */ | |
50 | ||
51 | static bool hax_allowed; | |
52 | ||
53 | struct hax_state hax_global; | |
54 | ||
55 | static void hax_vcpu_sync_state(CPUArchState *env, int modified); | |
56 | static int hax_arch_get_registers(CPUArchState *env); | |
57 | ||
58 | int hax_enabled(void) | |
59 | { | |
60 | return hax_allowed; | |
61 | } | |
62 | ||
63 | int valid_hax_tunnel_size(uint16_t size) | |
64 | { | |
65 | return size >= sizeof(struct hax_tunnel); | |
66 | } | |
67 | ||
68 | hax_fd hax_vcpu_get_fd(CPUArchState *env) | |
69 | { | |
70 | struct hax_vcpu_state *vcpu = ENV_GET_CPU(env)->hax_vcpu; | |
71 | if (!vcpu) { | |
72 | return HAX_INVALID_FD; | |
73 | } | |
74 | return vcpu->fd; | |
75 | } | |
76 | ||
77 | static int hax_get_capability(struct hax_state *hax) | |
78 | { | |
79 | int ret; | |
80 | struct hax_capabilityinfo capinfo, *cap = &capinfo; | |
81 | ||
82 | ret = hax_capability(hax, cap); | |
83 | if (ret) { | |
84 | return ret; | |
85 | } | |
86 | ||
87 | if ((cap->wstatus & HAX_CAP_WORKSTATUS_MASK) == HAX_CAP_STATUS_NOTWORKING) { | |
88 | if (cap->winfo & HAX_CAP_FAILREASON_VT) { | |
89 | DPRINTF | |
90 | ("VTX feature is not enabled, HAX driver will not work.\n"); | |
91 | } else if (cap->winfo & HAX_CAP_FAILREASON_NX) { | |
92 | DPRINTF | |
93 | ("NX feature is not enabled, HAX driver will not work.\n"); | |
94 | } | |
95 | return -ENXIO; | |
96 | ||
97 | } | |
98 | ||
99 | if (!(cap->winfo & HAX_CAP_UG)) { | |
100 | fprintf(stderr, "UG mode is not supported by the hardware.\n"); | |
101 | return -ENOTSUP; | |
102 | } | |
103 | ||
7a5235c9 YN |
104 | hax->supports_64bit_ramblock = !!(cap->winfo & HAX_CAP_64BIT_RAMBLOCK); |
105 | ||
47c1c8c1 VP |
106 | if (cap->wstatus & HAX_CAP_MEMQUOTA) { |
107 | if (cap->mem_quota < hax->mem_quota) { | |
108 | fprintf(stderr, "The VM memory needed exceeds the driver limit.\n"); | |
109 | return -ENOSPC; | |
110 | } | |
111 | } | |
112 | return 0; | |
113 | } | |
114 | ||
115 | static int hax_version_support(struct hax_state *hax) | |
116 | { | |
117 | int ret; | |
118 | struct hax_module_version version; | |
119 | ||
120 | ret = hax_mod_version(hax, &version); | |
121 | if (ret < 0) { | |
122 | return 0; | |
123 | } | |
124 | ||
125 | if (hax_min_version > version.cur_version) { | |
126 | fprintf(stderr, "Incompatible HAX module version %d,", | |
127 | version.cur_version); | |
128 | fprintf(stderr, "requires minimum version %d\n", hax_min_version); | |
129 | return 0; | |
130 | } | |
131 | if (hax_cur_version < version.compat_version) { | |
132 | fprintf(stderr, "Incompatible QEMU HAX API version %x,", | |
133 | hax_cur_version); | |
134 | fprintf(stderr, "requires minimum HAX API version %x\n", | |
135 | version.compat_version); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | return 1; | |
140 | } | |
141 | ||
142 | int hax_vcpu_create(int id) | |
143 | { | |
144 | struct hax_vcpu_state *vcpu = NULL; | |
145 | int ret; | |
146 | ||
147 | if (!hax_global.vm) { | |
148 | fprintf(stderr, "vcpu %x created failed, vm is null\n", id); | |
149 | return -1; | |
150 | } | |
151 | ||
152 | if (hax_global.vm->vcpus[id]) { | |
153 | fprintf(stderr, "vcpu %x allocated already\n", id); | |
154 | return 0; | |
155 | } | |
156 | ||
090627a9 | 157 | vcpu = g_new0(struct hax_vcpu_state, 1); |
47c1c8c1 VP |
158 | |
159 | ret = hax_host_create_vcpu(hax_global.vm->fd, id); | |
160 | if (ret) { | |
161 | fprintf(stderr, "Failed to create vcpu %x\n", id); | |
162 | goto error; | |
163 | } | |
164 | ||
165 | vcpu->vcpu_id = id; | |
166 | vcpu->fd = hax_host_open_vcpu(hax_global.vm->id, id); | |
167 | if (hax_invalid_fd(vcpu->fd)) { | |
168 | fprintf(stderr, "Failed to open the vcpu\n"); | |
169 | ret = -ENODEV; | |
170 | goto error; | |
171 | } | |
172 | ||
173 | hax_global.vm->vcpus[id] = vcpu; | |
174 | ||
175 | ret = hax_host_setup_vcpu_channel(vcpu); | |
176 | if (ret) { | |
177 | fprintf(stderr, "Invalid hax tunnel size\n"); | |
178 | ret = -EINVAL; | |
179 | goto error; | |
180 | } | |
181 | return 0; | |
182 | ||
183 | error: | |
184 | /* vcpu and tunnel will be closed automatically */ | |
185 | if (vcpu && !hax_invalid_fd(vcpu->fd)) { | |
186 | hax_close_fd(vcpu->fd); | |
187 | } | |
188 | ||
189 | hax_global.vm->vcpus[id] = NULL; | |
190 | g_free(vcpu); | |
191 | return -1; | |
192 | } | |
193 | ||
194 | int hax_vcpu_destroy(CPUState *cpu) | |
195 | { | |
196 | struct hax_vcpu_state *vcpu = cpu->hax_vcpu; | |
197 | ||
198 | if (!hax_global.vm) { | |
199 | fprintf(stderr, "vcpu %x destroy failed, vm is null\n", vcpu->vcpu_id); | |
200 | return -1; | |
201 | } | |
202 | ||
203 | if (!vcpu) { | |
204 | return 0; | |
205 | } | |
206 | ||
207 | /* | |
1d4f78e9 | 208 | * 1. The hax_tunnel is also destroyed when vcpu is destroyed |
47c1c8c1 VP |
209 | * 2. close fd will cause hax module vcpu be cleaned |
210 | */ | |
211 | hax_close_fd(vcpu->fd); | |
212 | hax_global.vm->vcpus[vcpu->vcpu_id] = NULL; | |
213 | g_free(vcpu); | |
214 | return 0; | |
215 | } | |
216 | ||
217 | int hax_init_vcpu(CPUState *cpu) | |
218 | { | |
219 | int ret; | |
220 | ||
221 | ret = hax_vcpu_create(cpu->cpu_index); | |
222 | if (ret < 0) { | |
223 | fprintf(stderr, "Failed to create HAX vcpu\n"); | |
224 | exit(-1); | |
225 | } | |
226 | ||
227 | cpu->hax_vcpu = hax_global.vm->vcpus[cpu->cpu_index]; | |
99f31832 | 228 | cpu->vcpu_dirty = true; |
47c1c8c1 VP |
229 | qemu_register_reset(hax_reset_vcpu_state, (CPUArchState *) (cpu->env_ptr)); |
230 | ||
231 | return ret; | |
232 | } | |
233 | ||
234 | struct hax_vm *hax_vm_create(struct hax_state *hax) | |
235 | { | |
236 | struct hax_vm *vm; | |
237 | int vm_id = 0, ret; | |
238 | ||
239 | if (hax_invalid_fd(hax->fd)) { | |
240 | return NULL; | |
241 | } | |
242 | ||
243 | if (hax->vm) { | |
244 | return hax->vm; | |
245 | } | |
246 | ||
090627a9 LQ |
247 | vm = g_new0(struct hax_vm, 1); |
248 | ||
47c1c8c1 VP |
249 | ret = hax_host_create_vm(hax, &vm_id); |
250 | if (ret) { | |
251 | fprintf(stderr, "Failed to create vm %x\n", ret); | |
252 | goto error; | |
253 | } | |
254 | vm->id = vm_id; | |
255 | vm->fd = hax_host_open_vm(hax, vm_id); | |
256 | if (hax_invalid_fd(vm->fd)) { | |
257 | fprintf(stderr, "Failed to open vm %d\n", vm_id); | |
258 | goto error; | |
259 | } | |
260 | ||
261 | hax->vm = vm; | |
262 | return vm; | |
263 | ||
264 | error: | |
265 | g_free(vm); | |
266 | hax->vm = NULL; | |
267 | return NULL; | |
268 | } | |
269 | ||
270 | int hax_vm_destroy(struct hax_vm *vm) | |
271 | { | |
272 | int i; | |
273 | ||
274 | for (i = 0; i < HAX_MAX_VCPU; i++) | |
275 | if (vm->vcpus[i]) { | |
276 | fprintf(stderr, "VCPU should be cleaned before vm clean\n"); | |
277 | return -1; | |
278 | } | |
279 | hax_close_fd(vm->fd); | |
280 | g_free(vm); | |
281 | hax_global.vm = NULL; | |
282 | return 0; | |
283 | } | |
284 | ||
285 | static void hax_handle_interrupt(CPUState *cpu, int mask) | |
286 | { | |
287 | cpu->interrupt_request |= mask; | |
288 | ||
289 | if (!qemu_cpu_is_self(cpu)) { | |
290 | qemu_cpu_kick(cpu); | |
291 | } | |
292 | } | |
293 | ||
294 | static int hax_init(ram_addr_t ram_size) | |
295 | { | |
296 | struct hax_state *hax = NULL; | |
297 | struct hax_qemu_version qversion; | |
298 | int ret; | |
299 | ||
300 | hax = &hax_global; | |
301 | ||
302 | memset(hax, 0, sizeof(struct hax_state)); | |
303 | hax->mem_quota = ram_size; | |
304 | ||
305 | hax->fd = hax_mod_open(); | |
306 | if (hax_invalid_fd(hax->fd)) { | |
307 | hax->fd = 0; | |
308 | ret = -ENODEV; | |
309 | goto error; | |
310 | } | |
311 | ||
312 | ret = hax_get_capability(hax); | |
313 | ||
314 | if (ret) { | |
315 | if (ret != -ENOSPC) { | |
316 | ret = -EINVAL; | |
317 | } | |
318 | goto error; | |
319 | } | |
320 | ||
321 | if (!hax_version_support(hax)) { | |
322 | ret = -EINVAL; | |
323 | goto error; | |
324 | } | |
325 | ||
326 | hax->vm = hax_vm_create(hax); | |
327 | if (!hax->vm) { | |
328 | fprintf(stderr, "Failed to create HAX VM\n"); | |
329 | ret = -EINVAL; | |
330 | goto error; | |
331 | } | |
332 | ||
333 | hax_memory_init(); | |
334 | ||
335 | qversion.cur_version = hax_cur_version; | |
336 | qversion.min_version = hax_min_version; | |
337 | hax_notify_qemu_version(hax->vm->fd, &qversion); | |
338 | cpu_interrupt_handler = hax_handle_interrupt; | |
339 | ||
340 | return ret; | |
341 | error: | |
342 | if (hax->vm) { | |
343 | hax_vm_destroy(hax->vm); | |
344 | } | |
345 | if (hax->fd) { | |
346 | hax_mod_close(hax); | |
347 | } | |
348 | ||
349 | return ret; | |
350 | } | |
351 | ||
352 | static int hax_accel_init(MachineState *ms) | |
353 | { | |
354 | int ret = hax_init(ms->ram_size); | |
355 | ||
356 | if (ret && (ret != -ENOSPC)) { | |
357 | fprintf(stderr, "No accelerator found.\n"); | |
358 | } else { | |
359 | fprintf(stdout, "HAX is %s and emulator runs in %s mode.\n", | |
360 | !ret ? "working" : "not working", | |
361 | !ret ? "fast virt" : "emulation"); | |
362 | } | |
363 | return ret; | |
364 | } | |
365 | ||
366 | static int hax_handle_fastmmio(CPUArchState *env, struct hax_fastmmio *hft) | |
367 | { | |
368 | if (hft->direction < 2) { | |
369 | cpu_physical_memory_rw(hft->gpa, (uint8_t *) &hft->value, hft->size, | |
370 | hft->direction); | |
371 | } else { | |
372 | /* | |
373 | * HAX API v4 supports transferring data between two MMIO addresses, | |
374 | * hft->gpa and hft->gpa2 (instructions such as MOVS require this): | |
375 | * hft->direction == 2: gpa ==> gpa2 | |
376 | */ | |
377 | uint64_t value; | |
378 | cpu_physical_memory_rw(hft->gpa, (uint8_t *) &value, hft->size, 0); | |
379 | cpu_physical_memory_rw(hft->gpa2, (uint8_t *) &value, hft->size, 1); | |
380 | } | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | static int hax_handle_io(CPUArchState *env, uint32_t df, uint16_t port, | |
386 | int direction, int size, int count, void *buffer) | |
387 | { | |
388 | uint8_t *ptr; | |
389 | int i; | |
390 | MemTxAttrs attrs = { 0 }; | |
391 | ||
392 | if (!df) { | |
393 | ptr = (uint8_t *) buffer; | |
394 | } else { | |
395 | ptr = buffer + size * count - size; | |
396 | } | |
397 | for (i = 0; i < count; i++) { | |
398 | address_space_rw(&address_space_io, port, attrs, | |
399 | ptr, size, direction == HAX_EXIT_IO_OUT); | |
400 | if (!df) { | |
401 | ptr += size; | |
402 | } else { | |
403 | ptr -= size; | |
404 | } | |
405 | } | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
410 | static int hax_vcpu_interrupt(CPUArchState *env) | |
411 | { | |
412 | CPUState *cpu = ENV_GET_CPU(env); | |
413 | struct hax_vcpu_state *vcpu = cpu->hax_vcpu; | |
414 | struct hax_tunnel *ht = vcpu->tunnel; | |
415 | ||
416 | /* | |
417 | * Try to inject an interrupt if the guest can accept it | |
418 | * Unlike KVM, HAX kernel check for the eflags, instead of qemu | |
419 | */ | |
420 | if (ht->ready_for_interrupt_injection && | |
421 | (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { | |
422 | int irq; | |
423 | ||
424 | irq = cpu_get_pic_interrupt(env); | |
425 | if (irq >= 0) { | |
426 | hax_inject_interrupt(env, irq); | |
427 | cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; | |
428 | } | |
429 | } | |
430 | ||
431 | /* If we have an interrupt but the guest is not ready to receive an | |
432 | * interrupt, request an interrupt window exit. This will | |
433 | * cause a return to userspace as soon as the guest is ready to | |
434 | * receive interrupts. */ | |
435 | if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { | |
436 | ht->request_interrupt_window = 1; | |
437 | } else { | |
438 | ht->request_interrupt_window = 0; | |
439 | } | |
440 | return 0; | |
441 | } | |
442 | ||
443 | void hax_raise_event(CPUState *cpu) | |
444 | { | |
445 | struct hax_vcpu_state *vcpu = cpu->hax_vcpu; | |
446 | ||
447 | if (!vcpu) { | |
448 | return; | |
449 | } | |
450 | vcpu->tunnel->user_event_pending = 1; | |
451 | } | |
452 | ||
453 | /* | |
454 | * Ask hax kernel module to run the CPU for us till: | |
455 | * 1. Guest crash or shutdown | |
456 | * 2. Need QEMU's emulation like guest execute MMIO instruction | |
457 | * 3. Guest execute HLT | |
458 | * 4. QEMU have Signal/event pending | |
459 | * 5. An unknown VMX exit happens | |
460 | */ | |
461 | static int hax_vcpu_hax_exec(CPUArchState *env) | |
462 | { | |
463 | int ret = 0; | |
464 | CPUState *cpu = ENV_GET_CPU(env); | |
465 | X86CPU *x86_cpu = X86_CPU(cpu); | |
466 | struct hax_vcpu_state *vcpu = cpu->hax_vcpu; | |
467 | struct hax_tunnel *ht = vcpu->tunnel; | |
468 | ||
469 | if (!hax_enabled()) { | |
470 | DPRINTF("Trying to vcpu execute at eip:" TARGET_FMT_lx "\n", env->eip); | |
471 | return 0; | |
472 | } | |
473 | ||
474 | cpu->halted = 0; | |
475 | ||
476 | if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { | |
477 | cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; | |
478 | apic_poll_irq(x86_cpu->apic_state); | |
479 | } | |
480 | ||
481 | if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { | |
482 | DPRINTF("\nhax_vcpu_hax_exec: handling INIT for %d\n", | |
483 | cpu->cpu_index); | |
484 | do_cpu_init(x86_cpu); | |
485 | hax_vcpu_sync_state(env, 1); | |
486 | } | |
487 | ||
488 | if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { | |
489 | DPRINTF("hax_vcpu_hax_exec: handling SIPI for %d\n", | |
490 | cpu->cpu_index); | |
491 | hax_vcpu_sync_state(env, 0); | |
492 | do_cpu_sipi(x86_cpu); | |
493 | hax_vcpu_sync_state(env, 1); | |
494 | } | |
495 | ||
496 | do { | |
497 | int hax_ret; | |
498 | ||
499 | if (cpu->exit_request) { | |
500 | ret = 1; | |
501 | break; | |
502 | } | |
503 | ||
504 | hax_vcpu_interrupt(env); | |
505 | ||
506 | qemu_mutex_unlock_iothread(); | |
457e0355 | 507 | cpu_exec_start(cpu); |
47c1c8c1 | 508 | hax_ret = hax_vcpu_run(vcpu); |
457e0355 | 509 | cpu_exec_end(cpu); |
47c1c8c1 | 510 | qemu_mutex_lock_iothread(); |
47c1c8c1 VP |
511 | |
512 | /* Simply continue the vcpu_run if system call interrupted */ | |
513 | if (hax_ret == -EINTR || hax_ret == -EAGAIN) { | |
514 | DPRINTF("io window interrupted\n"); | |
515 | continue; | |
516 | } | |
517 | ||
518 | if (hax_ret < 0) { | |
519 | fprintf(stderr, "vcpu run failed for vcpu %x\n", vcpu->vcpu_id); | |
520 | abort(); | |
521 | } | |
522 | switch (ht->_exit_status) { | |
523 | case HAX_EXIT_IO: | |
524 | ret = hax_handle_io(env, ht->pio._df, ht->pio._port, | |
525 | ht->pio._direction, | |
526 | ht->pio._size, ht->pio._count, vcpu->iobuf); | |
527 | break; | |
528 | case HAX_EXIT_FAST_MMIO: | |
529 | ret = hax_handle_fastmmio(env, (struct hax_fastmmio *) vcpu->iobuf); | |
530 | break; | |
531 | /* Guest state changed, currently only for shutdown */ | |
532 | case HAX_EXIT_STATECHANGE: | |
533 | fprintf(stdout, "VCPU shutdown request\n"); | |
cf83f140 | 534 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); |
47c1c8c1 VP |
535 | hax_vcpu_sync_state(env, 0); |
536 | ret = 1; | |
537 | break; | |
538 | case HAX_EXIT_UNKNOWN_VMEXIT: | |
539 | fprintf(stderr, "Unknown VMX exit %x from guest\n", | |
540 | ht->_exit_reason); | |
cf83f140 | 541 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
47c1c8c1 VP |
542 | hax_vcpu_sync_state(env, 0); |
543 | cpu_dump_state(cpu, stderr, fprintf, 0); | |
544 | ret = -1; | |
545 | break; | |
546 | case HAX_EXIT_HLT: | |
547 | if (!(cpu->interrupt_request & CPU_INTERRUPT_HARD) && | |
548 | !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { | |
549 | /* hlt instruction with interrupt disabled is shutdown */ | |
550 | env->eflags |= IF_MASK; | |
551 | cpu->halted = 1; | |
552 | cpu->exception_index = EXCP_HLT; | |
553 | ret = 1; | |
554 | } | |
555 | break; | |
556 | /* these situations will continue to hax module */ | |
557 | case HAX_EXIT_INTERRUPT: | |
558 | case HAX_EXIT_PAUSED: | |
559 | break; | |
560 | case HAX_EXIT_MMIO: | |
561 | /* Should not happen on UG system */ | |
562 | fprintf(stderr, "HAX: unsupported MMIO emulation\n"); | |
563 | ret = -1; | |
564 | break; | |
565 | case HAX_EXIT_REAL: | |
566 | /* Should not happen on UG system */ | |
567 | fprintf(stderr, "HAX: unimplemented real mode emulation\n"); | |
568 | ret = -1; | |
569 | break; | |
570 | default: | |
571 | fprintf(stderr, "Unknown exit %x from HAX\n", ht->_exit_status); | |
cf83f140 | 572 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
47c1c8c1 VP |
573 | hax_vcpu_sync_state(env, 0); |
574 | cpu_dump_state(cpu, stderr, fprintf, 0); | |
575 | ret = 1; | |
576 | break; | |
577 | } | |
578 | } while (!ret); | |
579 | ||
580 | if (cpu->exit_request) { | |
581 | cpu->exit_request = 0; | |
582 | cpu->exception_index = EXCP_INTERRUPT; | |
583 | } | |
584 | return ret < 0; | |
585 | } | |
586 | ||
587 | static void do_hax_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) | |
588 | { | |
589 | CPUArchState *env = cpu->env_ptr; | |
590 | ||
591 | hax_arch_get_registers(env); | |
99f31832 | 592 | cpu->vcpu_dirty = true; |
47c1c8c1 VP |
593 | } |
594 | ||
595 | void hax_cpu_synchronize_state(CPUState *cpu) | |
596 | { | |
99f31832 | 597 | if (!cpu->vcpu_dirty) { |
47c1c8c1 VP |
598 | run_on_cpu(cpu, do_hax_cpu_synchronize_state, RUN_ON_CPU_NULL); |
599 | } | |
600 | } | |
601 | ||
602 | static void do_hax_cpu_synchronize_post_reset(CPUState *cpu, | |
603 | run_on_cpu_data arg) | |
604 | { | |
605 | CPUArchState *env = cpu->env_ptr; | |
606 | ||
607 | hax_vcpu_sync_state(env, 1); | |
99f31832 | 608 | cpu->vcpu_dirty = false; |
47c1c8c1 VP |
609 | } |
610 | ||
611 | void hax_cpu_synchronize_post_reset(CPUState *cpu) | |
612 | { | |
613 | run_on_cpu(cpu, do_hax_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); | |
614 | } | |
615 | ||
616 | static void do_hax_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) | |
617 | { | |
618 | CPUArchState *env = cpu->env_ptr; | |
619 | ||
620 | hax_vcpu_sync_state(env, 1); | |
99f31832 | 621 | cpu->vcpu_dirty = false; |
47c1c8c1 VP |
622 | } |
623 | ||
624 | void hax_cpu_synchronize_post_init(CPUState *cpu) | |
625 | { | |
626 | run_on_cpu(cpu, do_hax_cpu_synchronize_post_init, RUN_ON_CPU_NULL); | |
627 | } | |
628 | ||
75e972da DG |
629 | static void do_hax_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) |
630 | { | |
99f31832 | 631 | cpu->vcpu_dirty = true; |
75e972da DG |
632 | } |
633 | ||
634 | void hax_cpu_synchronize_pre_loadvm(CPUState *cpu) | |
635 | { | |
636 | run_on_cpu(cpu, do_hax_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); | |
637 | } | |
638 | ||
47c1c8c1 VP |
639 | int hax_smp_cpu_exec(CPUState *cpu) |
640 | { | |
641 | CPUArchState *env = (CPUArchState *) (cpu->env_ptr); | |
642 | int fatal; | |
643 | int ret; | |
644 | ||
645 | while (1) { | |
646 | if (cpu->exception_index >= EXCP_INTERRUPT) { | |
647 | ret = cpu->exception_index; | |
648 | cpu->exception_index = -1; | |
649 | break; | |
650 | } | |
651 | ||
652 | fatal = hax_vcpu_hax_exec(env); | |
653 | ||
654 | if (fatal) { | |
655 | fprintf(stderr, "Unsupported HAX vcpu return\n"); | |
656 | abort(); | |
657 | } | |
658 | } | |
659 | ||
660 | return ret; | |
661 | } | |
662 | ||
663 | static void set_v8086_seg(struct segment_desc_t *lhs, const SegmentCache *rhs) | |
664 | { | |
665 | memset(lhs, 0, sizeof(struct segment_desc_t)); | |
666 | lhs->selector = rhs->selector; | |
667 | lhs->base = rhs->base; | |
668 | lhs->limit = rhs->limit; | |
669 | lhs->type = 3; | |
670 | lhs->present = 1; | |
671 | lhs->dpl = 3; | |
672 | lhs->operand_size = 0; | |
673 | lhs->desc = 1; | |
674 | lhs->long_mode = 0; | |
675 | lhs->granularity = 0; | |
676 | lhs->available = 0; | |
677 | } | |
678 | ||
679 | static void get_seg(SegmentCache *lhs, const struct segment_desc_t *rhs) | |
680 | { | |
681 | lhs->selector = rhs->selector; | |
682 | lhs->base = rhs->base; | |
683 | lhs->limit = rhs->limit; | |
684 | lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | |
685 | | (rhs->present * DESC_P_MASK) | |
686 | | (rhs->dpl << DESC_DPL_SHIFT) | |
687 | | (rhs->operand_size << DESC_B_SHIFT) | |
688 | | (rhs->desc * DESC_S_MASK) | |
689 | | (rhs->long_mode << DESC_L_SHIFT) | |
690 | | (rhs->granularity * DESC_G_MASK) | (rhs->available * DESC_AVL_MASK); | |
691 | } | |
692 | ||
693 | static void set_seg(struct segment_desc_t *lhs, const SegmentCache *rhs) | |
694 | { | |
695 | unsigned flags = rhs->flags; | |
696 | ||
697 | memset(lhs, 0, sizeof(struct segment_desc_t)); | |
698 | lhs->selector = rhs->selector; | |
699 | lhs->base = rhs->base; | |
700 | lhs->limit = rhs->limit; | |
701 | lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; | |
702 | lhs->present = (flags & DESC_P_MASK) != 0; | |
703 | lhs->dpl = rhs->selector & 3; | |
704 | lhs->operand_size = (flags >> DESC_B_SHIFT) & 1; | |
705 | lhs->desc = (flags & DESC_S_MASK) != 0; | |
706 | lhs->long_mode = (flags >> DESC_L_SHIFT) & 1; | |
707 | lhs->granularity = (flags & DESC_G_MASK) != 0; | |
708 | lhs->available = (flags & DESC_AVL_MASK) != 0; | |
709 | } | |
710 | ||
711 | static void hax_getput_reg(uint64_t *hax_reg, target_ulong *qemu_reg, int set) | |
712 | { | |
713 | target_ulong reg = *hax_reg; | |
714 | ||
715 | if (set) { | |
716 | *hax_reg = *qemu_reg; | |
717 | } else { | |
718 | *qemu_reg = reg; | |
719 | } | |
720 | } | |
721 | ||
722 | /* The sregs has been synced with HAX kernel already before this call */ | |
723 | static int hax_get_segments(CPUArchState *env, struct vcpu_state_t *sregs) | |
724 | { | |
725 | get_seg(&env->segs[R_CS], &sregs->_cs); | |
726 | get_seg(&env->segs[R_DS], &sregs->_ds); | |
727 | get_seg(&env->segs[R_ES], &sregs->_es); | |
728 | get_seg(&env->segs[R_FS], &sregs->_fs); | |
729 | get_seg(&env->segs[R_GS], &sregs->_gs); | |
730 | get_seg(&env->segs[R_SS], &sregs->_ss); | |
731 | ||
732 | get_seg(&env->tr, &sregs->_tr); | |
733 | get_seg(&env->ldt, &sregs->_ldt); | |
734 | env->idt.limit = sregs->_idt.limit; | |
735 | env->idt.base = sregs->_idt.base; | |
736 | env->gdt.limit = sregs->_gdt.limit; | |
737 | env->gdt.base = sregs->_gdt.base; | |
738 | return 0; | |
739 | } | |
740 | ||
741 | static int hax_set_segments(CPUArchState *env, struct vcpu_state_t *sregs) | |
742 | { | |
743 | if ((env->eflags & VM_MASK)) { | |
744 | set_v8086_seg(&sregs->_cs, &env->segs[R_CS]); | |
745 | set_v8086_seg(&sregs->_ds, &env->segs[R_DS]); | |
746 | set_v8086_seg(&sregs->_es, &env->segs[R_ES]); | |
747 | set_v8086_seg(&sregs->_fs, &env->segs[R_FS]); | |
748 | set_v8086_seg(&sregs->_gs, &env->segs[R_GS]); | |
749 | set_v8086_seg(&sregs->_ss, &env->segs[R_SS]); | |
750 | } else { | |
751 | set_seg(&sregs->_cs, &env->segs[R_CS]); | |
752 | set_seg(&sregs->_ds, &env->segs[R_DS]); | |
753 | set_seg(&sregs->_es, &env->segs[R_ES]); | |
754 | set_seg(&sregs->_fs, &env->segs[R_FS]); | |
755 | set_seg(&sregs->_gs, &env->segs[R_GS]); | |
756 | set_seg(&sregs->_ss, &env->segs[R_SS]); | |
757 | ||
758 | if (env->cr[0] & CR0_PE_MASK) { | |
759 | /* force ss cpl to cs cpl */ | |
760 | sregs->_ss.selector = (sregs->_ss.selector & ~3) | | |
761 | (sregs->_cs.selector & 3); | |
762 | sregs->_ss.dpl = sregs->_ss.selector & 3; | |
763 | } | |
764 | } | |
765 | ||
766 | set_seg(&sregs->_tr, &env->tr); | |
767 | set_seg(&sregs->_ldt, &env->ldt); | |
768 | sregs->_idt.limit = env->idt.limit; | |
769 | sregs->_idt.base = env->idt.base; | |
770 | sregs->_gdt.limit = env->gdt.limit; | |
771 | sregs->_gdt.base = env->gdt.base; | |
772 | return 0; | |
773 | } | |
774 | ||
47c1c8c1 VP |
775 | static int hax_sync_vcpu_register(CPUArchState *env, int set) |
776 | { | |
777 | struct vcpu_state_t regs; | |
778 | int ret; | |
779 | memset(®s, 0, sizeof(struct vcpu_state_t)); | |
780 | ||
781 | if (!set) { | |
782 | ret = hax_sync_vcpu_state(env, ®s, 0); | |
783 | if (ret < 0) { | |
784 | return -1; | |
785 | } | |
786 | } | |
787 | ||
788 | /* generic register */ | |
789 | hax_getput_reg(®s._rax, &env->regs[R_EAX], set); | |
790 | hax_getput_reg(®s._rbx, &env->regs[R_EBX], set); | |
791 | hax_getput_reg(®s._rcx, &env->regs[R_ECX], set); | |
792 | hax_getput_reg(®s._rdx, &env->regs[R_EDX], set); | |
793 | hax_getput_reg(®s._rsi, &env->regs[R_ESI], set); | |
794 | hax_getput_reg(®s._rdi, &env->regs[R_EDI], set); | |
795 | hax_getput_reg(®s._rsp, &env->regs[R_ESP], set); | |
796 | hax_getput_reg(®s._rbp, &env->regs[R_EBP], set); | |
797 | #ifdef TARGET_X86_64 | |
798 | hax_getput_reg(®s._r8, &env->regs[8], set); | |
799 | hax_getput_reg(®s._r9, &env->regs[9], set); | |
800 | hax_getput_reg(®s._r10, &env->regs[10], set); | |
801 | hax_getput_reg(®s._r11, &env->regs[11], set); | |
802 | hax_getput_reg(®s._r12, &env->regs[12], set); | |
803 | hax_getput_reg(®s._r13, &env->regs[13], set); | |
804 | hax_getput_reg(®s._r14, &env->regs[14], set); | |
805 | hax_getput_reg(®s._r15, &env->regs[15], set); | |
806 | #endif | |
807 | hax_getput_reg(®s._rflags, &env->eflags, set); | |
808 | hax_getput_reg(®s._rip, &env->eip, set); | |
809 | ||
810 | if (set) { | |
811 | regs._cr0 = env->cr[0]; | |
812 | regs._cr2 = env->cr[2]; | |
813 | regs._cr3 = env->cr[3]; | |
814 | regs._cr4 = env->cr[4]; | |
815 | hax_set_segments(env, ®s); | |
816 | } else { | |
817 | env->cr[0] = regs._cr0; | |
818 | env->cr[2] = regs._cr2; | |
819 | env->cr[3] = regs._cr3; | |
820 | env->cr[4] = regs._cr4; | |
821 | hax_get_segments(env, ®s); | |
822 | } | |
823 | ||
824 | if (set) { | |
825 | ret = hax_sync_vcpu_state(env, ®s, 1); | |
826 | if (ret < 0) { | |
827 | return -1; | |
828 | } | |
829 | } | |
47c1c8c1 VP |
830 | return 0; |
831 | } | |
832 | ||
833 | static void hax_msr_entry_set(struct vmx_msr *item, uint32_t index, | |
834 | uint64_t value) | |
835 | { | |
836 | item->entry = index; | |
837 | item->value = value; | |
838 | } | |
839 | ||
840 | static int hax_get_msrs(CPUArchState *env) | |
841 | { | |
842 | struct hax_msr_data md; | |
843 | struct vmx_msr *msrs = md.entries; | |
844 | int ret, i, n; | |
845 | ||
846 | n = 0; | |
847 | msrs[n++].entry = MSR_IA32_SYSENTER_CS; | |
848 | msrs[n++].entry = MSR_IA32_SYSENTER_ESP; | |
849 | msrs[n++].entry = MSR_IA32_SYSENTER_EIP; | |
850 | msrs[n++].entry = MSR_IA32_TSC; | |
851 | #ifdef TARGET_X86_64 | |
852 | msrs[n++].entry = MSR_EFER; | |
853 | msrs[n++].entry = MSR_STAR; | |
854 | msrs[n++].entry = MSR_LSTAR; | |
855 | msrs[n++].entry = MSR_CSTAR; | |
856 | msrs[n++].entry = MSR_FMASK; | |
857 | msrs[n++].entry = MSR_KERNELGSBASE; | |
858 | #endif | |
859 | md.nr_msr = n; | |
860 | ret = hax_sync_msr(env, &md, 0); | |
861 | if (ret < 0) { | |
862 | return ret; | |
863 | } | |
864 | ||
865 | for (i = 0; i < md.done; i++) { | |
866 | switch (msrs[i].entry) { | |
867 | case MSR_IA32_SYSENTER_CS: | |
868 | env->sysenter_cs = msrs[i].value; | |
869 | break; | |
870 | case MSR_IA32_SYSENTER_ESP: | |
871 | env->sysenter_esp = msrs[i].value; | |
872 | break; | |
873 | case MSR_IA32_SYSENTER_EIP: | |
874 | env->sysenter_eip = msrs[i].value; | |
875 | break; | |
876 | case MSR_IA32_TSC: | |
877 | env->tsc = msrs[i].value; | |
878 | break; | |
879 | #ifdef TARGET_X86_64 | |
880 | case MSR_EFER: | |
881 | env->efer = msrs[i].value; | |
882 | break; | |
883 | case MSR_STAR: | |
884 | env->star = msrs[i].value; | |
885 | break; | |
886 | case MSR_LSTAR: | |
887 | env->lstar = msrs[i].value; | |
888 | break; | |
889 | case MSR_CSTAR: | |
890 | env->cstar = msrs[i].value; | |
891 | break; | |
892 | case MSR_FMASK: | |
893 | env->fmask = msrs[i].value; | |
894 | break; | |
895 | case MSR_KERNELGSBASE: | |
896 | env->kernelgsbase = msrs[i].value; | |
897 | break; | |
898 | #endif | |
899 | } | |
900 | } | |
901 | ||
902 | return 0; | |
903 | } | |
904 | ||
905 | static int hax_set_msrs(CPUArchState *env) | |
906 | { | |
907 | struct hax_msr_data md; | |
908 | struct vmx_msr *msrs; | |
909 | msrs = md.entries; | |
910 | int n = 0; | |
911 | ||
912 | memset(&md, 0, sizeof(struct hax_msr_data)); | |
913 | hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); | |
914 | hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); | |
915 | hax_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); | |
916 | hax_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); | |
917 | #ifdef TARGET_X86_64 | |
918 | hax_msr_entry_set(&msrs[n++], MSR_EFER, env->efer); | |
919 | hax_msr_entry_set(&msrs[n++], MSR_STAR, env->star); | |
920 | hax_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); | |
921 | hax_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); | |
922 | hax_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); | |
923 | hax_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); | |
924 | #endif | |
925 | md.nr_msr = n; | |
926 | md.done = 0; | |
927 | ||
928 | return hax_sync_msr(env, &md, 1); | |
929 | } | |
930 | ||
931 | static int hax_get_fpu(CPUArchState *env) | |
932 | { | |
933 | struct fx_layout fpu; | |
934 | int i, ret; | |
935 | ||
936 | ret = hax_sync_fpu(env, &fpu, 0); | |
937 | if (ret < 0) { | |
938 | return ret; | |
939 | } | |
940 | ||
941 | env->fpstt = (fpu.fsw >> 11) & 7; | |
942 | env->fpus = fpu.fsw; | |
943 | env->fpuc = fpu.fcw; | |
944 | for (i = 0; i < 8; ++i) { | |
945 | env->fptags[i] = !((fpu.ftw >> i) & 1); | |
946 | } | |
947 | memcpy(env->fpregs, fpu.st_mm, sizeof(env->fpregs)); | |
948 | ||
949 | for (i = 0; i < 8; i++) { | |
950 | env->xmm_regs[i].ZMM_Q(0) = ldq_p(&fpu.mmx_1[i][0]); | |
951 | env->xmm_regs[i].ZMM_Q(1) = ldq_p(&fpu.mmx_1[i][8]); | |
952 | if (CPU_NB_REGS > 8) { | |
953 | env->xmm_regs[i + 8].ZMM_Q(0) = ldq_p(&fpu.mmx_2[i][0]); | |
954 | env->xmm_regs[i + 8].ZMM_Q(1) = ldq_p(&fpu.mmx_2[i][8]); | |
955 | } | |
956 | } | |
957 | env->mxcsr = fpu.mxcsr; | |
958 | ||
959 | return 0; | |
960 | } | |
961 | ||
962 | static int hax_set_fpu(CPUArchState *env) | |
963 | { | |
964 | struct fx_layout fpu; | |
965 | int i; | |
966 | ||
967 | memset(&fpu, 0, sizeof(fpu)); | |
968 | fpu.fsw = env->fpus & ~(7 << 11); | |
969 | fpu.fsw |= (env->fpstt & 7) << 11; | |
970 | fpu.fcw = env->fpuc; | |
971 | ||
972 | for (i = 0; i < 8; ++i) { | |
973 | fpu.ftw |= (!env->fptags[i]) << i; | |
974 | } | |
975 | ||
976 | memcpy(fpu.st_mm, env->fpregs, sizeof(env->fpregs)); | |
977 | for (i = 0; i < 8; i++) { | |
978 | stq_p(&fpu.mmx_1[i][0], env->xmm_regs[i].ZMM_Q(0)); | |
979 | stq_p(&fpu.mmx_1[i][8], env->xmm_regs[i].ZMM_Q(1)); | |
980 | if (CPU_NB_REGS > 8) { | |
981 | stq_p(&fpu.mmx_2[i][0], env->xmm_regs[i + 8].ZMM_Q(0)); | |
982 | stq_p(&fpu.mmx_2[i][8], env->xmm_regs[i + 8].ZMM_Q(1)); | |
983 | } | |
984 | } | |
985 | ||
986 | fpu.mxcsr = env->mxcsr; | |
987 | ||
988 | return hax_sync_fpu(env, &fpu, 1); | |
989 | } | |
990 | ||
991 | static int hax_arch_get_registers(CPUArchState *env) | |
992 | { | |
993 | int ret; | |
994 | ||
995 | ret = hax_sync_vcpu_register(env, 0); | |
996 | if (ret < 0) { | |
997 | return ret; | |
998 | } | |
999 | ||
1000 | ret = hax_get_fpu(env); | |
1001 | if (ret < 0) { | |
1002 | return ret; | |
1003 | } | |
1004 | ||
1005 | ret = hax_get_msrs(env); | |
1006 | if (ret < 0) { | |
1007 | return ret; | |
1008 | } | |
1009 | ||
df16af87 | 1010 | x86_update_hflags(env); |
47c1c8c1 VP |
1011 | return 0; |
1012 | } | |
1013 | ||
1014 | static int hax_arch_set_registers(CPUArchState *env) | |
1015 | { | |
1016 | int ret; | |
1017 | ret = hax_sync_vcpu_register(env, 1); | |
1018 | ||
1019 | if (ret < 0) { | |
1020 | fprintf(stderr, "Failed to sync vcpu reg\n"); | |
1021 | return ret; | |
1022 | } | |
1023 | ret = hax_set_fpu(env); | |
1024 | if (ret < 0) { | |
1025 | fprintf(stderr, "FPU failed\n"); | |
1026 | return ret; | |
1027 | } | |
1028 | ret = hax_set_msrs(env); | |
1029 | if (ret < 0) { | |
1030 | fprintf(stderr, "MSR failed\n"); | |
1031 | return ret; | |
1032 | } | |
1033 | ||
1034 | return 0; | |
1035 | } | |
1036 | ||
1037 | static void hax_vcpu_sync_state(CPUArchState *env, int modified) | |
1038 | { | |
1039 | if (hax_enabled()) { | |
1040 | if (modified) { | |
1041 | hax_arch_set_registers(env); | |
1042 | } else { | |
1043 | hax_arch_get_registers(env); | |
1044 | } | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | /* | |
1049 | * much simpler than kvm, at least in first stage because: | |
1050 | * We don't need consider the device pass-through, we don't need | |
1051 | * consider the framebuffer, and we may even remove the bios at all | |
1052 | */ | |
1053 | int hax_sync_vcpus(void) | |
1054 | { | |
1055 | if (hax_enabled()) { | |
1056 | CPUState *cpu; | |
1057 | ||
1058 | cpu = first_cpu; | |
1059 | if (!cpu) { | |
1060 | return 0; | |
1061 | } | |
1062 | ||
1063 | for (; cpu != NULL; cpu = CPU_NEXT(cpu)) { | |
1064 | int ret; | |
1065 | ||
1066 | ret = hax_arch_set_registers(cpu->env_ptr); | |
1067 | if (ret < 0) { | |
1068 | return ret; | |
1069 | } | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | return 0; | |
1074 | } | |
1075 | ||
1076 | void hax_reset_vcpu_state(void *opaque) | |
1077 | { | |
1078 | CPUState *cpu; | |
1079 | for (cpu = first_cpu; cpu != NULL; cpu = CPU_NEXT(cpu)) { | |
1080 | cpu->hax_vcpu->tunnel->user_event_pending = 0; | |
1081 | cpu->hax_vcpu->tunnel->ready_for_interrupt_injection = 0; | |
1082 | } | |
1083 | } | |
1084 | ||
1085 | static void hax_accel_class_init(ObjectClass *oc, void *data) | |
1086 | { | |
1087 | AccelClass *ac = ACCEL_CLASS(oc); | |
1088 | ac->name = "HAX"; | |
1089 | ac->init_machine = hax_accel_init; | |
1090 | ac->allowed = &hax_allowed; | |
1091 | } | |
1092 | ||
1093 | static const TypeInfo hax_accel_type = { | |
1094 | .name = ACCEL_CLASS_NAME("hax"), | |
1095 | .parent = TYPE_ACCEL, | |
1096 | .class_init = hax_accel_class_init, | |
1097 | }; | |
1098 | ||
1099 | static void hax_type_init(void) | |
1100 | { | |
1101 | type_register_static(&hax_accel_type); | |
1102 | } | |
1103 | ||
1104 | type_init(hax_type_init); |