DevHPET.cpp revision 63e3a547845f7c31bb4e892a66684b560dc63611
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * HPET virtual device - high precision event timer emulation
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Copyright (C) 2009-2011 Oracle Corporation
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * available from http://www.virtualbox.org. This file is free software;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * you can redistribute it and/or modify it under the terms of the GNU
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * 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.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Header Files *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Defined Constants And Macros *
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync*******************************************************************************/
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Current limitations:
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * - not entirely correct time of interrupt, i.e. never
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * schedule interrupt earlier than in 1ms
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * - statistics not implemented
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * - level-triggered mode not implemented
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Base address for MMIO
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Number of available timers, cannot be changed without
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * breaking saved states.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * 10000000 femtoseconds == 10ns
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * 69841279 femtoseconds == 69.84 ns (1 / 14.31818MHz)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Femptosecods in nanosecond
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync * Interrupt type
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* Delivery mode */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* Via APIC */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* Via FSB */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** Extract the timer count from the capabilities.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * @todo Check if the mask is correct. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define HPET_CAP_GET_TIMERS(a_u64) ( ((a_u64) >> 8) & 0xf )
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** The version of the saved state. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/** Empty saved state */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Acquires the HPET lock or returns.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync int rcLock = PDMCritSectEnter(&(a_pThis)->csLock, (a_rcBusy)); \
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync } while (0)
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync * Releases the HPET lock.
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync do { PDMCritSectLeave(&(a_pThis)->csLock); } while (0)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Acquires the TM lock and HPET lock, returns on failure.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define DEVHPET_LOCK_BOTH_RETURN(a_pThis, a_rcBusy) \
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync int rcLock = TMTimerLock((a_pThis)->aTimers[0].CTX_SUFF(pTimer), (a_rcBusy)); \
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rcLock = PDMCritSectEnter(&(a_pThis)->csLock, (a_rcBusy)); \
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync TMTimerUnlock((a_pThis)->aTimers[0].CTX_SUFF(pTimer)); \
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync } while (0)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync * Releases the HPET lock and TM lock.
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync TMTimerUnlock((a_pThis)->aTimers[0].CTX_SUFF(pTimer)); \
e3f5c51715cbf77ae2d2e9d05bafd00d69b1bec9vboxsync } while (0)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync/*******************************************************************************
b514c03a427443a7ad18c1202d2ee7acc47cf9afvboxsync* Structures and Typedefs *
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync*******************************************************************************/
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsynctypedef struct HpetTimer
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** The HPET timer - R3 Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Pointer to the instance data - R3 Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** The HPET timer - R0 Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Pointer to the instance data - R0 Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** The HPET timer - RC Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Pointer to the instance data - RC Ptr. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Timer index. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Wrap. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** Alignment. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** @name Memory-mapped, software visible timer registers.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Comparator. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** FSB route, not supported now. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** @name Hidden register state.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Last value written to comparator. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncAssertCompileMemberAlignment(HpetTimer, u64Config, sizeof(uint64_t));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsynctypedef struct HpetState
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the device instance. - R3 ptr. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** The HPET helpers - R3 Ptr. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the device instance. - R0 ptr. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** The HPET helpers - R0 Ptr. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the device instance. - RC ptr. */
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync /** The HPET helpers - RC Ptr. */
c6958b923ed12aadcf58ebbdbc80aadebbd9493evboxsync /** Timer structures. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Offset realtive to the virtual sync clock. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** @name Memory-mapped, software visible registers
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Capabilities. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Configuration. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Interrupt status register. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Main counter. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Global device lock. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** If we emulate ICH9 HPET (different frequency).
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @todo different number of timers */
b1cc88518a7578ee20491f3d97b9792c24c6428dvboxsyncDECLINLINE(bool) hpet32bitTimer(HpetTimer *pHpetTimer)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return ((u64Cfg & HPET_TN_SIZE_CAP) == 0) || ((u64Cfg & HPET_TN_32BIT) != 0);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(uint64_t) hpetInvalidValue(HpetTimer *pHpetTimer)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return hpet32bitTimer(pHpetTimer) ? UINT32_MAX : UINT64_MAX;
362838d79d234a41380be42aae9118850cc3c929vboxsyncDECLINLINE(uint32_t) hpetTimeAfter32(uint64_t a, uint64_t b)
362838d79d234a41380be42aae9118850cc3c929vboxsyncDECLINLINE(uint32_t) hpetTimeAfter64(uint64_t a, uint64_t b)
362838d79d234a41380be42aae9118850cc3c929vboxsyncDECLINLINE(uint64_t) hpetTicksToNs(HpetState *pThis, uint64_t value)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return (ASMMultU64ByU32DivByU32(value, (uint32_t)(pThis->u64Capabilities >> 32), FS_PER_NS));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(uint64_t) nsToHpetTicks(HpetState const *pThis, uint64_t u64Value)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return (ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, (uint32_t)(pThis->u64Capabilities >> 32)));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(uint64_t) hpetGetTicks(HpetState const *pThis)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * We can use any timer to get current time, they all go
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * with the same speed.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(uint64_t) hpetUpdateMasked(uint64_t u64NewValue,
aa32d4906f2f685992091893d5abdf27a2352a85vboxsyncDECLINLINE(bool) hpetBitJustSet(uint64_t u64OldValue,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(bool) hpetBitJustCleared(uint64_t u64OldValue,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncDECLINLINE(uint64_t) hpetComputeDiff(HpetTimer *pHpetTimer,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync u32Diff = (uint32_t)pHpetTimer->u64Cmp - (uint32_t)u64Now;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync u32Diff = ((int32_t)u32Diff > 0) ? u32Diff : (uint32_t)0;
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync u64Diff = ((int64_t)u64Diff > 0) ? u64Diff : (uint64_t)0;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic void hpetAdjustComparator(HpetTimer *pHpetTimer, uint64_t u64Now)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* While loop is suboptimal */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync while (hpetTimeAfter32(u64Now, pHpetTimer->u64Cmp))
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pHpetTimer->u64Cmp = (uint32_t)(pHpetTimer->u64Cmp + u64Period);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync while (hpetTimeAfter64(u64Now, pHpetTimer->u64Cmp))
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /* no wrapping on new timers */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint64_t u64Ticks = hpetGetTicks(pHpetTimer->CTX_SUFF(pHpet));
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync uint64_t u64Diff = hpetComputeDiff(pHpetTimer, u64Ticks);
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync * HPET spec says in one-shot 32-bit mode, generate an interrupt when
97dc0e92bcc0cddf896cbf620b689b095c7346davboxsync * counter wraps in addition to an interrupt with comparator match.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync uint32_t u32TillWrap = 0xffffffff - (uint32_t)u64Ticks + 1;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync Log(("wrap on timer %d: till=%u ticks=%lld diff64=%lld\n",
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync pHpetTimer->idxTimer, u32TillWrap, u64Ticks, u64Diff));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * HACK ALERT! Avoid killing VM with interrupts.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#if 1 /** @todo: HACK, rethink, may have negative impact on the guest */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(pHpetTimer->CTX_SUFF(pHpet), u64Diff)));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync TMTimerSetNano(pHpetTimer->CTX_SUFF(pTimer), hpetTicksToNs(pHpetTimer->CTX_SUFF(pHpet), u64Diff));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync uint32_t const u32Freq = RT_HI_U32(pHpetTimer->CTX_SUFF(pHpet)->u64Capabilities);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync TMTimerSetFrequencyHint(pHpetTimer->CTX_SUFF(pTimer), u32Freq / (uint32_t)u64Period);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=-=- Timer register accesses -=-=-=-=-=- */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Reads a HPET timer register.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns VBox strict status code.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param pThis The HPET instance.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param iTimerNo The timer index.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @param iTimerReg The index of the timer register to read.
static int hpetTimerRegRead32(HpetState const *pThis, uint32_t iTimerNo, uint32_t iTimerReg, uint32_t *pu32Value)
static unsigned s_cOccurences = 0;
*pu32Value = 0;
return VINF_SUCCESS;
switch (iTimerReg)
case HPET_TN_CFG:
case HPET_TN_CMP:
Log(("read HPET_TN_CMP+4 on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp));
case HPET_TN_ROUTE:
u32Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */
static unsigned s_cOccurences = 0;
u32Value = 0;
return VINF_SUCCESS;
static int hpetTimerRegWrite32(HpetState *pThis, uint32_t iTimerNo, uint32_t iTimerReg, uint32_t u32NewValue)
Assert(!PDMCritSectIsOwner(&pThis->csLock) || TMTimerIsLockOwner(pThis->aTimers[0].CTX_SUFF(pTimer)));
return VINF_SUCCESS;
switch (iTimerReg)
case HPET_TN_CFG:
AssertFailed();
| u32NewValue;
Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx tmr=%d\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period, iTimerNo));
case HPET_TN_ROUTE:
static unsigned s_cOccurences = 0;
return VINF_SUCCESS;
Assert(!PDMCritSectIsOwner(&pThis->csLock) || (idxReg != HPET_COUNTER && idxReg != HPET_COUNTER + 4));
switch (idxReg)
case HPET_ID:
case HPET_PERIOD:
case HPET_CFG:
case HPET_COUNTER:
case HPET_STATUS:
u32Value = 0;
return VINF_SUCCESS;
Assert(!PDMCritSectIsOwner(&pThis->csLock) || TMTimerIsLockOwner(pThis->aTimers[0].CTX_SUFF(pTimer)));
switch (idxReg)
case HPET_ID:
case HPET_CFG:
#ifdef IN_RING3
case HPET_STATUS:
if (u32NewValue != 0)
static unsigned s_cOccurrences = 0;
case HPET_COUNTER:
static unsigned s_cOccurences = 0;
return rc;
PDMBOTHCBDECL(int) hpetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
switch (cb)
return rc;
PDMBOTHCBDECL(int) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
int rc;
switch (cb)
return rc;
#ifdef IN_RING3
AssertFailed();
return VINF_SSM_DONT_CALL_AGAIN;
static DECLCALLBACK(int) hpetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
return VINF_SUCCESS;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - too many timers: saved=%#x config=%#x"),
return VINF_SUCCESS;
return rc;
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Capabilities does not match timer count: cTimers=%#x caps=%#x"),
return VINF_SUCCESS;
pThis->u64Capabilities |= ((uint64_t)(pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD) << 32);
bool fRCEnabled;
bool fR0Enabled;
if (fRCEnabled)
if (fR0Enabled)
rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetLiveExec, hpetSaveExec, hpetLoadExec);
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,