pmtimer.c

来自「xen虚拟机源代码安装包」· C语言 代码 · 共 285 行

C
285
字号
/* * 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 bits 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){    ASSERT(spin_is_locked(&s->lock));    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;        ASSERT(spin_is_locked(&s->lock));    /* 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;    spin_lock(&s->lock);    /* 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 << 23) / FREQUENCE_PMTIMER;    /* Reduced appropriately */    time_until_flip = (time_until_flip * pmt_cycles_until_flip) >> 23;    /* Wake up again near the next bit-flip */    set_timer(&s->timer, NOW() + time_until_flip + MILLISECS(1));    spin_unlock(&s->lock);}/* Handle port I/O to the PM1a_STS and PM1a_EN registers */static int handle_evt_io(    int dir, uint32_t port, uint32_t bytes, uint32_t *val){    struct vcpu *v = current;    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;    uint32_t addr, data, byte;    int i;    spin_lock(&s->lock);    if ( dir == IOREQ_WRITE )    {        /* Handle this I/O one byte at a time */        for ( i = bytes, addr = port, data = *val;              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: %x bytes (%x) at %x\n",                          bytes, *val, port);            }        }        /* Fix up the SCI state to match the new register state */        pmt_update_sci(s);    }    else /* p->dir == IOREQ_READ */    {        data = s->pm.pm1a_sts | (((uint32_t) s->pm.pm1a_en) << 16);        data >>= 8 * (port - PM1a_STS_ADDR);        if ( bytes == 1 ) data &= 0xff;        else if ( bytes == 2 ) data &= 0xffff;        *val = data;    }    spin_unlock(&s->lock);    return X86EMUL_OKAY;}/* Handle port I/O to the TMR_VAL register */static int handle_pmt_io(    int dir, uint32_t port, uint32_t bytes, uint32_t *val){    struct vcpu *v = current;    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;    if ( bytes != 4 )    {        gdprintk(XENLOG_WARNING, "HVM_PMT bad access\n");        return X86EMUL_OKAY;    }        if ( dir == IOREQ_READ )    {        spin_lock(&s->lock);        pmt_update_time(s);        *val = s->pm.tmr_val;        spin_unlock(&s->lock);        return X86EMUL_OKAY;    }    return X86EMUL_UNHANDLEABLE;}static int pmtimer_save(struct domain *d, hvm_domain_context_t *h){    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;    uint32_t x, msb = s->pm.tmr_val & TMR_VAL_MSB;    int rc;    spin_lock(&s->lock);    /* Update the counter to the guest's current time.  We always save     * with the domain paused, so the saved time should be after the     * 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.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 */    rc = hvm_save_entry(PMTIMER, 0, h, &s->pm);    spin_unlock(&s->lock);    return rc;}static int pmtimer_load(struct domain *d, hvm_domain_context_t *h){    PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;    spin_lock(&s->lock);    /* Reload the registers */    if ( hvm_load_entry(PMTIMER, h, &s->pm) )    {        spin_unlock(&s->lock);        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);    spin_unlock(&s->lock);        return 0;}HVM_REGISTER_SAVE_RESTORE(PMTIMER, pmtimer_save, pmtimer_load,                           1, HVMSR_PER_DOM);void pmtimer_init(struct vcpu *v){    PMTState *s = &v->domain->arch.hvm_domain.pl_time.vpmt;    spin_lock_init(&s->lock);    s->scale = ((uint64_t)FREQUENCE_PMTIMER << 32) / SYSTEM_TIME_HZ;    s->vcpu = v;    /* 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);}void pmtimer_reset(struct domain *d){    /* Reset the counter. */    d->arch.hvm_domain.pl_time.vpmt.pm.tmr_val = 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?