diff options
Diffstat (limited to 'sys/amd64/include/vmm.h')
-rw-r--r-- | sys/amd64/include/vmm.h | 153 |
1 files changed, 138 insertions, 15 deletions
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h index 5a359e9..63a9b3f 100644 --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -29,11 +29,14 @@ #ifndef _VMM_H_ #define _VMM_H_ +#include <x86/segments.h> + enum vm_suspend_how { VM_SUSPEND_NONE, VM_SUSPEND_RESET, VM_SUSPEND_POWEROFF, VM_SUSPEND_HALT, + VM_SUSPEND_TRIPLEFAULT, VM_SUSPEND_LAST }; @@ -75,6 +78,10 @@ enum vm_reg_name { VM_REG_GUEST_GDTR, VM_REG_GUEST_EFER, VM_REG_GUEST_CR2, + VM_REG_GUEST_PDPTE0, + VM_REG_GUEST_PDPTE1, + VM_REG_GUEST_PDPTE2, + VM_REG_GUEST_PDPTE3, VM_REG_LAST }; @@ -84,6 +91,16 @@ enum x2apic_state { X2APIC_STATE_LAST }; +#define VM_INTINFO_VECTOR(info) ((info) & 0xff) +#define VM_INTINFO_DEL_ERRCODE 0x800 +#define VM_INTINFO_RSVD 0x7ffff000 +#define VM_INTINFO_VALID 0x80000000 +#define VM_INTINFO_TYPE 0x700 +#define VM_INTINFO_HWINTR (0 << 8) +#define VM_INTINFO_NMI (2 << 8) +#define VM_INTINFO_HWEXCEPTION (3 << 8) +#define VM_INTINFO_SWINTR (4 << 8) + #ifdef _KERNEL #define VM_MAX_NAMELEN 32 @@ -99,6 +116,7 @@ struct vioapic; struct vlapic; struct vmspace; struct vm_object; +struct vm_guest_paging; struct pmap; typedef int (*vmm_init_func_t)(int ipinum); @@ -252,6 +270,14 @@ vcpu_is_running(struct vm *vm, int vcpu, int *hostcpu) return (vcpu_get_state(vm, vcpu, hostcpu) == VCPU_RUNNING); } +#ifdef _SYS_PROC_H_ +static int __inline +vcpu_should_yield(struct vm *vm, int vcpu) +{ + return (curthread->td_flags & (TDF_ASTPENDING | TDF_NEEDRESCHED)); +} +#endif + void *vcpu_stats(struct vm *vm, int vcpu); void vcpu_notify_event(struct vm *vm, int vcpuid, bool lapic_intr); struct vmspace *vm_get_vmspace(struct vm *vm); @@ -274,21 +300,63 @@ struct vatpit *vm_atpit(struct vm *vm); int vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *vme); /* - * Returns 0 if there is no exception pending for this vcpu. Returns 1 if an - * exception is pending and also updates 'vme'. The pending exception is - * cleared when this function returns. + * This function is called after a VM-exit that occurred during exception or + * interrupt delivery through the IDT. The format of 'intinfo' is described + * in Figure 15-1, "EXITINTINFO for All Intercepts", APM, Vol 2. * - * This function should only be called in the context of the thread that is - * executing this vcpu. + * If a VM-exit handler completes the event delivery successfully then it + * should call vm_exit_intinfo() to extinguish the pending event. For e.g., + * if the task switch emulation is triggered via a task gate then it should + * call this function with 'intinfo=0' to indicate that the external event + * is not pending anymore. + * + * Return value is 0 on success and non-zero on failure. */ -int vm_exception_pending(struct vm *vm, int vcpuid, struct vm_exception *vme); +int vm_exit_intinfo(struct vm *vm, int vcpuid, uint64_t intinfo); -void vm_inject_gp(struct vm *vm, int vcpuid); /* general protection fault */ -void vm_inject_ud(struct vm *vm, int vcpuid); /* undefined instruction fault */ -void vm_inject_pf(struct vm *vm, int vcpuid, int error_code, uint64_t cr2); +/* + * This function is called before every VM-entry to retrieve a pending + * event that should be injected into the guest. This function combines + * nested events into a double or triple fault. + * + * Returns 0 if there are no events that need to be injected into the guest + * and non-zero otherwise. + */ +int vm_entry_intinfo(struct vm *vm, int vcpuid, uint64_t *info); + +int vm_get_intinfo(struct vm *vm, int vcpuid, uint64_t *info1, uint64_t *info2); enum vm_reg_name vm_segment_name(int seg_encoding); +struct vm_copyinfo { + uint64_t gpa; + size_t len; + void *hva; + void *cookie; +}; + +/* + * Set up 'copyinfo[]' to copy to/from guest linear address space starting + * at 'gla' and 'len' bytes long. The 'prot' should be set to PROT_READ for + * a copyin or PROT_WRITE for a copyout. + * + * Returns 0 on success. + * Returns 1 if an exception was injected into the guest. + * Returns -1 otherwise. + * + * The 'copyinfo[]' can be passed to 'vm_copyin()' or 'vm_copyout()' only if + * the return value is 0. The 'copyinfo[]' resources should be freed by calling + * 'vm_copy_teardown()' after the copy is done. + */ +int vm_copy_setup(struct vm *vm, int vcpuid, struct vm_guest_paging *paging, + uint64_t gla, size_t len, int prot, struct vm_copyinfo *copyinfo, + int num_copyinfo); +void vm_copy_teardown(struct vm *vm, int vcpuid, struct vm_copyinfo *copyinfo, + int num_copyinfo); +void vm_copyin(struct vm *vm, int vcpuid, struct vm_copyinfo *copyinfo, + void *kaddr, size_t len); +void vm_copyout(struct vm *vm, int vcpuid, const void *kaddr, + struct vm_copyinfo *copyinfo, size_t len); #endif /* KERNEL */ #define VM_MAXCPU 16 /* maximum virtual cpus */ @@ -322,13 +390,16 @@ struct seg_desc { uint32_t limit; uint32_t access; }; -#define SEG_DESC_TYPE(desc) ((desc)->access & 0x001f) -#define SEG_DESC_PRESENT(desc) ((desc)->access & 0x0080) -#define SEG_DESC_DEF32(desc) ((desc)->access & 0x4000) -#define SEG_DESC_GRANULARITY(desc) ((desc)->access & 0x8000) -#define SEG_DESC_UNUSABLE(desc) ((desc)->access & 0x10000) +#define SEG_DESC_TYPE(access) ((access) & 0x001f) +#define SEG_DESC_DPL(access) (((access) >> 5) & 0x3) +#define SEG_DESC_PRESENT(access) (((access) & 0x0080) ? 1 : 0) +#define SEG_DESC_DEF32(access) (((access) & 0x4000) ? 1 : 0) +#define SEG_DESC_GRANULARITY(access) (((access) & 0x8000) ? 1 : 0) +#define SEG_DESC_UNUSABLE(access) (((access) & 0x10000) ? 1 : 0) enum vm_cpu_mode { + CPU_MODE_REAL, + CPU_MODE_PROTECTED, CPU_MODE_COMPATIBILITY, /* IA-32E mode (CS.L = 0) */ CPU_MODE_64BIT, /* IA-32E mode (CS.L = 1) */ }; @@ -364,11 +435,14 @@ struct vie { uint8_t num_valid; /* size of the instruction */ uint8_t num_processed; + uint8_t addrsize:4, opsize:4; /* address and operand sizes */ uint8_t rex_w:1, /* REX prefix */ rex_r:1, rex_x:1, rex_b:1, - rex_present:1; + rex_present:1, + opsize_override:1, /* Operand size override */ + addrsize_override:1; /* Address size override */ uint8_t mod:2, /* ModRM byte */ reg:4, @@ -410,6 +484,7 @@ enum vm_exitcode { VM_EXITCODE_IOAPIC_EOI, VM_EXITCODE_SUSPENDED, VM_EXITCODE_INOUT_STR, + VM_EXITCODE_TASK_SWITCH, VM_EXITCODE_MAX }; @@ -434,6 +509,22 @@ struct vm_inout_str { struct seg_desc seg_desc; }; +enum task_switch_reason { + TSR_CALL, + TSR_IRET, + TSR_JMP, + TSR_IDT_GATE, /* task gate in IDT */ +}; + +struct vm_task_switch { + uint16_t tsssel; /* new TSS selector */ + int ext; /* task switch due to external event */ + uint32_t errcode; + int errcode_valid; /* push 'errcode' on the new stack */ + enum task_switch_reason reason; + struct vm_guest_paging paging; +}; + struct vm_exit { enum vm_exitcode exitcode; int inst_length; /* 0 means unknown */ @@ -448,6 +539,7 @@ struct vm_exit { struct { uint64_t gpa; uint64_t gla; + int cs_d; /* CS.D */ struct vm_guest_paging paging; struct vie vie; } inst_emul; @@ -487,7 +579,38 @@ struct vm_exit { struct { enum vm_suspend_how how; } suspended; + struct vm_task_switch task_switch; } u; }; +/* APIs to inject faults into the guest */ +void vm_inject_fault(void *vm, int vcpuid, int vector, int errcode_valid, + int errcode); + +static void __inline +vm_inject_ud(void *vm, int vcpuid) +{ + vm_inject_fault(vm, vcpuid, IDT_UD, 0, 0); +} + +static void __inline +vm_inject_gp(void *vm, int vcpuid) +{ + vm_inject_fault(vm, vcpuid, IDT_GP, 1, 0); +} + +static void __inline +vm_inject_ac(void *vm, int vcpuid, int errcode) +{ + vm_inject_fault(vm, vcpuid, IDT_AC, 1, errcode); +} + +static void __inline +vm_inject_ss(void *vm, int vcpuid, int errcode) +{ + vm_inject_fault(vm, vcpuid, IDT_SS, 1, errcode); +} + +void vm_inject_pf(void *vm, int vcpuid, int error_code, uint64_t cr2); + #endif /* _VMM_H_ */ |