[Xen-changelog] [xen-unstable] [HVM] Intercept ACPI pm-timer registers

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[Xen-changelog] [xen-unstable] [HVM] Intercept ACPI pm-timer registers

Xen patchbot-unstable
# HG changeset patch
# User Tim Deegan <[hidden email]>
# Date 1174900383 0
# Node ID 3afefd64e39271dbd0d9ea26f79f41a05b89fdd8
# Parent  dccb274295ae4df8812a645ea4809e82ae54b2e0
[HVM] Intercept ACPI pm-timer registers
Bring the PM1a_STS and PM1a_EN registers into Xen and use them to deliver
SCI to the guest before it sees the MSB of the pm-timer change.
Also correct some of the semantics of the registers.
Signed-off-by: Tim Deegan <[hidden email]>
---
 tools/ioemu/hw/piix4acpi.c    |  243 ++----------------------------------------
 xen/arch/x86/hvm/hvm.c        |    3
 xen/arch/x86/hvm/pmtimer.c    |  220 ++++++++++++++++++++++++++++++++++----
 xen/include/asm-x86/hvm/io.h  |    2
 xen/include/asm-x86/hvm/vpt.h |    4
 xen/include/public/hvm/save.h |    4
 6 files changed, 220 insertions(+), 256 deletions(-)

diff -r dccb274295ae -r 3afefd64e392 tools/ioemu/hw/piix4acpi.c
--- a/tools/ioemu/hw/piix4acpi.c Mon Mar 26 01:13:16 2007 +0100
+++ b/tools/ioemu/hw/piix4acpi.c Mon Mar 26 09:13:03 2007 +0000
@@ -52,126 +52,16 @@ typedef struct AcpiDeviceState AcpiDevic
 typedef struct AcpiDeviceState AcpiDeviceState;
 AcpiDeviceState *acpi_device_table;
 
-typedef struct PM1Event_BLK {
-    uint16_t pm1_status; /* pm1a_EVT_BLK */
-    uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */
-}PM1Event_BLK;
-
 typedef struct PCIAcpiState {
     PCIDevice dev;
-    uint16_t irq;
-    uint16_t pm1_status; /* pm1a_EVT_BLK */
-    uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */
     uint16_t pm1_control; /* pm1a_ECNT_BLK */
-    uint32_t pm1_timer; /* pmtmr_BLK */
-    uint64_t old_vmck_ticks; /* using vm_clock counter */
 } PCIAcpiState;
-
-static PCIAcpiState *acpi_state;
-
-static void acpi_reset(PCIAcpiState *s)
-{
-    uint8_t *pci_conf;
-    pci_conf = s->dev.config;
-
-    pci_conf[0x42] = 0x00;
-    pci_conf[0x43] = 0x00;
-    s->irq = 9;
-    s->pm1_status = 0;
-    s->pm1_enable = 0x00;    /* TMROF_EN should cleared */
-    s->pm1_control = SCI_EN; /* SCI_EN */
-    s->pm1_timer = 0;
-    s->old_vmck_ticks = qemu_get_clock(vm_clock);
-}
-
-/*byte access  */
-static void acpiPm1Status_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    if ((val&TMROF_STS)==TMROF_STS)
-        s->pm1_status = s->pm1_status&!TMROF_STS;
-
-    if ((val&GBL_STS)==GBL_STS)
-        s->pm1_status = s->pm1_status&!GBL_STS;
-
-/*     printf("acpiPm1Status_writeb \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */
-}
-
-static uint32_t acpiPm1Status_readb(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = s->pm1_status;
-/*         printf("acpiPm1Status_readb \n addr %x val:%x\n", addr, val); */
-
-   return val;
-}
-
-static void acpiPm1StatusP1_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_status = (val<<8)||(s->pm1_status);
-/*     printf("acpiPm1StatusP1_writeb \n addr %x val:%x\n", addr, val); */
-}
-
-static uint32_t acpiPm1StatusP1_readb(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = (s->pm1_status)>>8;
-    printf("acpiPm1StatusP1_readb \n addr %x val:%x\n", addr, val);
-
-    return val;
-}
-
-static void acpiPm1Enable_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_enable = val;
-/*   printf("acpiPm1Enable_writeb \n addr %x val:%x\n", addr, val); */
-}
-
-static uint32_t acpiPm1Enable_readb(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = (s->pm1_enable)||0x1;
-/*  printf("acpiPm1Enable_readb \n addr %x val:%x\n", addr, val); */
-
-    return val;
-}
-
-static void acpiPm1EnableP1_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_enable = (val<<8)||(s->pm1_enable);
-/*    printf("acpiPm1EnableP1_writeb \n addr %x val:%x\n", addr, val); */
-
-}
-
-static uint32_t acpiPm1EnableP1_readb(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = (s->pm1_enable)>>8;
-/*  printf("acpiPm1EnableP1_readb \n addr %x val:%x\n", addr, val); */
-
-    return val;
-}
 
 static void acpiPm1Control_writeb(void *opaque, uint32_t addr, uint32_t val)
 {
     PCIAcpiState *s = opaque;
 
-    s->pm1_control = val;
+    s->pm1_control = (s->pm1_control & 0xff00) | (val & 0xff);
 /*  printf("acpiPm1Control_writeb \n addr %x val:%x\n", addr, val); */
 
 }
