DevHPET.cpp revision af5224eb6b6676bc892a3f5abeb21f602547d31c
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync * HPET virtual device - high precision event timer emulation
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Copyright (C) 2009-2010 Sun Microsystems, Inc.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * available from http://www.virtualbox.org. This file is free software;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * you can redistribute it and/or modify it under the terms of the GNU
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * additional information or have any questions.
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync/*******************************************************************************
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync* Header Files *
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync*******************************************************************************/
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Current limitations:
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * - not entirely correct time of interrupt, i.e. never
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * schedule interrupt earlier than in 1ms
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * - statistics not implemented
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * - level-triggered mode not implemented
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Base address for MMIO
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Number of available timers, cannot be changed without
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * breaking saved states.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * 10000000 femtoseconds == 10ns
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Femptosecods in nanosecond
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Interrupt type
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/* Delivery mode */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/* Via APIC */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/* Via FSB */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/** The version of the saved state. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/* Empty saved state */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsynctypedef struct HpetTimer
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET timer - R3 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the instance data - R3 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET timer - R0 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the instance data - R0 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET timer - RC Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the instance data - RC Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* timer number*/
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Alignment */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Memory-mapped, software visible timer registers */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* comparator */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* FSB route, not supported now */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Hidden register state */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Last value written to comparator */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsynctypedef struct HpetState
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the device instance. - R3 ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET helpers - R3 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the device instance. - R0 ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET helpers - R0 Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** Pointer to the device instance. - RC ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** The HPET helpers - RC Ptr. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Timer structures */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Offset realtive to the system clock */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Memory-mapped, software visible registers */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* capabilities */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* configuration */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* interrupt status register */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* main counter */
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync /* Global device lock */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * We shall declare MMIO accessors as extern "C" to avoid name mangling
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * and let them be found during R0/RC module init.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Maybe PDMBOTHCBDECL macro shall have extern "C" part in it.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncPDMBOTHCBDECL(int) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncPDMBOTHCBDECL(int) hpetMMIORead (PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Temporary control to disble locking if problems found
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic const bool fHpetLocking = true;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncDECLINLINE(int) hpetLock(HpetState* pThis, int rcBusy)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic uint32_t hpetTimeAfter32(uint64_t a, uint64_t b)
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsyncstatic uint32_t hpetTimeAfter64(uint64_t a, uint64_t b)
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync return (ASMMultU64ByU32DivByU32(value, HPET_CLK_PERIOD, FS_PER_NS));
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync return (ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, HPET_CLK_PERIOD));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * We can use any timer to get current time, they all go
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * with the same speed.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync return nsToHpetTicks(TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer)) +
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync return (!(u64OldValue & u64Mask) && (u64NewValue & u64Mask));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync return ((u64OldValue & u64Mask) && !(u64NewValue & u64Mask));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncDECLINLINE(uint64_t) hpetComputeDiff(HpetTimer* pTimer,
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync u32Diff = (uint32_t)pTimer->u64Cmp - (uint32_t)u64Now;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync u32Diff = ((int32_t)u32Diff > 0) ? u32Diff : (uint32_t)0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync u64Diff = ((int64_t)u64Diff > 0) ? u64Diff : (uint64_t)0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if ((pTimer->u64Config & HPET_TN_PERIODIC) && (u64Period != 0))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* While loop is suboptimal */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pTimer->u64Cmp = (uint32_t)(pTimer->u64Cmp + u64Period);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync uint64_t u64Ticks = hpetGetTicks(pTimer->CTX_SUFF(pHpet));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* no wrapping on new timers */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Spec says in one-shot 32-bit mode, generate an interrupt when
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * counter wraps in addition to an interrupt with comparator match.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if ((pTimer->u64Config & HPET_TN_32BIT) && !(pTimer->u64Config & HPET_TN_PERIODIC))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Avoid killing VM with interrupts */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* @todo: HACK, rethink, may have negative impact on the guest */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(u64Diff)));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync TMTimerSetNano(pTimer->CTX_SUFF(pTimer), hpetTicksToNs(u64Diff));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic uint32_t getTimerIrq(struct HpetTimer *pTimer)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Per spec, in legacy mode HPET timers wired as:
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * timer 0: IRQ0 for PIC and IRQ2 for APIC
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * timer 1: IRQ8 for both PIC and APIC
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * ISA IRQ delivery logic will take care of correct delivery
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * to the different ICs.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync (pTimer->CTX_SUFF(pHpet)->u64Config & HPET_CFG_LEGACY))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync return (pTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("read HPET_TN_CFG on %d\n", pTimer->u8TimerNumber));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("read HPET_TN_CFG+4 on %d\n", pTimer->u8TimerNumber));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("read HPET_TN_CMP on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("read HPET_TN_CMP+4 on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("read HPET_TN_ROUTE on %d\n", pTimer->u8TimerNumber));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("invalid HPET register read %d on %d\n", iTimerReg, pTimer->u8TimerNumber));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync *pValue = (uint32_t)(pThis->u64Capabilities >> 32);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** @todo: is it correct? */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync *pValue = (iIndex == HPET_COUNTER) ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = timerRegRead32(pThis, iTimerNo, iTimerReg, &u32Temp);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if ((iNewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("level-triggered config not yet supported\n"));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** We only care about lower 32-bits so far */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync updateMasked(iNewValue, iOldValue, HPET_TN_CFG_WRITE_MASK);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync case HPET_TN_CMP: /* lower bits of comparator register */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("write HPET_TN_CMP on %d: %x\n", iTimerNo, iNewValue));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* HPET_TN_SETVAL allows to adjust comparator w/o updating period, and it's cleared on access */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync iNewValue &= (pTimer->u64Config & HPET_TN_32BIT ? ~0U : ~0ULL) >> 1;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pTimer->u64Period = (pTimer->u64Period & 0xffffffff00000000ULL)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pTimer->u64Cmp = (pTimer->u64Cmp & 0xffffffff00000000ULL)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log2(("after HPET_TN_CMP cmp=%llx per=%llx\n", pTimer->u64Cmp, pTimer->u64Period));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync case HPET_TN_CMP + 4: /* upper bits of comparator register */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("write HPET_TN_CMP + 4 on %d: %x\n", iTimerNo, iNewValue));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* HPET_TN_SETVAL allows to adjust comparator w/o updating period, and it's cleared on access */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pTimer->u64Period = (pTimer->u64Period & 0xffffffffULL)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx\n", pTimer->u64Cmp, pTimer->u64Period));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("invalid timer register write: %d\n", iTimerReg));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Don't do anything complicated outside of R3 */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#else /* IN_RING3 */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = pThis->pHpetHlpR3->pfnSetLegacyMode(pThis->pDevInsR3, fActivate);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * This check must be here, before actual update, as hpetLegacyMode
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * may request retry in R3 - so we must keep state intact.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if (isBitJustSet(iOldValue, iNewValue, HPET_CFG_LEGACY))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync else if (isBitJustCleared(iOldValue, iNewValue, HPET_CFG_LEGACY))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pThis->u64Config = updateMasked(iNewValue, iOldValue, HPET_CFG_WRITE_MASK);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if (isBitJustSet(iOldValue, iNewValue, HPET_CFG_ENABLE))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Enable main counter and interrupt generation. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pThis->u64HpetOffset = hpetTicksToNs(pThis->u64HpetCounter)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync for (i = 0; i < HPET_NUM_TIMERS; i++)
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync else if (isBitJustCleared(iOldValue, iNewValue, HPET_CFG_ENABLE))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Halt main counter and disable interrupt generation. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync for (i = 0; i < HPET_NUM_TIMERS; i++)
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync pThis->u64Config = updateMasked((uint64_t)iNewValue << 32,
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync 0xffffffff00000000ULL);
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync // clear ISR for all set bits in iNewValue, see p. 14 of HPET spec
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogRel(("Writing HPET_STATUS + 4 with non-zero, ignored\n"));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pThis->u64HpetCounter = (pThis->u64HpetCounter & 0xffffffff00000000ULL) | iNewValue;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pThis->u64HpetCounter = (pThis->u64HpetCounter & 0xffffffffULL)
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync LogRel(("invalid HPET config write: %x\n", iIndex));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync HpetState * pThis = PDMINS_2_DATA(pDevIns, HpetState*);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync uint32_t iIndex = (uint32_t)(GCPhysAddr - HPET_BASE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync LogFlow(("hpetMMIORead: %llx (%x)\n", (uint64_t)GCPhysAddr, iIndex));
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;
Assert(false);
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,