KVM: local APIC TPR access reporting facility
Avi Kivity [Mon, 22 Oct 2007 14:50:39 +0000 (16:50 +0200)]
Add a facility to report on accesses to the local apic tpr even if the
local apic is emulated in the kernel.  This is basically a hack that
allows userspace to patch Windows which tends to bang on the tpr a lot.

Signed-off-by: Avi Kivity <avi@qumranet.com>

arch/x86/kvm/lapic.c
arch/x86/kvm/x86.c
include/asm-x86/kvm_host.h
include/linux/kvm.h
include/linux/kvm_host.h

index 4076331..50c3f3a 100644 (file)
@@ -551,6 +551,23 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)
        return tmcct;
 }
 
+static void __report_tpr_access(struct kvm_lapic *apic, bool write)
+{
+       struct kvm_vcpu *vcpu = apic->vcpu;
+       struct kvm_run *run = vcpu->run;
+
+       set_bit(KVM_REQ_REPORT_TPR_ACCESS, &vcpu->requests);
+       kvm_x86_ops->cache_regs(vcpu);
+       run->tpr_access.rip = vcpu->arch.rip;
+       run->tpr_access.is_write = write;
+}
+
+static inline void report_tpr_access(struct kvm_lapic *apic, bool write)
+{
+       if (apic->vcpu->arch.tpr_access_reporting)
+               __report_tpr_access(apic, write);
+}
+
 static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
 {
        u32 val = 0;
@@ -568,6 +585,9 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
                val = apic_get_tmcct(apic);
                break;
 
+       case APIC_TASKPRI:
+               report_tpr_access(apic, false);
+               /* fall thru */
        default:
                apic_update_ppr(apic);
                val = apic_get_reg(apic, offset);
@@ -677,6 +697,7 @@ static void apic_mmio_write(struct kvm_io_device *this,
                break;
 
        case APIC_TASKPRI:
+               report_tpr_access(apic, true);
                apic_set_tpr(apic, val & 0xff);
                break;
 
index 513258c..c2b8088 100644 (file)
@@ -684,6 +684,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_USER_MEMORY:
        case KVM_CAP_SET_TSS_ADDR:
        case KVM_CAP_EXT_CPUID:
+       case KVM_CAP_VAPIC:
                r = 1;
                break;
        default:
@@ -1055,6 +1056,15 @@ static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
        return 0;
 }
 
+static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu,
+                                          struct kvm_tpr_access_ctl *tac)
+{
+       if (tac->flags)
+               return -EINVAL;
+       vcpu->arch.tpr_access_reporting = !!tac->enabled;
+       return 0;
+}
+
 long kvm_arch_vcpu_ioctl(struct file *filp,
                         unsigned int ioctl, unsigned long arg)
 {
@@ -1148,6 +1158,21 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
        case KVM_SET_MSRS:
                r = msr_io(vcpu, argp, do_set_msr, 0);
                break;
+       case KVM_TPR_ACCESS_REPORTING: {
+               struct kvm_tpr_access_ctl tac;
+
+               r = -EFAULT;
+               if (copy_from_user(&tac, argp, sizeof tac))
+                       goto out;
+               r = vcpu_ioctl_tpr_access_reporting(vcpu, &tac);
+               if (r)
+                       goto out;
+               r = -EFAULT;
+               if (copy_to_user(argp, &tac, sizeof tac))
+                       goto out;
+               r = 0;
+               break;
+       };
        default:
                r = -EINVAL;
        }
index ced1beb..6e649af 100644 (file)
@@ -211,6 +211,7 @@ struct kvm_vcpu_arch {
        int mp_state;
        int sipi_vector;
        u64 ia32_misc_enable_msr;
+       bool tpr_access_reporting;
 
        struct kvm_mmu mmu;
 
index de9f28d..850f5ef 100644 (file)
@@ -72,6 +72,7 @@ struct kvm_irqchip {
 #define KVM_EXIT_FAIL_ENTRY       9
 #define KVM_EXIT_INTR             10
 #define KVM_EXIT_SET_TPR          11
+#define KVM_EXIT_TPR_ACCESS       12
 
 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
 struct kvm_run {
@@ -130,6 +131,12 @@ struct kvm_run {
                        __u32 longmode;
                        __u32 pad;
                } hypercall;
+               /* KVM_EXIT_TPR_ACCESS */
+               struct {
+                       __u64 rip;
+                       __u32 is_write;
+                       __u32 pad;
+               } tpr_access;
                /* Fix the size of the union. */
                char padding[256];
        };
@@ -202,6 +209,13 @@ struct kvm_signal_mask {
        __u8  sigset[0];
 };
 
+/* for KVM_TPR_ACCESS_REPORTING */
+struct kvm_tpr_access_ctl {
+       __u32 enabled;
+       __u32 flags;
+       __u32 reserved[8];
+};
+
 #define KVMIO 0xAE
 
 /*
@@ -229,6 +243,7 @@ struct kvm_signal_mask {
 #define KVM_CAP_USER_MEMORY 3
 #define KVM_CAP_SET_TSS_ADDR 4
 #define KVM_CAP_EXT_CPUID 5
+#define KVM_CAP_VAPIC 6
 
 /*
  * ioctls for VM fds
@@ -274,5 +289,7 @@ struct kvm_signal_mask {
 #define KVM_SET_LAPIC             _IOW(KVMIO,  0x8f, struct kvm_lapic_state)
 #define KVM_SET_CPUID2            _IOW(KVMIO,  0x90, struct kvm_cpuid2)
 #define KVM_GET_CPUID2            _IOWR(KVMIO, 0x91, struct kvm_cpuid2)
+/* Available with KVM_CAP_VAPIC */
+#define KVM_TPR_ACCESS_REPORTING  _IOWR(KVMIO,  0x92, struct kvm_tpr_access_ctl)
 
 #endif
index 953b50a..9ff5904 100644 (file)
@@ -35,7 +35,7 @@
  * vcpu->requests bit members
  */
 #define KVM_REQ_TLB_FLUSH          0
-
+#define KVM_REQ_REPORT_TPR_ACCESS  2
 
 struct kvm_vcpu;
 extern struct kmem_cache *kvm_vcpu_cache;