Skip to content

Commit f1c6366

Browse files
tlendackybonzini
authored andcommitted
KVM: SVM: Add required changes to support intercepts under SEV-ES
When a guest is running under SEV-ES, the hypervisor cannot access the guest register state. There are numerous places in the KVM code where certain registers are accessed that are not allowed to be accessed (e.g. RIP, CR0, etc). Add checks to prevent register accesses and add intercept update support at various points within the KVM code. Also, when handling a VMGEXIT, exceptions are passed back through the GHCB. Since the RDMSR/WRMSR intercepts (may) inject a #GP on error, update the SVM intercepts to handle this for SEV-ES guests. Signed-off-by: Tom Lendacky <[email protected]> [Redo MSR part using the .complete_emulated_msr callback. - Paolo] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent f9a4d62 commit f1c6366

File tree

3 files changed

+84
-13
lines changed

3 files changed

+84
-13
lines changed

arch/x86/include/asm/svm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
178178
#define LBR_CTL_ENABLE_MASK BIT_ULL(0)
179179
#define VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK BIT_ULL(1)
180180

181-
#define SVM_INTERRUPT_SHADOW_MASK 1
181+
#define SVM_INTERRUPT_SHADOW_MASK BIT_ULL(0)
182+
#define SVM_GUEST_INTERRUPT_MASK BIT_ULL(1)
182183

183184
#define SVM_IOIO_STR_SHIFT 2
184185
#define SVM_IOIO_REP_SHIFT 3

arch/x86/kvm/svm/svm.c

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <asm/irq_remapping.h>
3636
#include <asm/spec-ctrl.h>
3737
#include <asm/cpu_device_id.h>
38+
#include <asm/traps.h>
3839

