Commit 3cfc3092f40bc37c57ba556cfd8de4218f2135ab
Committed by
Avi Kivity
1 parent
65ac726404
Exists in
master
and in
4 other branches
KVM: x86: Add KVM_GET/SET_VCPU_EVENTS
This new IOCTL exports all yet user-invisible states related to exceptions, interrupts, and NMIs. Together with appropriate user space changes, this fixes sporadic problems of vmsave/restore, live migration and system reset. [avi: future-proof abi by adding a flags field] Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> Signed-off-by: Avi Kivity <avi@redhat.com>
Showing 7 changed files with 214 additions and 0 deletions Side-by-side Diff
Documentation/kvm/api.txt
... | ... | @@ -653,6 +653,55 @@ |
653 | 653 | __u32 pad[9]; |
654 | 654 | }; |
655 | 655 | |
656 | +4.29 KVM_GET_VCPU_EVENTS | |
657 | + | |
658 | +Capability: KVM_CAP_VCPU_EVENTS | |
659 | +Architectures: x86 | |
660 | +Type: vm ioctl | |
661 | +Parameters: struct kvm_vcpu_event (out) | |
662 | +Returns: 0 on success, -1 on error | |
663 | + | |
664 | +Gets currently pending exceptions, interrupts, and NMIs as well as related | |
665 | +states of the vcpu. | |
666 | + | |
667 | +struct kvm_vcpu_events { | |
668 | + struct { | |
669 | + __u8 injected; | |
670 | + __u8 nr; | |
671 | + __u8 has_error_code; | |
672 | + __u8 pad; | |
673 | + __u32 error_code; | |
674 | + } exception; | |
675 | + struct { | |
676 | + __u8 injected; | |
677 | + __u8 nr; | |
678 | + __u8 soft; | |
679 | + __u8 pad; | |
680 | + } interrupt; | |
681 | + struct { | |
682 | + __u8 injected; | |
683 | + __u8 pending; | |
684 | + __u8 masked; | |
685 | + __u8 pad; | |
686 | + } nmi; | |
687 | + __u32 sipi_vector; | |
688 | + __u32 flags; /* must be zero */ | |
689 | +}; | |
690 | + | |
691 | +4.30 KVM_SET_VCPU_EVENTS | |
692 | + | |
693 | +Capability: KVM_CAP_VCPU_EVENTS | |
694 | +Architectures: x86 | |
695 | +Type: vm ioctl | |
696 | +Parameters: struct kvm_vcpu_event (in) | |
697 | +Returns: 0 on success, -1 on error | |
698 | + | |
699 | +Set pending exceptions, interrupts, and NMIs as well as related states of the | |
700 | +vcpu. | |
701 | + | |
702 | +See KVM_GET_VCPU_EVENTS for the data structure. | |
703 | + | |
704 | + | |
656 | 705 | 5. The kvm_run structure |
657 | 706 | |
658 | 707 | Application code obtains a pointer to the kvm_run structure by |
arch/x86/include/asm/kvm.h
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | #define __KVM_HAVE_MCE |
21 | 21 | #define __KVM_HAVE_PIT_STATE2 |
22 | 22 | #define __KVM_HAVE_XEN_HVM |
23 | +#define __KVM_HAVE_VCPU_EVENTS | |
23 | 24 | |
24 | 25 | /* Architectural interrupt line count. */ |
25 | 26 | #define KVM_NR_INTERRUPTS 256 |
... | ... | @@ -252,5 +253,32 @@ |
252 | 253 | __u8 pit_reinject; |
253 | 254 | __u8 reserved[31]; |
254 | 255 | }; |
256 | + | |
257 | +/* for KVM_GET/SET_VCPU_EVENTS */ | |
258 | +struct kvm_vcpu_events { | |
259 | + struct { | |
260 | + __u8 injected; | |
261 | + __u8 nr; | |
262 | + __u8 has_error_code; | |
263 | + __u8 pad; | |
264 | + __u32 error_code; | |
265 | + } exception; | |
266 | + struct { | |
267 | + __u8 injected; | |
268 | + __u8 nr; | |
269 | + __u8 soft; | |
270 | + __u8 pad; | |
271 | + } interrupt; | |
272 | + struct { | |
273 | + __u8 injected; | |
274 | + __u8 pending; | |
275 | + __u8 masked; | |
276 | + __u8 pad; | |
277 | + } nmi; | |
278 | + __u32 sipi_vector; | |
279 | + __u32 flags; | |
280 | + __u32 reserved[10]; | |
281 | +}; | |
282 | + | |
255 | 283 | #endif /* _ASM_X86_KVM_H */ |
arch/x86/include/asm/kvm_host.h
... | ... | @@ -523,6 +523,8 @@ |
523 | 523 | bool has_error_code, u32 error_code); |
524 | 524 | int (*interrupt_allowed)(struct kvm_vcpu *vcpu); |
525 | 525 | int (*nmi_allowed)(struct kvm_vcpu *vcpu); |
526 | + bool (*get_nmi_mask)(struct kvm_vcpu *vcpu); | |
527 | + void (*set_nmi_mask)(struct kvm_vcpu *vcpu, bool masked); | |
526 | 528 | void (*enable_nmi_window)(struct kvm_vcpu *vcpu); |
527 | 529 | void (*enable_irq_window)(struct kvm_vcpu *vcpu); |
528 | 530 | void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr); |
arch/x86/kvm/svm.c
... | ... | @@ -2499,6 +2499,26 @@ |
2499 | 2499 | !(svm->vcpu.arch.hflags & HF_NMI_MASK); |
2500 | 2500 | } |
2501 | 2501 | |
2502 | +static bool svm_get_nmi_mask(struct kvm_vcpu *vcpu) | |
2503 | +{ | |
2504 | + struct vcpu_svm *svm = to_svm(vcpu); | |
2505 | + | |
2506 | + return !!(svm->vcpu.arch.hflags & HF_NMI_MASK); | |
2507 | +} | |
2508 | + | |
2509 | +static void svm_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked) | |
2510 | +{ | |
2511 | + struct vcpu_svm *svm = to_svm(vcpu); | |
2512 | + | |
2513 | + if (masked) { | |
2514 | + svm->vcpu.arch.hflags |= HF_NMI_MASK; | |
2515 | + svm->vmcb->control.intercept |= (1UL << INTERCEPT_IRET); | |
2516 | + } else { | |
2517 | + svm->vcpu.arch.hflags &= ~HF_NMI_MASK; | |
2518 | + svm->vmcb->control.intercept &= ~(1UL << INTERCEPT_IRET); | |
2519 | + } | |
2520 | +} | |
2521 | + | |
2502 | 2522 | static int svm_interrupt_allowed(struct kvm_vcpu *vcpu) |
2503 | 2523 | { |
2504 | 2524 | struct vcpu_svm *svm = to_svm(vcpu); |
... | ... | @@ -2946,6 +2966,8 @@ |
2946 | 2966 | .queue_exception = svm_queue_exception, |
2947 | 2967 | .interrupt_allowed = svm_interrupt_allowed, |
2948 | 2968 | .nmi_allowed = svm_nmi_allowed, |
2969 | + .get_nmi_mask = svm_get_nmi_mask, | |
2970 | + .set_nmi_mask = svm_set_nmi_mask, | |
2949 | 2971 | .enable_nmi_window = enable_nmi_window, |
2950 | 2972 | .enable_irq_window = enable_irq_window, |
2951 | 2973 | .update_cr8_intercept = update_cr8_intercept, |
arch/x86/kvm/vmx.c
... | ... | @@ -2639,6 +2639,34 @@ |
2639 | 2639 | GUEST_INTR_STATE_NMI)); |
2640 | 2640 | } |
2641 | 2641 | |
2642 | +static bool vmx_get_nmi_mask(struct kvm_vcpu *vcpu) | |
2643 | +{ | |
2644 | + if (!cpu_has_virtual_nmis()) | |
2645 | + return to_vmx(vcpu)->soft_vnmi_blocked; | |
2646 | + else | |
2647 | + return !!(vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & | |
2648 | + GUEST_INTR_STATE_NMI); | |
2649 | +} | |
2650 | + | |
2651 | +static void vmx_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked) | |
2652 | +{ | |
2653 | + struct vcpu_vmx *vmx = to_vmx(vcpu); | |
2654 | + | |
2655 | + if (!cpu_has_virtual_nmis()) { | |
2656 | + if (vmx->soft_vnmi_blocked != masked) { | |
2657 | + vmx->soft_vnmi_blocked = masked; | |
2658 | + vmx->vnmi_blocked_time = 0; | |
2659 | + } | |
2660 | + } else { | |
2661 | + if (masked) | |
2662 | + vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, | |
2663 | + GUEST_INTR_STATE_NMI); | |
2664 | + else | |
2665 | + vmcs_clear_bits(GUEST_INTERRUPTIBILITY_INFO, | |
2666 | + GUEST_INTR_STATE_NMI); | |
2667 | + } | |
2668 | +} | |
2669 | + | |
2642 | 2670 | static int vmx_interrupt_allowed(struct kvm_vcpu *vcpu) |
2643 | 2671 | { |
2644 | 2672 | return (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && |
... | ... | @@ -3985,6 +4013,8 @@ |
3985 | 4013 | .queue_exception = vmx_queue_exception, |
3986 | 4014 | .interrupt_allowed = vmx_interrupt_allowed, |
3987 | 4015 | .nmi_allowed = vmx_nmi_allowed, |
4016 | + .get_nmi_mask = vmx_get_nmi_mask, | |
4017 | + .set_nmi_mask = vmx_set_nmi_mask, | |
3988 | 4018 | .enable_nmi_window = enable_nmi_window, |
3989 | 4019 | .enable_irq_window = enable_irq_window, |
3990 | 4020 | .update_cr8_intercept = update_cr8_intercept, |
arch/x86/kvm/x86.c
... | ... | @@ -1342,6 +1342,7 @@ |
1342 | 1342 | case KVM_CAP_SET_IDENTITY_MAP_ADDR: |
1343 | 1343 | case KVM_CAP_XEN_HVM: |
1344 | 1344 | case KVM_CAP_ADJUST_CLOCK: |
1345 | + case KVM_CAP_VCPU_EVENTS: | |
1345 | 1346 | r = 1; |
1346 | 1347 | break; |
1347 | 1348 | case KVM_CAP_COALESCED_MMIO: |
... | ... | @@ -1883,6 +1884,61 @@ |
1883 | 1884 | return 0; |
1884 | 1885 | } |
1885 | 1886 | |
1887 | +static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, | |
1888 | + struct kvm_vcpu_events *events) | |
1889 | +{ | |
1890 | + vcpu_load(vcpu); | |
1891 | + | |
1892 | + events->exception.injected = vcpu->arch.exception.pending; | |
1893 | + events->exception.nr = vcpu->arch.exception.nr; | |
1894 | + events->exception.has_error_code = vcpu->arch.exception.has_error_code; | |
1895 | + events->exception.error_code = vcpu->arch.exception.error_code; | |
1896 | + | |
1897 | + events->interrupt.injected = vcpu->arch.interrupt.pending; | |
1898 | + events->interrupt.nr = vcpu->arch.interrupt.nr; | |
1899 | + events->interrupt.soft = vcpu->arch.interrupt.soft; | |
1900 | + | |
1901 | + events->nmi.injected = vcpu->arch.nmi_injected; | |
1902 | + events->nmi.pending = vcpu->arch.nmi_pending; | |
1903 | + events->nmi.masked = kvm_x86_ops->get_nmi_mask(vcpu); | |
1904 | + | |
1905 | + events->sipi_vector = vcpu->arch.sipi_vector; | |
1906 | + | |
1907 | + events->flags = 0; | |
1908 | + | |
1909 | + vcpu_put(vcpu); | |
1910 | +} | |
1911 | + | |
1912 | +static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, | |
1913 | + struct kvm_vcpu_events *events) | |
1914 | +{ | |
1915 | + if (events->flags) | |
1916 | + return -EINVAL; | |
1917 | + | |
1918 | + vcpu_load(vcpu); | |
1919 | + | |
1920 | + vcpu->arch.exception.pending = events->exception.injected; | |
1921 | + vcpu->arch.exception.nr = events->exception.nr; | |
1922 | + vcpu->arch.exception.has_error_code = events->exception.has_error_code; | |
1923 | + vcpu->arch.exception.error_code = events->exception.error_code; | |
1924 | + | |
1925 | + vcpu->arch.interrupt.pending = events->interrupt.injected; | |
1926 | + vcpu->arch.interrupt.nr = events->interrupt.nr; | |
1927 | + vcpu->arch.interrupt.soft = events->interrupt.soft; | |
1928 | + if (vcpu->arch.interrupt.pending && irqchip_in_kernel(vcpu->kvm)) | |
1929 | + kvm_pic_clear_isr_ack(vcpu->kvm); | |
1930 | + | |
1931 | + vcpu->arch.nmi_injected = events->nmi.injected; | |
1932 | + vcpu->arch.nmi_pending = events->nmi.pending; | |
1933 | + kvm_x86_ops->set_nmi_mask(vcpu, events->nmi.masked); | |
1934 | + | |
1935 | + vcpu->arch.sipi_vector = events->sipi_vector; | |
1936 | + | |
1937 | + vcpu_put(vcpu); | |
1938 | + | |
1939 | + return 0; | |
1940 | +} | |
1941 | + | |
1886 | 1942 | long kvm_arch_vcpu_ioctl(struct file *filp, |
1887 | 1943 | unsigned int ioctl, unsigned long arg) |
1888 | 1944 | { |
... | ... | @@ -2038,6 +2094,27 @@ |
2038 | 2094 | if (copy_from_user(&mce, argp, sizeof mce)) |
2039 | 2095 | goto out; |
2040 | 2096 | r = kvm_vcpu_ioctl_x86_set_mce(vcpu, &mce); |
2097 | + break; | |
2098 | + } | |
2099 | + case KVM_GET_VCPU_EVENTS: { | |
2100 | + struct kvm_vcpu_events events; | |
2101 | + | |
2102 | + kvm_vcpu_ioctl_x86_get_vcpu_events(vcpu, &events); | |
2103 | + | |
2104 | + r = -EFAULT; | |
2105 | + if (copy_to_user(argp, &events, sizeof(struct kvm_vcpu_events))) | |
2106 | + break; | |
2107 | + r = 0; | |
2108 | + break; | |
2109 | + } | |
2110 | + case KVM_SET_VCPU_EVENTS: { | |
2111 | + struct kvm_vcpu_events events; | |
2112 | + | |
2113 | + r = -EFAULT; | |
2114 | + if (copy_from_user(&events, argp, sizeof(struct kvm_vcpu_events))) | |
2115 | + break; | |
2116 | + | |
2117 | + r = kvm_vcpu_ioctl_x86_set_vcpu_events(vcpu, &events); | |
2041 | 2118 | break; |
2042 | 2119 | } |
2043 | 2120 | default: |
include/linux/kvm.h
... | ... | @@ -489,6 +489,9 @@ |
489 | 489 | #endif |
490 | 490 | #define KVM_CAP_ADJUST_CLOCK 39 |
491 | 491 | #define KVM_CAP_INTERNAL_ERROR_DATA 40 |
492 | +#ifdef __KVM_HAVE_VCPU_EVENTS | |
493 | +#define KVM_CAP_VCPU_EVENTS 41 | |
494 | +#endif | |
492 | 495 | |
493 | 496 | #ifdef KVM_CAP_IRQ_ROUTING |
494 | 497 | |
... | ... | @@ -672,6 +675,9 @@ |
672 | 675 | /* IA64 stack access */ |
673 | 676 | #define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) |
674 | 677 | #define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) |
678 | +/* Available with KVM_CAP_VCPU_EVENTS */ | |
679 | +#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) | |
680 | +#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) | |
675 | 681 | |
676 | 682 | #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) |
677 | 683 |