Skip to content

Commit d7507a9

Browse files
committed
KVM: SVM: Treat exit_code as an unsigned 64-bit value through all of KVM
Fix KVM's long-standing buggy handling of SVM's exit_code as a 32-bit value. Per the APM and Xen commit d1bd157fbc ("Big merge the HVM full-virtualisation abstractions.") (which is arguably more trustworthy than KVM), offset 0x70 is a single 64-bit value: 070h 63:0 EXITCODE Track exit_code as a single u64 to prevent reintroducing bugs where KVM neglects to correctly set bits 63:32. Fixes: 6aa8b73 ("[PATCH] kvm: userspace interface") Cc: Jim Mattson <jmattson@google.com> Cc: Yosry Ahmed <yosry.ahmed@linux.dev> Reviewed-by: Yosry Ahmed <yosry.ahmed@linux.dev> Link: https://patch.msgid.link/20251230211347.4099600-6-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 405fce6 commit d7507a9

11 files changed

Lines changed: 42 additions & 69 deletions

File tree

arch/x86/include/asm/svm.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
137137
u32 int_vector;
138138
u32 int_state;
139139
u8 reserved_3[4];
140-
u32 exit_code;
141-
u32 exit_code_hi;
140+
u64 exit_code;
142141
u64 exit_info_1;
143142
u64 exit_info_2;
144143
u32 exit_int_info;

arch/x86/include/uapi/asm/svm.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,38 +103,38 @@
103103
#define SVM_EXIT_VMGEXIT 0x403
104104

105105
/* SEV-ES software-defined VMGEXIT events */
106-
#define SVM_VMGEXIT_MMIO_READ 0x80000001
107-
#define SVM_VMGEXIT_MMIO_WRITE 0x80000002
108-
#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003
109-
#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004
110-
#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005
106+
#define SVM_VMGEXIT_MMIO_READ 0x80000001ull
107+
#define SVM_VMGEXIT_MMIO_WRITE 0x80000002ull
108+
#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003ull
109+
#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004ull
110+
#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005ull
111111
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0
112112
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1
113-
#define SVM_VMGEXIT_PSC 0x80000010
114-
#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011
115-
#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012
116-
#define SVM_VMGEXIT_AP_CREATION 0x80000013
113+
#define SVM_VMGEXIT_PSC 0x80000010ull
114+
#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011ull
115+
#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012ull
116+
#define SVM_VMGEXIT_AP_CREATION 0x80000013ull
117117
#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0
118118
#define SVM_VMGEXIT_AP_CREATE 1
119119
#define SVM_VMGEXIT_AP_DESTROY 2
120-
#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018
121-
#define SVM_VMGEXIT_SAVIC 0x8000001a
120+
#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018ull
121+
#define SVM_VMGEXIT_SAVIC 0x8000001aull
122122
#define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0
123123
#define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA 1
124124
#define SVM_VMGEXIT_SAVIC_SELF_GPA ~0ULL
125-
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
126-
#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
125+
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffdull
126+
#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffeull
127127
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
128128
/* SW_EXITINFO1[3:0] */ \
129129
(((((u64)reason_set) & 0xf)) | \
130130
/* SW_EXITINFO1[11:4] */ \
131131
((((u64)reason_code) & 0xff) << 4))
132-
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
132+
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffffull
133133

134134
/* Exit code reserved for hypervisor/software use */
135-
#define SVM_EXIT_SW 0xf0000000
135+
#define SVM_EXIT_SW 0xf0000000ull
136136

137-
#define SVM_EXIT_ERR -1
137+
#define SVM_EXIT_ERR -1ull
138138

139139
#define SVM_EXIT_REASONS \
140140
{ SVM_EXIT_READ_CR0, "read_cr0" }, \

arch/x86/kvm/svm/hyperv.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ void svm_hv_inject_synthetic_vmexit_post_tlb_flush(struct kvm_vcpu *vcpu)
1111
struct vcpu_svm *svm = to_svm(vcpu);
1212

