experimental code part 1 (KVM kernel)
-------------------------------------


This code introduces the actual emulation of the PM Timer register
plus some helper functions to create and configure the in-kernel
PM Timer. The emulation utilizes the 'kvm_io_bus' infrastructure.



diff -up ./arch/x86/include/asm/kvm_host.h.orig1 
./arch/x86/include/asm/kvm_host.h
--- ./arch/x86/include/asm/kvm_host.h.orig1     2010-12-05 09:35:17.000000000 
+0100
+++ ./arch/x86/include/asm/kvm_host.h   2010-12-10 12:14:29.282686691 +0100
@@ -459,6 +459,10 @@ struct kvm_arch {
        /* fields used by HYPER-V emulation */
        u64 hv_guest_os_id;
        u64 hv_hypercall;
+
+#ifdef KVM_CAP_PMTMR
+       struct kvm_pmtmr *vpmtmr;
+#endif
 };
 
 struct kvm_vm_stat {
diff -up ./arch/x86/kvm/i8254.c.orig1 ./arch/x86/kvm/i8254.c
--- ./arch/x86/kvm/i8254.c.orig1        2010-12-05 09:35:17.000000000 +0100
+++ ./arch/x86/kvm/i8254.c      2010-12-10 12:09:36.877729064 +0100
@@ -51,7 +51,7 @@
 #define RW_STATE_WORD1 4
 
 /* Compute with 96 bit intermediate result: (a*b)/c */
-static u64 muldiv64(u64 a, u32 b, u32 c)
+u64 muldiv64(u64 a, u32 b, u32 c)
 {
        union {
                u64 ll;
diff -up ./arch/x86/kvm/Makefile.orig1 ./arch/x86/kvm/Makefile
--- ./arch/x86/kvm/Makefile.orig1       2010-12-05 09:35:17.000000000 +0100
+++ ./arch/x86/kvm/Makefile     2010-12-10 12:07:14.379811121 +0100
@@ -12,7 +12,7 @@ kvm-$(CONFIG_IOMMU_API)       += $(addprefix .
 kvm-$(CONFIG_KVM_ASYNC_PF)     += $(addprefix ../../../virt/kvm/, async_pf.o)
 
 kvm-y                  += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
-                          i8254.o timer.o
+                          i8254.o timer.o pmtmr.o
 kvm-intel-y            += vmx.o
 kvm-amd-y              += svm.o
 
diff -up ./arch/x86/kvm/pmtmr.c.orig1 ./arch/x86/kvm/pmtmr.c
--- ./arch/x86/kvm/pmtmr.c.orig1        2010-12-10 12:05:39.878691941 +0100
+++ ./arch/x86/kvm/pmtmr.c      2010-12-10 12:06:00.987738524 +0100
@@ -0,0 +1,151 @@
+/*
+ * in-kernel ACPI PM Timer emulation
+ *
+ * Note: 'timer carry interrupt' is not implemented
+ */
+
+#include <linux/kvm_host.h>
+
+#ifdef KVM_CAP_PMTMR
+
+#include "pmtmr.h"
+
+static int emulate_acpi_reg_pmtmr(struct kvm_pmtmr *pmtmr, void *data, int len)
+{
+       s64 tmp;
+       u32 running_count;
+
+       if (len != 4)
+               return -EOPNOTSUPP;
+
+       tmp = ktime_to_ns(ktime_get()) + pmtmr->clock_offset;
+       running_count = (u32)muldiv64(tmp, KVM_ACPI_PMTMR_FREQ, NSEC_PER_SEC);
+       *(u32 *)data = running_count & KVM_ACPI_PMTMR_MASK;
+
+#ifdef KVM_ACPI_PMTMR_STATS
+       pmtmr->read_count++;
+#endif
+       return 0;
+}
+
+/*
+ * This function returns true for I/O ports in the range from 'PM base'
+ * to 'PM Timer' (this range contains the PM1 Status and the PM1 Enable
+ * registers).
+ */
+static inline int pmtmr_in_range(struct kvm_pmtmr *pmtmr, gpa_t ioport)
+{
+       return ((ioport >= pmtmr->pm_io_base) &&
+               (ioport <= pmtmr->pm_io_base + KVM_ACPI_REG_PMTMR));
+}
+
+static inline struct kvm_pmtmr *dev_to_pmtmr(struct kvm_io_device *dev)
+{
+        return container_of(dev, struct kvm_pmtmr, dev);
+}
+
+static int pmtmr_ioport_read(struct kvm_io_device *this,
+                            gpa_t ioport, int len, void *data)
+{
+       struct kvm_pmtmr *pmtmr = dev_to_pmtmr(this);
+
+       if (!pmtmr_in_range(pmtmr, ioport))
+               return -EOPNOTSUPP;
+
+       switch (ioport - pmtmr->pm_io_base) {
+       case KVM_ACPI_REG_PMTMR:
+               /* emulate PM Timer read if in-kernel emulation is enabled */
+               if (pmtmr->state == KVM_PMTMR_STATE_ENABLED)
+                       return(emulate_acpi_reg_pmtmr(pmtmr, data, len));
+
+               /* fall thru */
+       default:
+               /* let qemu userspace handle everything else */
+               return -EOPNOTSUPP;
+       }
+}
+
+static int pmtmr_ioport_write(struct kvm_io_device *this,
+                             gpa_t ioport, int len, const void *data)
+{
+       struct kvm_pmtmr *pmtmr = dev_to_pmtmr(this);
+
+       if (!pmtmr_in_range(pmtmr, ioport))
+               return -EOPNOTSUPP;
+
+       switch (ioport - pmtmr->pm_io_base) {
+       case KVM_ACPI_REG_PMTMR:
+               /* ignore PM Timer write */
+               return 0;
+       case KVM_ACPI_REG_PMEN:
+               if (len == 2) {
+                       u16 val = *(u16 *)data;
+                       /*
+                        * Fall back to qemu userspace PM Timer emulation if
+                        * the VM sets the 'timer carry interrupt enable' bit
+                        * in the PM1 Enable register.
+                        */
+                       if (val & KVM_ACPI_PMTMR_TMR_EN)
+                               /* disable in-kernel PM Timer emulation */
+                               pmtmr->state = KVM_PMTMR_STATE_DISABLED;
+               }
+               /* fall thru */
+       default:
+               /* let qemu userspace handle everything else */
+               return -EOPNOTSUPP;
+       }
+}
+
+static void pmtmr_destroy(struct kvm_io_device *this)
+{
+       struct kvm_pmtmr *pmtmr = dev_to_pmtmr(this);
+       kfree(pmtmr);
+}
+
+static const struct kvm_io_device_ops pmtmr_dev_ops = {
+       .read       = pmtmr_ioport_read,
+       .write      = pmtmr_ioport_write,
+       .destructor = pmtmr_destroy
+};
+
+int kvm_create_pmtmr(struct kvm *kvm)
+{
+       struct kvm_pmtmr *pmtmr;
+       int ret;
+
+       if (kvm->arch.vpmtmr)
+               return -EEXIST;
+
+       pmtmr = kzalloc(sizeof(struct kvm_pmtmr), GFP_KERNEL);
+       if (!pmtmr)
+               return -ENOMEM;
+
+       kvm_iodevice_init(&pmtmr->dev, &pmtmr_dev_ops);
+       ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, &pmtmr->dev);
+       if (ret < 0)
+               goto fail;
+
+       pmtmr->state = KVM_PMTMR_STATE_CREATED;
+       kvm->arch.vpmtmr = pmtmr;
+       return 0;
+fail:
+       kfree(pmtmr);
+       return ret;
+}
+
+int kvm_configure_pmtmr(struct kvm *kvm, struct kvm_pmtmr_config *conf)
+{
+       struct kvm_pmtmr *pmtmr = kvm->arch.vpmtmr;
+
+       if (!pmtmr)
+               return -ENXIO;
+       if (pmtmr->state == KVM_PMTMR_STATE_DISABLED)
+               return -EINVAL;
+
+       pmtmr->pm_io_base = conf->pm_io_base;
+       pmtmr->clock_offset = conf->clock_offset;
+       pmtmr->state = KVM_PMTMR_STATE_ENABLED;
+       return 0;
+}
+
+#endif
diff -up ./arch/x86/kvm/pmtmr.h.orig1 ./arch/x86/kvm/pmtmr.h
--- ./arch/x86/kvm/pmtmr.h.orig1        2010-12-10 12:05:43.964878651 +0100
+++ ./arch/x86/kvm/pmtmr.h      2010-12-10 12:06:00.987738524 +0100
@@ -0,0 +1,35 @@
+#ifndef __PMTMR_H
+#define __PMTMR_H
+
+#include "iodev.h"
+
+#define KVM_ACPI_PMTMR_STATS
+
+#define KVM_ACPI_PMTMR_FREQ    3579545
+#define KVM_ACPI_PMTMR_MASK    0xffffff
+
+#define KVM_ACPI_PMTMR_TMR_EN  1       /* timer carry interrupt enable bit */
+                                       /* in PM1 Enable register           */
+
+#define KVM_ACPI_REG_PMEN      2       /* offset of PM1 enable register    */
+#define KVM_ACPI_REG_PMTMR     8       /* offset of PM Timer register      */
+
+struct kvm_pmtmr {
+       struct kvm_io_device dev;
+       gpa_t pm_io_base;
+       s64 clock_offset;
+#define KVM_PMTMR_STATE_CREATED         -1
+#define KVM_PMTMR_STATE_ENABLED          0
+#define KVM_PMTMR_STATE_DISABLED         1
+       u64 state;
+#ifdef KVM_ACPI_PMTMR_STATS
+       u64 read_count;
+#endif
+};
+
+int kvm_create_pmtmr(struct kvm *kvm);
+int kvm_configure_pmtmr(struct kvm *kvm, struct kvm_pmtmr_config *conf);
+
+extern u64 muldiv64(u64 a, u32 b, u32 c);      /* defined in i8254.c */
+
+#endif
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to