3940
#include <asm/virtext.h>
4041
#include "trace.h"
@@ -339,6 +340,13 @@ static int skip_emulated_instruction(struct kvm_vcpu *vcpu)
339340
{
340341
struct vcpu_svm *svm = to_svm(vcpu);
341342

343+
/*
344+
* SEV-ES does not expose the next RIP. The RIP update is controlled by
345+
* the type of exit and the #VC handler in the guest.
346+
*/
347+
if (sev_es_guest(vcpu->kvm))
348+
goto done;
349+
342350
if (nrips && svm->vmcb->control.next_rip != 0) {
343351
WARN_ON_ONCE(!static_cpu_has(X86_FEATURE_NRIPS));
344352
svm->next_rip = svm->vmcb->control.next_rip;
@@ -350,6 +358,8 @@ static int skip_emulated_instruction(struct kvm_vcpu *vcpu)
350358
} else {
351359
kvm_rip_write(vcpu, svm->next_rip);
352360
}
361+
362+
done:
353363
svm_set_interrupt_shadow(vcpu, 0);
354364

355365
return 1;
@@ -1651,9 +1661,18 @@ static void svm_set_gdt(struct kvm_vcpu *vcpu, struct desc_ptr *dt)
16511661

16521662
static void update_cr0_intercept(struct vcpu_svm *svm)
16531663
{
1654-
ulong gcr0 = svm->vcpu.arch.cr0;
1655-
u64 *hcr0 = &svm->vmcb->save.cr0;
1664+
ulong gcr0;
1665+
u64 *hcr0;
1666+
1667+
/*
1668+
* SEV-ES guests must always keep the CR intercepts cleared. CR
1669+
* tracking is done using the CR write traps.
1670+
*/
1671+
if (sev_es_guest(svm->vcpu.kvm))
1672+
return;
16561673

1674+
gcr0 = svm->vcpu.arch.cr0;
1675+
hcr0 = &svm->vmcb->save.cr0;
16571676
*hcr0 = (*hcr0 & ~SVM_CR0_SELECTIVE_MASK)
16581677
| (gcr0 & SVM_CR0_SELECTIVE_MASK);
16591678

@@ -1673,7 +1692,7 @@ void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
16731692
struct vcpu_svm *svm = to_svm(vcpu);
16741693

16751694
#ifdef CONFIG_X86_64
1676-
if (vcpu->arch.efer & EFER_LME) {
1695+
if (vcpu->arch.efer & EFER_LME && !vcpu->arch.guest_state_protected) {
16771696
if (!is_paging(vcpu) && (cr0 & X86_CR0_PG)) {
16781697
vcpu->arch.efer |= EFER_LMA;
16791698
svm->vmcb->save.efer |= EFER_LMA | EFER_LME;
@@ -2583,6 +2602,20 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
25832602
return 0;
25842603
}
25852604

2605+
static int svm_complete_emulated_msr(struct kvm_vcpu *vcpu, int err)
2606+
{
2607+
struct vcpu_svm *svm = to_svm(vcpu);
2608+
if (!sev_es_guest(svm->vcpu.kvm) || !err)
2609+
return kvm_complete_insn_gp(&svm->vcpu, err);
2610+
2611+
ghcb_set_sw_exit_info_1(svm->ghcb, 1);
2612+
ghcb_set_sw_exit_info_2(svm->ghcb,
2613+
X86_TRAP_GP |
2614+
SVM_EVTINJ_TYPE_EXEPT |
2615+
SVM_EVTINJ_VALID);
2616+
return 1;
2617+
}
2618+
25862619
static int rdmsr_interception(struct vcpu_svm *svm)
25872620
{
25882621
return kvm_emulate_rdmsr(&svm->vcpu);
@@ -2801,7 +2834,14 @@ static int interrupt_window_interception(struct vcpu_svm *svm)
28012834
static int pause_interception(struct vcpu_svm *svm)
28022835
{
28032836
struct kvm_vcpu *vcpu = &svm->vcpu;
2804-
bool in_kernel = (svm_get_cpl(vcpu) == 0);
2837+
bool in_kernel;
2838+
2839+
/*
2840+
* CPL is not made available for an SEV-ES guest, therefore
2841+
* vcpu->arch.preempted_in_kernel can never be true. Just
2842+
* set in_kernel to false as well.
2843+
*/
2844+
in_kernel = !sev_es_guest(svm->vcpu.kvm) && svm_get_cpl(vcpu) == 0;
28052845

28062846
if (!kvm_pause_in_guest(vcpu->kvm))
28072847
grow_ple_window(vcpu);
@@ -3064,10 +3104,13 @@ static int handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
30643104

30653105
trace_kvm_exit(exit_code, vcpu, KVM_ISA_SVM);
30663106

3067-
if (!svm_is_intercept(svm, INTERCEPT_CR0_WRITE))
3068-
vcpu->arch.cr0 = svm->vmcb->save.cr0;
3069-
if (npt_enabled)
3070-
vcpu->arch.cr3 = svm->vmcb->save.cr3;
3107+
/* SEV-ES guests must use the CR write traps to track CR registers. */
3108+
if (!sev_es_guest(vcpu->kvm)) {
3109+
if (!svm_is_intercept(svm, INTERCEPT_CR0_WRITE))
3110+
vcpu->arch.cr0 = svm->vmcb->save.cr0;
3111+
if (npt_enabled)
3112+
vcpu->arch.cr3 = svm->vmcb->save.cr3;
3113+
}
30713114

30723115
if (is_guest_mode(vcpu)) {
30733116
int vmexit;
@@ -3179,6 +3222,13 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
31793222
{
31803223
struct vcpu_svm *svm = to_svm(vcpu);
31813224

3225+
/*
3226+
* SEV-ES guests must always keep the CR intercepts cleared. CR
3227+
* tracking is done using the CR write traps.
3228+
*/
3229+
if (sev_es_guest(vcpu->kvm))
3230+
return;
3231+
31823232
if (nested_svm_virtualize_tpr(vcpu))
31833233
return;
31843234

@@ -3250,7 +3300,14 @@ bool svm_interrupt_blocked(struct kvm_vcpu *vcpu)
32503300
if (!gif_set(svm))
32513301
return true;
32523302

3253-
if (is_guest_mode(vcpu)) {
3303+
if (sev_es_guest(svm->vcpu.kvm)) {
3304+
/*
3305+
* SEV-ES guests to not expose RFLAGS. Use the VMCB interrupt mask
3306+
* bit to determine the state of the IF flag.
3307+
*/
3308+
if (!(vmcb->control.int_state & SVM_GUEST_INTERRUPT_MASK))
3309+
return true;
3310+
} else if (is_guest_mode(vcpu)) {
32543311
/* As long as interrupts are being delivered... */
32553312
if ((svm->nested.ctl.int_ctl & V_INTR_MASKING_MASK)
32563313
? !(svm->nested.hsave->save.rflags & X86_EFLAGS_IF)
@@ -3432,6 +3489,12 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
34323489
svm->vcpu.arch.nmi_injected = true;
34333490
break;
34343491
case SVM_EXITINTINFO_TYPE_EXEPT:
3492+
/*
3493+
* Never re-inject a #VC exception.
3494+
*/
3495+
if (vector == X86_TRAP_VC)
3496+
break;
3497+
34353498
/*
34363499
* In case of software exceptions, do not reinject the vector,
34373500
* but re-execute the instruction instead. Rewind RIP first
@@ -4306,7 +4369,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
43064369
.apic_init_signal_blocked = svm_apic_init_signal_blocked,
43074370

43084371
.msr_filter_changed = svm_msr_filter_changed,
4309-
.complete_emulated_msr = kvm_complete_insn_gp,
4372+
.complete_emulated_msr = svm_complete_emulated_msr,
43104373
};
43114374

43124375
static struct kvm_x86_init_ops svm_init_ops __initdata = {

arch/x86/kvm/x86.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4006,7 +4006,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
40064006
{
40074007
int idx;
40084008

4009-
if (vcpu->preempted)
4009+
if (vcpu->preempted && !vcpu->arch.guest_state_protected)
40104010
vcpu->arch.preempted_in_kernel = !kvm_x86_ops.get_cpl(vcpu);
40114011

40124012
/*
@@ -8149,7 +8149,14 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
81498149
{
81508150
struct kvm_run *kvm_run = vcpu->run;
81518151

8152-
kvm_run->if_flag = (kvm_get_rflags(vcpu) & X86_EFLAGS_IF) != 0;
8152+
/*
8153+
* if_flag is obsolete and useless, so do not bother
8154+
* setting it for SEV-ES guests. Userspace can just
8155+
* use kvm_run->ready_for_interrupt_injection.
8156+
*/
8157+
kvm_run->if_flag = !vcpu->arch.guest_state_protected
8158+
&& (kvm_get_rflags(vcpu) & X86_EFLAGS_IF) != 0;
8159+
81538160
kvm_run->flags = is_smm(vcpu) ? KVM_RUN_X86_SMM : 0;
81548161
kvm_run->cr8 = kvm_get_cr8(vcpu);
81558162
kvm_run->apic_base = kvm_get_apic_base(vcpu);

0 commit comments

Comments
 (0)