Skip to content

Commit 59073aa

Browse files
jsmattsonjrbonzini
authored andcommitted
kvm: x86: Add exception payload fields to kvm_vcpu_events
The per-VM capability KVM_CAP_EXCEPTION_PAYLOAD (to be introduced in a later commit) adds the following fields to struct kvm_vcpu_events: exception_has_payload, exception_payload, and exception.pending. With this capability set, all of the details of vcpu->arch.exception, including the payload for a pending exception, are reported to userspace in response to KVM_GET_VCPU_EVENTS. With this capability clear, the original ABI is preserved, and the exception.injected field is set for either pending or injected exceptions. When userspace calls KVM_SET_VCPU_EVENTS with KVM_CAP_EXCEPTION_PAYLOAD clear, exception.injected is no longer translated to exception.pending. KVM_SET_VCPU_EVENTS can now only establish a pending exception when KVM_CAP_EXCEPTION_PAYLOAD is set. Reported-by: Jim Mattson <jmattson@google.com> Suggested-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Jim Mattson <jmattson@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent c851436 commit 59073aa

File tree

5 files changed

+77
-26
lines changed

5 files changed

+77
-26
lines changed

Documentation/virtual/kvm/api.txt

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ struct kvm_vcpu_events {
850850
__u8 injected;
851851
__u8 nr;
852852
__u8 has_error_code;
853-
__u8 pad;
853+
__u8 pending;
854854
__u32 error_code;
855855
} exception;
856856
struct {
@@ -873,16 +873,23 @@ struct kvm_vcpu_events {
873873
__u8 smm_inside_nmi;
874874
__u8 latched_init;
875875
} smi;
876-
__u32 reserved[9];
876+
__u8 reserved[27];
877+
__u8 exception_has_payload;
878+
__u64 exception_payload;
877879
};
878880

879-
Only two fields are defined in the flags field:
881+
The following bits are defined in the flags field:
880882

881-
- KVM_VCPUEVENT_VALID_SHADOW may be set in the flags field to signal that
883+
- KVM_VCPUEVENT_VALID_SHADOW may be set to signal that
882884
interrupt.shadow contains a valid state.
883885

884-
- KVM_VCPUEVENT_VALID_SMM may be set in the flags field to signal that
885-
smi contains a valid state.
886+
- KVM_VCPUEVENT_VALID_SMM may be set to signal that smi contains a
887+
valid state.
888+
889+
- KVM_VCPUEVENT_VALID_PAYLOAD may be set to signal that the
890+
exception_has_payload, exception_payload, and exception.pending
891+
fields contain a valid state. This bit will be set whenever
892+
KVM_CAP_EXCEPTION_PAYLOAD is enabled.
886893

887894
ARM/ARM64:
888895

@@ -962,6 +969,11 @@ shall be written into the VCPU.
962969

963970
KVM_VCPUEVENT_VALID_SMM can only be set if KVM_CAP_X86_SMM is available.
964971

972+
If KVM_CAP_EXCEPTION_PAYLOAD is enabled, KVM_VCPUEVENT_VALID_PAYLOAD
973+
can be set in the flags field to signal that the
974+
exception_has_payload, exception_payload, and exception.pending fields
975+
contain a valid state and shall be written into the VCPU.
976+
965977
ARM/ARM64:
966978

967979
Set the pending SError exception state for this VCPU. It is not possible to

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,7 @@ struct kvm_arch {
919919
bool x2apic_broadcast_quirk_disabled;
920920

921921
bool guest_can_read_msr_platform_info;
922+
bool exception_payload_enabled;
922923
};
923924

