aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/ioemu/hw/piix4acpi.c243
-rw-r--r--xen/arch/x86/hvm/hvm.c3
-rw-r--r--xen/arch/x86/hvm/pmtimer.c208
-rw-r--r--xen/include/asm-x86/hvm/io.h2
-rw-r--r--xen/include/asm-x86/hvm/vpt.h4
-rw-r--r--xen/include/public/hvm/save.h4
6 files changed, 214 insertions, 250 deletions
diff --git a/tools/ioemu/hw/piix4acpi.c b/tools/ioemu/hw/piix4acpi.c
index aabd5ca7e9..c4a8e38cd0 100644
--- a/tools/ioemu/hw/piix4acpi.c
+++ b/tools/ioemu/hw/piix4acpi.c
@@ -52,126 +52,16 @@
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(void *opaque, uint32_t addr)
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 *opaque, uint32_t addr, uint32_t val)
{
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(void *opaque, uint32_t addr)
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;
@@ -217,50 +108,6 @@ static uint32_t acpiPm1ControlP1_readb(void *opaque, uint32_t addr)
/* 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)
{
PCIAcpiState *s = opaque;
@@ -282,56 +129,13 @@ static uint32_t acpiPm1Control_readw(void *opaque, uint32_t addr)
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, int region_num,
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, int devfn)
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, int devfn)
*/
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 --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 17d0cf25ee..2c783bfe7a 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -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 --git a/xen/arch/x86/hvm/pmtimer.c b/xen/arch/x86/hvm/pmtimer.c
index a396d27df4..321f664124 100644
--- a/xen/arch/x86/hvm/pmtimer.c
+++ b/xen/arch/x86/hvm/pmtimer.c
@@ -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 handle_pmt_io(ioreq_t *p)
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, hvm_domain_context_t *h)
* 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, hvm_domain_context_t *h)
{
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, pmtimer_save, pmtimer_load,
1, HVMSR_PER_DOM);
-void pmtimer_init(struct vcpu *v, int base)
+void pmtimer_init(struct vcpu *v)
{
PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
- s->pm.timer = 0;
+ 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. */
+ /* 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);
- register_portio_handler(v->domain, base, 4, handle_pmt_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 --git a/xen/include/asm-x86/hvm/io.h b/xen/include/asm-x86/hvm/io.h
index 13c5dee97d..092b037516 100644
--- a/xen/include/asm-x86/hvm/io.h
+++ b/xen/include/asm-x86/hvm/io.h
@@ -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 --git a/xen/include/asm-x86/hvm/vpt.h b/xen/include/asm-x86/hvm/vpt.h
index bda54adfc3..96cea8883c 100644
--- a/xen/include/asm-x86/hvm/vpt.h
+++ b/xen/include/asm-x86/hvm/vpt.h
@@ -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_init(struct vcpu *v, int base);
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 --git a/xen/include/public/hvm/save.h b/xen/include/public/hvm/save.h
index 9b1a7c24e9..debb86a97d 100644
--- a/xen/include/public/hvm/save.h
+++ b/xen/include/public/hvm/save.h
@@ -392,7 +392,9 @@ DECLARE_HVM_SAVE_TYPE(HPET, 12, struct hvm_hw_hpet);
*/
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);