1313
svm->vmcb->control.exit_code = HV_SVM_EXITCODE_ENL;
14-
svm->vmcb->control.exit_code_hi = 0;
1514
svm->vmcb->control.exit_info_1 = HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH;
1615
svm->vmcb->control.exit_info_2 = 0;
1716
nested_svm_vmexit(svm);

arch/x86/kvm/svm/nested.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
4545
* correctly fill in the high bits of exit_info_1.
4646
*/
4747
vmcb->control.exit_code = SVM_EXIT_NPF;
48-
vmcb->control.exit_code_hi = 0;
4948
vmcb->control.exit_info_1 = (1ULL << 32);
5049
vmcb->control.exit_info_2 = fault->address;
5150
}
@@ -441,7 +440,6 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu,
441440
to->int_vector = from->int_vector;
442441
to->int_state = from->int_state;
443442
to->exit_code = from->exit_code;
444-
to->exit_code_hi = from->exit_code_hi;
445443
to->exit_info_1 = from->exit_info_1;
446444
to->exit_info_2 = from->exit_info_2;
447445
to->exit_int_info = from->exit_int_info;
@@ -747,8 +745,8 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm,
747745
enter_guest_mode(vcpu);
748746

749747
/*
750-
* Filled at exit: exit_code, exit_code_hi, exit_info_1, exit_info_2,
751-
* exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes.
748+
* Filled at exit: exit_code, exit_info_1, exit_info_2, exit_int_info,
749+
* exit_int_info_err, next_rip, insn_len, insn_bytes.
752750
*/
753751

754752
if (guest_cpu_cap_has(vcpu, X86_FEATURE_VGIF) &&
@@ -1018,7 +1016,6 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu)
10181016
if (!nested_vmcb_check_save(vcpu) ||
10191017
!nested_vmcb_check_controls(vcpu)) {
10201018
vmcb12->control.exit_code = SVM_EXIT_ERR;
1021-
vmcb12->control.exit_code_hi = -1u;
10221019
vmcb12->control.exit_info_1 = 0;
10231020
vmcb12->control.exit_info_2 = 0;
10241021
goto out;
@@ -1051,7 +1048,6 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu)
10511048
svm->soft_int_injected = false;
10521049

10531050
svm->vmcb->control.exit_code = SVM_EXIT_ERR;
1054-
svm->vmcb->control.exit_code_hi = -1u;
10551051
svm->vmcb->control.exit_info_1 = 0;
10561052
svm->vmcb->control.exit_info_2 = 0;
10571053

@@ -1163,7 +1159,6 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
11631159

11641160
vmcb12->control.int_state = vmcb02->control.int_state;
11651161
vmcb12->control.exit_code = vmcb02->control.exit_code;
1166-
vmcb12->control.exit_code_hi = vmcb02->control.exit_code_hi;
11671162
vmcb12->control.exit_info_1 = vmcb02->control.exit_info_1;
11681163
vmcb12->control.exit_info_2 = vmcb02->control.exit_info_2;
11691164

@@ -1460,7 +1455,7 @@ static int nested_svm_intercept_ioio(struct vcpu_svm *svm)
14601455

14611456
static int nested_svm_intercept(struct vcpu_svm *svm)
14621457
{
1463-
u32 exit_code = svm->vmcb->control.exit_code;
1458+
u64 exit_code = svm->vmcb->control.exit_code;
14641459
int vmexit = NESTED_EXIT_HOST;
14651460

14661461
if (svm_is_vmrun_failure(exit_code))
@@ -1532,7 +1527,6 @@ static void nested_svm_inject_exception_vmexit(struct kvm_vcpu *vcpu)
15321527
struct vmcb *vmcb = svm->vmcb;
15331528

15341529
vmcb->control.exit_code = SVM_EXIT_EXCP_BASE + ex->vector;
1535-
vmcb->control.exit_code_hi = 0;
15361530

15371531
if (ex->has_error_code)
15381532
vmcb->control.exit_info_1 = ex->error_code;
@@ -1708,7 +1702,6 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst,
17081702
dst->int_vector = from->int_vector;
17091703
dst->int_state = from->int_state;
17101704
dst->exit_code = from->exit_code;
1711-
dst->exit_code_hi = from->exit_code_hi;
17121705
dst->exit_info_1 = from->exit_info_1;
17131706
dst->exit_info_2 = from->exit_info_2;
17141707
dst->exit_int_info = from->exit_int_info;

arch/x86/kvm/svm/sev.c

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,11 +3270,6 @@ void sev_free_vcpu(struct kvm_vcpu *vcpu)
32703270
kvfree(svm->sev_es.ghcb_sa);
32713271
}
32723272

