ia64/xen-unstable

changeset 14554:3afefd64e392

[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 <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Mon Mar 26 09:13:03 2007 +0000 (2007-03-26)
parents dccb274295ae
children 56caf0e37e6a 7ba1e697beda
files tools/ioemu/hw/piix4acpi.c xen/arch/x86/hvm/hvm.c xen/arch/x86/hvm/pmtimer.c xen/include/asm-x86/hvm/io.h xen/include/asm-x86/hvm/vpt.h xen/include/public/hvm/save.h
line diff
     1.1 --- a/tools/ioemu/hw/piix4acpi.c	Mon Mar 26 01:13:16 2007 +0100
     1.2 +++ b/tools/ioemu/hw/piix4acpi.c	Mon Mar 26 09:13:03 2007 +0000
     1.3 @@ -52,126 +52,16 @@
     1.4  typedef struct AcpiDeviceState AcpiDeviceState;
     1.5  AcpiDeviceState *acpi_device_table;
     1.6  
     1.7 -typedef struct PM1Event_BLK {
     1.8 -    uint16_t pm1_status; /* pm1a_EVT_BLK */
     1.9 -    uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */
    1.10 -}PM1Event_BLK;
    1.11 -
    1.12  typedef struct PCIAcpiState {
    1.13      PCIDevice dev;
    1.14 -    uint16_t irq;
    1.15 -    uint16_t pm1_status; /* pm1a_EVT_BLK */
    1.16 -    uint16_t pm1_enable; /* pm1a_EVT_BLK+2 */
    1.17      uint16_t pm1_control; /* pm1a_ECNT_BLK */
    1.18 -    uint32_t pm1_timer; /* pmtmr_BLK */
    1.19 -    uint64_t old_vmck_ticks; /* using vm_clock counter */
    1.20  } PCIAcpiState;
    1.21  
    1.22 -static PCIAcpiState *acpi_state;
    1.23 -
    1.24 -static void acpi_reset(PCIAcpiState *s)
    1.25 -{
    1.26 -    uint8_t *pci_conf;
    1.27 -    pci_conf = s->dev.config;
    1.28 -
    1.29 -    pci_conf[0x42] = 0x00;
    1.30 -    pci_conf[0x43] = 0x00;
    1.31 -    s->irq = 9;
    1.32 -    s->pm1_status = 0;
    1.33 -    s->pm1_enable = 0x00;    /* TMROF_EN should cleared */
    1.34 -    s->pm1_control = SCI_EN; /* SCI_EN */
    1.35 -    s->pm1_timer = 0;
    1.36 -    s->old_vmck_ticks = qemu_get_clock(vm_clock);
    1.37 -}
    1.38 -
    1.39 -/*byte access  */
    1.40 -static void acpiPm1Status_writeb(void *opaque, uint32_t addr, uint32_t val)
    1.41 -{
    1.42 -    PCIAcpiState *s = opaque;
    1.43 -
    1.44 -    if ((val&TMROF_STS)==TMROF_STS)
    1.45 -        s->pm1_status = s->pm1_status&!TMROF_STS;
    1.46 -
    1.47 -    if ((val&GBL_STS)==GBL_STS)
    1.48 -        s->pm1_status = s->pm1_status&!GBL_STS;
    1.49 -
    1.50 -/*     printf("acpiPm1Status_writeb \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */
    1.51 -}
    1.52 -
    1.53 -static uint32_t acpiPm1Status_readb(void *opaque, uint32_t addr)
    1.54 -{
    1.55 -    PCIAcpiState *s = opaque;
    1.56 -    uint32_t val;
    1.57 -
    1.58 -    val = s->pm1_status;
    1.59 -/*         printf("acpiPm1Status_readb \n addr %x val:%x\n", addr, val); */
    1.60 -
    1.61 -   return val;
    1.62 -}
    1.63 -
    1.64 -static void acpiPm1StatusP1_writeb(void *opaque, uint32_t addr, uint32_t val)
    1.65 -{
    1.66 -    PCIAcpiState *s = opaque;
    1.67 -
    1.68 -    s->pm1_status = (val<<8)||(s->pm1_status);
    1.69 -/*     printf("acpiPm1StatusP1_writeb \n addr %x val:%x\n", addr, val); */
    1.70 -}
    1.71 -
    1.72 -static uint32_t acpiPm1StatusP1_readb(void *opaque, uint32_t addr)
    1.73 -{
    1.74 -    PCIAcpiState *s = opaque;
    1.75 -    uint32_t val;
    1.76 -
    1.77 -    val = (s->pm1_status)>>8;
    1.78 -    printf("acpiPm1StatusP1_readb \n addr %x val:%x\n", addr, val);
    1.79 -
    1.80 -    return val;
    1.81 -}
    1.82 -
    1.83 -static void acpiPm1Enable_writeb(void *opaque, uint32_t addr, uint32_t val)
    1.84 -{
    1.85 -    PCIAcpiState *s = opaque;
    1.86 -
    1.87 -    s->pm1_enable = val;
    1.88 -/*   printf("acpiPm1Enable_writeb \n addr %x val:%x\n", addr, val); */
    1.89 -}
    1.90 -
    1.91 -static uint32_t acpiPm1Enable_readb(void *opaque, uint32_t addr)
    1.92 -{
    1.93 -    PCIAcpiState *s = opaque;
    1.94 -    uint32_t val;
    1.95 -
    1.96 -    val = (s->pm1_enable)||0x1;
    1.97 -/*  printf("acpiPm1Enable_readb \n addr %x val:%x\n", addr, val); */
    1.98 -
    1.99 -    return val;
   1.100 -}
   1.101 -
   1.102 -static void acpiPm1EnableP1_writeb(void *opaque, uint32_t addr, uint32_t val)
   1.103 -{
   1.104 -    PCIAcpiState *s = opaque;
   1.105 -
   1.106 -    s->pm1_enable = (val<<8)||(s->pm1_enable);
   1.107 -/*    printf("acpiPm1EnableP1_writeb \n addr %x val:%x\n", addr, val); */
   1.108 -
   1.109 -}
   1.110 -
   1.111 -static uint32_t acpiPm1EnableP1_readb(void *opaque, uint32_t addr)
   1.112 -{
   1.113 -    PCIAcpiState *s = opaque;
   1.114 -    uint32_t val;
   1.115 -
   1.116 -    val = (s->pm1_enable)>>8;
   1.117 -/*  printf("acpiPm1EnableP1_readb \n addr %x val:%x\n", addr, val); */
   1.118 -
   1.119 -    return val;
   1.120 -}
   1.121 -
   1.122  static void acpiPm1Control_writeb(void *opaque, uint32_t addr, uint32_t val)
   1.123  {
   1.124      PCIAcpiState *s = opaque;
   1.125  
   1.126 -    s->pm1_control = val;
   1.127 +    s->pm1_control = (s->pm1_control & 0xff00) | (val & 0xff);
   1.128  /*  printf("acpiPm1Control_writeb \n addr %x val:%x\n", addr, val); */
   1.129  
   1.130  }
   1.131 @@ -181,7 +71,8 @@ static uint32_t acpiPm1Control_readb(voi
   1.132      PCIAcpiState *s = opaque;
   1.133      uint32_t val;
   1.134  
   1.135 -    val = s->pm1_control;
   1.136 +    /* Mask out the write-only bits */
   1.137 +    val = s->pm1_control & ~(GBL_RLS|SLP_EN) & 0xff;
   1.138  /*    printf("acpiPm1Control_readb \n addr %x val:%x\n", addr, val); */
   1.139  
   1.140      return val;
   1.141 @@ -191,14 +82,13 @@ static void acpiPm1ControlP1_writeb(void
   1.142  {
   1.143      PCIAcpiState *s = opaque;
   1.144  
   1.145 -    s->pm1_control = (val<<8)||(s->pm1_control);
   1.146 +    s->pm1_control = (s->pm1_control & 0xff) | (val << 8);
   1.147  /*    printf("acpiPm1ControlP1_writeb \n addr %x val:%x\n", addr, val); */
   1.148  
   1.149      // Check for power off request
   1.150 -
   1.151 +    val <<= 8;
   1.152      if (((val & SLP_EN) != 0) &&
   1.153          ((val & SLP_TYP_MASK) == SLP_VAL)) {
   1.154 -        s->pm1_timer=0x0; //clear ACPI timer
   1.155          qemu_system_shutdown_request();
   1.156      }
   1.157  }
   1.158 @@ -208,7 +98,8 @@ static uint32_t acpiPm1ControlP1_readb(v
   1.159      PCIAcpiState *s = opaque;
   1.160      uint32_t val;
   1.161  
   1.162 -    val = (s->pm1_control)>>8;
   1.163 +    /* Mask out the write-only bits */
   1.164 +    val = (s->pm1_control & ~(GBL_RLS|SLP_EN)) >> 8;
   1.165  /*    printf("acpiPm1ControlP1_readb \n addr %x val:%x\n", addr, val); */
   1.166  
   1.167      return val;
   1.168 @@ -217,50 +108,6 @@ static uint32_t acpiPm1ControlP1_readb(v
   1.169  
   1.170  /* word access   */
   1.171  
   1.172 -static void acpiPm1Status_writew(void *opaque, uint32_t addr, uint32_t val)
   1.173 -{
   1.174 -    PCIAcpiState *s = opaque;
   1.175 -
   1.176 -    if ((val&TMROF_STS)==TMROF_STS)
   1.177 -        s->pm1_status = s->pm1_status&!TMROF_STS;
   1.178 -
   1.179 -    if ((val&GBL_STS)==GBL_STS)
   1.180 -        s->pm1_status = s->pm1_status&!GBL_STS;
   1.181 -
   1.182 -/*    printf("acpiPm1Status_writew \n addr %x val:%x pm1_status:%x \n", addr, val,s->pm1_status); */
   1.183 -}
   1.184 -
   1.185 -static uint32_t acpiPm1Status_readw(void *opaque, uint32_t addr)
   1.186 -{
   1.187 -    PCIAcpiState *s = opaque;
   1.188 -    uint32_t val;
   1.189 -
   1.190 -    val = s->pm1_status;
   1.191 -/*    printf("acpiPm1Status_readw \n addr %x val:%x\n", addr, val); */
   1.192 -
   1.193 -    return val;
   1.194 -}
   1.195 -
   1.196 -static void acpiPm1Enable_writew(void *opaque, uint32_t addr, uint32_t val)
   1.197 -{
   1.198 -    PCIAcpiState *s = opaque;
   1.199 -
   1.200 -    s->pm1_enable = val;
   1.201 -/*    printf("acpiPm1Enable_writew \n addr %x val:%x\n", addr, val); */
   1.202 -
   1.203 -}
   1.204 -
   1.205 -static uint32_t acpiPm1Enable_readw(void *opaque, uint32_t addr)
   1.206 -{
   1.207 -    PCIAcpiState *s = opaque;
   1.208 -    uint32_t val;
   1.209 -
   1.210 -    val = s->pm1_enable;
   1.211 -/*    printf("acpiPm1Enable_readw \n addr %x val:%x\n", addr, val); */
   1.212 -
   1.213 -   return val;
   1.214 -}
   1.215 -
   1.216  static void acpiPm1Control_writew(void *opaque, uint32_t addr, uint32_t val)
   1.217  {
   1.218      PCIAcpiState *s = opaque;
   1.219 @@ -282,56 +129,13 @@ static uint32_t acpiPm1Control_readw(voi
   1.220      PCIAcpiState *s = opaque;
   1.221      uint32_t val;
   1.222  
   1.223 -    val = s->pm1_control;
   1.224 +    /* Mask out the write-only bits */
   1.225 +    val = s->pm1_control & ~(GBL_RLS|SLP_EN);
   1.226  /*    printf("acpiPm1Control_readw \n addr %x val:%x\n", addr, val);  */
   1.227  
   1.228      return val;
   1.229  }
   1.230  
   1.231 -/* dword access */
   1.232 -
   1.233 -static void acpiPm1Event_writel(void *opaque, uint32_t addr, uint32_t val)
   1.234 -{
   1.235 -    PCIAcpiState *s = opaque;
   1.236 -
   1.237 -    s->pm1_status = val;
   1.238 -    s->pm1_enable = val>>16;
   1.239 -/*     printf("acpiPm1Event_writel \n addr %x val:%x \n", addr, val); */
   1.240 -
   1.241 -}
   1.242 -
   1.243 -static uint32_t acpiPm1Event_readl(void *opaque, uint32_t addr)
   1.244 -{
   1.245 -    PCIAcpiState *s = opaque;
   1.246 -    uint32_t val;
   1.247 -
   1.248 -    val = s->pm1_status|(s->pm1_enable<<16);
   1.249 -/*    printf("acpiPm1Event_readl \n addr %x val:%x\n", addr, val);    */
   1.250 -
   1.251 -    return val;
   1.252 -}
   1.253 -
   1.254 -static void acpiPm1Timer_writel(void *opaque, uint32_t addr, uint32_t val)
   1.255 -{
   1.256 -    PCIAcpiState *s = opaque;
   1.257 -
   1.258 -    s->pm1_timer = val;
   1.259 -    s->old_vmck_ticks = qemu_get_clock(vm_clock) +
   1.260 -        muldiv64(val, FREQUENCE_PMTIMER, ticks_per_sec);
   1.261 -}
   1.262 -
   1.263 -static uint32_t acpiPm1Timer_readl(void *opaque, uint32_t addr)
   1.264 -{
   1.265 -    PCIAcpiState *s = opaque;
   1.266 -    int64_t current_vmck_ticks = qemu_get_clock(vm_clock);
   1.267 -    int64_t vmck_ticks_delta = current_vmck_ticks - s->old_vmck_ticks;
   1.268 -
   1.269 -    if (s->old_vmck_ticks)
   1.270 -        s->pm1_timer += muldiv64(vmck_ticks_delta, FREQUENCE_PMTIMER,
   1.271 -                                 ticks_per_sec);
   1.272 -    s->old_vmck_ticks = current_vmck_ticks;
   1.273 -    return s->pm1_timer;
   1.274 -}
   1.275  
   1.276  static void acpi_map(PCIDevice *pci_dev, int region_num,
   1.277                      uint32_t addr, uint32_t size, int type)
   1.278 @@ -341,37 +145,14 @@ static void acpi_map(PCIDevice *pci_dev,
   1.279      printf("register acpi io\n");
   1.280  
   1.281      /* Byte access */
   1.282 -    register_ioport_write(addr, 1, 1, acpiPm1Status_writeb, d);
   1.283 -    register_ioport_read(addr, 1, 1, acpiPm1Status_readb, d);
   1.284 -    register_ioport_write(addr+1, 1, 1, acpiPm1StatusP1_writeb, d);
   1.285 -    register_ioport_read(addr+1, 1, 1, acpiPm1StatusP1_readb, d);
   1.286 -
   1.287 -    register_ioport_write(addr + 2, 1, 1, acpiPm1Enable_writeb, d);
   1.288 -    register_ioport_read(addr + 2, 1, 1, acpiPm1Enable_readb, d);
   1.289 -    register_ioport_write(addr + 2 +1, 1, 1, acpiPm1EnableP1_writeb, d);
   1.290 -    register_ioport_read(addr + 2 +1, 1, 1, acpiPm1EnableP1_readb, d);
   1.291 -
   1.292      register_ioport_write(addr + 4, 1, 1, acpiPm1Control_writeb, d);
   1.293      register_ioport_read(addr + 4, 1, 1, acpiPm1Control_readb, d);
   1.294      register_ioport_write(addr + 4 + 1, 1, 1, acpiPm1ControlP1_writeb, d);
   1.295      register_ioport_read(addr + 4 +1, 1, 1, acpiPm1ControlP1_readb, d);
   1.296  
   1.297      /* Word access */
   1.298 -    register_ioport_write(addr, 2, 2, acpiPm1Status_writew, d);
   1.299 -    register_ioport_read(addr, 2, 2, acpiPm1Status_readw, d);
   1.300 -
   1.301 -    register_ioport_write(addr + 2, 2, 2, acpiPm1Enable_writew, d);
   1.302 -    register_ioport_read(addr + 2, 2, 2, acpiPm1Enable_readw, d);
   1.303 -
   1.304      register_ioport_write(addr + 4, 2, 2, acpiPm1Control_writew, d);
   1.305      register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d);
   1.306 -
   1.307 -    /* DWord access */
   1.308 -    register_ioport_write(addr, 4, 4, acpiPm1Event_writel, d);
   1.309 -    register_ioport_read(addr, 4, 4, acpiPm1Event_readl, d);
   1.310 -
   1.311 -    register_ioport_write(addr + 8, 4, 4, acpiPm1Timer_writel, d);
   1.312 -    register_ioport_read(addr + 8, 4, 4, acpiPm1Timer_readl, d);
   1.313  }
   1.314  
   1.315  /* PIIX4 acpi pci configuration space, func 2 */
   1.316 @@ -385,7 +166,6 @@ void pci_piix4_acpi_init(PCIBus *bus, in
   1.317          bus, "PIIX4 ACPI", sizeof(PCIAcpiState),
   1.318          devfn, NULL, NULL);
   1.319  
   1.320 -    acpi_state = d;
   1.321      pci_conf = d->dev.config;
   1.322      pci_conf[0x00] = 0x86;  /* Intel */
   1.323      pci_conf[0x01] = 0x80;
   1.324 @@ -408,6 +188,9 @@ void pci_piix4_acpi_init(PCIBus *bus, in
   1.325       */
   1.326      pci_conf[0x40] = 0x41; /* Special device-specific BAR at 0x40 */
   1.327      pci_conf[0x41] = 0x1f;
   1.328 +    pci_conf[0x42] = 0x00;
   1.329 +    pci_conf[0x43] = 0x00;
   1.330 +    d->pm1_control = SCI_EN;
   1.331 +
   1.332      acpi_map(d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO);
   1.333 -    acpi_reset(d);
   1.334  }
     2.1 --- a/xen/arch/x86/hvm/hvm.c	Mon Mar 26 01:13:16 2007 +0100
     2.2 +++ b/xen/arch/x86/hvm/hvm.c	Mon Mar 26 09:13:03 2007 +0000
     2.3 @@ -218,6 +218,7 @@ void hvm_domain_destroy(struct domain *d
     2.4  {
     2.5      pit_deinit(d);
     2.6      rtc_deinit(d);
     2.7 +    pmtimer_deinit(d);
     2.8      hpet_deinit(d);
     2.9  
    2.10      if ( d->arch.hvm_domain.shared_page_va )
    2.11 @@ -303,7 +304,7 @@ int hvm_vcpu_initialise(struct vcpu *v)
    2.12  
    2.13      pit_init(v, cpu_khz);
    2.14      rtc_init(v, RTC_PORT(0));
    2.15 -    pmtimer_init(v, ACPI_PM_TMR_BLK_ADDRESS);
    2.16 +    pmtimer_init(v);
    2.17      hpet_init(v);
    2.18   
    2.19      /* Init guest TSC to start from zero. */
     3.1 --- a/xen/arch/x86/hvm/pmtimer.c	Mon Mar 26 01:13:16 2007 +0100
     3.2 +++ b/xen/arch/x86/hvm/pmtimer.c	Mon Mar 26 09:13:03 2007 +0000
     3.3 @@ -1,12 +1,172 @@
     3.4 +/*
     3.5 + * hvm/pmtimer.c: emulation of the ACPI PM timer 
     3.6 + *
     3.7 + * Copyright (c) 2007, XenSource inc.
     3.8 + * Copyright (c) 2006, Intel Corporation.
     3.9 + *
    3.10 + * This program is free software; you can redistribute it and/or modify it
    3.11 + * under the terms and conditions of the GNU General Public License,
    3.12 + * version 2, as published by the Free Software Foundation.
    3.13 + *
    3.14 + * This program is distributed in the hope it will be useful, but WITHOUT
    3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
    3.17 + * more details.
    3.18 + *
    3.19 + * You should have received a copy of the GNU General Public License along with
    3.20 + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    3.21 + * Place - Suite 330, Boston, MA 02111-1307 USA.
    3.22 + */
    3.23 +
    3.24  #include <asm/hvm/vpt.h>
    3.25  #include <asm/hvm/io.h>
    3.26  #include <asm/hvm/support.h>
    3.27  
    3.28 +/* Slightly more readable port I/O addresses for the registers we intercept */
    3.29 +#define PM1a_STS_ADDR (ACPI_PM1A_EVT_BLK_ADDRESS)
    3.30 +#define PM1a_EN_ADDR  (ACPI_PM1A_EVT_BLK_ADDRESS + 2)
    3.31 +#define TMR_VAL_ADDR  (ACPI_PM_TMR_BLK_ADDRESS)
    3.32 +
    3.33 +/* The interesting bit of the PM1a_STS register */
    3.34 +#define TMR_STS    (1 << 0)
    3.35 +#define PWRBTN_STS (1 << 5)
    3.36 +#define GBL_STS    (1 << 8)
    3.37 +
    3.38 +/* The same in PM1a_EN */
    3.39 +#define TMR_EN     (1 << 0)
    3.40 +#define PWRBTN_EN  (1 << 5)
    3.41 +#define GBL_EN     (1 << 8)
    3.42 +
    3.43 +/* Mask of bits in PM1a_STS that can generate an SCI.  Although the ACPI
    3.44 + * spec lists other bits, the PIIX4, which we are emulating, only
    3.45 + * supports these three.  For now, we only use TMR_STS; in future we
    3.46 + * will let qemu set the other bits */
    3.47 +#define SCI_MASK (TMR_STS|PWRBTN_STS|GBL_STS) 
    3.48 +
    3.49 +/* SCI IRQ number (must match SCI_INT number in ACPI FADT in hvmloader) */
    3.50 +#define SCI_IRQ 9
    3.51 +
    3.52 +/* We provide a 32-bit counter (must match the TMR_VAL_EXT bit in the FADT) */
    3.53 +#define TMR_VAL_MASK  (0xffffffff)
    3.54 +#define TMR_VAL_MSB   (0x80000000)
    3.55 +
    3.56 +
    3.57 +/* Dispatch SCIs based on the PM1a_STS and PM1a_EN registers */
    3.58 +static void pmt_update_sci(PMTState *s)
    3.59 +{
    3.60 +    if ( s->pm.pm1a_en & s->pm.pm1a_sts & SCI_MASK )
    3.61 +        hvm_isa_irq_assert(s->vcpu->domain, SCI_IRQ);
    3.62 +    else
    3.63 +        hvm_isa_irq_deassert(s->vcpu->domain, SCI_IRQ);
    3.64 +}
    3.65 +
    3.66 +/* Set the correct value in the timer, accounting for time elapsed
    3.67 + * since the last time we did that. */
    3.68 +static void pmt_update_time(PMTState *s)
    3.69 +{
    3.70 +    uint64_t curr_gtime;
    3.71 +    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
    3.72 +    
    3.73 +    /* Update the timer */
    3.74 +    curr_gtime = hvm_get_guest_time(s->vcpu);
    3.75 +    s->pm.tmr_val += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
    3.76 +    s->pm.tmr_val &= TMR_VAL_MASK;
    3.77 +    s->last_gtime = curr_gtime;
    3.78 +    
    3.79 +    /* If the counter's MSB has changed, set the status bit */
    3.80 +    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
    3.81 +    {
    3.82 +        s->pm.pm1a_sts |= TMR_STS;
    3.83 +        pmt_update_sci(s);
    3.84 +    }
    3.85 +}
    3.86 +
    3.87 +/* This function should be called soon after each time the MSB of the
    3.88 + * pmtimer register rolls over, to make sure we update the status
    3.89 + * registers and SCI at least once per rollover */
    3.90 +static void pmt_timer_callback(void *opaque)
    3.91 +{
    3.92 +    PMTState *s = opaque;
    3.93 +    uint32_t pmt_cycles_until_flip;
    3.94 +    uint64_t time_until_flip;
    3.95 +    
    3.96 +    /* Recalculate the timer and make sure we get an SCI if we need one */
    3.97 +    pmt_update_time(s);
    3.98 +    
    3.99 +    /* How close are we to the next MSB flip? */
   3.100 +    pmt_cycles_until_flip = TMR_VAL_MSB - (s->pm.tmr_val & (TMR_VAL_MSB - 1));
   3.101 +    
   3.102 +    /* Overall time between MSB flips */
   3.103 +    time_until_flip = (1000000000ULL << 31) / FREQUENCE_PMTIMER;
   3.104 +    
   3.105 +    /* Reduced appropriately */
   3.106 +    time_until_flip = (time_until_flip * pmt_cycles_until_flip) / (1ULL<<31);
   3.107 +    
   3.108 +    /* Wake up again near the next bit-flip */
   3.109 +    set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));
   3.110 +}
   3.111 +
   3.112 +
   3.113 +/* Handle port I/O to the PM1a_STS and PM1a_EN registers */
   3.114 +static int handle_evt_io(ioreq_t *p)
   3.115 +{
   3.116 +    struct vcpu *v = current;
   3.117 +    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
   3.118 +    uint32_t addr, data, byte;
   3.119 +    int i;
   3.120 +
   3.121 +    if ( p->dir == 0 ) /* Write */
   3.122 +    {
   3.123 +        /* Handle this I/O one byte at a time */
   3.124 +        for ( i = p->size, addr = p->addr, data = p->data;
   3.125 +              i > 0;
   3.126 +              i--, addr++, data >>= 8 )
   3.127 +        {
   3.128 +            byte = data & 0xff;
   3.129 +            switch(addr) 
   3.130 +            {
   3.131 +                /* PM1a_STS register bits are write-to-clear */
   3.132 +            case PM1a_STS_ADDR:
   3.133 +                s->pm.pm1a_sts &= ~byte;
   3.134 +                break;
   3.135 +            case PM1a_STS_ADDR + 1:
   3.136 +                s->pm.pm1a_sts &= ~(byte << 8);
   3.137 +                break;
   3.138 +                
   3.139 +            case PM1a_EN_ADDR:
   3.140 +                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff00) | byte;
   3.141 +                break;
   3.142 +            case PM1a_EN_ADDR + 1:
   3.143 +                s->pm.pm1a_en = (s->pm.pm1a_en & 0xff) | (byte << 8);
   3.144 +                break;
   3.145 +                
   3.146 +            default:
   3.147 +                gdprintk(XENLOG_WARNING, 
   3.148 +                         "Bad ACPI PM register write: %"PRIu64
   3.149 +                         " bytes (%#"PRIx64") at %"PRIx64"\n", 
   3.150 +                         p->size, p->data, p->addr);
   3.151 +            }
   3.152 +        }
   3.153 +        /* Fix up the SCI state to match the new register state */
   3.154 +        pmt_update_sci(s);
   3.155 +    }
   3.156 +    else /* Read */
   3.157 +    {
   3.158 +        data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);
   3.159 +        data >>= 8 * (p->addr - PM1a_STS_ADDR);
   3.160 +        if ( p->size == 1 ) data &= 0xff;
   3.161 +        else if ( p->size == 2 ) data &= 0xffff;
   3.162 +        p->data = data;
   3.163 +    }
   3.164 +    return 1;
   3.165 +}
   3.166 +
   3.167 +
   3.168 +/* Handle port I/O to the TMR_VAL register */
   3.169  static int handle_pmt_io(ioreq_t *p)
   3.170  {
   3.171      struct vcpu *v = current;
   3.172      PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
   3.173 -    uint64_t curr_gtime;
   3.174  
   3.175      if (p->size != 4 ||
   3.176          p->data_is_ptr ||
   3.177 @@ -19,12 +179,8 @@ static int handle_pmt_io(ioreq_t *p)
   3.178          /* PM_TMR_BLK is read-only */
   3.179          return 1;
   3.180      } else if (p->dir == 1) { /* read */
   3.181 -        /* Set the correct value in the timer, accounting for time
   3.182 -         * elapsed since the last time we did that. */
   3.183 -        curr_gtime = hvm_get_guest_time(s->vcpu);
   3.184 -        s->pm.timer += ((curr_gtime - s->last_gtime) * s->scale) >> 32;
   3.185 -        p->data = s->pm.timer;
   3.186 -        s->last_gtime = curr_gtime;
   3.187 +        pmt_update_time(s);
   3.188 +        p->data = s->pm.tmr_val;
   3.189          return 1;
   3.190      }
   3.191      return 0;
   3.192 @@ -33,6 +189,7 @@ static int handle_pmt_io(ioreq_t *p)
   3.193  static int pmtimer_save(struct domain *d, hvm_domain_context_t *h)
   3.194  {
   3.195      PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
   3.196 +    uint32_t msb = s->pm.tmr_val & TMR_VAL_MSB;
   3.197      uint32_t x;
   3.198  
   3.199      /* Update the counter to the guest's current time.  We always save
   3.200 @@ -40,7 +197,12 @@ static int pmtimer_save(struct domain *d
   3.201       * last_gtime, but just in case, make sure we only go forwards */
   3.202      x = ((s->vcpu->arch.hvm_vcpu.guest_time - s->last_gtime) * s->scale) >> 32;
   3.203      if ( x < 1UL<<31 )
   3.204 -        s->pm.timer += x;
   3.205 +        s->pm.tmr_val += x;
   3.206 +    if ( (s->pm.tmr_val & TMR_VAL_MSB) != msb )
   3.207 +        s->pm.pm1a_sts |= TMR_STS;
   3.208 +    /* No point in setting the SCI here because we'll already have saved the 
   3.209 +     * IRQ and *PIC state; we'll fix it up when we restore the domain */
   3.210 +
   3.211      return hvm_save_entry(PMTIMER, 0, h, &s->pm);
   3.212  }
   3.213  
   3.214 @@ -48,12 +210,15 @@ static int pmtimer_load(struct domain *d
   3.215  {
   3.216      PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
   3.217  
   3.218 -    /* Reload the counter */
   3.219 +    /* Reload the registers */
   3.220      if ( hvm_load_entry(PMTIMER, h, &s->pm) )
   3.221          return -EINVAL;
   3.222  
   3.223      /* Calculate future counter values from now. */
   3.224      s->last_gtime = hvm_get_guest_time(s->vcpu);
   3.225 +
   3.226 +    /* Set the SCI state from the registers */ 
   3.227 +    pmt_update_sci(s);
   3.228      
   3.229      return 0;
   3.230  }
   3.231 @@ -62,19 +227,30 @@ HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtim
   3.232                            1, HVMSR_PER_DOM);
   3.233  
   3.234  
   3.235 -void pmtimer_init(struct vcpu *v, int base)
   3.236 +void pmtimer_init(struct vcpu *v)
   3.237  {
   3.238      PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;
   3.239  
   3.240 -    s->pm.timer = 0;
   3.241 +    s->pm.tmr_val = 0;
   3.242 +    s->pm.pm1a_sts = 0;
   3.243 +    s->pm.pm1a_en = 0;
   3.244 +
   3.245      s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / ticks_per_sec(v);
   3.246      s->vcpu = v;
   3.247  
   3.248 -    /* Not implemented: we should set TMR_STS (bit 0 of PM1a_STS) every
   3.249 -     * time the timer's top bit flips, and generate an SCI if TMR_EN
   3.250 -     * (bit 0 of PM1a_EN) is set.  For now, those registers are in
   3.251 -     * qemu-dm, and we just calculate the timer's value on demand. */  
   3.252 +    /* Intercept port I/O (need two handlers because PM1a_CNT is between
   3.253 +     * PM1a_EN and TMR_VAL and is handled by qemu) */
   3.254 +    register_portio_handler(v->domain, TMR_VAL_ADDR, 4, handle_pmt_io);
   3.255 +    register_portio_handler(v->domain, PM1a_STS_ADDR, 4, handle_evt_io);
   3.256  
   3.257 -    register_portio_handler(v->domain, base, 4, handle_pmt_io);
   3.258 +    /* Set up callback to fire SCIs when the MSB of TMR_VAL changes */
   3.259 +    init_timer(&s->timer, pmt_timer_callback, s, v->processor);
   3.260 +    pmt_timer_callback(s);
   3.261  }
   3.262  
   3.263 +
   3.264 +void pmtimer_deinit(struct domain *d)
   3.265 +{
   3.266 +    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
   3.267 +    kill_timer(&s->timer);
   3.268 +}
     4.1 --- a/xen/include/asm-x86/hvm/io.h	Mon Mar 26 01:13:16 2007 +0100
     4.2 +++ b/xen/include/asm-x86/hvm/io.h	Mon Mar 26 09:13:03 2007 +0000
     4.3 @@ -80,7 +80,7 @@ struct hvm_io_op {
     4.4      struct cpu_user_regs    io_context; /* current context */
     4.5  };
     4.6  
     4.7 -#define MAX_IO_HANDLER              8
     4.8 +#define MAX_IO_HANDLER              9
     4.9  
    4.10  #define HVM_PORTIO                  0
    4.11  #define HVM_MMIO                    1
     5.1 --- a/xen/include/asm-x86/hvm/vpt.h	Mon Mar 26 01:13:16 2007 +0100
     5.2 +++ b/xen/include/asm-x86/hvm/vpt.h	Mon Mar 26 09:13:03 2007 +0000
     5.3 @@ -101,6 +101,7 @@ typedef struct PMTState {
     5.4      struct vcpu *vcpu;          /* Keeps sync with this vcpu's guest-time */
     5.5      uint64_t last_gtime;        /* Last (guest) time we updated the timer */
     5.6      uint64_t scale;             /* Multiplier to get from tsc to timer ticks */
     5.7 +    struct timer timer;         /* To make sure we send SCIs */
     5.8  } PMTState;
     5.9  
    5.10  struct pl_time {    /* platform time */
    5.11 @@ -132,7 +133,8 @@ void rtc_init(struct vcpu *v, int base);
    5.12  void rtc_migrate_timers(struct vcpu *v);
    5.13  void rtc_deinit(struct domain *d);
    5.14  int is_rtc_periodic_irq(void *opaque);
    5.15 -void pmtimer_init(struct vcpu *v, int base);
    5.16 +void pmtimer_init(struct vcpu *v);
    5.17 +void pmtimer_deinit(struct domain *d);
    5.18  
    5.19  void hpet_migrate_timers(struct vcpu *v);
    5.20  void hpet_init(struct vcpu *v);
     6.1 --- a/xen/include/public/hvm/save.h	Mon Mar 26 01:13:16 2007 +0100
     6.2 +++ b/xen/include/public/hvm/save.h	Mon Mar 26 09:13:03 2007 +0000
     6.3 @@ -392,7 +392,9 @@ DECLARE_HVM_SAVE_TYPE(HPET, 12, struct h
     6.4   */
     6.5  
     6.6  struct hvm_hw_pmtimer {
     6.7 -    uint32_t timer;
     6.8 +    uint32_t tmr_val;   /* PM_TMR_BLK.TMR_VAL: 24bit free-running counter */
     6.9 +    uint16_t pm1a_sts;  /* PM1a_EVT_BLK.PM1a_STS: status register */
    6.10 +    uint16_t pm1a_en;   /* PM1a_EVT_BLK.PM1a_EN: enable register */
    6.11  };
    6.12  
    6.13  DECLARE_HVM_SAVE_TYPE(PMTIMER, 13, struct hvm_hw_pmtimer);