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/vmm_cpuid.h>
28 #include <palacios/vmx.h>
29 #include <palacios/vmm_ctrl_regs.h>
30 #include <palacios/vmm_lowlevel.h>
31 #include <palacios/vmx_ctrl_regs.h>
32 #include <palacios/vmx_assist.h>
33 #include <palacios/vmm_halt.h>
35 #ifdef CONFIG_TELEMETRY
36 #include <palacios/vmm_telemetry.h>
40 static int inline check_vmcs_write(vmcs_field_t field, addr_t val) {
43 ret = vmcs_write(field, val);
45 if (ret != VMX_SUCCESS) {
46 PrintError("VMWRITE error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
52 static int inline check_vmcs_read(vmcs_field_t field, void * val) {
55 ret = vmcs_read(field, val);
57 if (ret != VMX_SUCCESS) {
58 PrintError("VMREAD error on %s!: %d\n", v3_vmcs_field_to_str(field), ret);
64 static int inline handle_cr_access(struct guest_info * info, ulong_t exit_qual) {
65 struct vmx_exit_cr_qual * cr_qual = (struct vmx_exit_cr_qual *)&exit_qual;
67 // PrintDebug("Control register: %d\n", cr_qual->access_type);
68 switch(cr_qual->cr_id) {
70 //PrintDebug("Handling CR0 Access\n");
71 return v3_vmx_handle_cr0_access(info);
73 //PrintDebug("Handling CR3 Access\n");
74 return v3_vmx_handle_cr3_access(info);
76 PrintError("Unhandled CR access: %d\n", cr_qual->cr_id);
84 /* At this point the GPRs are already copied into the guest_info state */
85 int v3_handle_vmx_exit(struct v3_gprs * gprs, struct guest_info * info, struct v3_ctrl_regs * ctrl_regs) {
87 uint32_t exit_reason = 0;
89 struct vmx_data * vmx_info = (struct vmx_data *)(info->vmm_data);
90 struct vmx_exit_idt_vec_info idt_vec_info;
93 v3_update_time(info, tmp_tsc - info->time_state.cached_host_tsc);
97 check_vmcs_read(VMCS_EXIT_REASON, &exit_reason);
98 check_vmcs_read(VMCS_EXIT_QUAL, &exit_qual);
100 //PrintDebug("VMX Exit taken, id-qual: %u-%lu\n", exit_reason, exit_qual);
102 /* Update guest state */
103 v3_load_vmcs_guest_state(info);
105 // Load execution controls
106 check_vmcs_read(VMCS_PIN_CTRLS, &(vmx_info->pin_ctrls.value));
107 check_vmcs_read(VMCS_PROC_CTRLS, &(vmx_info->pri_proc_ctrls.value));
109 if (vmx_info->pri_proc_ctrls.sec_ctrls) {
110 check_vmcs_read(VMCS_SEC_PROC_CTRLS, &(vmx_info->sec_proc_ctrls.value));
113 info->mem_mode = v3_get_vm_mem_mode(info);
114 info->cpu_mode = v3_get_vm_cpu_mode(info);
116 // Check if we got interrupted while delivering interrupt
117 // Variable will be used later if this is true
119 check_vmcs_read(VMCS_IDT_VECTOR_INFO, &(idt_vec_info.value));
121 if ((info->intr_state.irq_started == 1) && (idt_vec_info.valid == 0)) {
122 #ifdef CONFIG_DEBUG_INTERRUPTS
123 PrintDebug("Calling v3_injecting_intr\n");
125 info->intr_state.irq_started = 0;
126 v3_injecting_intr(info, info->intr_state.irq_vector, V3_EXTERNAL_IRQ);
133 if ((info->num_exits % 5000) == 0) {
134 PrintDebug("VMX Exit %d\n", (uint32_t)info->num_exits);
137 #ifdef CONFIG_TELEMETRY
138 if (info->enable_telemetry) {
139 v3_telemetry_start_exit(info);
143 switch (exit_reason) {
144 case VMEXIT_INFO_EXCEPTION_OR_NMI: {
146 pf_error_t error_code;
148 check_vmcs_read(VMCS_EXIT_INT_INFO, &int_info);
149 check_vmcs_read(VMCS_EXIT_INT_ERR, &error_code);
151 // JRL: Change "0x0e" to a macro value
152 if ((uint8_t)int_info == 0x0e) {
153 #ifdef CONFIG_DEBUG_SHADOW_PAGING
154 PrintDebug("Page Fault at %p error_code=%x\n", (void *)exit_qual, *(uint32_t *)&error_code);
157 if (info->shdw_pg_mode == SHADOW_PAGING) {
158 if (v3_handle_shadow_pagefault(info, (addr_t)exit_qual, error_code) == -1) {
159 PrintError("Error handling shadow page fault\n");
163 PrintError("Page fault in unimplemented paging mode\n");
167 PrintError("Unknown exception: 0x%x\n", (uint8_t)int_info);
175 if (info->shdw_pg_mode == SHADOW_PAGING) {
176 if (v3_handle_shadow_invlpg(info) == -1) {
177 PrintError("Error handling INVLPG\n");
184 if (v3_handle_cpuid(info) == -1) {
185 PrintError("Error Handling CPUID instruction\n");
191 if (v3_handle_msr_read(info) == -1) {
192 PrintError("Error handling MSR Read\n");
198 if (v3_handle_msr_write(info) == -1) {
199 PrintError("Error handling MSR Write\n");
209 // VMCALL is a 3 byte op
210 // We do this early because some hypercalls can change the rip...
213 if (v3_handle_hypercall(info) == -1) {
217 case VMEXIT_IO_INSTR: {
218 struct vmx_exit_io_qual * io_qual = (struct vmx_exit_io_qual *)&exit_qual;
220 if (io_qual->dir == 0) {
221 if (io_qual->string) {
222 if (v3_handle_vmx_io_outs(info) == -1) {
223 PrintError("Error in outs IO handler\n");
227 if (v3_handle_vmx_io_out(info) == -1) {
228 PrintError("Error in out IO handler\n");
233 if (io_qual->string) {
234 if(v3_handle_vmx_io_ins(info) == -1) {
235 PrintError("Error in ins IO handler\n");
239 if (v3_handle_vmx_io_in(info) == -1) {
240 PrintError("Error in in IO handler\n");
247 case VMEXIT_CR_REG_ACCESSES:
248 if (handle_cr_access(info, exit_qual) != 0) {
249 PrintError("Error handling CR access\n");
255 PrintDebug("Guest halted\n");
257 if (v3_handle_halt(info) == -1) {
258 PrintError("Error handling halt instruction\n");
268 case VMEXIT_EXTERNAL_INTR:
269 // Interrupts are handled outside switch
271 case VMEXIT_INTR_WINDOW:
273 vmx_info->pri_proc_ctrls.int_wndw_exit = 0;
274 check_vmcs_write(VMCS_PROC_CTRLS, vmx_info->pri_proc_ctrls.value);
276 #ifdef CONFIG_DEBUG_INTERRUPTS
277 PrintDebug("Interrupts available again! (RIP=%llx)\n", info->rip);
282 PrintError("Unhandled VMEXIT: %s (%u), %lu (0x%lx)\n",
283 v3_vmx_exit_code_to_str(exit_reason),
284 exit_reason, exit_qual, exit_qual);
288 #ifdef CONFIG_TELEMETRY
289 if (info->enable_telemetry) {
290 v3_telemetry_end_exit(info, exit_reason);
295 /* Check for pending exceptions to inject */
296 if (v3_excp_pending(info)) {
297 struct vmx_entry_int_info int_info;
300 // In VMX, almost every exception is hardware
301 // Software exceptions are pretty much only for breakpoint or overflow
303 int_info.vector = v3_get_excp_number(info);
305 if (info->excp_state.excp_error_code_valid) {
306 check_vmcs_write(VMCS_ENTRY_EXCP_ERR, info->excp_state.excp_error_code);
307 int_info.error_code = 1;
309 #ifdef CONFIG_DEBUG_INTERRUPTS
310 PrintDebug("Injecting exception %d with error code %x\n",
311 int_info.vector, info->excp_state.excp_error_code);
316 #ifdef CONFIG_DEBUG_INTERRUPTS
317 PrintDebug("Injecting exception %d (EIP=%p)\n", int_info.vector, (void *)info->rip);
319 check_vmcs_write(VMCS_ENTRY_INT_INFO, int_info.value);
321 v3_injecting_excp(info, int_info.vector);
323 } else if (((struct rflags *)&(info->ctrl_regs.rflags))->intr == 1) {
325 if ((info->intr_state.irq_started == 1) && (idt_vec_info.valid == 1)) {
327 #ifdef CONFIG_DEBUG_INTERRUPTS
328 PrintDebug("IRQ pending from previous injection\n");
331 // Copy the IDT vectoring info over to reinject the old interrupt
332 if (idt_vec_info.error_code == 1) {
333 uint32_t err_code = 0;
335 check_vmcs_read(VMCS_IDT_VECTOR_ERR, &err_code);
336 check_vmcs_write(VMCS_ENTRY_EXCP_ERR, err_code);
339 idt_vec_info.undef = 0;
340 check_vmcs_write(VMCS_ENTRY_INT_INFO, idt_vec_info.value);
343 struct vmx_entry_int_info ent_int;
346 switch (v3_intr_pending(info)) {
347 case V3_EXTERNAL_IRQ: {
348 info->intr_state.irq_vector = v3_get_intr(info);
349 ent_int.vector = info->intr_state.irq_vector;
351 ent_int.error_code = 0;
354 #ifdef CONFIG_DEBUG_INTERRUPTS
355 PrintDebug("Injecting Interrupt %d at exit %u(EIP=%p)\n",
356 info->intr_state.irq_vector,
357 (uint32_t)info->num_exits,
361 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
362 info->intr_state.irq_started = 1;
367 PrintDebug("Injecting NMI\n");
372 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
375 case V3_SOFTWARE_INTR:
376 PrintDebug("Injecting software interrupt\n");
380 check_vmcs_write(VMCS_ENTRY_INT_INFO, ent_int.value);
384 // Not sure what to do here, Intel doesn't have virtual IRQs
385 // May be the same as external interrupts/IRQs
388 case V3_INVALID_INTR:
393 } else if ((v3_intr_pending(info)) && (vmx_info->pri_proc_ctrls.int_wndw_exit == 0)) {
394 // Enable INTR window exiting so we know when IF=1
397 check_vmcs_read(VMCS_EXIT_INSTR_LEN, &instr_len);
399 #ifdef CONFIG_DEBUG_INTERRUPTS
400 PrintDebug("Enabling Interrupt-Window exiting: %d\n", instr_len);
403 vmx_info->pri_proc_ctrls.int_wndw_exit = 1;
404 check_vmcs_write(VMCS_PROC_CTRLS, vmx_info->pri_proc_ctrls.value);
407 check_vmcs_write(VMCS_GUEST_CR0, info->ctrl_regs.cr0);
408 check_vmcs_write(VMCS_GUEST_CR3, info->ctrl_regs.cr3);
409 check_vmcs_write(VMCS_GUEST_CR4, info->ctrl_regs.cr4);
410 check_vmcs_write(VMCS_GUEST_RIP, info->rip);
411 check_vmcs_write(VMCS_GUEST_RSP, info->vm_regs.rsp);
413 check_vmcs_write(VMCS_CR0_READ_SHDW, info->shdw_pg_state.guest_cr0);
417 rdtscll(info->time_state.cached_host_tsc);
422 static const char VMEXIT_INFO_EXCEPTION_OR_NMI_STR[] = "VMEXIT_INFO_EXCEPTION_OR_NMI";
423 static const char VMEXIT_EXTERNAL_INTR_STR[] = "VMEXIT_EXTERNAL_INTR";
424 static const char VMEXIT_TRIPLE_FAULT_STR[] = "VMEXIT_TRIPLE_FAULT";
425 static const char VMEXIT_INIT_SIGNAL_STR[] = "VMEXIT_INIT_SIGNAL";
426 static const char VMEXIT_STARTUP_IPI_STR[] = "VMEXIT_STARTUP_IPI";
427 static const char VMEXIT_IO_SMI_STR[] = "VMEXIT_IO_SMI";
428 static const char VMEXIT_OTHER_SMI_STR[] = "VMEXIT_OTHER_SMI";
429 static const char VMEXIT_INTR_WINDOW_STR[] = "VMEXIT_INTR_WINDOW";
430 static const char VMEXIT_NMI_WINDOW_STR[] = "VMEXIT_NMI_WINDOW";
431 static const char VMEXIT_TASK_SWITCH_STR[] = "VMEXIT_TASK_SWITCH";
432 static const char VMEXIT_CPUID_STR[] = "VMEXIT_CPUID";
433 static const char VMEXIT_HLT_STR[] = "VMEXIT_HLT";
434 static const char VMEXIT_INVD_STR[] = "VMEXIT_INVD";
435 static const char VMEXIT_INVLPG_STR[] = "VMEXIT_INVLPG";
436 static const char VMEXIT_RDPMC_STR[] = "VMEXIT_RDPMC";
437 static const char VMEXIT_RDTSC_STR[] = "VMEXIT_RDTSC";
438 static const char VMEXIT_RSM_STR[] = "VMEXIT_RSM";
439 static const char VMEXIT_VMCALL_STR[] = "VMEXIT_VMCALL";
440 static const char VMEXIT_VMCLEAR_STR[] = "VMEXIT_VMCLEAR";
441 static const char VMEXIT_VMLAUNCH_STR[] = "VMEXIT_VMLAUNCH";
442 static const char VMEXIT_VMPTRLD_STR[] = "VMEXIT_VMPTRLD";
443 static const char VMEXIT_VMPTRST_STR[] = "VMEXIT_VMPTRST";
444 static const char VMEXIT_VMREAD_STR[] = "VMEXIT_VMREAD";
445 static const char VMEXIT_VMRESUME_STR[] = "VMEXIT_VMRESUME";
446 static const char VMEXIT_VMWRITE_STR[] = "VMEXIT_VMWRITE";
447 static const char VMEXIT_VMXOFF_STR[] = "VMEXIT_VMXOFF";
448 static const char VMEXIT_VMXON_STR[] = "VMEXIT_VMXON";
449 static const char VMEXIT_CR_REG_ACCESSES_STR[] = "VMEXIT_CR_REG_ACCESSES";
450 static const char VMEXIT_MOV_DR_STR[] = "VMEXIT_MOV_DR";
451 static const char VMEXIT_IO_INSTR_STR[] = "VMEXIT_IO_INSTR";
452 static const char VMEXIT_RDMSR_STR[] = "VMEXIT_RDMSR";
453 static const char VMEXIT_WRMSR_STR[] = "VMEXIT_WRMSR";
454 static const char VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE_STR[] = "VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE";
455 static const char VMEXIT_ENTRY_FAIL_MSR_LOAD_STR[] = "VMEXIT_ENTRY_FAIL_MSR_LOAD";
456 static const char VMEXIT_MWAIT_STR[] = "VMEXIT_MWAIT";
457 static const char VMEXIT_MONITOR_STR[] = "VMEXIT_MONITOR";
458 static const char VMEXIT_PAUSE_STR[] = "VMEXIT_PAUSE";
459 static const char VMEXIT_ENTRY_FAILURE_MACHINE_CHECK_STR[] = "VMEXIT_ENTRY_FAILURE_MACHINE_CHECK";
460 static const char VMEXIT_TPR_BELOW_THRESHOLD_STR[] = "VMEXIT_TPR_BELOW_THRESHOLD";
461 static const char VMEXIT_APIC_STR[] = "VMEXIT_APIC";
462 static const char VMEXIT_GDTR_IDTR_STR[] = "VMEXIT_GDTR_IDTR";
463 static const char VMEXIT_LDTR_TR_STR[] = "VMEXIT_LDTR_TR";
464 static const char VMEXIT_EPT_VIOLATION_STR[] = "VMEXIT_EPT_VIOLATION";
465 static const char VMEXIT_EPT_CONFIG_STR[] = "VMEXIT_EPT_CONFIG";
466 static const char VMEXIT_INVEPT_STR[] = "VMEXIT_INVEPT";
467 static const char VMEXIT_RDTSCP_STR[] = "VMEXIT_RDTSCP";
468 static const char VMEXIT_EXPIRED_PREEMPT_TIMER_STR[] = "VMEXIT_EXPIRED_PREEMPT_TIMER";
469 static const char VMEXIT_INVVPID_STR[] = "VMEXIT_INVVPID";
470 static const char VMEXIT_WBINVD_STR[] = "VMEXIT_WBINVD";
471 static const char VMEXIT_XSETBV_STR[] = "VMEXIT_XSETBV";
473 const char * v3_vmx_exit_code_to_str(vmx_exit_t exit)
476 case VMEXIT_INFO_EXCEPTION_OR_NMI:
477 return VMEXIT_INFO_EXCEPTION_OR_NMI_STR;
478 case VMEXIT_EXTERNAL_INTR:
479 return VMEXIT_EXTERNAL_INTR_STR;
480 case VMEXIT_TRIPLE_FAULT:
481 return VMEXIT_TRIPLE_FAULT_STR;
482 case VMEXIT_INIT_SIGNAL:
483 return VMEXIT_INIT_SIGNAL_STR;
484 case VMEXIT_STARTUP_IPI:
485 return VMEXIT_STARTUP_IPI_STR;
487 return VMEXIT_IO_SMI_STR;
488 case VMEXIT_OTHER_SMI:
489 return VMEXIT_OTHER_SMI_STR;
490 case VMEXIT_INTR_WINDOW:
491 return VMEXIT_INTR_WINDOW_STR;
492 case VMEXIT_NMI_WINDOW:
493 return VMEXIT_NMI_WINDOW_STR;
494 case VMEXIT_TASK_SWITCH:
495 return VMEXIT_TASK_SWITCH_STR;
497 return VMEXIT_CPUID_STR;
499 return VMEXIT_HLT_STR;
501 return VMEXIT_INVD_STR;
503 return VMEXIT_INVLPG_STR;
505 return VMEXIT_RDPMC_STR;
507 return VMEXIT_RDTSC_STR;
509 return VMEXIT_RSM_STR;
511 return VMEXIT_VMCALL_STR;
513 return VMEXIT_VMCLEAR_STR;
514 case VMEXIT_VMLAUNCH:
515 return VMEXIT_VMLAUNCH_STR;
517 return VMEXIT_VMPTRLD_STR;
519 return VMEXIT_VMPTRST_STR;
521 return VMEXIT_VMREAD_STR;
522 case VMEXIT_VMRESUME:
523 return VMEXIT_VMRESUME_STR;
525 return VMEXIT_VMWRITE_STR;
527 return VMEXIT_VMXOFF_STR;
529 return VMEXIT_VMXON_STR;
530 case VMEXIT_CR_REG_ACCESSES:
531 return VMEXIT_CR_REG_ACCESSES_STR;
533 return VMEXIT_MOV_DR_STR;
534 case VMEXIT_IO_INSTR:
535 return VMEXIT_IO_INSTR_STR;
537 return VMEXIT_RDMSR_STR;
539 return VMEXIT_WRMSR_STR;
540 case VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE:
541 return VMEXIT_ENTRY_FAIL_INVALID_GUEST_STATE_STR;
542 case VMEXIT_ENTRY_FAIL_MSR_LOAD:
543 return VMEXIT_ENTRY_FAIL_MSR_LOAD_STR;
545 return VMEXIT_MWAIT_STR;
547 return VMEXIT_MONITOR_STR;
549 return VMEXIT_PAUSE_STR;
550 case VMEXIT_ENTRY_FAILURE_MACHINE_CHECK:
551 return VMEXIT_ENTRY_FAILURE_MACHINE_CHECK_STR;
552 case VMEXIT_TPR_BELOW_THRESHOLD:
553 return VMEXIT_TPR_BELOW_THRESHOLD_STR;
555 return VMEXIT_APIC_STR;
556 case VMEXIT_GDTR_IDTR:
557 return VMEXIT_GDTR_IDTR_STR;
559 return VMEXIT_LDTR_TR_STR;
560 case VMEXIT_EPT_VIOLATION:
561 return VMEXIT_EPT_VIOLATION_STR;
562 case VMEXIT_EPT_CONFIG:
563 return VMEXIT_EPT_CONFIG_STR;
565 return VMEXIT_INVEPT_STR;
567 return VMEXIT_RDTSCP_STR;
568 case VMEXIT_EXPIRED_PREEMPT_TIMER:
569 return VMEXIT_EXPIRED_PREEMPT_TIMER_STR;
571 return VMEXIT_INVVPID_STR;
573 return VMEXIT_WBINVD_STR;
575 return VMEXIT_XSETBV_STR;