DevHPET.cpp revision 034f0367d3b0431c6346b1a3af3abb435ee50d4e
0f70ed40798198e1d9099c6ae3bdb239d2b8cf0dvboxsync * HPET virtual device - high precision event timer emulation
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Copyright (C) 2009-2010 Sun Microsystems, Inc.
a9749534ba173982f6c3bafe8d51ccd22960e493vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * available from http://www.virtualbox.org. This file is free software;
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * you can redistribute it and/or modify it under the terms of the GNU
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * General Public License (GPL) as published by the Free Software
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * additional information or have any questions.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Header Files *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Current limitations:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * - not entirely correct time of interrupt, i.e. never
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * schedule interrupt earlier than in 1ms
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * - statistics not implemented
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Base address for MMIO
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync * Number of available timers, cannot be changed without
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync * breaking saved states.
500aaaf3dc1d98456808e7618db3fb2e7c8fb8e0vboxsync * 10000000 femtoseconds == 10ns
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Femptosecods in nanosecond
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Interrupt type
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Delivery mode */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Via APIC */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Via FSB */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync/** The version of the saved state. */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync/* Empty saved state */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsynctypedef struct HpetTimer
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** The HPET timer - R3 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - R3 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET timer - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET timer - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* timer number*/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Alignment */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Memory-mapped, software visible timer registers */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* comparator */
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync /* FSB route, not supported now */
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync /* Hidden register state */
0566198d915e05531503035c7db85385839e708evboxsync /* Last value written to comparator */
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsynctypedef struct HpetState
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync /** Pointer to the device instance. - R3 ptr. */
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync /** The HPET helpers - R3 Ptr. */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** Pointer to the device instance. - R0 ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET helpers - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the device instance. - RC ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET helpers - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Timer structures */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Offset realtive to the system clock */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Memory-mapped, software visible registers */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* capabilities */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* configuration */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* interrupt status register */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* main counter */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Global device lock */
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync * We shall declare MMIO accessors as extern "C" to avoid name mangling
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync * and let them be found during R0/RC module init.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Maybe PDMBOTHCBDECL macro shall have extern "C" part in it.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncPDMBOTHCBDECL(int) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncPDMBOTHCBDECL(int) hpetMMIORead (PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Temporary control to disble locking if problems found
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic const bool fHpetLocking = true;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncDECLINLINE(int) hpetLock(HpetState* pThis, int rcBusy)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic uint32_t hpetTimeAfter32(uint64_t a, uint64_t b)
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsyncstatic uint32_t hpetTimeAfter64(uint64_t a, uint64_t b)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return (ASMMultU64ByU32DivByU32(value, HPET_CLK_PERIOD, FS_PER_NS));
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync return (ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, HPET_CLK_PERIOD));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * We can use any timer to get current time, they all go
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync * with the same speed.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return nsToHpetTicks(TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer)) +
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync return (!(u64OldValue & u64Mask) && (u64NewValue & u64Mask));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return ((u64OldValue & u64Mask) && !(u64NewValue & u64Mask));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncDECLINLINE(uint64_t) hpetComputeDiff(HpetTimer* pTimer,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u32Diff = (uint32_t)pTimer->u64Cmp - (uint32_t)u64Now;
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync u32Diff = ((int32_t)u32Diff > 0) ? u32Diff : (uint32_t)0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64Diff = ((int64_t)u64Diff > 0) ? u64Diff : (uint64_t)0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if ((pTimer->u64Config & HPET_TN_PERIODIC) && (u64Period != 0))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* While loop is suboptimal */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync pTimer->u64Cmp = (uint32_t)(pTimer->u64Cmp + u64Period);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Ticks = hpetGetTicks(pTimer->CTX_SUFF(pHpet));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* no wrapping on new timers */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Spec says in one-shot 32-bit mode, generate an interrupt when
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * counter wraps in addition to an interrupt with comparator match.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if ((pTimer->u64Config & HPET_TN_32BIT) && !(pTimer->u64Config & HPET_TN_PERIODIC))
b4feef6ee36ff3c271b06e7e52e22580cc66174bvboxsync /* Avoid killing VM with interrupts */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* @todo: HACK, rethink, may have negative impact on the guest */
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(u64Diff)));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync TMTimerSetNano(pTimer->CTX_SUFF(pTimer), hpetTicksToNs(u64Diff));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic uint32_t getTimerIrq(struct HpetTimer *pTimer)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Per spec, in legacy mode HPET timers wired as:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * timer 0: IRQ0 for PIC and IRQ2 for APIC
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * timer 1: IRQ8 for both PIC and APIC
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * As primary usecase for HPET is APIC config, we pretend
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * being always APIC, although for safety we shall check currect IC.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * @todo: implement private interface between HPET and PDM
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * to allow figuring that out and enabling/disabling
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * PIT and RTC
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * nike: Linux refuses to boot with HPET, claiming that 8259 timer not
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * connected to IO-APIC, if we use IRQ2, so let's use IRQ0 for now.
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync (pTimer->CTX_SUFF(pHpet)->u64Config & HPET_CFG_LEGACY))
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync return (pTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CFG on %d\n", pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CFG+4 on %d\n", pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CMP on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CMP+4 on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
77da7a074c86956d36759983037056c00cb87535vboxsync Log(("read HPET_TN_ROUTE on %d\n", pTimer->u8TimerNumber));
d5b5f09d8841828e647de9da5003fda55ca4cd5evboxsync LogRel(("invalid HPET register read %d on %d\n", iTimerReg, pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pThis->u64Capabilities >> 32);
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** @todo: is it correct? */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync *pValue = (iIndex == HPET_COUNTER) ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
int rc;
return VINF_SUCCESS;
return rc;
switch (iTimerReg)
case HPET_TN_CFG:
Assert(false);
| iNewValue;
| iNewValue;
case HPET_TN_ROUTE:
Assert(false);
return VINF_SUCCESS;
bool fActivate)
#ifndef IN_RING3
return rc;
switch (iIndex)
case HPET_ID:
case HPET_CFG:
return rc;
for (i = 0; i < HPET_NUM_TIMERS; i++)
for (i = 0; i < HPET_NUM_TIMERS; i++)
case HPET_STATUS:
if (iNewValue != 0)
case HPET_COUNTER:
return rc;
void * pvUser,
void * pv,
unsigned cb)
return rc;
switch (cb)
} value;
return rc;
void * pvUser,
void * pv,
unsigned cb)
return rc;
switch (cb)
} value;
return rc;
#ifdef IN_RING3
return VINF_SUCCESS;
return VINF_SUCCESS;
return VINF_SSM_DONT_CALL_AGAIN;
int rc;
return VINF_SUCCESS;
int rc;
return VINF_SUCCESS;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - wrong number of timers: saved=%#x config=%#x"), u8NumTimers, HPET_NUM_TIMERS);
return VINF_SUCCESS;
return VINF_SUCCESS;
void * pvUser)
int rc;
for (i = 0; i < HPET_NUM_TIMERS; i++)
int rc;
for (i = 0; i < HPET_NUM_TIMERS; i++)
return rc;
return VINF_SUCCESS;
for (i = 0; i < HPET_NUM_TIMERS; i++)
int rc;
bool fRCEnabled = false;
bool fR0Enabled = false;
return rc;
return rc;
return rc;
if (fRCEnabled)
return rc;
return VERR_INTERNAL_ERROR;
if (fR0Enabled)
return rc;
return VERR_INTERNAL_ERROR;
rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetLiveExec, hpetSaveExec, hpetLoadExec);
return rc;
return VINF_SUCCESS;
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
sizeof(HpetState),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,