KVM: x86: API changes for SMM support
authorPaolo Bonzini <pbonzini@redhat.com>
Wed, 1 Apr 2015 13:06:40 +0000 (15:06 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 4 Jun 2015 14:01:11 +0000 (16:01 +0200)
This patch includes changes to the external API for SMM support.
Userspace can predicate the availability of the new fields and
ioctls on a new capability, KVM_CAP_X86_SMM, which is added at the end
of the patch series.

Reviewed-by: Radim Krčmář <rkrcmar@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virtual/kvm/api.txt
arch/x86/include/asm/kvm_host.h
arch/x86/include/uapi/asm/kvm.h
arch/x86/kvm/kvm_cache_regs.h
arch/x86/kvm/lapic.h
arch/x86/kvm/x86.c
include/uapi/linux/kvm.h

index 695544420ff265690917802e82a5bc2c57a4d578..2ddefd58b1aa9087851e65790332f529c9029c8c 100644 (file)
@@ -820,11 +820,21 @@ struct kvm_vcpu_events {
        } nmi;
        __u32 sipi_vector;
        __u32 flags;
+       struct {
+               __u8 smm;
+               __u8 pending;
+               __u8 smm_inside_nmi;
+               __u8 latched_init;
+       } smi;
 };
 
-KVM_VCPUEVENT_VALID_SHADOW may be set in the flags field to signal that
-interrupt.shadow contains a valid state. Otherwise, this field is undefined.
+Only two fields are defined in the flags field:
+
+- KVM_VCPUEVENT_VALID_SHADOW may be set in the flags field to signal that
+  interrupt.shadow contains a valid state.
 
+- KVM_VCPUEVENT_VALID_SMM may be set in the flags field to signal that
+  smi contains a valid state.
 
 4.32 KVM_SET_VCPU_EVENTS
 
@@ -841,17 +851,20 @@ vcpu.
 See KVM_GET_VCPU_EVENTS for the data structure.
 
 Fields that may be modified asynchronously by running VCPUs can be excluded
-from the update. These fields are nmi.pending and sipi_vector. Keep the
-corresponding bits in the flags field cleared to suppress overwriting the
-current in-kernel state. The bits are:
+from the update. These fields are nmi.pending, sipi_vector, smi.smm,
+smi.pending. Keep the corresponding bits in the flags field cleared to
+suppress overwriting the current in-kernel state. The bits are:
 
 KVM_VCPUEVENT_VALID_NMI_PENDING - transfer nmi.pending to the kernel
 KVM_VCPUEVENT_VALID_SIPI_VECTOR - transfer sipi_vector
+KVM_VCPUEVENT_VALID_SMM         - transfer the smi sub-struct.
 
 If KVM_CAP_INTR_SHADOW is available, KVM_VCPUEVENT_VALID_SHADOW can be set in
 the flags field to signal that interrupt.shadow contains a valid state and
 shall be written into the VCPU.
 
+KVM_VCPUEVENT_VALID_SMM can only be set if KVM_CAP_X86_SMM is available.
+
 
 4.33 KVM_GET_DEBUGREGS
 
@@ -2979,6 +2992,16 @@ len must be a multiple of sizeof(struct kvm_s390_irq). It must be > 0
 and it must not exceed (max_vcpus + 32) * sizeof(struct kvm_s390_irq),
 which is the maximum number of possibly pending cpu-local interrupts.
 
+4.90 KVM_SMI
+
+Capability: KVM_CAP_X86_SMM
+Architectures: x86
+Type: vcpu ioctl
+Parameters: none
+Returns: 0 on success, -1 on error
+
+Queues an SMI on the thread's vcpu.
+
 5. The kvm_run structure
 ------------------------
 
@@ -3014,7 +3037,12 @@ an interrupt can be injected now with KVM_INTERRUPT.
 The value of the current interrupt flag.  Only valid if in-kernel
 local APIC is not used.
 
-       __u8 padding2[2];
+       __u16 flags;
+
+More architecture-specific flags detailing state of the VCPU that may
+affect the device's behavior.  The only currently defined flag is
+KVM_RUN_X86_SMM, which is valid on x86 machines and is set if the
+VCPU is in system management mode.
 
        /* in (pre_kvm_run), out (post_kvm_run) */
        __u64 cr8;