@@ -181,7 +71,8 @@ static uint32_t acpiPm1Control_readb(voi
     PCIAcpiState *s = opaque;
     uint32_t val;
 
-    val = s->pm1_control;
+    /* Mask out the write-only bits */
+    val = s->pm1_control & ~(GBL_RLS|SLP_EN) & 0xff;
 /*    printf("acpiPm1Control_readb \n addr %x val:%x\n", addr, val); */
 
     return val;
@@ -191,14 +82,13 @@ static void acpiPm1ControlP1_writeb(void
 {
     PCIAcpiState *s = opaque;
 
-    s->pm1_control = (val<<8)||(s->pm1_control);
+    s->pm1_control = (s->pm1_control & 0xff) | (val << 8);
 /*    printf("acpiPm1ControlP1_writeb \n addr %x val:%x\n", addr, val); */
 
     // Check for power off request
-
+    val <<= 8;
     if (((val & SLP_EN) != 0) &&
         ((val & SLP_TYP_MASK) == SLP_VAL)) {
-        s->pm1_timer=0x0; //clear ACPI timer
         qemu_system_shutdown_request();
     }
 }
@@ -208,7 +98,8 @@ static uint32_t acpiPm1ControlP1_readb(v
     PCIAcpiState *s = opaque;
     uint32_t val;
 
-    val = (s->pm1_control)>>8;
+    /* Mask out the write-only bits */
+    val = (s->pm1_control & ~(GBL_RLS|SLP_EN)) >> 8;
 /*    printf("acpiPm1ControlP1_readb \n addr %x val:%x\n", addr, val); */
 
     return val;
@@ -216,50 +107,6 @@ static uint32_t acpiPm1ControlP1_readb(v
 
 
 /* word access   */
-
-static void acpiPm1Status_writew(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    if ((val&TMROF_STS)==TMROF_STS)
-        s->pm1_status = s->pm1_status&!TMROF_STS;
-
-    if ((val&GBL_STS)==GBL_STS)
-        s->pm1_status = s->pm1_status&!GBL_STS;
-
-/*    printf("acpiPm1Status_writew \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */
-}
-
-static uint32_t acpiPm1Status_readw(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = s->pm1_status;
-/*    printf("acpiPm1Status_readw \n addr %x val:%x\n", addr, val); */
-
-    return val;
-}
-
-static void acpiPm1Enable_writew(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_enable = val;
-/*    printf("acpiPm1Enable_writew \n addr %x val:%x\n", addr, val); */
-
-}
-
-static uint32_t acpiPm1Enable_readw(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = s->pm1_enable;
-/*    printf("acpiPm1Enable_readw \n addr %x val:%x\n", addr, val); */
-
-   return val;
-}
 
 static void acpiPm1Control_writew(void *opaque, uint32_t addr, uint32_t val)
 {
@@ -282,56 +129,13 @@ static uint32_t acpiPm1Control_readw(voi
     PCIAcpiState *s = opaque;
     uint32_t val;
 
-    val = s->pm1_control;
+    /* Mask out the write-only bits */
+    val = s->pm1_control & ~(GBL_RLS|SLP_EN);
 /*    printf("acpiPm1Control_readw \n addr %x val:%x\n", addr, val);  */
 
     return val;
 }
 
-/* dword access */
-
-static void acpiPm1Event_writel(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_status = val;
-    s->pm1_enable = val>>16;
-/*     printf("acpiPm1Event_writel \n addr %x val:%x \n", addr, val); */
-
-}
-
-static uint32_t acpiPm1Event_readl(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    uint32_t val;
-
-    val = s->pm1_status|(s->pm1_enable<<16);
-/*    printf("acpiPm1Event_readl \n addr %x val:%x\n", addr, val);    */
-
-    return val;
-}
-
-static void acpiPm1Timer_writel(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCIAcpiState *s = opaque;
-
-    s->pm1_timer = val;
-    s->old_vmck_ticks = qemu_get_clock(vm_clock) +
-        muldiv64(val, FREQUENCE_PMTIMER, ticks_per_sec);
-}
-
-static uint32_t acpiPm1Timer_readl(void *opaque, uint32_t addr)
-{
-    PCIAcpiState *s = opaque;
-    int64_t current_vmck_ticks = qemu_get_clock(vm_clock);
-    int64_t vmck_ticks_delta = current_vmck_ticks - s->old_vmck_ticks;
-
-    if (s->old_vmck_ticks)
-        s->pm1_timer += muldiv64(vmck_ticks_delta, FREQUENCE_PMTIMER,
-                                 ticks_per_sec);
-    s->old_vmck_ticks = current_vmck_ticks;
-    return s->pm1_timer;
-}
 
 static void acpi_map(PCIDevice *pci_dev, int region_num,
                     uint32_t addr, uint32_t size, int type)
@@ -341,37 +145,14 @@ static void acpi_map(PCIDevice *pci_dev,
     printf("register acpi io\n");
 
     /* Byte access */
-    register_ioport_write(addr, 1, 1, acpiPm1Status_writeb, d);
-    register_ioport_read(addr, 1, 1, acpiPm1Status_readb, d);
-    register_ioport_write(addr+1, 1, 1, acpiPm1StatusP1_writeb, d);
-    register_ioport_read(addr+1, 1, 1, acpiPm1StatusP1_readb, d);
-
-    register_ioport_write(addr + 2, 1, 1, acpiPm1Enable_writeb, d);
-    register_ioport_read(addr + 2, 1, 1, acpiPm1Enable_readb, d);
-    register_ioport_write(addr + 2 +1, 1, 1, acpiPm1EnableP1_writeb, d);
-    register_ioport_read(addr + 2 +1, 1, 1, acpiPm1EnableP1_readb, d);
-
     register_ioport_write(addr + 4, 1, 1, acpiPm1Control_writeb, d);
     register_ioport_read(addr + 4, 1, 1, acpiPm1Control_readb, d);
     register_ioport_write(addr + 4 + 1, 1, 1, acpiPm1ControlP1_writeb, d);
     register_ioport_read(addr + 4 +1, 1, 1, acpiPm1ControlP1_readb, d);
 
     /* Word access */
-    register_ioport_write(addr, 2, 2, acpiPm1Status_writew, d);
-    register_ioport_read(addr, 2, 2, acpiPm1Status_readw, d);
-
-    register_ioport_write(addr + 2, 2, 2, acpiPm1Enable_writew, d);
-    register_ioport_read(addr + 2, 2, 2, acpiPm1Enable_readw, d);
-
     register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d);
     register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d);
-
-    /* DWord access */
-    register_ioport_write(addr, 4, 4, acpiPm1Event_writel, d);
-    register_ioport_read(addr, 4, 4, acpiPm1Event_readl, d);
-
-    register_ioport_write(addr + 8, 4, 4, acpiPm1Timer_writel, d);
-    register_ioport_read(addr + 8, 4, 4, acpiPm1Timer_readl, d);
 }
 
 /* PIIX4 acpi pci configuration space, func 2 */
@@ -385,7 +166,6 @@ void pci_piix4_acpi_init(PCIBus *bus, in
         bus, "PIIX4 ACPI", sizeof(PCIAcpiState),
         devfn, NULL, NULL);
 
-    acpi_state = d;
     pci_conf = d->dev.config;
     pci_conf[0x00] = 0x86;  /* Intel */
     pci_conf[0x01] = 0x80;
@@ -408,6 +188,9 @@ void pci_piix4_acpi_init(PCIBus *bus, in
      */
     pci_conf[0x40] = 0x41; /* Special device-specific BAR at 0x40 */
     pci_conf[0x41] = 0x1f;
+    pci_conf[0x42] = 0x00;
+    pci_conf[0x43] = 0x00;
+    d->pm1_control = SCI_EN;
+
     acpi_map(d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO);
-    acpi_reset(d);
 }
diff -r dccb274295ae -r 3afefd64e392 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Mon Mar 26 01:13:16 2007 +0100
+++ b/xen/arch/x86/hvm/hvm.c Mon Mar 26 09:13:03 2007 +0000
@@ -218,6 +218,7 @@ void hvm_domain_destroy(struct domain *d
 {
     pit_deinit(d);
     rtc_deinit(d);
+    pmtimer_deinit(d);
     hpet_deinit(d);
 
     if ( d->arch.hvm_domain.shared_page_va )
@@ -303,7 +304,7 @@ int hvm_vcpu_initialise(struct vcpu *v)
 
     pit_init(v, cpu_khz);
     rtc_init(v, RTC_PORT(0));
-    pmtimer_init(v, ACPI_PM_TMR_BLK_ADDRESS);
+    pmtimer_init(v);
     hpet_init(v);
 
     /* Init guest TSC to start from zero. */
diff -r dccb274295ae -r 3afefd64e392 xen/arch/x86/hvm/pmtimer.c
--- a/xen/arch/x86/hvm/pmtimer.c Mon Mar 26 01:13:16 2007 +0100
+++ b/xen/arch/x86/hvm/pmtimer.c Mon Mar 26 09:13:03 2007 +0000
@@ -1,12 +1,172 @@
+/*
+ * hvm/pmtimer.c: emulation of the ACPI PM timer
+ *
+ * Copyright (c) 2007, XenSource inc.
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
 #include <asm/hvm/vpt.h>
 #include <asm/hvm/io.h>
 #include <asm/hvm/support.h>
 
+/* Slightly more readable port I/O addresses for the registers we intercept */
+#define PM1a_STS_ADDR (ACPI_PM1A_EVT_BLK_ADDRESS)
+#define PM1a_EN_ADDR  (ACPI_PM1A_EVT_BLK_ADDRESS + 2)
+#define TMR_VAL_ADDR  (ACPI_PM_TMR_BLK_ADDRESS)
+
+/* The interesting bit of the PM1a_STS register */
+#define TMR_STS    (1 << 0)
+#define PWRBTN_STS (1 << 5)
+#define GBL_STS    (1 << 8)
+
+/* The same in PM1a_EN */
+#define TMR_EN     (1 << 0)
+#define PWRBTN_EN  (1 << 5)
+#define GBL_EN     (1 << 8)
+
+/* Mask of bits in PM1a_STS that can generate an SCI.  Although the ACPI
+ * spec lists other bits, the PIIX4, which we are emulating, only
+ * supports these three.  For now, we only use TMR_STS; in future we
+ * will let qemu set the other bits */
+#define SCI_MASK (TMR_STS|PWRBTN_STS|GBL_STS)
+
+/* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
+#define SCI_IRQ 9
+
+/* We provide a 32-bit counter (must match the TMR_VAL_EXT bit in the FADT) */
+#define TMR_VAL_MASK  (0xffffffff)
+#define TMR_VAL_MSB   (0x80000000)
+
+
+/* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
+static void pmt_update_sci(PMTState *s)
+{
+    if ( s->pm.pm1a_en & s->pm.pm1a_sts & SCI_MASK )
+        hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ);
+    else
+        hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
+}
+
+/* Set the correct value in the timer, accounting for time elapsed
+ * since the last time we did that. */
+static void pmt_update_time(PMTState *s)
+{
+    uint64_t curr_gtime;
+    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
+    
+    /* Update the timer */
+    curr_gtime = hvm_get_guest_time(s->vcpu);
+    s->pm.tmr_val += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
+    s->pm.tmr_val &= TMR_VAL_MASK;
+    s->last_gtime = curr_gtime;
+    
+    /* If the counter's MSB has changed, set the status bit */
+    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
+    {
+        s->pm.pm1a_sts |= TMR_STS;
+        pmt_update_sci(s);
+    }
+}
+
+/* This function should be called soon after each time the MSB of the
+ * pmtimer register rolls over, to make sure we update the status
+ * registers and SCI at least once per rollover */
+static void pmt_timer_callback(void *opaque)
+{
+    PMTState *s = opaque;
+    uint32_t pmt_cycles_until_flip;
+    uint64_t time_until_flip;
+    
+    /* Recalculate the timer and make sure we get an SCI if we need one */
+    pmt_update_time(s);
+    
+    /* How close are we to the next MSB flip? */
+    pmt_cycles_until_flip = TMR_VAL_MSB - (s->pm.tmr_val & (TMR_VAL_MSB - 1));
+    
+    /* Overall time between MSB flips */
+    time_until_flip = (1000000000ULL << 31) / FREQUENCE_PMTIMER;
+    
+    /* Reduced appropriately */
+    time_until_flip = (time_until_flip * pmt_cycles_until_flip) / (1ULL<<31);
+    
+    /* Wake up again near the next bit-flip */
+    set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
+}
+
+
+/* Handle port I/O to the PM1a_STS and PM1a_EN registers */
+static int handle_evt_io(ioreq_t *p)
+{
+    struct vcpu *v = current;
+    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
+    uint32_t addr, data, byte;
+    int i;
+
+    if ( p->dir == 0 ) /* Write */
+    {
+        /* Handle this I/O one byte at a time */
+        for ( i = p->size, addr = p->addr, data = p->data;
+              i > 0;
+              i--, addr++, data >>= 8 )
+        {
+            byte = data & 0xff;
+            switch(addr)
+            {
+                /* PM1a_STS register bits are write-to-clear */
+            case PM1a_STS_ADDR:
+                s->pm.pm1a_sts &= ~byte;
+                break;
+            case PM1a_STS_ADDR + 1:
+                s->pm.pm1a_sts &= ~(byte << 8);
+                break;
+                
+            case PM1a_EN_ADDR:
+                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff00) | byte;
+                break;
+            case PM1a_EN_ADDR + 1:
+                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff) | (byte << 8);
+                break;
+                
+            default:
+                gdprintk(XENLOG_WARNING,
+                         "Bad ACPI PM register write: %"PRIu64
+                         " bytes (%#"PRIx64") at %"PRIx64"\n",
+                         p->size, p->data, p->addr);
+            }
+        }
+        /* Fix up the SCI state to match the new register state */
+        pmt_update_sci(s);
+    }
+    else /* Read */
+    {
+        data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);
+        data >>= 8 * (p->addr - PM1a_STS_ADDR);
+        if ( p->size == 1 ) data &= 0xff;
+        else if ( p->size == 2 ) data &= 0xffff;
+        p->data = data;
+    }
+    return 1;
+}
+
+
+/* Handle port I/O to the TMR_VAL register */
 static int handle_pmt_io(ioreq_t *p)
 {
     struct vcpu *v = current;
     PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
-    uint64_t curr_gtime;
 
     if (p->size != 4 ||
         p->data_is_ptr ||
@@ -19,12 +179,8 @@ static int handle_pmt_io(ioreq_t *p)
         /* PM_TMR_BLK is read-only */
         return 1;
     } else if (p->dir == 1) { /* read */
-        /* Set the correct value in the timer, accounting for time
-         * elapsed since the last time we did that. */
-        curr_gtime = hvm_get_guest_time(s->vcpu);
-        s->pm.timer += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
-        p->data = s->pm.timer;
-        s->last_gtime = curr_gtime;
+        pmt_update_time(s);
+        p->data = s->pm.tmr_val;
         return 1;
     }
     return 0;
@@ -33,6 +189,7 @@ static int pmtimer_save(struct domain *d
 static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
 {
     PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
+    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
     uint32_t x;
 
     /* Update the counter to the guest's current time.  We always save
@@ -40,7 +197,12 @@ static int pmtimer_save(struct domain *d
      * last_gtime, but just in case, make sure we only go forwards */
     x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
     if ( x < 1UL<<31 )
-        s->pm.timer += x;
+        s->pm.tmr_val += x;
+    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
+        s->pm.pm1a_sts |= TMR_STS;
+    /* No point in setting the SCI here because we'll already have saved the
+     * IRQ and *PIC state; we'll fix it up when we restore the domain */
+
     return hvm_save_entry(PMTIMER, 0, h, &s->pm);
 }
 
@@ -48,12 +210,15 @@ static int pmtimer_load(struct domain *d
 {
     PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
 
-    /* Reload the counter */
+    /* Reload the registers */
     if ( hvm_load_entry(PMTIMER, h, &s->pm) )
         return -EINVAL;
 
     /* Calculate future counter values from now. */
     s->last_gtime = hvm_get_guest_time(s->vcpu);
+
+    /* Set the SCI state from the registers */
+    pmt_update_sci(s);
     
     return 0;
 }
@@ -62,19 +227,30 @@ HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtim
                           1, HVMSR_PER_DOM);
 
 
-void pmtimer_init(struct vcpu *v, int base)
-{
-    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
-
-    s->pm.timer = 0;
+void pmtimer_init(struct vcpu *v)
+{
+    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
+
+    s->pm.tmr_val = 0;
+    s->pm.pm1a_sts = 0;
+    s->pm.pm1a_en = 0;
+
     s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
     s->vcpu = v;
 
-    /* Not implemented: we should set TMR_STS (bit 0 of PM1a_STS) every
-     * time the timer's top bit flips, and generate an SCI if TMR_EN
-     * (bit 0 of PM1a_EN) is set.  For now, those registers are in
-     * qemu-dm, and we just calculate the timer's value on demand. */  
-
-    register_portio_handler(v->domain, base, 4, handle_pmt_io);
-}
-
+    /* Intercept port I/O (need two handlers because PM1a_CNT is between
+     * PM1a_EN and TMR_VAL and is handled by qemu) */
+    register_portio_handler(v->domain, TMR_VAL_ADDR, 4, handle_pmt_io);
+    register_portio_handler(v->domain, PM1a_STS_ADDR, 4, handle_evt_io);
+
+    /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
+    init_timer(&s->timer, pmt_timer_callback, s, v->processor);
+    pmt_timer_callback(s);
+}
+
+
+void pmtimer_deinit(struct domain *d)
+{
+    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
+    kill_timer(&s->timer);
+}
diff -r dccb274295ae -r 3afefd64e392 xen/include/asm-x86/hvm/io.h
--- a/xen/include/asm-x86/hvm/io.h Mon Mar 26 01:13:16 2007 +0100
+++ b/xen/include/asm-x86/hvm/io.h Mon Mar 26 09:13:03 2007 +0000
@@ -80,7 +80,7 @@ struct hvm_io_op {
     struct cpu_user_regs    io_context; /* current context */
 };
 
-#define MAX_IO_HANDLER              8
+#define MAX_IO_HANDLER              9
 
 #define HVM_PORTIO                  0
 #define HVM_MMIO                    1
diff -r dccb274295ae -r 3afefd64e392 xen/include/asm-x86/hvm/vpt.h
--- a/xen/include/asm-x86/hvm/vpt.h Mon Mar 26 01:13:16 2007 +0100
+++ b/xen/include/asm-x86/hvm/vpt.h Mon Mar 26 09:13:03 2007 +0000
@@ -101,6 +101,7 @@ typedef struct PMTState {
     struct vcpu *vcpu;          /* Keeps sync with this vcpu's guest-time */
     uint64_t last_gtime;        /* Last (guest) time we updated the timer */
     uint64_t scale;             /* Multiplier to get from tsc to timer ticks */
+    struct timer timer;         /* To make sure we send SCIs */
 } PMTState;
 
 struct pl_time {    /* platform time */
@@ -132,7 +133,8 @@ void rtc_migrate_timers(struct vcpu *v);
 void rtc_migrate_timers(struct vcpu *v);
 void rtc_deinit(struct domain *d);
 int is_rtc_periodic_irq(void *opaque);
-void pmtimer_init(struct vcpu *v, int base);
+void pmtimer_init(struct vcpu *v);
+void pmtimer_deinit(struct domain *d);
 
 void hpet_migrate_timers(struct vcpu *v);
 void hpet_init(struct vcpu *v);
diff -r dccb274295ae -r 3afefd64e392 xen/include/public/hvm/save.h
--- a/xen/include/public/hvm/save.h Mon Mar 26 01:13:16 2007 +0100
+++ b/xen/include/public/hvm/save.h Mon Mar 26 09:13:03 2007 +0000
@@ -392,7 +392,9 @@ DECLARE_HVM_SAVE_TYPE(HPET, 12, struct h
  */
 
 struct hvm_hw_pmtimer {
-    uint32_t timer;
+    uint32_t tmr_val;   /* PM_TMR_BLK.TMR_VAL: 24bit free-running counter */
+    uint16_t pm1a_sts;  /* PM1a_EVT_BLK.PM1a_STS: status register */
+    uint16_t pm1a_en;   /* PM1a_EVT_BLK.PM1a_EN: enable register */
 };
 
 DECLARE_HVM_SAVE_TYPE(PMTIMER, 13, struct hvm_hw_pmtimer);

_______________________________________________
Xen-changelog mailing list
[hidden email]
http://lists.xensource.com/xen-changelog