2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20 #include <palacios/vmx_handler.h>
21 #include <palacios/vmm_types.h>
22 #include <palacios/vmm.h>
23 #include <palacios/vmcs.h>
24 #include <palacios/vmx_lowlevel.h>
25 #include <palacios/vmx_io.h>
26 #include <palacios/vmx.h>
27 #include <palacios/vmm_ctrl_regs.h>
28 #include <palacios/vmm_lowlevel.h>
29 #include <palacios/vmx_ctrl_regs.h>
30 #include <palacios/vmx_assist.h>
31 #include <palacios/vmm_halt.h>
33 #ifdef CONFIG_TELEMETRY
34 #include <palacios/vmm_telemetry.h>
38 static int inline check_vmcs_write(vmcs_field_t field, addr_t val) {
41 ret = vmcs_write(field, val);
43 if (ret != VMX_SUCCESS) {
44 PrintError("VMWRITE error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
50 static int inline check_vmcs_read(vmcs_field_t field, void * val) {
53 ret = vmcs_read(field, val);
55 if (ret != VMX_SUCCESS) {
56 PrintError("VMREAD error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
62 static int inline handle_cr_access(struct guest_info * info, ulong_t exit_qual) {
63 struct vmx_exit_cr_qual * cr_qual = (struct vmx_exit_cr_qual *)&exit_qual;
65 // PrintDebug("Control register: %d\n", cr_qual->access_type);
66 switch(cr_qual->cr_id) {
68 PrintDebug("Handling CR0 Access\n");
69 return v3_vmx_handle_cr0_access(info);
71 PrintDebug("Handling CR3 Access\n");
72 return v3_vmx_handle_cr3_access(info);
74 PrintError("Unhandled CR access: %d\n", cr_qual->cr_id);
82 /* At this point the GPRs are already copied into the guest_info state */
83 int v3_handle_vmx_exit(struct v3_gprs * gprs, struct guest_info * info, struct v3_ctrl_regs * ctrl_regs) {
85 uint32_t exit_reason = 0;
87 struct vmx_data * vmx_info = (struct vmx_data *)(info->vmm_data);
88 struct vmx_exit_idt_vec_info idt_vec_info;
91 v3_update_time(info, tmp_tsc - info->time_state.cached_host_tsc);
95 check_vmcs_read(VMCS_EXIT_REASON, &exit_reason);
96 check_vmcs_read(VMCS_EXIT_QUAL, &exit_qual);
98 //PrintDebug("VMX Exit taken, id-qual: %u-%lu\n", exit_reason, exit_qual);
100 /* Update guest state */
101 v3_load_vmcs_guest_state(info);
103 // Load execution controls
104 check_vmcs_read(VMCS_PIN_CTRLS, &(vmx_info->pin_ctrls.value));
105 check_vmcs_read(VMCS_PROC_CTRLS, &(vmx_info->pri_proc_ctrls.value));
107 if (vmx_info->pri_proc_ctrls.sec_ctrls) {
108 check_vmcs_read(VMCS_SEC_PROC_CTRLS, &(vmx_info->sec_proc_ctrls.value));
111 info->mem_mode = v3_get_vm_mem_mode(info);
112 info->cpu_mode = v3_get_vm_cpu_mode(info);
114 // Check if we got interrupted while delivering interrupt
115 // Variable will be used later if this is true
117 check_vmcs_read(VMCS_IDT_VECTOR_INFO, &(idt_vec_info.value));
119 if ((info->intr_state.irq_started == 1) && (idt_vec_info.valid == 0)) {
120 #ifdef CONFIG_DEBUG_INTERRUPTS
121 PrintDebug("Calling v3_injecting_intr\n");
123 info->intr_state.irq_started = 0;
124 v3_injecting_intr(info, info->intr_state.irq_vector, V3_EXTERNAL_IRQ);
131 if ((info->num_exits % 5000) == 0) {
132 PrintDebug("VMX Exit %d\n", (uint32_t)info->num_exits);
135 #ifdef CONFIG_TELEMETRY
136 if (info->enable_telemetry) {
137 v3_telemetry_start_exit(info);
141 switch (exit_reason) {
142 case VMEXIT_INFO_EXCEPTION_OR_NMI: {
144 pf_error_t error_code;
146 check_vmcs_read(VMCS_EXIT_INT_INFO, &int_info);
147 check_vmcs_read(VMCS_EXIT_INT_ERR, &error_code);
149 // JRL: Change "0x0e" to a macro value
150 if ((uint8_t)int_info == 0x0e) {
151 #ifdef CONFIG_DEBUG_SHADOW_PAGING
152 PrintDebug("Page Fault at %p error_code=%x\n", (void *)exit_qual, *(uint32_t *)&error_code);
155 if (info->shdw_pg_mode == SHADOW_PAGING) {
156 if (v3_handle_shadow_pagefault(info, (addr_t)exit_qual, error_code) == -1) {
157 PrintError("Error handling shadow page fault\n");
161 PrintError("Page fault in unimplemented paging mode\n");
165 PrintError("Unknown exception: 0x%x\n", (uint8_t)int_info);
173 if (info->shdw_pg_mode == SHADOW_PAGING) {
174 if (v3_handle_shadow_invlpg(info) == -1) {
175 PrintError("Error handling INVLPG\n");
183 uint32_t target = info->vm_regs.rax;
185 v3_cpuid(target, (addr_t *)&(info->vm_regs.rax), (addr_t *)&(info->vm_regs.rbx),
186 (addr_t *)&(info->vm_regs.rcx), (addr_t *)&(info->vm_regs.rdx));
188 check_vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
190 info->rip += instr_len;
195 if (v3_handle_msr_read(info) == -1) {
196 PrintError("Error handling MSR Read\n");
202 if (v3_handle_msr_write(info) == -1) {
203 PrintError("Error handling MSR Write\n");
208 case VMEXIT_IO_INSTR: {
209 struct vmx_exit_io_qual * io_qual = (struct vmx_exit_io_qual *)&exit_qual;
211 if (io_qual->dir == 0) {
212 if (io_qual->string) {
213 if (v3_handle_vmx_io_outs(info) == -1) {
214 PrintError("Error in outs IO handler\n");
218 if (v3_handle_vmx_io_out(info) == -1) {
219 PrintError("Error in out IO handler\n");
224 if (io_qual->string) {
225 if(v3_handle_vmx_io_ins(info) == -1) {
226 PrintError("Error in ins IO handler\n");
230 if (v3_handle_vmx_io_in(info) == -1) {
231 PrintError("Error in in IO handler\n");
238 case VMEXIT_CR_REG_ACCESSES:
239 if (handle_cr_access(info, exit_qual) != 0) {
240 PrintError("Error handling CR access\n");
246 PrintDebug("Guest halted\n");
248 if (v3_handle_halt(info) == -1) {
249 PrintError("Error handling halt instruction\n");
259 case VMEXIT_EXTERNAL_INTR:
260 // Interrupts are handled outside switch
262 case VMEXIT_INTR_WINDOW:
264 vmx_info->pri_proc_ctrls.int_wndw_exit = 0;
265 check_vmcs_write(VMCS_PROC_CTRLS, vmx_info->pri_proc_ctrls.value);
267 #ifdef CONFIG_DEBUG_INTERRUPTS
268 PrintDebug("Interrupts available again! (RIP=%llx)\n", info->rip);
273 PrintError("Unhandled VMEXIT: %s (%u), %lu (0x%lx)\n",
274 v3_vmx_exit_code_to_str(exit_reason),
275 exit_reason, exit_qual, exit_qual);
279 #ifdef CONFIG_TELEMETRY
280 if (info->enable_telemetry) {
281 v3_telemetry_end_exit(info, exit_reason);
286 /* Check for pending exceptions to inject */
287 if (v3_excp_pending(info)) {
288 struct vmx_entry_int_info int_info;
291 // In VMX, almost every exception is hardware
292 // Software exceptions are pretty much only for breakpoint or overflow
294 int_info.vector = v3_get_excp_number(info);
296 if (info->excp_state.excp_error_code_valid) {
297 check_vmcs_write(VMCS_ENTRY_EXCP_ERR, info->excp_state.excp_error_code);
298 int_info.error_code = 1;
300 PrintDebug("Injecting exception %d with error code %x\n",
301 int_info.vector, info->excp_state.excp_error_code);
305 PrintDebug("Injecting exception %d (EIP=%p)\n", int_info.vector, (void *)info->rip);
306 check_vmcs_write(VMCS_ENTRY_INT_INFO, int_info.value);
308 v3_injecting_excp(info, int_info.vector);
310 } else if (((struct rflags *)&(info->ctrl_regs.rflags))->intr == 1) {
312 if ((info->intr_state.irq_started == 1) && (idt_vec_info.valid == 1)) {
314 #ifdef CONFIG_DEBUG_INTERRUPTS
315 PrintDebug("IRQ pending from previous injection\n");
318 // Copy the IDT vectoring info over to reinject the old interrupt
319 if (idt_vec_info.error_code == 1) {
320 uint32_t err_code = 0;
322 check_vmcs_read(VMCS_IDT_VECTOR_ERR, &err_code);
323 check_vmcs_write(VMCS_ENTRY_EXCP_ERR, err_code);
326 idt_vec_info.undef = 0;
327 check_vmcs_write(VMCS_ENTRY_INT_INFO, idt_vec_info.value);
330 struct vmx_entry_int_info ent_int;
333 switch (v3_intr_pending(info)) {
334 case V3_EXTERNAL_IRQ: {
335 info->intr_state.irq_vector = v3_get_intr(info);
336 ent_int.vector = info->intr_state.irq_vector;
338 ent_int.error_code = 0;
341 #ifdef CONFIG_DEBUG_INTERRUPTS
342 PrintDebug("Injecting Interrupt %d at exit %u(EIP=%p)\n",
343 info->intr_state.irq_vector,
344 (uint32_t)info->num_exits,
348 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
349 info->intr_state.irq_started = 1;
354 PrintDebug("Injecting NMI\n");
359 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
362 case V3_SOFTWARE_INTR:
363 PrintDebug("Injecting software interrupt\n");
367 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
371 // Not sure what to do here, Intel doesn't have virtual IRQs
372 // May be the same as external interrupts/IRQs
375 case V3_INVALID_INTR:
380 } else if ((v3_intr_pending(info)) && (vmx_info->pri_proc_ctrls.int_wndw_exit == 0)) {
381 // Enable INTR window exiting so we know when IF=1
384 check_vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
386 #ifdef CONFIG_DEBUG_INTERRUPTS
387 PrintDebug("Enabling Interrupt-Window exiting: %d\n", instr_len);
390 vmx_info->pri_proc_ctrls.int_wndw_exit = 1;
391 check_vmcs_write(VMCS_PROC_CTRLS, vmx_info->pri_proc_ctrls.value);
394 check_vmcs_write(VMCS_GUEST_CR0, info->ctrl_regs.cr0);
395 check_vmcs_write(VMCS_GUEST_CR3, info->ctrl_regs.cr3);
396 check_vmcs_write(VMCS_GUEST_CR4, info->ctrl_regs.cr4);
397 check_vmcs_write(VMCS_GUEST_RIP, info->rip);
398 check_vmcs_write(VMCS_GUEST_RSP, info->vm_regs.rsp);
400 check_vmcs_write(VMCS_CR0_READ_SHDW, info->shdw_pg_state.guest_cr0);
404 rdtscll(info->time_state.cached_host_tsc);
409 static const char VMEXIT_INFO_EXCEPTION_OR_NMI_STR[] = "VMEXIT_INFO_EXCEPTION_OR_NMI";
410 static const char VMEXIT_EXTERNAL_INTR_STR[] = "VMEXIT_EXTERNAL_INTR";
411 static const char VMEXIT_TRIPLE_FAULT_STR[] = "VMEXIT_TRIPLE_FAULT";
412 static const char VMEXIT_INIT_SIGNAL_STR[] = "VMEXIT_INIT_SIGNAL";
413 static const char VMEXIT_STARTUP_IPI_STR[] = "VMEXIT_STARTUP_IPI";
414 static const char VMEXIT_IO_SMI_STR[] = "VMEXIT_IO_SMI";
415 static const char VMEXIT_OTHER_SMI_STR[] = "VMEXIT_OTHER_SMI";
416 static const char VMEXIT_INTR_WINDOW_STR[] = "VMEXIT_INTR_WINDOW";
417 static const char VMEXIT_NMI_WINDOW_STR[] = "VMEXIT_NMI_WINDOW";
418 static const char VMEXIT_TASK_SWITCH_STR[] = "VMEXIT_TASK_SWITCH";
419 static const char VMEXIT_CPUID_STR[] = "VMEXIT_CPUID";
420 static const char VMEXIT_HLT_STR[] = "VMEXIT_HLT";
421 static const char VMEXIT_INVD_STR[] = "VMEXIT_INVD";
422 static const char VMEXIT_INVLPG_STR[] = "VMEXIT_INVLPG";
423 static const char VMEXIT_RDPMC_STR[] = "VMEXIT_RDPMC";
424 static const char VMEXIT_RDTSC_STR[] = "VMEXIT_RDTSC";
425 static const char VMEXIT_RSM_STR[] = "VMEXIT_RSM";
426 static const char VMEXIT_VMCALL_STR[] = "VMEXIT_VMCALL";
427 static const char VMEXIT_VMCLEAR_STR[] = "VMEXIT_VMCLEAR";
428 static const char VMEXIT_VMLAUNCH_STR[] = "VMEXIT_VMLAUNCH";
429 static const char VMEXIT_VMPTRLD_STR[] = "VMEXIT_VMPTRLD";
430 static const char VMEXIT_VMPTRST_STR[] = "VMEXIT_VMPTRST";
431 static const char VMEXIT_VMREAD_STR[] = "VMEXIT_VMREAD";
432 static const char VMEXIT_VMRESUME_STR[] = "VMEXIT_VMRESUME";
433 static const char VMEXIT_VMWRITE_STR[] = "VMEXIT_VMWRITE";
434 static const char VMEXIT_VMXOFF_STR[] = "VMEXIT_VMXOFF";
435 static const char VMEXIT_VMXON_STR[] = "VMEXIT_VMXON";
436 static const char VMEXIT_CR_REG_ACCESSES_STR[] = "VMEXIT_CR_REG_ACCESSES";
437 static const char VMEXIT_MOV_DR_STR[] = "VMEXIT_MOV_DR";
438 static const char VMEXIT_IO_INSTR_STR[] = "VMEXIT_IO_INSTR";
439 static const char VMEXIT_RDMSR_STR[] = "VMEXIT_RDMSR";
440 static const char VMEXIT_WRMSR_STR[] = "VMEXIT_WRMSR";
441 static const char VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE_STR[] = "VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE";
442 static const char VMEXIT_ENTRY_FAIL_MSR_LOAD_STR[] = "VMEXIT_ENTRY_FAIL_MSR_LOAD";
443 static const char VMEXIT_MWAIT_STR[] = "VMEXIT_MWAIT";
444 static const char VMEXIT_MONITOR_STR[] = "VMEXIT_MONITOR";
445 static const char VMEXIT_PAUSE_STR[] = "VMEXIT_PAUSE";
446 static const char VMEXIT_ENTRY_FAILURE_MACHINE_CHECK_STR[] = "VMEXIT_ENTRY_FAILURE_MACHINE_CHECK";
447 static const char VMEXIT_TPR_BELOW_THRESHOLD_STR[] = "VMEXIT_TPR_BELOW_THRESHOLD";
448 static const char VMEXIT_APIC_STR[] = "VMEXIT_APIC";
449 static const char VMEXIT_GDTR_IDTR_STR[] = "VMEXIT_GDTR_IDTR";
450 static const char VMEXIT_LDTR_TR_STR[] = "VMEXIT_LDTR_TR";
451 static const char VMEXIT_EPT_VIOLATION_STR[] = "VMEXIT_EPT_VIOLATION";
452 static const char VMEXIT_EPT_CONFIG_STR[] = "VMEXIT_EPT_CONFIG";
453 static const char VMEXIT_INVEPT_STR[] = "VMEXIT_INVEPT";
454 static const char VMEXIT_RDTSCP_STR[] = "VMEXIT_RDTSCP";
455 static const char VMEXIT_EXPIRED_PREEMPT_TIMER_STR[] = "VMEXIT_EXPIRED_PREEMPT_TIMER";
456 static const char VMEXIT_INVVPID_STR[] = "VMEXIT_INVVPID";
457 static const char VMEXIT_WBINVD_STR[] = "VMEXIT_WBINVD";
458 static const char VMEXIT_XSETBV_STR[] = "VMEXIT_XSETBV";
460 const char * v3_vmx_exit_code_to_str(vmx_exit_t exit)
463 case VMEXIT_INFO_EXCEPTION_OR_NMI:
464 return VMEXIT_INFO_EXCEPTION_OR_NMI_STR;
465 case VMEXIT_EXTERNAL_INTR:
466 return VMEXIT_EXTERNAL_INTR_STR;
467 case VMEXIT_TRIPLE_FAULT:
468 return VMEXIT_TRIPLE_FAULT_STR;
469 case VMEXIT_INIT_SIGNAL:
470 return VMEXIT_INIT_SIGNAL_STR;
471 case VMEXIT_STARTUP_IPI:
472 return VMEXIT_STARTUP_IPI_STR;
474 return VMEXIT_IO_SMI_STR;
475 case VMEXIT_OTHER_SMI:
476 return VMEXIT_OTHER_SMI_STR;
477 case VMEXIT_INTR_WINDOW:
478 return VMEXIT_INTR_WINDOW_STR;
479 case VMEXIT_NMI_WINDOW:
480 return VMEXIT_NMI_WINDOW_STR;
481 case VMEXIT_TASK_SWITCH:
482 return VMEXIT_TASK_SWITCH_STR;
484 return VMEXIT_CPUID_STR;
486 return VMEXIT_HLT_STR;
488 return VMEXIT_INVD_STR;
490 return VMEXIT_INVLPG_STR;
492 return VMEXIT_RDPMC_STR;
494 return VMEXIT_RDTSC_STR;
496 return VMEXIT_RSM_STR;
498 return VMEXIT_VMCALL_STR;
500 return VMEXIT_VMCLEAR_STR;
501 case VMEXIT_VMLAUNCH:
502 return VMEXIT_VMLAUNCH_STR;
504 return VMEXIT_VMPTRLD_STR;
506 return VMEXIT_VMPTRST_STR;
508 return VMEXIT_VMREAD_STR;
509 case VMEXIT_VMRESUME:
510 return VMEXIT_VMRESUME_STR;
512 return VMEXIT_VMWRITE_STR;
514 return VMEXIT_VMXOFF_STR;
516 return VMEXIT_VMXON_STR;
517 case VMEXIT_CR_REG_ACCESSES:
518 return VMEXIT_CR_REG_ACCESSES_STR;
520 return VMEXIT_MOV_DR_STR;
521 case VMEXIT_IO_INSTR:
522 return VMEXIT_IO_INSTR_STR;
524 return VMEXIT_RDMSR_STR;
526 return VMEXIT_WRMSR_STR;
527 case VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE:
528 return VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE_STR;
529 case VMEXIT_ENTRY_FAIL_MSR_LOAD:
530 return VMEXIT_ENTRY_FAIL_MSR_LOAD_STR;
532 return VMEXIT_MWAIT_STR;
534 return VMEXIT_MONITOR_STR;
536 return VMEXIT_PAUSE_STR;
537 case VMEXIT_ENTRY_FAILURE_MACHINE_CHECK:
538 return VMEXIT_ENTRY_FAILURE_MACHINE_CHECK_STR;
539 case VMEXIT_TPR_BELOW_THRESHOLD:
540 return VMEXIT_TPR_BELOW_THRESHOLD_STR;
542 return VMEXIT_APIC_STR;
543 case VMEXIT_GDTR_IDTR:
544 return VMEXIT_GDTR_IDTR_STR;
546 return VMEXIT_LDTR_TR_STR;
547 case VMEXIT_EPT_VIOLATION:
548 return VMEXIT_EPT_VIOLATION_STR;
549 case VMEXIT_EPT_CONFIG:
550 return VMEXIT_EPT_CONFIG_STR;
552 return VMEXIT_INVEPT_STR;
554 return VMEXIT_RDTSCP_STR;
555 case VMEXIT_EXPIRED_PREEMPT_TIMER:
556 return VMEXIT_EXPIRED_PREEMPT_TIMER_STR;
558 return VMEXIT_INVVPID_STR;
560 return VMEXIT_WBINVD_STR;
562 return VMEXIT_XSETBV_STR;