index 4e299fcd0eb6ff5aa3ad16a470af75a35b1eea35..d52d7aea375fd7f83181989e55adeeffdf32d195 100644 (file)
@@ -471,6 +471,7 @@ struct kvm_vcpu_arch {
        atomic_t nmi_queued;  /* unprocessed asynchronous NMIs */
        unsigned nmi_pending; /* NMI queued after currently running handler */
        bool nmi_injected;    /* Trying to inject an NMI this entry */
+       bool smi_pending;    /* SMI queued after currently running handler */
 
        struct mtrr_state_type mtrr_state;
        u64 pat;
@@ -1115,6 +1116,8 @@ enum {
 #define HF_NMI_MASK            (1 << 3)
 #define HF_IRET_MASK           (1 << 4)
 #define HF_GUEST_MASK          (1 << 5) /* VCPU is in guest-mode */
+#define HF_SMM_MASK            (1 << 6)
+#define HF_SMM_INSIDE_NMI_MASK (1 << 7)
 
 /*
  * Hardware virtualization extension instructions may fault if a
index 2fec75e4b1e1e219a34af450d35309642674dbd9..a4ae82eb82aa8485146aee83376ba26fb9306afe 100644 (file)
@@ -106,6 +106,8 @@ struct kvm_ioapic_state {
 #define KVM_IRQCHIP_IOAPIC       2
 #define KVM_NR_IRQCHIPS          3
 
+#define KVM_RUN_X86_SMM                 (1 << 0)
+
 /* for KVM_GET_REGS and KVM_SET_REGS */
 struct kvm_regs {
        /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
@@ -281,6 +283,7 @@ struct kvm_reinject_control {
 #define KVM_VCPUEVENT_VALID_NMI_PENDING        0x00000001
 #define KVM_VCPUEVENT_VALID_SIPI_VECTOR        0x00000002
 #define KVM_VCPUEVENT_VALID_SHADOW     0x00000004
+#define KVM_VCPUEVENT_VALID_SMM                0x00000008
 
 /* Interrupt shadow states */
 #define KVM_X86_SHADOW_INT_MOV_SS      0x01
@@ -309,7 +312,13 @@ struct kvm_vcpu_events {
        } nmi;
        __u32 sipi_vector;
        __u32 flags;
-       __u32 reserved[10];
+       struct {
+               __u8 smm;
+               __u8 pending;
+               __u8 smm_inside_nmi;
+               __u8 latched_init;
+       } smi;
+       __u32 reserved[9];
 };
 
 /* for KVM_GET/SET_DEBUGREGS */
index 544076c4f44bc330ba28b74b07ad3966e990168f..e1e89ee4af750dc51f78cfbf8aa71e22d77b4cd1 100644 (file)
@@ -99,4 +99,9 @@ static inline bool is_guest_mode(struct kvm_vcpu *vcpu)
        return vcpu->arch.hflags & HF_GUEST_MASK;
 }
 
+static inline bool is_smm(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.hflags & HF_SMM_MASK;
+}
+
 #endif
index 9d8fcde52027123fe867559b7deff5ffd67bc080..f2f4e10ab7724640fb02ea3b6ea31ec08cfe95a4 100644 (file)
@@ -159,6 +159,11 @@ static inline bool kvm_lowest_prio_delivery(struct kvm_lapic_irq *irq)
                        irq->msi_redir_hint);
 }
 
+static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
+}
+
 bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
 
 void wait_lapic_expire(struct kvm_vcpu *vcpu);