3273-
static u64 kvm_get_cached_sw_exit_code(struct vmcb_control_area *control)
3274-
{
3275-
return (((u64)control->exit_code_hi) << 32) | control->exit_code;
3276-
}
3277-
32783273
static void dump_ghcb(struct vcpu_svm *svm)
32793274
{
32803275
struct vmcb_control_area *control = &svm->vmcb->control;
@@ -3296,7 +3291,7 @@ static void dump_ghcb(struct vcpu_svm *svm)
32963291
*/
32973292
pr_err("GHCB (GPA=%016llx) snapshot:\n", svm->vmcb->control.ghcb_gpa);
32983293
pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_code",
3299-
kvm_get_cached_sw_exit_code(control), kvm_ghcb_sw_exit_code_is_valid(svm));
3294+
control->exit_code, kvm_ghcb_sw_exit_code_is_valid(svm));
33003295
pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_1",
33013296
control->exit_info_1, kvm_ghcb_sw_exit_info_1_is_valid(svm));
33023297
pr_err("%-20s%016llx is_valid: %u\n", "sw_exit_info_2",
@@ -3330,7 +3325,6 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
33303325
struct vmcb_control_area *control = &svm->vmcb->control;
33313326
struct kvm_vcpu *vcpu = &svm->vcpu;
33323327
struct ghcb *ghcb = svm->sev_es.ghcb;
3333-
u64 exit_code;
33343328

33353329
/*
33363330
* The GHCB protocol so far allows for the following data
@@ -3364,9 +3358,7 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
33643358
__kvm_emulate_msr_write(vcpu, MSR_IA32_XSS, kvm_ghcb_get_xss(svm));
33653359

33663360
/* Copy the GHCB exit information into the VMCB fields */
3367-
exit_code = kvm_ghcb_get_sw_exit_code(svm);
3368-
control->exit_code = lower_32_bits(exit_code);
3369-
control->exit_code_hi = upper_32_bits(exit_code);
3361+
control->exit_code = kvm_ghcb_get_sw_exit_code(svm);
33703362
control->exit_info_1 = kvm_ghcb_get_sw_exit_info_1(svm);
33713363
control->exit_info_2 = kvm_ghcb_get_sw_exit_info_2(svm);
33723364
svm->sev_es.sw_scratch = kvm_ghcb_get_sw_scratch_if_valid(svm);
@@ -3379,15 +3371,8 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
33793371
{
33803372
struct vmcb_control_area *control = &svm->vmcb->control;
33813373
struct kvm_vcpu *vcpu = &svm->vcpu;
3382-
u64 exit_code;
33833374
u64 reason;
33843375

3385-
/*
3386-
* Retrieve the exit code now even though it may not be marked valid
3387-
* as it could help with debugging.
3388-
*/
3389-
exit_code = kvm_get_cached_sw_exit_code(control);
3390-
33913376
/* Only GHCB Usage code 0 is supported */
33923377
if (svm->sev_es.ghcb->ghcb_usage) {
33933378
reason = GHCB_ERR_INVALID_USAGE;
@@ -3401,7 +3386,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
34013386
!kvm_ghcb_sw_exit_info_2_is_valid(svm))
34023387
goto vmgexit_err;
34033388

3404-
switch (exit_code) {
3389+
switch (control->exit_code) {
34053390
case SVM_EXIT_READ_DR7:
34063391
break;
34073392
case SVM_EXIT_WRITE_DR7:
@@ -3502,15 +3487,19 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
35023487
return 0;
35033488

35043489
vmgexit_err:
3490+
/*
3491+
* Print the exit code even though it may not be marked valid as it
3492+
* could help with debugging.
3493+
*/
35053494
if (reason == GHCB_ERR_INVALID_USAGE) {
35063495
vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n",
35073496
svm->sev_es.ghcb->ghcb_usage);
35083497
} else if (reason == GHCB_ERR_INVALID_EVENT) {
35093498
vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n",
3510-
exit_code);
3499+
control->exit_code);
35113500
} else {
35123501
vcpu_unimpl(vcpu, "vmgexit: exit code %#llx input is not valid\n",
3513-
exit_code);
3502+
control->exit_code);
35143503
dump_ghcb(svm);
35153504
}
35163505

