/* * Copyright (C) 2018 Intel Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * * this file contains pure vmx operations */ #include #include #include #include #include /** * @pre addr != NULL && addr is 4KB-aligned * rev[31:0] 32 bits located at vmxon region physical address * @pre rev[30:0] == VMCS revision && rev[31] == 0 */ static inline void exec_vmxon(void *addr) { /* Turn VMX on, pre-conditions can avoid VMfailInvalid * here no need check RFLAGS since it will generate #GP or #UD * except VMsuccess. SDM 30.3 */ asm volatile ( "vmxon (%%rax)\n" : : "a"(addr) : "cc", "memory"); } /* Per cpu data to hold the vmxon_region for each pcpu. * It will be used again when we start a pcpu after the pcpu was down. * S3 enter/exit will use it. * Only run on current pcpu. */ void vmx_on(void) { uint64_t tmp64; uint32_t tmp32; void *vmxon_region_va = (void *)get_cpu_var(vmxon_region); uint64_t vmxon_region_pa; /* Initialize vmxon page with revision id from IA32 VMX BASIC MSR */ tmp32 = (uint32_t)msr_read(MSR_IA32_VMX_BASIC); (void)memcpy_s(vmxon_region_va, 4U, (void *)&tmp32, 4U); /* Turn on CR0.NE and CR4.VMXE */ CPU_CR_READ(cr0, &tmp64); CPU_CR_WRITE(cr0, tmp64 | CR0_NE); CPU_CR_READ(cr4, &tmp64); CPU_CR_WRITE(cr4, tmp64 | CR4_VMXE); /* Read Feature ControL MSR */ tmp64 = msr_read(MSR_IA32_FEATURE_CONTROL); /* Check if feature control is locked */ if ((tmp64 & MSR_IA32_FEATURE_CONTROL_LOCK) == 0U) { /* Lock and enable VMX support */ tmp64 |= (MSR_IA32_FEATURE_CONTROL_LOCK | MSR_IA32_FEATURE_CONTROL_VMX_NO_SMX); msr_write(MSR_IA32_FEATURE_CONTROL, tmp64); } /* Turn ON VMX */ vmxon_region_pa = hva2hpa(vmxon_region_va); exec_vmxon(&vmxon_region_pa); } static inline void exec_vmxoff(void) { asm volatile ("vmxoff" : : : "memory"); } /** * @pre addr != NULL && addr is 4KB-aligned * @pre addr != VMXON pointer */ void exec_vmclear(void *addr) { /* pre-conditions can avoid VMfail * here no need check RFLAGS since it will generate #GP or #UD * except VMsuccess. SDM 30.3 */ asm volatile ( "vmclear (%%rax)\n" : : "a"(addr) : "cc", "memory"); } /** * @pre addr != NULL && addr is 4KB-aligned * @pre addr != VMXON pointer */ void exec_vmptrld(void *addr) { /* pre-conditions can avoid VMfail * here no need check RFLAGS since it will generate #GP or #UD * except VMsuccess. SDM 30.3 */ asm volatile ( "vmptrld (%%rax)\n" : : "a"(addr) : "cc", "memory"); } /* * @pre vcpu != NULL */ void load_va_vmcs(const uint8_t *vmcs_va) { uint64_t vmcs_pa; vmcs_pa = hva2hpa(vmcs_va); exec_vmptrld((void *)&vmcs_pa); } /* * @pre vcpu != NULL */ void clear_va_vmcs(const uint8_t *vmcs_va) { uint64_t vmcs_pa; vmcs_pa = hva2hpa(vmcs_va); exec_vmclear((void *)&vmcs_pa); } /** * only run on current pcpu */ void vmx_off(void) { void **vmcs_ptr = &get_cpu_var(vmcs_run); if (*vmcs_ptr != NULL) { clear_va_vmcs(*vmcs_ptr); *vmcs_ptr = NULL; } exec_vmxoff(); } uint64_t exec_vmread64(uint32_t field_full) { uint64_t value; asm volatile ( "vmread %%rdx, %%rax " : "=a" (value) : "d"(field_full) : "cc"); return value; } uint32_t exec_vmread32(uint32_t field) { uint64_t value; value = exec_vmread64(field); return (uint32_t)value; } uint16_t exec_vmread16(uint32_t field) { uint64_t value; value = exec_vmread64(field); return (uint16_t)value; } void exec_vmwrite64(uint32_t field_full, uint64_t value) { asm volatile ( "vmwrite %%rax, %%rdx " : : "a" (value), "d"(field_full) : "cc"); } void exec_vmwrite32(uint32_t field, uint32_t value) { exec_vmwrite64(field, (uint64_t)value); } void exec_vmwrite16(uint32_t field, uint16_t value) { exec_vmwrite64(field, (uint64_t)value); }