924925
struct kvm_vm_stat {

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ struct kvm_reinject_control {
288288
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
289289
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
290290
#define KVM_VCPUEVENT_VALID_SMM 0x00000008
291+
#define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010
291292

292293
/* Interrupt shadow states */
293294
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
@@ -299,7 +300,7 @@ struct kvm_vcpu_events {
299300
__u8 injected;
300301
__u8 nr;
301302
__u8 has_error_code;
302-
__u8 pad;
303+
__u8 pending;
303304
__u32 error_code;
304305
} exception;
305306
struct {
@@ -322,7 +323,9 @@ struct kvm_vcpu_events {
322323
__u8 smm_inside_nmi;
323324
__u8 latched_init;
324325
} smi;
325-
__u32 reserved[9];
326+
__u8 reserved[27];
327+
__u8 exception_has_payload;
328+
__u64 exception_payload;
326329
};
327330

328331
/* for KVM_GET/SET_DEBUGREGS */

arch/x86/kvm/x86.c

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,19 +3373,33 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
33733373
struct kvm_vcpu_events *events)
33743374
{
33753375
process_nmi(vcpu);
3376+
33763377
/*
3377-
* FIXME: pass injected and pending separately. This is only
3378-
* needed for nested virtualization, whose state cannot be
3379-
* migrated yet. For now we can combine them.
3378+
* The API doesn't provide the instruction length for software
3379+
* exceptions, so don't report them. As long as the guest RIP
3380+
* isn't advanced, we should expect to encounter the exception
3381+
* again.
33803382
*/
3381-
events->exception.injected =
3382-
(vcpu->arch.exception.pending ||
3383-
vcpu->arch.exception.injected) &&
3384-
!kvm_exception_is_soft(vcpu->arch.exception.nr);
3383+
if (kvm_exception_is_soft(vcpu->arch.exception.nr)) {
3384+
events->exception.injected = 0;
3385+
events->exception.pending = 0;
3386+
} else {
3387+
events->exception.injected = vcpu->arch.exception.injected;
3388+
events->exception.pending = vcpu->arch.exception.pending;
3389+
/*
3390+
* For ABI compatibility, deliberately conflate
3391+
* pending and injected exceptions when
3392+
* KVM_CAP_EXCEPTION_PAYLOAD isn't enabled.
3393+
*/
3394+
if (!vcpu->kvm->arch.exception_payload_enabled)
3395+
events->exception.injected |=
3396+
vcpu->arch.exception.pending;
3397+
}
33853398
events->exception.nr = vcpu->arch.exception.nr;
33863399
events->exception.has_error_code = vcpu->arch.exception.has_error_code;
3387-
events->exception.pad = 0;
33883400
events->exception.error_code = vcpu->arch.exception.error_code;
3401+
events->exception_has_payload = vcpu->arch.exception.has_payload;
3402+
events->exception_payload = vcpu->arch.exception.payload;
33893403

33903404
events->interrupt.injected =
33913405
vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft;
@@ -3409,6 +3423,9 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
34093423
events->flags = (KVM_VCPUEVENT_VALID_NMI_PENDING
34103424
| KVM_VCPUEVENT_VALID_SHADOW
34113425
| KVM_VCPUEVENT_VALID_SMM);
3426+
if (vcpu->kvm->arch.exception_payload_enabled)
3427+
events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
3428+
34123429
memset(&events->reserved, 0, sizeof(events->reserved));
34133430
}
34143431

@@ -3420,12 +3437,24 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
34203437
if (events->flags & ~(KVM_VCPUEVENT_VALID_NMI_PENDING
34213438
| KVM_VCPUEVENT_VALID_SIPI_VECTOR
34223439
| KVM_VCPUEVENT_VALID_SHADOW
3423-
| KVM_VCPUEVENT_VALID_SMM))
3440+
| KVM_VCPUEVENT_VALID_SMM
3441+
| KVM_VCPUEVENT_VALID_PAYLOAD))
34243442
return -EINVAL;
34253443

3426-
if (events->exception.injected &&
3427-
(events->exception.nr > 31 || events->exception.nr == NMI_VECTOR ||
3428-
is_guest_mode(vcpu)))
3444+
if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) {
3445+
if (!vcpu->kvm->arch.exception_payload_enabled)
3446+
return -EINVAL;
3447+
if (events->exception.pending)
3448+
events->exception.injected = 0;
3449+
else
3450+
events->exception_has_payload = 0;
3451+
} else {
3452+
events->exception.pending = 0;
3453+
events->exception_has_payload = 0;
3454+
}
3455+
3456+
if ((events->exception.injected || events->exception.pending) &&
3457+
(events->exception.nr > 31 || events->exception.nr == NMI_VECTOR))
34293458
return -EINVAL;
34303459

34313460
/* INITs are latched while in SMM */
@@ -3435,13 +3464,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
34353464
return -EINVAL;
34363465

34373466
process_nmi(vcpu);
3438-
vcpu->arch.exception.injected = false;
3439-
vcpu->arch.exception.pending = events->exception.injected;
3467+
vcpu->arch.exception.injected = events->exception.injected;
3468+
vcpu->arch.exception.pending = events->exception.pending;
34403469
vcpu->arch.exception.nr = events->exception.nr;
34413470
vcpu->arch.exception.has_error_code = events->exception.has_error_code;
34423471
vcpu->arch.exception.error_code = events->exception.error_code;
3443-
vcpu->arch.exception.has_payload = false;
3444-
vcpu->arch.exception.payload = 0;
3472+
vcpu->arch.exception.has_payload = events->exception_has_payload;
3473+
vcpu->arch.exception.payload = events->exception_payload;
34453474

34463475
vcpu->arch.interrupt.injected = events->interrupt.injected;
34473476
vcpu->arch.interrupt.nr = events->interrupt.nr;

tools/arch/x86/include/uapi/asm/kvm.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ struct kvm_reinject_control {
288288
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
289289
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
290290
#define KVM_VCPUEVENT_VALID_SMM 0x00000008
291+
#define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010
291292

292293
/* Interrupt shadow states */
293294
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
@@ -299,7 +300,10 @@ struct kvm_vcpu_events {
299300
__u8 injected;
300301
__u8 nr;
301302
__u8 has_error_code;
302-
__u8 pad;
303+
union {
304+
__u8 pad;
305+
__u8 pending;
306+
};
303307
__u32 error_code;
304308
} exception;
305309
struct {
@@ -322,7 +326,9 @@ struct kvm_vcpu_events {
322326
__u8 smm_inside_nmi;
323327
__u8 latched_init;
324328
} smi;
325-
__u32 reserved[9];
329+
__u8 reserved[27];
330+
__u8 exception_has_payload;
331+
__u64 exception_payload;
326332
};
327333

328334
/* for KVM_GET/SET_DEBUGREGS */

0 commit comments

Comments
 (0)