index 7aec25f2f45cb10d1e06b538449e0f9b66c1a9d0..aa46ac1ff48b2e9d5f44c4d470069362f25ab848 100644 (file)
@@ -3101,6 +3101,11 @@ static int kvm_vcpu_ioctl_nmi(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int kvm_vcpu_ioctl_smi(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
 static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu,
                                           struct kvm_tpr_access_ctl *tac)
 {
@@ -3206,8 +3211,15 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 
        events->sipi_vector = 0; /* never valid when reporting to user space */
 
+       events->smi.smm = is_smm(vcpu);
+       events->smi.pending = vcpu->arch.smi_pending;
+       events->smi.smm_inside_nmi =
+               !!(vcpu->arch.hflags & HF_SMM_INSIDE_NMI_MASK);
+       events->smi.latched_init = kvm_lapic_latched_init(vcpu);
+
        events->flags = (KVM_VCPUEVENT_VALID_NMI_PENDING
-                        | KVM_VCPUEVENT_VALID_SHADOW);
+                        | KVM_VCPUEVENT_VALID_SHADOW
+                        | KVM_VCPUEVENT_VALID_SMM);
        memset(&events->reserved, 0, sizeof(events->reserved));
 }
 
@@ -3216,7 +3228,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 {
        if (events->flags & ~(KVM_VCPUEVENT_VALID_NMI_PENDING
                              | KVM_VCPUEVENT_VALID_SIPI_VECTOR
-                             | KVM_VCPUEVENT_VALID_SHADOW))
+                             | KVM_VCPUEVENT_VALID_SHADOW
+                             | KVM_VCPUEVENT_VALID_SMM))
                return -EINVAL;
 
        process_nmi(vcpu);
@@ -3241,6 +3254,24 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
            kvm_vcpu_has_lapic(vcpu))
                vcpu->arch.apic->sipi_vector = events->sipi_vector;
 
+       if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
+               if (events->smi.smm)
+                       vcpu->arch.hflags |= HF_SMM_MASK;
+               else
+                       vcpu->arch.hflags &= ~HF_SMM_MASK;
+               vcpu->arch.smi_pending = events->smi.pending;
+               if (events->smi.smm_inside_nmi)
+                       vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
+               else
+                       vcpu->arch.hflags &= ~HF_SMM_INSIDE_NMI_MASK;
+               if (kvm_vcpu_has_lapic(vcpu)) {
+                       if (events->smi.latched_init)
+                               set_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
+                       else
+                               clear_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
+               }
+       }
+
        kvm_make_request(KVM_REQ_EVENT, vcpu);
 
        return 0;
@@ -3500,6 +3531,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                r = kvm_vcpu_ioctl_nmi(vcpu);
                break;
        }
+       case KVM_SMI: {
+               r = kvm_vcpu_ioctl_smi(vcpu);
+               break;
+       }
        case KVM_SET_CPUID: {
                struct kvm_cpuid __user *cpuid_arg = argp;
                struct kvm_cpuid cpuid;
@@ -6182,6 +6217,7 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
        struct kvm_run *kvm_run = vcpu->run;
 
        kvm_run->if_flag = (kvm_get_rflags(vcpu) & X86_EFLAGS_IF) != 0;
+       kvm_run->flags = is_smm(vcpu) ? KVM_RUN_X86_SMM : 0;
        kvm_run->cr8 = kvm_get_cr8(vcpu);
        kvm_run->apic_base = kvm_get_apic_base(vcpu);
        if (irqchip_in_kernel(vcpu->kvm))
index 75bd9f7fd8465d8008f7648a79bf4270bc4c8bfe..eace8babd227decc42c63f418449ac7f903c5a1b 100644 (file)
@@ -202,7 +202,7 @@ struct kvm_run {
        __u32 exit_reason;
        __u8 ready_for_interrupt_injection;
        __u8 if_flag;
-       __u8 padding2[2];
+       __u16 flags;
 
        /* in (pre_kvm_run), out (post_kvm_run) */
        __u64 cr8;
@@ -815,6 +815,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_S390_IRQ_STATE 114
 #define KVM_CAP_PPC_HWRNG 115
 #define KVM_CAP_DISABLE_QUIRKS 116
+#define KVM_CAP_X86_SMM 117
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1200,6 +1201,8 @@ struct kvm_s390_ucas_mapping {
 /* Available with KVM_CAP_S390_IRQ_STATE */
 #define KVM_S390_SET_IRQ_STATE   _IOW(KVMIO, 0xb5, struct kvm_s390_irq_state)
 #define KVM_S390_GET_IRQ_STATE   _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state)
+/* Available with KVM_CAP_X86_SMM */
+#define KVM_SMI                   _IO(KVMIO,   0xb7)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3         (1 << 1)