2020-03-24 10:41:52 +01:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Kernel - based Virtual Machine driver for Linux
*
* AMD SVM support
*
* Copyright ( C ) 2006 Qumranet , Inc .
* Copyright 2010 Red Hat , Inc . and / or its affiliates .
*
* Authors :
* Yaniv Kamay < yaniv @ qumranet . com >
* Avi Kivity < avi @ qumranet . com >
*/
# define pr_fmt(fmt) "SVM: " fmt
# include <linux/kvm_types.h>
# include <linux/kvm_host.h>
# include <linux/kernel.h>
# include <asm/msr-index.h>
2020-05-04 11:28:25 -04:00
# include <asm/debugreg.h>
2020-03-24 10:41:52 +01:00
# include "kvm_emulate.h"
# include "trace.h"
# include "mmu.h"
# include "x86.h"
# include "svm.h"
static void nested_svm_inject_npf_exit ( struct kvm_vcpu * vcpu ,
struct x86_exception * fault )
{
struct vcpu_svm * svm = to_svm ( vcpu ) ;
if ( svm - > vmcb - > control . exit_code ! = SVM_EXIT_NPF ) {
/*
* TODO : track the cause of the nested page fault , and
* correctly fill in the high bits of exit_info_1 .
*/
svm - > vmcb - > control . exit_code = SVM_EXIT_NPF ;
svm - > vmcb - > control . exit_code_hi = 0 ;
svm - > vmcb - > control . exit_info_1 = ( 1ULL < < 32 ) ;
svm - > vmcb - > control . exit_info_2 = fault - > address ;
}
svm - > vmcb - > control . exit_info_1 & = ~ 0xffffffffULL ;
svm - > vmcb - > control . exit_info_1 | = fault - > error_code ;
/*
* The present bit is always zero for page structure faults on real
* hardware .
*/
if ( svm - > vmcb - > control . exit_info_1 & ( 2ULL < < 32 ) )
svm - > vmcb - > control . exit_info_1 & = ~ 1 ;
nested_svm_vmexit ( svm ) ;
}
static u64 nested_svm_get_tdp_pdptr ( struct kvm_vcpu * vcpu , int index )
{
struct vcpu_svm * svm = to_svm ( vcpu ) ;
u64 cr3 = svm - > nested . nested_cr3 ;
u64 pdpte ;
int ret ;
ret = kvm_vcpu_read_guest_page ( vcpu , gpa_to_gfn ( __sme_clr ( cr3 ) ) , & pdpte ,
offset_in_page ( cr3 ) + index * 8 , 8 ) ;
if ( ret )
return 0 ;
return pdpte ;
}
static unsigned long nested_svm_get_tdp_cr3 ( struct kvm_vcpu * vcpu )
{
struct vcpu_svm * svm = to_svm ( vcpu ) ;
return svm - > nested . nested_cr3 ;
}
static void nested_svm_init_mmu_context ( struct kvm_vcpu * vcpu )
{
WARN_ON ( mmu_is_nested ( vcpu ) ) ;
vcpu - > arch . mmu = & vcpu - > arch . guest_mmu ;
kvm_init_shadow_mmu ( vcpu ) ;
vcpu - > arch . mmu - > get_guest_pgd = nested_svm_get_tdp_cr3 ;
vcpu - > arch . mmu - > get_pdptr = nested_svm_get_tdp_pdptr ;
vcpu - > arch . mmu - > inject_page_fault = nested_svm_inject_npf_exit ;
2020-05-01 21:32:34 -07:00
vcpu - > arch . mmu - > shadow_root_level = vcpu - > arch . tdp_level ;
2020-03-24 10:41:52 +01:00
reset_shadow_zero_bits_mask ( vcpu , vcpu - > arch . mmu ) ;
vcpu - > arch . walk_mmu = & vcpu - > arch . nested_mmu ;
}
static void nested_svm_uninit_mmu_context ( struct kvm_vcpu * vcpu )
{
vcpu - > arch . mmu = & vcpu - > arch . root_mmu ;
vcpu - > arch . walk_mmu = & vcpu - > arch . root_mmu ;
}
void recalc_intercepts ( struct vcpu_svm * svm )
{
struct vmcb_control_area * c , * h ;
struct nested_state * g ;
mark_dirty ( svm - > vmcb , VMCB_INTERCEPTS ) ;
if ( ! is_guest_mode ( & svm - > vcpu ) )
return ;
c = & svm - > vmcb - > control ;
h = & svm - > nested . hsave - > control ;
g = & svm - > nested ;
2020-05-16 08:42:28 -04:00
svm - > nested . host_intercept_exceptions = h - > intercept_exceptions ;
2020-03-24 10:41:52 +01:00
c - > intercept_cr = h - > intercept_cr ;
c - > intercept_dr = h - > intercept_dr ;
c - > intercept_exceptions = h - > intercept_exceptions ;
c - > intercept = h - > intercept ;
if ( svm - > vcpu . arch . hflags & HF_VINTR_MASK ) {
/* We only want the cr8 intercept bits of L1 */
c - > intercept_cr & = ~ ( 1U < < INTERCEPT_CR8_READ ) ;
c - > intercept_cr & = ~ ( 1U < < INTERCEPT_CR8_WRITE ) ;
/*
* Once running L2 with HF_VINTR_MASK , EFLAGS . IF does not
* affect any interrupt we may want to inject ; therefore ,
* interrupt window vmexits are irrelevant to L0 .
*/
c - > intercept & = ~ ( 1ULL < < INTERCEPT_VINTR ) ;
}
/* We don't want to see VMMCALLs from a nested guest */
c - > intercept & = ~ ( 1ULL < < INTERCEPT_VMMCALL ) ;
c - > intercept_cr | = g - > intercept_cr ;
c - > intercept_dr | = g - > intercept_dr ;
c - > intercept_exceptions | = g - > intercept_exceptions ;
c - > intercept | = g - > intercept ;
}
static void copy_vmcb_control_area ( struct vmcb * dst_vmcb , struct vmcb * from_vmcb )
{
struct vmcb_control_area * dst = & dst_vmcb - > control ;
struct vmcb_control_area * from = & from_vmcb - > control ;
dst - > intercept_cr = from - > intercept_cr ;
dst - > intercept_dr = from - > intercept_dr ;
dst - > intercept_exceptions = from - > intercept_exceptions ;
dst - > intercept = from - > intercept ;
dst - > iopm_base_pa = from - > iopm_base_pa ;
dst - > msrpm_base_pa = from - > msrpm_base_pa ;
dst - > tsc_offset = from - > tsc_offset ;
2020-05-20 08:02:17 -04:00
/* asid not copied, it is handled manually for svm->vmcb. */
2020-03-24 10:41:52 +01:00
dst - > tlb_ctl = from - > tlb_ctl ;
dst - > int_ctl = from - > int_ctl ;
dst - > int_vector = from - > int_vector ;
dst - > int_state = from - > int_state ;
dst - > exit_code = from - > exit_code ;
dst - > exit_code_hi = from - > exit_code_hi ;
dst - > exit_info_1 = from - > exit_info_1 ;
dst - > exit_info_2 = from - > exit_info_2 ;
dst - > exit_int_info = from - > exit_int_info ;
dst - > exit_int_info_err = from - > exit_int_info_err ;
dst - > nested_ctl = from - > nested_ctl ;
dst - > event_inj = from - > event_inj ;
dst - > event_inj_err = from - > event_inj_err ;
dst - > nested_cr3 = from - > nested_cr3 ;
dst - > virt_ext = from - > virt_ext ;
dst - > pause_filter_count = from - > pause_filter_count ;
dst - > pause_filter_thresh = from - > pause_filter_thresh ;
}
static bool nested_svm_vmrun_msrpm ( struct vcpu_svm * svm )
{
/*
* This function merges the msr permission bitmaps of kvm and the
* nested vmcb . It is optimized in that it only merges the parts where
* the kvm msr permission bitmap may contain zero bits
*/
int i ;
if ( ! ( svm - > nested . intercept & ( 1ULL < < INTERCEPT_MSR_PROT ) ) )
return true ;
for ( i = 0 ; i < MSRPM_OFFSETS ; i + + ) {
u32 value , p ;
u64 offset ;
if ( msrpm_offsets [ i ] = = 0xffffffff )
break ;
p = msrpm_offsets [ i ] ;
offset = svm - > nested . vmcb_msrpm + ( p * 4 ) ;
if ( kvm_vcpu_read_guest ( & svm - > vcpu , offset , & value , 4 ) )
return false ;
svm - > nested . msrpm [ p ] = svm - > msrpm [ p ] | value ;
}
svm - > vmcb - > control . msrpm_base_pa = __sme_set ( __pa ( svm - > nested . msrpm ) ) ;
return true ;
}
static bool nested_vmcb_checks ( struct vmcb * vmcb )
{
if ( ( vmcb - > save . efer & EFER_SVME ) = = 0 )
return false ;
2020-04-09 16:50:33 -04:00
if ( ( ( vmcb - > save . cr0 & X86_CR0_CD ) = = 0 ) & &
( vmcb - > save . cr0 & X86_CR0_NW ) )
return false ;
2020-03-24 10:41:52 +01:00
if ( ( vmcb - > control . intercept & ( 1ULL < < INTERCEPT_VMRUN ) ) = = 0 )
return false ;
if ( vmcb - > control . asid = = 0 )
return false ;
if ( ( vmcb - > control . nested_ctl & SVM_NESTED_CTL_NP_ENABLE ) & &
! npt_enabled )
return false ;
return true ;
}
void enter_svm_guest_mode ( struct vcpu_svm * svm , u64 vmcb_gpa ,
struct vmcb * nested_vmcb , struct kvm_host_map * map )
{
bool evaluate_pending_interrupts =
is_intercept ( svm , INTERCEPT_VINTR ) | |
is_intercept ( svm , INTERCEPT_IRET ) ;
if ( kvm_get_rflags ( & svm - > vcpu ) & X86_EFLAGS_IF )
svm - > vcpu . arch . hflags | = HF_HIF_MASK ;
else
svm - > vcpu . arch . hflags & = ~ HF_HIF_MASK ;
if ( nested_vmcb - > control . nested_ctl & SVM_NESTED_CTL_NP_ENABLE ) {
svm - > nested . nested_cr3 = nested_vmcb - > control . nested_cr3 ;
nested_svm_init_mmu_context ( & svm - > vcpu ) ;
}
/* Load the nested guest state */
svm - > vmcb - > save . es = nested_vmcb - > save . es ;
svm - > vmcb - > save . cs = nested_vmcb - > save . cs ;
svm - > vmcb - > save . ss = nested_vmcb - > save . ss ;
svm - > vmcb - > save . ds = nested_vmcb - > save . ds ;
svm - > vmcb - > save . gdtr = nested_vmcb - > save . gdtr ;
svm - > vmcb - > save . idtr = nested_vmcb - > save . idtr ;
kvm_set_rflags ( & svm - > vcpu , nested_vmcb - > save . rflags ) ;
svm_set_efer ( & svm - > vcpu , nested_vmcb - > save . efer ) ;
svm_set_cr0 ( & svm - > vcpu , nested_vmcb - > save . cr0 ) ;
svm_set_cr4 ( & svm - > vcpu , nested_vmcb - > save . cr4 ) ;
if ( npt_enabled ) {
svm - > vmcb - > save . cr3 = nested_vmcb - > save . cr3 ;
svm - > vcpu . arch . cr3 = nested_vmcb - > save . cr3 ;
} else
( void ) kvm_set_cr3 ( & svm - > vcpu , nested_vmcb - > save . cr3 ) ;
/* Guest paging mode is active - reset mmu */
kvm_mmu_reset_context ( & svm - > vcpu ) ;
svm - > vmcb - > save . cr2 = svm - > vcpu . arch . cr2 = nested_vmcb - > save . cr2 ;
kvm_rax_write ( & svm - > vcpu , nested_vmcb - > save . rax ) ;
kvm_rsp_write ( & svm - > vcpu , nested_vmcb - > save . rsp ) ;
kvm_rip_write ( & svm - > vcpu , nested_vmcb - > save . rip ) ;
/* In case we don't even reach vcpu_run, the fields are not updated */
svm - > vmcb - > save . rax = nested_vmcb - > save . rax ;
svm - > vmcb - > save . rsp = nested_vmcb - > save . rsp ;
svm - > vmcb - > save . rip = nested_vmcb - > save . rip ;
svm - > vmcb - > save . dr7 = nested_vmcb - > save . dr7 ;
2020-05-04 11:28:25 -04:00
svm - > vcpu . arch . dr6 = nested_vmcb - > save . dr6 ;
2020-03-24 10:41:52 +01:00
svm - > vmcb - > save . cpl = nested_vmcb - > save . cpl ;
svm - > nested . vmcb_msrpm = nested_vmcb - > control . msrpm_base_pa & ~ 0x0fffULL ;
svm - > nested . vmcb_iopm = nested_vmcb - > control . iopm_base_pa & ~ 0x0fffULL ;
/* cache intercepts */
svm - > nested . intercept_cr = nested_vmcb - > control . intercept_cr ;
svm - > nested . intercept_dr = nested_vmcb - > control . intercept_dr ;
svm - > nested . intercept_exceptions = nested_vmcb - > control . intercept_exceptions ;
svm - > nested . intercept = nested_vmcb - > control . intercept ;
2020-03-20 14:28:12 -07:00
svm_flush_tlb ( & svm - > vcpu ) ;
2020-03-24 10:41:52 +01:00
svm - > vmcb - > control . int_ctl = nested_vmcb - > control . int_ctl | V_INTR_MASKING_MASK ;
if ( nested_vmcb - > control . int_ctl & V_INTR_MASKING_MASK )
svm - > vcpu . arch . hflags | = HF_VINTR_MASK ;
else
svm - > vcpu . arch . hflags & = ~ HF_VINTR_MASK ;
svm - > vcpu . arch . tsc_offset + = nested_vmcb - > control . tsc_offset ;
svm - > vmcb - > control . tsc_offset = svm - > vcpu . arch . tsc_offset ;
svm - > vmcb - > control . virt_ext = nested_vmcb - > control . virt_ext ;
svm - > vmcb - > control . int_vector = nested_vmcb - > control . int_vector ;
svm - > vmcb - > control . int_state = nested_vmcb - > control . int_state ;
svm - > vmcb - > control . event_inj = nested_vmcb - > control . event_inj ;
svm - > vmcb - > control . event_inj_err = nested_vmcb - > control . event_inj_err ;
svm - > vmcb - > control . pause_filter_count =
nested_vmcb - > control . pause_filter_count ;
svm - > vmcb - > control . pause_filter_thresh =
nested_vmcb - > control . pause_filter_thresh ;
kvm_vcpu_unmap ( & svm - > vcpu , map , true ) ;
/* Enter Guest-Mode */
enter_guest_mode ( & svm - > vcpu ) ;
/*
* Merge guest and host intercepts - must be called with vcpu in
* guest - mode to take affect here
*/
recalc_intercepts ( svm ) ;
svm - > nested . vmcb = vmcb_gpa ;
/*
* If L1 had a pending IRQ / NMI before executing VMRUN ,
* which wasn ' t delivered because it was disallowed ( e . g .
* interrupts disabled ) , L0 needs to evaluate if this pending
* event should cause an exit from L2 to L1 or be delivered
* directly to L2 .
*
* Usually this would be handled by the processor noticing an
* IRQ / NMI window request . However , VMRUN can unblock interrupts
* by implicitly setting GIF , so force L0 to perform pending event
* evaluation by requesting a KVM_REQ_EVENT .
*/
enable_gif ( svm ) ;
if ( unlikely ( evaluate_pending_interrupts ) )
kvm_make_request ( KVM_REQ_EVENT , & svm - > vcpu ) ;
mark_all_dirty ( svm - > vmcb ) ;
}
int nested_svm_vmrun ( struct vcpu_svm * svm )
{
int ret ;
struct vmcb * nested_vmcb ;
struct vmcb * hsave = svm - > nested . hsave ;
struct vmcb * vmcb = svm - > vmcb ;
struct kvm_host_map map ;
u64 vmcb_gpa ;
2020-04-23 10:52:48 -04:00
if ( is_smm ( & svm - > vcpu ) ) {
kvm_queue_exception ( & svm - > vcpu , UD_VECTOR ) ;
return 1 ;
}
2020-03-24 10:41:52 +01:00
2020-04-23 10:52:48 -04:00
vmcb_gpa = svm - > vmcb - > save . rax ;
2020-03-24 10:41:52 +01:00
ret = kvm_vcpu_map ( & svm - > vcpu , gpa_to_gfn ( vmcb_gpa ) , & map ) ;
if ( ret = = - EINVAL ) {
kvm_inject_gp ( & svm - > vcpu , 0 ) ;
return 1 ;
} else if ( ret ) {
return kvm_skip_emulated_instruction ( & svm - > vcpu ) ;
}
ret = kvm_skip_emulated_instruction ( & svm - > vcpu ) ;
nested_vmcb = map . hva ;
if ( ! nested_vmcb_checks ( nested_vmcb ) ) {
nested_vmcb - > control . exit_code = SVM_EXIT_ERR ;
nested_vmcb - > control . exit_code_hi = 0 ;
nested_vmcb - > control . exit_info_1 = 0 ;
nested_vmcb - > control . exit_info_2 = 0 ;
kvm_vcpu_unmap ( & svm - > vcpu , & map , true ) ;
return ret ;
}
trace_kvm_nested_vmrun ( svm - > vmcb - > save . rip , vmcb_gpa ,
nested_vmcb - > save . rip ,
nested_vmcb - > control . int_ctl ,
nested_vmcb - > control . event_inj ,
nested_vmcb - > control . nested_ctl ) ;
trace_kvm_nested_intercepts ( nested_vmcb - > control . intercept_cr & 0xffff ,
nested_vmcb - > control . intercept_cr > > 16 ,
nested_vmcb - > control . intercept_exceptions ,
nested_vmcb - > control . intercept ) ;
/* Clear internal status */
kvm_clear_exception_queue ( & svm - > vcpu ) ;
kvm_clear_interrupt_queue ( & svm - > vcpu ) ;
/*
* Save the old vmcb , so we don ' t need to pick what we save , but can
* restore everything when a VMEXIT occurs
*/
hsave - > save . es = vmcb - > save . es ;
hsave - > save . cs = vmcb - > save . cs ;
hsave - > save . ss = vmcb - > save . ss ;
hsave - > save . ds = vmcb - > save . ds ;
hsave - > save . gdtr = vmcb - > save . gdtr ;
hsave - > save . idtr = vmcb - > save . idtr ;
hsave - > save . efer = svm - > vcpu . arch . efer ;
hsave - > save . cr0 = kvm_read_cr0 ( & svm - > vcpu ) ;
hsave - > save . cr4 = svm - > vcpu . arch . cr4 ;
hsave - > save . rflags = kvm_get_rflags ( & svm - > vcpu ) ;
hsave - > save . rip = kvm_rip_read ( & svm - > vcpu ) ;
hsave - > save . rsp = vmcb - > save . rsp ;
hsave - > save . rax = vmcb - > save . rax ;
if ( npt_enabled )
hsave - > save . cr3 = vmcb - > save . cr3 ;
else
hsave - > save . cr3 = kvm_read_cr3 ( & svm - > vcpu ) ;
copy_vmcb_control_area ( hsave , vmcb ) ;
2020-04-23 13:22:27 -04:00
svm - > nested . nested_run_pending = 1 ;
2020-03-24 10:41:52 +01:00
enter_svm_guest_mode ( svm , vmcb_gpa , nested_vmcb , & map ) ;
if ( ! nested_svm_vmrun_msrpm ( svm ) ) {
svm - > vmcb - > control . exit_code = SVM_EXIT_ERR ;
svm - > vmcb - > control . exit_code_hi = 0 ;
svm - > vmcb - > control . exit_info_1 = 0 ;
svm - > vmcb - > control . exit_info_2 = 0 ;
nested_svm_vmexit ( svm ) ;
}
return ret ;
}
void nested_svm_vmloadsave ( struct vmcb * from_vmcb , struct vmcb * to_vmcb )
{
to_vmcb - > save . fs = from_vmcb - > save . fs ;
to_vmcb - > save . gs = from_vmcb - > save . gs ;
to_vmcb - > save . tr = from_vmcb - > save . tr ;
to_vmcb - > save . ldtr = from_vmcb - > save . ldtr ;
to_vmcb - > save . kernel_gs_base = from_vmcb - > save . kernel_gs_base ;
to_vmcb - > save . star = from_vmcb - > save . star ;
to_vmcb - > save . lstar = from_vmcb - > save . lstar ;
to_vmcb - > save . cstar = from_vmcb - > save . cstar ;
to_vmcb - > save . sfmask = from_vmcb - > save . sfmask ;
to_vmcb - > save . sysenter_cs = from_vmcb - > save . sysenter_cs ;
to_vmcb - > save . sysenter_esp = from_vmcb - > save . sysenter_esp ;
to_vmcb - > save . sysenter_eip = from_vmcb - > save . sysenter_eip ;
}
int nested_svm_vmexit ( struct vcpu_svm * svm )
{
int rc ;
struct vmcb * nested_vmcb ;
struct vmcb * hsave = svm - > nested . hsave ;
struct vmcb * vmcb = svm - > vmcb ;
struct kvm_host_map map ;
trace_kvm_nested_vmexit_inject ( vmcb - > control . exit_code ,
vmcb - > control . exit_info_1 ,
vmcb - > control . exit_info_2 ,
vmcb - > control . exit_int_info ,
vmcb - > control . exit_int_info_err ,
KVM_ISA_SVM ) ;
rc = kvm_vcpu_map ( & svm - > vcpu , gpa_to_gfn ( svm - > nested . vmcb ) , & map ) ;
if ( rc ) {
if ( rc = = - EINVAL )
kvm_inject_gp ( & svm - > vcpu , 0 ) ;
return 1 ;
}
nested_vmcb = map . hva ;
/* Exit Guest-Mode */
leave_guest_mode ( & svm - > vcpu ) ;
svm - > nested . vmcb = 0 ;
2020-04-23 13:13:09 -04:00
/* in case we halted in L2 */
svm - > vcpu . arch . mp_state = KVM_MP_STATE_RUNNABLE ;
2020-03-24 10:41:52 +01:00
/* Give the current vmcb to the guest */
disable_gif ( svm ) ;
nested_vmcb - > save . es = vmcb - > save . es ;
nested_vmcb - > save . cs = vmcb - > save . cs ;
nested_vmcb - > save . ss = vmcb - > save . ss ;
nested_vmcb - > save . ds = vmcb - > save . ds ;
nested_vmcb - > save . gdtr = vmcb - > save . gdtr ;
nested_vmcb - > save . idtr = vmcb - > save . idtr ;
nested_vmcb - > save . efer = svm - > vcpu . arch . efer ;
nested_vmcb - > save . cr0 = kvm_read_cr0 ( & svm - > vcpu ) ;
nested_vmcb - > save . cr3 = kvm_read_cr3 ( & svm - > vcpu ) ;
nested_vmcb - > save . cr2 = vmcb - > save . cr2 ;
nested_vmcb - > save . cr4 = svm - > vcpu . arch . cr4 ;
nested_vmcb - > save . rflags = kvm_get_rflags ( & svm - > vcpu ) ;
2020-05-27 11:01:02 +02:00
nested_vmcb - > save . rip = kvm_rip_read ( & svm - > vcpu ) ;
nested_vmcb - > save . rsp = kvm_rsp_read ( & svm - > vcpu ) ;
nested_vmcb - > save . rax = kvm_rax_read ( & svm - > vcpu ) ;
2020-03-24 10:41:52 +01:00
nested_vmcb - > save . dr7 = vmcb - > save . dr7 ;
2020-05-04 11:28:25 -04:00
nested_vmcb - > save . dr6 = svm - > vcpu . arch . dr6 ;
2020-03-24 10:41:52 +01:00
nested_vmcb - > save . cpl = vmcb - > save . cpl ;
nested_vmcb - > control . int_ctl = vmcb - > control . int_ctl ;
nested_vmcb - > control . int_vector = vmcb - > control . int_vector ;
nested_vmcb - > control . int_state = vmcb - > control . int_state ;
nested_vmcb - > control . exit_code = vmcb - > control . exit_code ;
nested_vmcb - > control . exit_code_hi = vmcb - > control . exit_code_hi ;
nested_vmcb - > control . exit_info_1 = vmcb - > control . exit_info_1 ;
nested_vmcb - > control . exit_info_2 = vmcb - > control . exit_info_2 ;
nested_vmcb - > control . exit_int_info = vmcb - > control . exit_int_info ;
nested_vmcb - > control . exit_int_info_err = vmcb - > control . exit_int_info_err ;
if ( svm - > nrips_enabled )
nested_vmcb - > control . next_rip = vmcb - > control . next_rip ;
/*
* If we emulate a VMRUN / # VMEXIT in the same host # vmexit cycle we have
* to make sure that we do not lose injected events . So check event_inj
* here and copy it to exit_int_info if it is valid .
* Exit_int_info and event_inj can ' t be both valid because the case
* below only happens on a VMRUN instruction intercept which has
* no valid exit_int_info set .
*/
if ( vmcb - > control . event_inj & SVM_EVTINJ_VALID ) {
struct vmcb_control_area * nc = & nested_vmcb - > control ;
nc - > exit_int_info = vmcb - > control . event_inj ;
nc - > exit_int_info_err = vmcb - > control . event_inj_err ;
}
nested_vmcb - > control . tlb_ctl = 0 ;
nested_vmcb - > control . event_inj = 0 ;
nested_vmcb - > control . event_inj_err = 0 ;
nested_vmcb - > control . pause_filter_count =
svm - > vmcb - > control . pause_filter_count ;
nested_vmcb - > control . pause_filter_thresh =
svm - > vmcb - > control . pause_filter_thresh ;
/* We always set V_INTR_MASKING and remember the old value in hflags */
if ( ! ( svm - > vcpu . arch . hflags & HF_VINTR_MASK ) )
nested_vmcb - > control . int_ctl & = ~ V_INTR_MASKING_MASK ;
/* Restore the original control entries */
copy_vmcb_control_area ( vmcb , hsave ) ;
svm - > vcpu . arch . tsc_offset = svm - > vmcb - > control . tsc_offset ;
kvm_clear_exception_queue ( & svm - > vcpu ) ;
kvm_clear_interrupt_queue ( & svm - > vcpu ) ;
svm - > nested . nested_cr3 = 0 ;
/* Restore selected save entries */
svm - > vmcb - > save . es = hsave - > save . es ;
svm - > vmcb - > save . cs = hsave - > save . cs ;
svm - > vmcb - > save . ss = hsave - > save . ss ;
svm - > vmcb - > save . ds = hsave - > save . ds ;
svm - > vmcb - > save . gdtr = hsave - > save . gdtr ;
svm - > vmcb - > save . idtr = hsave - > save . idtr ;
kvm_set_rflags ( & svm - > vcpu , hsave - > save . rflags ) ;
svm_set_efer ( & svm - > vcpu , hsave - > save . efer ) ;
svm_set_cr0 ( & svm - > vcpu , hsave - > save . cr0 | X86_CR0_PE ) ;
svm_set_cr4 ( & svm - > vcpu , hsave - > save . cr4 ) ;
if ( npt_enabled ) {
svm - > vmcb - > save . cr3 = hsave - > save . cr3 ;
svm - > vcpu . arch . cr3 = hsave - > save . cr3 ;
} else {
( void ) kvm_set_cr3 ( & svm - > vcpu , hsave - > save . cr3 ) ;
}
kvm_rax_write ( & svm - > vcpu , hsave - > save . rax ) ;
kvm_rsp_write ( & svm - > vcpu , hsave - > save . rsp ) ;
kvm_rip_write ( & svm - > vcpu , hsave - > save . rip ) ;
svm - > vmcb - > save . dr7 = 0 ;
svm - > vmcb - > save . cpl = 0 ;
svm - > vmcb - > control . exit_int_info = 0 ;
mark_all_dirty ( svm - > vmcb ) ;
kvm_vcpu_unmap ( & svm - > vcpu , & map , true ) ;
nested_svm_uninit_mmu_context ( & svm - > vcpu ) ;
kvm_mmu_reset_context ( & svm - > vcpu ) ;
kvm_mmu_load ( & svm - > vcpu ) ;
/*
* Drop what we picked up for L2 via svm_complete_interrupts ( ) so it
* doesn ' t end up in L1 .
*/
svm - > vcpu . arch . nmi_injected = false ;
kvm_clear_exception_queue ( & svm - > vcpu ) ;
kvm_clear_interrupt_queue ( & svm - > vcpu ) ;
return 0 ;
}
static int nested_svm_exit_handled_msr ( struct vcpu_svm * svm )
{
u32 offset , msr , value ;
int write , mask ;
if ( ! ( svm - > nested . intercept & ( 1ULL < < INTERCEPT_MSR_PROT ) ) )
return NESTED_EXIT_HOST ;
msr = svm - > vcpu . arch . regs [ VCPU_REGS_RCX ] ;
offset = svm_msrpm_offset ( msr ) ;
write = svm - > vmcb - > control . exit_info_1 & 1 ;
mask = 1 < < ( ( 2 * ( msr & 0xf ) ) + write ) ;
if ( offset = = MSR_INVALID )
return NESTED_EXIT_DONE ;
/* Offset is in 32 bit units but need in 8 bit units */
offset * = 4 ;
if ( kvm_vcpu_read_guest ( & svm - > vcpu , svm - > nested . vmcb_msrpm + offset , & value , 4 ) )
return NESTED_EXIT_DONE ;
return ( value & mask ) ? NESTED_EXIT_DONE : NESTED_EXIT_HOST ;
}
static int nested_svm_intercept_ioio ( struct vcpu_svm * svm )
{
unsigned port , size , iopm_len ;
u16 val , mask ;
u8 start_bit ;
u64 gpa ;
if ( ! ( svm - > nested . intercept & ( 1ULL < < INTERCEPT_IOIO_PROT ) ) )
return NESTED_EXIT_HOST ;
port = svm - > vmcb - > control . exit_info_1 > > 16 ;
size = ( svm - > vmcb - > control . exit_info_1 & SVM_IOIO_SIZE_MASK ) > >
SVM_IOIO_SIZE_SHIFT ;
gpa = svm - > nested . vmcb_iopm + ( port / 8 ) ;
start_bit = port % 8 ;
iopm_len = ( start_bit + size > 8 ) ? 2 : 1 ;
mask = ( 0xf > > ( 4 - size ) ) < < start_bit ;
val = 0 ;
if ( kvm_vcpu_read_guest ( & svm - > vcpu , gpa , & val , iopm_len ) )
return NESTED_EXIT_DONE ;
return ( val & mask ) ? NESTED_EXIT_DONE : NESTED_EXIT_HOST ;
}
static int nested_svm_intercept ( struct vcpu_svm * svm )
{
u32 exit_code = svm - > vmcb - > control . exit_code ;
int vmexit = NESTED_EXIT_HOST ;
switch ( exit_code ) {
case SVM_EXIT_MSR :
vmexit = nested_svm_exit_handled_msr ( svm ) ;
break ;
case SVM_EXIT_IOIO :
vmexit = nested_svm_intercept_ioio ( svm ) ;
break ;
case SVM_EXIT_READ_CR0 . . . SVM_EXIT_WRITE_CR8 : {
u32 bit = 1U < < ( exit_code - SVM_EXIT_READ_CR0 ) ;
if ( svm - > nested . intercept_cr & bit )
vmexit = NESTED_EXIT_DONE ;
break ;
}
case SVM_EXIT_READ_DR0 . . . SVM_EXIT_WRITE_DR7 : {
u32 bit = 1U < < ( exit_code - SVM_EXIT_READ_DR0 ) ;
if ( svm - > nested . intercept_dr & bit )
vmexit = NESTED_EXIT_DONE ;
break ;
}
case SVM_EXIT_EXCP_BASE . . . SVM_EXIT_EXCP_BASE + 0x1f : {
2020-05-16 08:42:28 -04:00
/*
* Host - intercepted exceptions have been checked already in
* nested_svm_exit_special . There is nothing to do here ,
* the vmexit is injected by svm_check_nested_events .
*/
vmexit = NESTED_EXIT_DONE ;
2020-03-24 10:41:52 +01:00
break ;
}
case SVM_EXIT_ERR : {
vmexit = NESTED_EXIT_DONE ;
break ;
}
default : {
u64 exit_bits = 1ULL < < ( exit_code - SVM_EXIT_INTR ) ;
if ( svm - > nested . intercept & exit_bits )
vmexit = NESTED_EXIT_DONE ;
}
}
return vmexit ;
}
int nested_svm_exit_handled ( struct vcpu_svm * svm )
{
int vmexit ;
vmexit = nested_svm_intercept ( svm ) ;
if ( vmexit = = NESTED_EXIT_DONE )
nested_svm_vmexit ( svm ) ;
return vmexit ;
}
int nested_svm_check_permissions ( struct vcpu_svm * svm )
{
if ( ! ( svm - > vcpu . arch . efer & EFER_SVME ) | |
! is_paging ( & svm - > vcpu ) ) {
kvm_queue_exception ( & svm - > vcpu , UD_VECTOR ) ;
return 1 ;
}
if ( svm - > vmcb - > save . cpl ) {
kvm_inject_gp ( & svm - > vcpu , 0 ) ;
return 1 ;
}
return 0 ;
}
2020-05-16 08:42:28 -04:00
static bool nested_exit_on_exception ( struct vcpu_svm * svm )
2020-03-24 10:41:52 +01:00
{
2020-05-16 08:42:28 -04:00
unsigned int nr = svm - > vcpu . arch . exception . nr ;
2020-03-24 10:41:52 +01:00
2020-05-16 08:42:28 -04:00
return ( svm - > nested . intercept_exceptions & ( 1 < < nr ) ) ;
}
2020-03-24 10:41:52 +01:00
2020-05-16 08:42:28 -04:00
static void nested_svm_inject_exception_vmexit ( struct vcpu_svm * svm )
{
unsigned int nr = svm - > vcpu . arch . exception . nr ;
2020-03-24 10:41:52 +01:00
svm - > vmcb - > control . exit_code = SVM_EXIT_EXCP_BASE + nr ;
svm - > vmcb - > control . exit_code_hi = 0 ;
2020-05-16 08:42:28 -04:00
if ( svm - > vcpu . arch . exception . has_error_code )
svm - > vmcb - > control . exit_info_1 = svm - > vcpu . arch . exception . error_code ;
2020-03-24 10:41:52 +01:00
/*
* EXITINFO2 is undefined for all exception intercepts other
* than # PF .
*/
2020-05-16 08:42:28 -04:00
if ( nr = = PF_VECTOR ) {
if ( svm - > vcpu . arch . exception . nested_apf )
svm - > vmcb - > control . exit_info_2 = svm - > vcpu . arch . apf . nested_apf_token ;
else if ( svm - > vcpu . arch . exception . has_payload )
svm - > vmcb - > control . exit_info_2 = svm - > vcpu . arch . exception . payload ;
else
svm - > vmcb - > control . exit_info_2 = svm - > vcpu . arch . cr2 ;
} else if ( nr = = DB_VECTOR ) {
/* See inject_pending_event. */
kvm_deliver_exception_payload ( & svm - > vcpu ) ;
if ( svm - > vcpu . arch . dr7 & DR7_GD ) {
svm - > vcpu . arch . dr7 & = ~ DR7_GD ;
kvm_update_dr7 ( & svm - > vcpu ) ;
}
} else
WARN_ON ( svm - > vcpu . arch . exception . has_payload ) ;
2020-03-24 10:41:52 +01:00
2020-05-16 08:42:28 -04:00
nested_svm_vmexit ( svm ) ;
2020-03-24 10:41:52 +01:00
}
2020-04-23 08:17:28 -04:00
static void nested_svm_smi ( struct vcpu_svm * svm )
{
svm - > vmcb - > control . exit_code = SVM_EXIT_SMI ;
svm - > vmcb - > control . exit_info_1 = 0 ;
svm - > vmcb - > control . exit_info_2 = 0 ;
nested_svm_vmexit ( svm ) ;
}
2020-04-14 16:11:06 -04:00
static void nested_svm_nmi ( struct vcpu_svm * svm )
{
svm - > vmcb - > control . exit_code = SVM_EXIT_NMI ;
svm - > vmcb - > control . exit_info_1 = 0 ;
svm - > vmcb - > control . exit_info_2 = 0 ;
nested_svm_vmexit ( svm ) ;
}
2020-03-24 10:41:52 +01:00
static void nested_svm_intr ( struct vcpu_svm * svm )
{
2020-04-23 13:15:33 -04:00
trace_kvm_nested_intr_vmexit ( svm - > vmcb - > save . rip ) ;
2020-03-24 10:41:52 +01:00
svm - > vmcb - > control . exit_code = SVM_EXIT_INTR ;
svm - > vmcb - > control . exit_info_1 = 0 ;
svm - > vmcb - > control . exit_info_2 = 0 ;
2020-04-23 13:15:33 -04:00
nested_svm_vmexit ( svm ) ;
2020-03-24 10:41:52 +01:00
}
2020-04-17 10:24:18 -04:00
static int svm_check_nested_events ( struct kvm_vcpu * vcpu )
2020-03-24 10:41:52 +01:00
{
struct vcpu_svm * svm = to_svm ( vcpu ) ;
bool block_nested_events =
2020-05-16 08:46:00 -04:00
kvm_event_needs_reinjection ( vcpu ) | | svm - > nested . nested_run_pending ;
2020-03-24 10:41:52 +01:00
2020-05-16 08:42:28 -04:00
if ( vcpu - > arch . exception . pending ) {
if ( block_nested_events )
return - EBUSY ;
if ( ! nested_exit_on_exception ( svm ) )
return 0 ;
nested_svm_inject_exception_vmexit ( svm ) ;
return 0 ;
}
2020-04-23 08:13:10 -04:00
if ( vcpu - > arch . smi_pending & & ! svm_smi_blocked ( vcpu ) ) {
2020-04-23 08:17:28 -04:00
if ( block_nested_events )
return - EBUSY ;
2020-04-23 08:13:10 -04:00
if ( ! nested_exit_on_smi ( svm ) )
return 0 ;
2020-04-23 08:17:28 -04:00
nested_svm_smi ( svm ) ;
return 0 ;
}
2020-04-23 08:13:10 -04:00
if ( vcpu - > arch . nmi_pending & & ! svm_nmi_blocked ( vcpu ) ) {
2020-04-14 16:11:06 -04:00
if ( block_nested_events )
return - EBUSY ;
2020-04-23 08:13:10 -04:00
if ( ! nested_exit_on_nmi ( svm ) )
return 0 ;
2020-04-14 16:11:06 -04:00
nested_svm_nmi ( svm ) ;
return 0 ;
}
2020-04-23 08:13:10 -04:00
if ( kvm_cpu_has_interrupt ( vcpu ) & & ! svm_interrupt_blocked ( vcpu ) ) {
2020-03-24 10:41:52 +01:00
if ( block_nested_events )
return - EBUSY ;
2020-04-23 08:13:10 -04:00
if ( ! nested_exit_on_intr ( svm ) )
return 0 ;
2020-03-24 10:41:52 +01:00
nested_svm_intr ( svm ) ;
return 0 ;
}
return 0 ;
}
int nested_svm_exit_special ( struct vcpu_svm * svm )
{
u32 exit_code = svm - > vmcb - > control . exit_code ;
switch ( exit_code ) {
case SVM_EXIT_INTR :
case SVM_EXIT_NMI :
case SVM_EXIT_NPF :
2020-05-16 08:42:28 -04:00
return NESTED_EXIT_HOST ;
case SVM_EXIT_EXCP_BASE . . . SVM_EXIT_EXCP_BASE + 0x1f : {
u32 excp_bits = 1 < < ( exit_code - SVM_EXIT_EXCP_BASE ) ;
if ( get_host_vmcb ( svm ) - > control . intercept_exceptions & excp_bits )
2020-03-24 10:41:52 +01:00
return NESTED_EXIT_HOST ;
2020-05-16 08:42:28 -04:00
else if ( exit_code = = SVM_EXIT_EXCP_BASE + PF_VECTOR & &
svm - > vcpu . arch . apf . host_apf_reason )
/* Trap async PF even if not shadowing */
2020-03-24 10:41:52 +01:00
return NESTED_EXIT_HOST ;
break ;
2020-05-16 08:42:28 -04:00
}
2020-03-24 10:41:52 +01:00
default :
break ;
}
return NESTED_EXIT_CONTINUE ;
}
2020-04-17 10:24:18 -04:00
struct kvm_x86_nested_ops svm_nested_ops = {
. check_events = svm_check_nested_events ,
} ;