@@ -4349,7 +4338,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
43494338
{
43504339
struct vcpu_svm *svm = to_svm(vcpu);
43514340
struct vmcb_control_area *control = &svm->vmcb->control;
4352-
u64 ghcb_gpa, exit_code;
4341+
u64 ghcb_gpa;
43534342
int ret;
43544343

43554344
/* Validate the GHCB */
@@ -4391,8 +4380,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
43914380

43924381
svm_vmgexit_success(svm, 0);
43934382

4394-
exit_code = kvm_get_cached_sw_exit_code(control);
4395-
switch (exit_code) {
4383+
switch (control->exit_code) {
43964384
case SVM_VMGEXIT_MMIO_READ:
43974385
ret = setup_vmgexit_scratch(svm, true, control->exit_info_2);
43984386
if (ret)
@@ -4484,7 +4472,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
44844472
ret = -EINVAL;
44854473
break;
44864474
default:
4487-
ret = svm_invoke_exit_handler(vcpu, exit_code);
4475+
ret = svm_invoke_exit_handler(vcpu, control->exit_code);
44884476
}
44894477

44904478
return ret;

arch/x86/kvm/svm/svm.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2466,7 +2466,6 @@ static bool check_selective_cr0_intercepted(struct kvm_vcpu *vcpu,
24662466

24672467
if (cr0 ^ val) {
24682468
svm->vmcb->control.exit_code = SVM_EXIT_CR0_SEL_WRITE;
2469-
svm->vmcb->control.exit_code_hi = 0;
24702469
ret = (nested_svm_exit_handled(svm) == NESTED_EXIT_DONE);
24712470
}
24722471

@@ -3299,7 +3298,7 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
32993298
pr_err("%-20s%08x\n", "int_ctl:", control->int_ctl);
33003299
pr_err("%-20s%08x\n", "int_vector:", control->int_vector);
33013300
pr_err("%-20s%08x\n", "int_state:", control->int_state);
3302-
pr_err("%-20s%08x\n", "exit_code:", control->exit_code);
3301+
pr_err("%-20s%016llx\n", "exit_code:", control->exit_code);
33033302
pr_err("%-20s%016llx\n", "exit_info1:", control->exit_info_1);
33043303
pr_err("%-20s%016llx\n", "exit_info2:", control->exit_info_2);
33053304
pr_err("%-20s%08x\n", "exit_int_info:", control->exit_int_info);
@@ -3549,7 +3548,6 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
35493548
{
35503549
struct vcpu_svm *svm = to_svm(vcpu);
35513550
struct kvm_run *kvm_run = vcpu->run;
3552-
u32 exit_code = svm->vmcb->control.exit_code;
35533551

35543552
/* SEV-ES guests must use the CR write traps to track CR registers. */
35553553
if (!sev_es_guest(vcpu->kvm)) {
@@ -3585,7 +3583,7 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
35853583
if (exit_fastpath != EXIT_FASTPATH_NONE)
35863584
return 1;
35873585

3588-
return svm_invoke_exit_handler(vcpu, exit_code);
3586+
return svm_invoke_exit_handler(vcpu, svm->vmcb->control.exit_code);
35893587
}
35903588

35913589
static int pre_svm_run(struct kvm_vcpu *vcpu)
@@ -4670,7 +4668,6 @@ static int svm_check_intercept(struct kvm_vcpu *vcpu,
46704668
if (static_cpu_has(X86_FEATURE_NRIPS))
46714669
vmcb->control.next_rip = info->next_rip;
46724670
vmcb->control.exit_code = icpt_info.exit_code;
4673-
vmcb->control.exit_code_hi = 0;
46744671
vmexit = nested_svm_exit_handled(svm);
46754672

46764673
ret = (vmexit == NESTED_EXIT_DONE) ? X86EMUL_INTERCEPTED

arch/x86/kvm/svm/svm.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ struct vmcb_ctrl_area_cached {
160160
u32 int_ctl;
161161
u32 int_vector;
162162
u32 int_state;
163-
u32 exit_code;
164-
u32 exit_code_hi;
163+
u64 exit_code;
165164
u64 exit_info_1;
166165
u64 exit_info_2;
167166
u32 exit_int_info;
@@ -787,7 +786,6 @@ int nested_svm_vmexit(struct vcpu_svm *svm);
787786
static inline int nested_svm_simple_vmexit(struct vcpu_svm *svm, u32 exit_code)
788787
{
789788
svm->vmcb->control.exit_code = exit_code;
790-
svm->vmcb->control.exit_code_hi = 0;
791789
svm->vmcb->control.exit_info_1 = 0;
792790
svm->vmcb->control.exit_info_2 = 0;
793791
return nested_svm_vmexit(svm);

arch/x86/kvm/trace.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,10 @@ TRACE_EVENT(kvm_apic,
383383
#define kvm_print_exit_reason(exit_reason, isa) \
384384
(isa == KVM_ISA_VMX) ? \
385385
__print_symbolic(exit_reason & 0xffff, VMX_EXIT_REASONS) : \
386-
__print_symbolic(exit_reason, SVM_EXIT_REASONS), \
386+
__print_symbolic_u64(exit_reason, SVM_EXIT_REASONS), \
387387
(isa == KVM_ISA_VMX && exit_reason & ~0xffff) ? " " : "", \
388388
(isa == KVM_ISA_VMX) ? \
389-
__print_flags(exit_reason & ~0xffff, " ", VMX_EXIT_REASON_FLAGS) : ""
389+
__print_flags_u64(exit_reason & ~0xffff, " ", VMX_EXIT_REASON_FLAGS) : ""
390390

391391
#define TRACE_EVENT_KVM_EXIT(name) \
392392
TRACE_EVENT(name, \
@@ -781,7 +781,7 @@ TRACE_EVENT_KVM_EXIT(kvm_nested_vmexit);
781781
* Tracepoint for #VMEXIT reinjected to the guest
782782
*/
783783
TRACE_EVENT(kvm_nested_vmexit_inject,
784-
TP_PROTO(__u32 exit_code,
784+
TP_PROTO(__u64 exit_code,
785785
__u64 exit_info1, __u64 exit_info2,
786786
__u32 exit_int_info, __u32 exit_int_info_err, __u32 isa),
787787
TP_ARGS(exit_code, exit_info1, exit_info2,

include/hyperv/hvgdk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ struct hv_vmcb_enlightenments {
281281
#define HV_VMCB_NESTED_ENLIGHTENMENTS 31
282282

283283
/* Synthetic VM-Exit */
284-
#define HV_SVM_EXITCODE_ENL 0xf0000000
284+
#define HV_SVM_EXITCODE_ENL 0xf0000000ull
285285
#define HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH (1)
286286

287287
/* VM_PARTITION_ASSIST_PAGE */

tools/testing/selftests/kvm/include/x86/svm.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
9292
u32 int_vector;
9393
u32 int_state;
9494
u8 reserved_3[4];
95-
u32 exit_code;
96-
u32 exit_code_hi;
95+
u64 exit_code;
9796
u64 exit_info_1;
9897
u64 exit_info_2;
9998
u32 exit_int_info;

0 commit comments

Comments
 (0)