DevHPET.cpp revision 034f0367d3b0431c6346b1a3af3abb435ee50d4e
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* $Id$ */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** @file
0f70ed40798198e1d9099c6ae3bdb239d2b8cf0dvboxsync * HPET virtual device - high precision event timer emulation
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
0f70ed40798198e1d9099c6ae3bdb239d2b8cf0dvboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Copyright (C) 2009-2010 Sun Microsystems, Inc.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *
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 *
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/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Header Files *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define LOG_GROUP LOG_GROUP_DEV_HPET
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <VBox/pdmdev.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <VBox/log.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <VBox/stam.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <iprt/assert.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <iprt/string.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include <iprt/asm.h>
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#include "../Builtins.h"
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
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 */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Base address for MMIO
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_BASE 0xfed00000
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync * Number of available timers, cannot be changed without
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync * breaking saved states.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_NUM_TIMERS 3
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync/*
500aaaf3dc1d98456808e7618db3fb2e7c8fb8e0vboxsync * 10000000 femtoseconds == 10ns
8ffcab9595cc0d56977968cd496363502fd814aevboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_CLK_PERIOD 10000000UL
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Femptosecods in nanosecond
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define FS_PER_NS 1000000
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Interrupt type
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TIMER_TYPE_LEVEL 1
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TIMER_TYPE_EDGE 0
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Delivery mode */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Via APIC */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TIMER_DELIVERY_APIC 0
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/* Via FSB */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TIMER_DELIVERY_FSB 1
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TIMER_CAP_FSB_INT_DEL (1 << 15)
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TIMER_CAP_PER_INT (1 << 4)
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_CFG_ENABLE 0x001 /* ENABLE_CNF */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_CFG_LEGACY 0x002 /* LEG_RT_CNF */
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_ID 0x000
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_PERIOD 0x004
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_CFG 0x010
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_STATUS 0x020
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_COUNTER 0x0f0
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_TN_CFG 0x000
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_TN_CMP 0x008
77da7a074c86956d36759983037056c00cb87535vboxsync#define HPET_TN_ROUTE 0x010
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_CFG_WRITE_MASK 0x3
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync#define HPET_TN_ENABLE 0x004
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_PERIODIC 0x008
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_PERIODIC_CAP 0x010
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TN_SIZE_CAP 0x020
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#define HPET_TN_SETVAL 0x040
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_32BIT 0x100
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_INT_ROUTE_MASK 0x3e00
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_CFG_WRITE_MASK 0x3f4e
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_INT_ROUTE_SHIFT 9
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_INT_ROUTE_CAP_SHIFT 32
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync/** The version of the saved state. */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync#define HPET_SAVED_STATE_VERSION 2
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync/* Empty saved state */
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync#define HPET_SAVED_STATE_VERSION_EMPTY 1
d5b5f09d8841828e647de9da5003fda55ca4cd5evboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsyncstruct HpetState;
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsynctypedef struct HpetTimer
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** The HPET timer - R3 Ptr. */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync PTMTIMERR3 pTimerR3;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - R3 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync R3PTRTYPE(struct HpetState *) pHpetR3;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET timer - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PTMTIMERR0 pTimerR0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync R0PTRTYPE(struct HpetState *) pHpetR0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET timer - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PTMTIMERRC pTimerRC;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the instance data - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync RCPTRTYPE(struct HpetState *) pHpetRC;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* timer number*/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint8_t u8TimerNumber;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Wrap */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint8_t u8Wrap;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Alignment */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint32_t alignment0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Memory-mapped, software visible timer registers */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Configuration/capabilities */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Config;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* comparator */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Cmp;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync /* FSB route, not supported now */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync uint64_t u64Fsb;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync /* Hidden register state */
0566198d915e05531503035c7db85385839e708evboxsync /* Last value written to comparator */
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync uint64_t u64Period;
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync} HpetTimer;
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsynctypedef struct HpetState
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync{
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync /** Pointer to the device instance. - R3 ptr. */
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync PPDMDEVINSR3 pDevInsR3;
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync /** The HPET helpers - R3 Ptr. */
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync PCPDMHPETHLPR3 pHpetHlpR3;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** Pointer to the device instance. - R0 ptr. */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync PPDMDEVINSR0 pDevInsR0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET helpers - R0 Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PCPDMHPETHLPR0 pHpetHlpR0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Pointer to the device instance. - RC ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PPDMDEVINSRC pDevInsRC;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The HPET helpers - RC Ptr. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PCPDMHPETHLPRC pHpetHlpRC;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Timer structures */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync HpetTimer aTimers[HPET_NUM_TIMERS];
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Offset realtive to the system clock */
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64HpetOffset;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Memory-mapped, software visible registers */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* capabilities */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync uint64_t u64Capabilities;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync /* configuration */
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync uint64_t u64Config;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* interrupt status register */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Isr;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* main counter */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64HpetCounter;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* Global device lock */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PDMCRITSECT csLock;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync} HpetState;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
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.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsyncRT_C_DECLS_BEGIN
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);
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsyncRT_C_DECLS_END
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Temporary control to disble locking if problems found
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic const bool fHpetLocking = true;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncDECLINLINE(int) hpetLock(HpetState* pThis, int rcBusy)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (!fHpetLocking)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return VINF_SUCCESS;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return PDMCritSectEnter(&pThis->csLock, rcBusy);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncDECLINLINE(void) hpetUnlock(HpetState* pThis)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync if (!fHpetLocking)
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync return;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync PDMCritSectLeave(&pThis->csLock);
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync}
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic uint32_t hpetTimeAfter32(uint64_t a, uint64_t b)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return ((int32_t)(b) - (int32_t)(a) <= 0);
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync}
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsyncstatic uint32_t hpetTimeAfter64(uint64_t a, uint64_t b)
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync{
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync return ((int64_t)(b) - (int64_t)(a) <= 0);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic uint64_t hpetTicksToNs(uint64_t value)
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return (ASMMultU64ByU32DivByU32(value, HPET_CLK_PERIOD, FS_PER_NS));
8ffcab9595cc0d56977968cd496363502fd814aevboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsyncstatic uint64_t nsToHpetTicks(uint64_t u64Value)
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync{
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync return (ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, HPET_CLK_PERIOD));
011bebd8930c3fa3df178d1c82ae88cc73c70d39vboxsync}
84d24b25be3d7e4004bf1601d8e0754398111a04vboxsync
7f67048412d241d45c0835b9c403a5bb1c879030vboxsyncstatic uint64_t hpetGetTicks(HpetState* pThis)
8ffcab9595cc0d56977968cd496363502fd814aevboxsync{
8ffcab9595cc0d56977968cd496363502fd814aevboxsync /*
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * We can use any timer to get current time, they all go
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync * with the same speed.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return nsToHpetTicks(TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer)) +
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync pThis->u64HpetOffset);
7f67048412d241d45c0835b9c403a5bb1c879030vboxsync}
7f67048412d241d45c0835b9c403a5bb1c879030vboxsync
7f67048412d241d45c0835b9c403a5bb1c879030vboxsyncstatic uint64_t updateMasked(uint64_t u64NewValue,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64OldValue,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64Mask)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64NewValue &= u64Mask;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64NewValue |= u64OldValue & ~u64Mask;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return u64NewValue;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync}
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
e2843ed205192b88e54eef60ad541d00bbbc932avboxsyncstatic bool isBitJustSet(uint64_t u64OldValue,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64NewValue,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64Mask)
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync{
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync return (!(u64OldValue & u64Mask) && (u64NewValue & u64Mask));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic bool isBitJustCleared(uint64_t u64OldValue,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t u64NewValue,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Mask)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return ((u64OldValue & u64Mask) && !(u64NewValue & u64Mask));
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncDECLINLINE(uint64_t) hpetComputeDiff(HpetTimer* pTimer,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Now)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (pTimer->u64Config & HPET_TN_32BIT)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint32_t u32Diff;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u32Diff = (uint32_t)pTimer->u64Cmp - (uint32_t)u64Now;
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync u32Diff = ((int32_t)u32Diff > 0) ? u32Diff : (uint32_t)0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return (uint64_t)u32Diff;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync } else {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Diff;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64Diff = pTimer->u64Cmp - u64Now;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64Diff = ((int64_t)u64Diff > 0) ? u64Diff : (uint64_t)0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return u64Diff;
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic void hpetAdjustComparator(HpetTimer* pTimer,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Now)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync uint64_t u64Period = pTimer->u64Period;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if ((pTimer->u64Config & HPET_TN_PERIODIC) && (u64Period != 0))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* While loop is suboptimal */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (pTimer->u64Config & HPET_TN_32BIT)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync while (hpetTimeAfter32(u64Now, pTimer->u64Cmp))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync pTimer->u64Cmp = (uint32_t)(pTimer->u64Cmp + u64Period);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync else
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync while (hpetTimeAfter64(u64Now, pTimer->u64Cmp))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync pTimer->u64Cmp += u64Period;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic void hpetProgramTimer(HpetTimer *pTimer)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Diff;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint32_t u32TillWrap;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Ticks = hpetGetTicks(pTimer->CTX_SUFF(pHpet));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* no wrapping on new timers */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync pTimer->u8Wrap = 0;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync hpetAdjustComparator(pTimer, u64Ticks);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64Diff = hpetComputeDiff(pTimer, u64Ticks);
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync
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 */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if ((pTimer->u64Config & HPET_TN_32BIT) && !(pTimer->u64Config & HPET_TN_PERIODIC))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u32TillWrap = 0xffffffff - (uint32_t)u64Ticks;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (u32TillWrap < (uint32_t)u64Diff)
150e55a1de2d8702b09de9dd08e488cc9da197d9vboxsync {
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync u64Diff = u32TillWrap;
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync pTimer->u8Wrap = 1;
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync }
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync }
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync
b4feef6ee36ff3c271b06e7e52e22580cc66174bvboxsync /* Avoid killing VM with interrupts */
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync#if 1
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* @todo: HACK, rethink, may have negative impact on the guest */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (u64Diff == 0)
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync u64Diff = 100000; /* 1 millisecond */
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync#endif
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(u64Diff)));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync TMTimerSetNano(pTimer->CTX_SUFF(pTimer), hpetTicksToNs(u64Diff));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic uint32_t getTimerIrq(struct HpetTimer *pTimer)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync{
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /*
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 */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /*
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.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync */
f581f3e365dbaec7822752c865314476d86c7e16vboxsync if ((pTimer->u8TimerNumber <= 1) &&
8a8d7629deae8875b70c6899e8b0f683b2a543e1vboxsync (pTimer->CTX_SUFF(pHpet)->u64Config & HPET_CFG_LEGACY))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync return (pTimer->u8TimerNumber == 0) ? 2 : 8;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync else
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync return (pTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync}
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic int timerRegRead32(HpetState* pThis,
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint32_t iTimerNo,
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync uint32_t iTimerReg,
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync uint32_t * pValue)
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync{
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync HpetTimer *pTimer;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync if (iTimerNo >= HPET_NUM_TIMERS)
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync {
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync return VINF_SUCCESS;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync }
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync pTimer = &pThis->aTimers[iTimerNo];
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync switch (iTimerReg)
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync {
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_TN_CFG:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CFG on %d\n", pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pTimer->u64Config);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_TN_CFG + 4:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CFG+4 on %d\n", pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pTimer->u64Config >> 32);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_TN_CMP:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CMP on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pTimer->u64Cmp);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_TN_CMP + 4:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_TN_CMP+4 on %d, cmp=%llx\n", pTimer->u8TimerNumber, pTimer->u64Cmp));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pTimer->u64Cmp >> 32);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
77da7a074c86956d36759983037056c00cb87535vboxsync case HPET_TN_ROUTE:
77da7a074c86956d36759983037056c00cb87535vboxsync Log(("read HPET_TN_ROUTE on %d\n", pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pTimer->u64Fsb >> 32);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync default:
d5b5f09d8841828e647de9da5003fda55ca4cd5evboxsync LogRel(("invalid HPET register read %d on %d\n", iTimerReg, pTimer->u8TimerNumber));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync }
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync return VINF_SUCCESS;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync}
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsyncstatic int configRegRead32(HpetState* pThis,
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync uint32_t iIndex,
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync uint32_t *pValue)
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync{
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync switch (iIndex)
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync {
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_ID:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_ID\n"));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pThis->u64Capabilities);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync case HPET_PERIOD:
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync Log(("read HPET_PERIOD\n"));
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync *pValue = (uint32_t)(pThis->u64Capabilities >> 32);
2622c26c6b4105d944a29c5e2c77b6ef26e10101vboxsync break;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync case HPET_CFG:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Log(("read HPET_CFG\n"));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *pValue = (uint32_t)(pThis->u64Config);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync break;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync case HPET_CFG + 4:
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync Log(("read of HPET_CFG + 4\n"));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *pValue = (uint32_t)(pThis->u64Config >> 32);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync break;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync case HPET_COUNTER:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync case HPET_COUNTER + 4:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync {
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64Ticks;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Log(("read HPET_COUNTER\n"));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (pThis->u64Config & HPET_CFG_ENABLE)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync u64Ticks = hpetGetTicks(pThis);
d5b5f09d8841828e647de9da5003fda55ca4cd5evboxsync else
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync u64Ticks = pThis->u64HpetCounter;
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync /** @todo: is it correct? */
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync *pValue = (iIndex == HPET_COUNTER) ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync break;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync }
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync case HPET_STATUS:
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync Log(("read HPET_STATUS\n"));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync *pValue = (uint32_t)(pThis->u64Isr);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync break;
2e2dec6e64c09dd7e3fe4ad0ee8bb5cf7d63762evboxsync default:
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync Log(("invalid HPET register read: %x\n", iIndex));
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync break;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync }
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync return VINF_SUCCESS;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync}
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync
e2843ed205192b88e54eef60ad541d00bbbc932avboxsyncstatic int timerRegWrite32(HpetState* pThis,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint32_t iTimerNo,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint32_t iTimerReg,
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint32_t iNewValue)
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync{
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync HpetTimer * pTimer;
e2843ed205192b88e54eef60ad541d00bbbc932avboxsync uint64_t iOldValue = 0;
uint32_t u32Temp;
int rc;
if (iTimerNo >= HPET_NUM_TIMERS)
{
LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
return VINF_SUCCESS;
}
pTimer = &pThis->aTimers[iTimerNo];
rc = timerRegRead32(pThis, iTimerNo, iTimerReg, &u32Temp);
if (RT_FAILURE(rc))
return rc;
iOldValue = u32Temp;
switch (iTimerReg)
{
case HPET_TN_CFG:
{
Log(("write HPET_TN_CFG: %d\n", iTimerNo));
/** We only care about lower 32-bits so far */
pTimer->u64Config =
updateMasked(iNewValue, iOldValue, HPET_TN_CFG_WRITE_MASK);
if (iNewValue & HPET_TN_32BIT)
{
pTimer->u64Cmp = (uint32_t)pTimer->u64Cmp;
pTimer->u64Period = (uint32_t)pTimer->u64Period;
}
if (iNewValue & HPET_TIMER_TYPE_LEVEL)
{
LogRel(("level-triggered config not yet supported\n"));
Assert(false);
}
break;
}
case HPET_TN_CFG + 4: /* Interrupt capabilities */
{
Log(("write HPET_TN_CFG + 4, useless\n"));
break;
}
case HPET_TN_CMP: /* lower bits of comparator register */
{
Log(("write HPET_TN_CMP on %d: %x\n", iTimerNo, iNewValue));
if (pTimer->u64Config & HPET_TN_32BIT)
iNewValue = (uint32_t)iNewValue;
if (pTimer->u64Config & HPET_TN_SETVAL)
{
/* HPET_TN_SETVAL allows to adjust comparator w/o updating period, and it's cleared on access */
if (pTimer->u64Config & HPET_TN_32BIT)
pTimer->u64Config &= ~HPET_TN_SETVAL;
} else if (pTimer->u64Config & HPET_TN_PERIODIC)
{
iNewValue &= (pTimer->u64Config & HPET_TN_32BIT ? ~0U : ~0ULL) >> 1;
pTimer->u64Period = (pTimer->u64Period & 0xffffffff00000000ULL)
| iNewValue;
}
pTimer->u64Cmp = (pTimer->u64Cmp & 0xffffffff00000000ULL)
| iNewValue;
Log2(("after HPET_TN_CMP cmp=%llx per=%llx\n", pTimer->u64Cmp, pTimer->u64Period));
if (pThis->u64Config & HPET_CFG_ENABLE)
hpetProgramTimer(pTimer);
break;
}
case HPET_TN_CMP + 4: /* upper bits of comparator register */
{
Log(("write HPET_TN_CMP + 4 on %d: %x\n", iTimerNo, iNewValue));
if (pTimer->u64Config & HPET_TN_32BIT)
break;
if (pTimer->u64Config & HPET_TN_SETVAL)
{
/* HPET_TN_SETVAL allows to adjust comparator w/o updating period, and it's cleared on access */
pTimer->u64Config &= ~HPET_TN_SETVAL;
} else if (pTimer->u64Config & HPET_TN_PERIODIC)
{
pTimer->u64Period = (pTimer->u64Period & 0xffffffffULL)
| ((uint64_t)iNewValue << 32);
}
pTimer->u64Cmp = (pTimer->u64Cmp & 0xffffffffULL)
| ((uint64_t)iNewValue << 32);
Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx\n", pTimer->u64Cmp, pTimer->u64Period));
if (pThis->u64Config & HPET_CFG_ENABLE)
hpetProgramTimer(pTimer);
break;
}
case HPET_TN_ROUTE:
{
Log(("write HPET_TN_ROUTE\n"));
break;
}
case HPET_TN_ROUTE + 4:
{
Log(("write HPET_TN_ROUTE + 4\n"));
break;
}
default:
{
LogRel(("invalid timer register write: %d\n", iTimerReg));
Assert(false);
break;
}
}
return VINF_SUCCESS;
}
static int hpetLegacyMode(HpetState* pThis,
bool fActivate)
{
int rc = VINF_SUCCESS;
#ifndef IN_RING3
/* Don't do anything complicated outside of R3 */
rc = VINF_IOM_HC_MMIO_WRITE;
#else /* IN_RING3 */
if (pThis->pHpetHlpR3)
rc = pThis->pHpetHlpR3->pfnSetLegacyMode(pThis->pDevInsR3, fActivate);
#endif
return rc;
}
static int configRegWrite32(HpetState* pThis,
uint32_t iIndex,
uint32_t iNewValue)
{
int rc = VINF_SUCCESS;
switch (iIndex)
{
case HPET_ID:
case HPET_ID + 4:
{
Log(("write HPET_ID, useless\n"));
break;
}
case HPET_CFG:
{
uint32_t i, iOldValue;
Log(("write HPET_CFG: %x\n", iNewValue));
iOldValue = (uint32_t)(pThis->u64Config);
/*
* This check must be here, before actual update, as hpetLegacyMode
* may request retry in R3 - so we must keep state intact.
*/
if (isBitJustSet(iOldValue, iNewValue, HPET_CFG_LEGACY))
{
rc = hpetLegacyMode(pThis, true);
}
else if (isBitJustCleared(iOldValue, iNewValue, HPET_CFG_LEGACY))
{
rc = hpetLegacyMode(pThis, false);
}
if (rc != VINF_SUCCESS)
return rc;
pThis->u64Config = updateMasked(iNewValue, iOldValue, HPET_CFG_WRITE_MASK);
if (isBitJustSet(iOldValue, iNewValue, HPET_CFG_ENABLE))
{
/* Enable main counter and interrupt generation. */
pThis->u64HpetOffset = hpetTicksToNs(pThis->u64HpetCounter)
- TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer));
for (i = 0; i < HPET_NUM_TIMERS; i++)
if (pThis->aTimers[i].u64Cmp != ~0ULL)
hpetProgramTimer(&pThis->aTimers[i]);
}
else if (isBitJustCleared(iOldValue, iNewValue, HPET_CFG_ENABLE))
{
/* Halt main counter and disable interrupt generation. */
pThis->u64HpetCounter = hpetGetTicks(pThis);
for (i = 0; i < HPET_NUM_TIMERS; i++)
TMTimerStop(pThis->aTimers[i].CTX_SUFF(pTimer));
}
break;
}
case HPET_CFG + 4:
{
Log(("write HPET_CFG + 4: %x\n", iNewValue));
pThis->u64Config = updateMasked((uint64_t)iNewValue << 32,
pThis->u64Config,
0xffffffff00000000ULL);
break;
}
case HPET_STATUS:
{
Log(("write HPET_STATUS: %x\n", iNewValue));
// clear ISR for all set bits in iNewValue, see p. 14 of HPET spec
pThis->u64Isr &= ~((uint64_t)iNewValue);
break;
}
case HPET_STATUS + 4:
{
Log(("write HPET_STATUS + 4: %x\n", iNewValue));
if (iNewValue != 0)
LogRel(("Writing HPET_STATUS + 4 with non-zero, ignored\n"));
break;
}
case HPET_COUNTER:
{
pThis->u64HpetCounter = (pThis->u64HpetCounter & 0xffffffff00000000ULL) | iNewValue;
Log(("write HPET_COUNTER: %#x -> %llx\n",
iNewValue, pThis->u64HpetCounter));
break;
}
case HPET_COUNTER + 4:
{
pThis->u64HpetCounter = (pThis->u64HpetCounter & 0xffffffffULL)
| (((uint64_t)iNewValue) << 32);
Log(("write HPET_COUNTER + 4: %#x -> %llx\n",
iNewValue, pThis->u64HpetCounter));
break;
}
default:
LogRel(("invalid HPET config write: %x\n", iIndex));
break;
}
return rc;
}
PDMBOTHCBDECL(int) hpetMMIORead(PPDMDEVINS pDevIns,
void * pvUser,
RTGCPHYS GCPhysAddr,
void * pv,
unsigned cb)
{
HpetState * pThis = PDMINS_2_DATA(pDevIns, HpetState*);
int rc = VINF_SUCCESS;
uint32_t iIndex = (uint32_t)(GCPhysAddr - HPET_BASE);
LogFlow(("hpetMMIORead: %llx (%x)\n", (uint64_t)GCPhysAddr, iIndex));
rc = hpetLock(pThis, VINF_IOM_HC_MMIO_READ);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
return rc;
switch (cb)
{
case 1:
case 2:
Log(("Narrow read: %d\n", cb));
rc = VERR_INTERNAL_ERROR;
break;
case 4:
{
if ((iIndex >= 0x100) && (iIndex < 0x400))
rc = timerRegRead32(pThis, (iIndex - 0x100) / 0x20, (iIndex - 0x100) % 0x20, (uint32_t*)pv);
else
rc = configRegRead32(pThis, iIndex, (uint32_t*)pv);
break;
}
case 8:
{
union {
uint32_t u32[2];
uint64_t u64;
} value;
/* Unaligned accesses not allowed */
if (iIndex % 8 != 0)
{
AssertMsgFailed(("Unaligned HPET read access\n"));
rc = VERR_INTERNAL_ERROR;
break;
}
// for 8-byte accesses we just split them, happens under lock anyway
if ((iIndex >= 0x100) && (iIndex < 0x400))
{
uint32_t iTimer = (iIndex - 0x100) / 0x20;
uint32_t iTimerReg = (iIndex - 0x100) % 0x20;
rc = timerRegRead32(pThis, iTimer, iTimerReg, &value.u32[0]);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
break;
rc = timerRegRead32(pThis, iTimer, iTimerReg + 4, &value.u32[1]);
}
else
{
rc = configRegRead32(pThis, iIndex, &value.u32[0]);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
break;
rc = configRegRead32(pThis, iIndex+4, &value.u32[1]);
}
if (rc == VINF_SUCCESS)
*(uint64_t*)pv = value.u64;
break;
}
default:
AssertReleaseMsgFailed(("cb=%d\n", cb)); /* for now we assume simple accesses. */
rc = VERR_INTERNAL_ERROR;
}
hpetUnlock(pThis);
return rc;
}
PDMBOTHCBDECL(int) hpetMMIOWrite(PPDMDEVINS pDevIns,
void * pvUser,
RTGCPHYS GCPhysAddr,
void * pv,
unsigned cb)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState*);
int rc = VINF_SUCCESS;
uint32_t iIndex = (uint32_t)(GCPhysAddr - HPET_BASE);
LogFlow(("hpetMMIOWrite: %llx (%x) <- %x\n",
(uint64_t)GCPhysAddr, iIndex, *(uint32_t*)pv));
rc = hpetLock(pThis, VINF_IOM_HC_MMIO_WRITE);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
return rc;
switch (cb)
{
case 1:
case 2:
Log(("Narrow write: %d\n", cb));
rc = VERR_INTERNAL_ERROR;
break;
case 4:
{
if ((iIndex >= 0x100) && (iIndex < 0x400))
rc = timerRegWrite32(pThis,
(iIndex - 0x100) / 0x20,
(iIndex - 0x100) % 0x20,
*(uint32_t*)pv);
else
rc = configRegWrite32(pThis, iIndex, *(uint32_t*)pv);
break;
}
case 8:
{
union {
uint32_t u32[2];
uint64_t u64;
} value;
/* Unaligned accesses not allowed */
if (iIndex % 8 != 0)
{
AssertMsgFailed(("Unaligned HPET write access\n"));
rc = VERR_INTERNAL_ERROR;
break;
}
value.u64 = *(uint64_t*)pv;
// for 8-byte accesses we just split them, happens under lock anyway
if ((iIndex >= 0x100) && (iIndex < 0x400))
{
uint32_t iTimer = (iIndex - 0x100) / 0x20;
uint32_t iTimerReg = (iIndex - 0x100) % 0x20;
rc = timerRegWrite32(pThis, iTimer, iTimerReg, value.u32[0]);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
break;
rc = timerRegWrite32(pThis, iTimer, iTimerReg + 4, value.u32[1]);
}
else
{
rc = configRegWrite32(pThis, iIndex, value.u32[0]);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
break;
rc = configRegWrite32(pThis, iIndex+4, value.u32[1]);
}
break;
}
default:
AssertReleaseMsgFailed(("cb=%d\n", cb)); /* for now we assume simple accesses. */
rc = VERR_INTERNAL_ERROR;
}
hpetUnlock(pThis);
return rc;
}
#ifdef IN_RING3
static int hpetSaveTimer(HpetTimer *pTimer,
PSSMHANDLE pSSM)
{
TMR3TimerSave(pTimer->pTimerR3, pSSM);
SSMR3PutU8 (pSSM, pTimer->u8Wrap);
SSMR3PutU64 (pSSM, pTimer->u64Config);
SSMR3PutU64 (pSSM, pTimer->u64Cmp);
SSMR3PutU64 (pSSM, pTimer->u64Fsb);
SSMR3PutU64 (pSSM, pTimer->u64Period);
return VINF_SUCCESS;
}
static int hpetLoadTimer(HpetTimer *pTimer,
PSSMHANDLE pSSM)
{
TMR3TimerLoad(pTimer->pTimerR3, pSSM);
SSMR3GetU8(pSSM, &pTimer->u8Wrap);
SSMR3GetU64(pSSM, &pTimer->u64Config);
SSMR3GetU64(pSSM, &pTimer->u64Cmp);
SSMR3GetU64(pSSM, &pTimer->u64Fsb);
SSMR3GetU64(pSSM, &pTimer->u64Period);
return VINF_SUCCESS;
}
/**
* @copydoc FNSSMDEVLIVEEXEC
*/
static DECLCALLBACK(int) hpetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
SSMR3PutU8(pSSM, HPET_NUM_TIMERS);
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* Saves a state of the HPET device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
static DECLCALLBACK(int) hpetSaveExec(PPDMDEVINS pDevIns,
PSSMHANDLE pSSM)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
uint32_t iTimer;
int rc;
/* The config. */
hpetLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
for (iTimer = 0; iTimer < HPET_NUM_TIMERS; iTimer++)
{
rc = hpetSaveTimer(&pThis->aTimers[iTimer], pSSM);
AssertRCReturn(rc, rc);
}
SSMR3PutU64(pSSM, pThis->u64HpetOffset);
SSMR3PutU64(pSSM, pThis->u64Capabilities);
SSMR3PutU64(pSSM, pThis->u64Config);
SSMR3PutU64(pSSM, pThis->u64Isr);
SSMR3PutU64(pSSM, pThis->u64HpetCounter);
return VINF_SUCCESS;
}
/**
* Loads a HPET device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param uVersion The data unit version number.
* @param uPass The data pass.
*/
static DECLCALLBACK(int) hpetLoadExec(PPDMDEVINS pDevIns,
PSSMHANDLE pSSM,
uint32_t uVersion,
uint32_t uPass)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
uint32_t iTimer;
int rc;
if (uVersion == HPET_SAVED_STATE_VERSION_EMPTY)
return VINF_SUCCESS;
if (uVersion != HPET_SAVED_STATE_VERSION)
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
uint8_t u8NumTimers;
rc = SSMR3GetU8(pSSM, &u8NumTimers); AssertRCReturn(rc, rc);
if (u8NumTimers != HPET_NUM_TIMERS)
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - wrong number of timers: saved=%#x config=%#x"), u8NumTimers, HPET_NUM_TIMERS);
if (uPass != SSM_PASS_FINAL)
return VINF_SUCCESS;
for (iTimer = 0; iTimer < HPET_NUM_TIMERS; iTimer++)
{
rc = hpetLoadTimer(&pThis->aTimers[iTimer], pSSM);
AssertRCReturn(rc, rc);
}
SSMR3GetU64(pSSM, &pThis->u64HpetOffset);
SSMR3GetU64(pSSM, &pThis->u64Capabilities);
SSMR3GetU64(pSSM, &pThis->u64Config);
SSMR3GetU64(pSSM, &pThis->u64Isr);
SSMR3GetU64(pSSM, &pThis->u64HpetCounter);
return VINF_SUCCESS;
}
static void irqUpdate(struct HpetTimer *pTimer)
{
uint32_t irq = getTimerIrq(pTimer);
HpetState* pThis = pTimer->CTX_SUFF(pHpet);
/** @todo: is it correct? */
if ((pTimer->u64Config & HPET_TN_ENABLE) &&
(pThis->u64Config & HPET_CFG_ENABLE))
{
Log4(("HPET: raising IRQ %d\n", irq));
if ((pTimer->u64Config & HPET_TIMER_TYPE_LEVEL) == 0)
{
pThis->u64Isr |= (uint64_t)(1 << pTimer->u8TimerNumber);
pThis->pHpetHlpR3->pfnSetIrq(pThis->CTX_SUFF(pDevIns), irq, PDM_IRQ_LEVEL_FLIP_FLOP);
}
}
}
/**
* Device timer callback function.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
* @param pvUser Pointer to the HPET timer state.
*/
static DECLCALLBACK(void) hpetTimer(PPDMDEVINS pDevIns,
PTMTIMER pTmTimer,
void * pvUser)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
HpetTimer *pTimer = (HpetTimer *)pvUser;
uint64_t u64Period = pTimer->u64Period;
uint64_t u64CurTick = hpetGetTicks(pThis);
uint64_t u64Diff;
int rc;
if (pTimer == NULL)
return;
/* Lock in R3 must either block or succeed */
rc = hpetLock(pThis, VERR_IGNORED);
AssertLogRelRCReturnVoid(rc);
if ((pTimer->u64Config & HPET_TN_PERIODIC) && (u64Period != 0))
{
hpetAdjustComparator(pTimer, u64CurTick);
u64Diff = hpetComputeDiff(pTimer, u64CurTick);
Log4(("HPET: periodical: next in %lld\n", hpetTicksToNs(u64Diff)));
TMTimerSetNano(pTmTimer, hpetTicksToNs(u64Diff));
}
else if ((pTimer->u64Config & HPET_TN_32BIT) &&
!(pTimer->u64Config & HPET_TN_PERIODIC))
{
if (pTimer->u8Wrap)
{
u64Diff = hpetComputeDiff(pTimer, u64CurTick);
TMTimerSetNano(pTmTimer, hpetTicksToNs(u64Diff));
pTimer->u8Wrap = 0;
}
}
/* Should it really be under lock, does it really matter? */
irqUpdate(pTimer);
hpetUnlock(pThis);
}
/**
* Relocation notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* @param offDelta The delta relative to the old address.
*/
static DECLCALLBACK(void) hpetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
unsigned i;
LogFlow(("hpetRelocate:\n"));
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pThis->pHpetHlpRC = pThis->pHpetHlpR3->pfnGetRCHelpers(pDevIns);
for (i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
{
HpetTimer *pTm = &pThis->aTimers[i];
if (pTm->pTimerR3)
pTm->pTimerRC = TMTimerRCPtr(pTm->pTimerR3);
pTm->pHpetRC = PDMINS_2_DATA_RCPTR(pDevIns);
}
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static DECLCALLBACK(void) hpetReset(PPDMDEVINS pDevIns)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
unsigned i;
LogFlow(("hpetReset:\n"));
pThis->u64Config = 0;
for (i = 0; i < HPET_NUM_TIMERS; i++)
{
HpetTimer *pTimer = &pThis->aTimers[i];
pTimer->u8TimerNumber = i;
pTimer->u64Cmp = ~0ULL;
/* capable of periodic operations and 64-bits */
pTimer->u64Config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
/* We can do all IRQs */
uint32_t u32RoutingCap = 0xffffffff;
pTimer->u64Config |= ((uint64_t)u32RoutingCap) << 32;
pTimer->u64Period = 0ULL;
pTimer->u8Wrap = 0;
}
pThis->u64HpetCounter = 0ULL;
pThis->u64HpetOffset = 0ULL;
/* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
uint32_t u32Vendor = 0x8086;
uint32_t u32Caps =
(1 << 15) /* LEG_RT_CAP, LegacyReplacementRoute capable */ |
(1 << 13) /* COUNTER_SIZE_CAP, main counter is 64-bit capable */ |
((HPET_NUM_TIMERS-1) << 8) /* NUM_TIM_CAP, number of timers -1 */ |
1 /* REV_ID, revision, must not be 0 */;
pThis->u64Capabilities = (u32Vendor << 16) | u32Caps;
pThis->u64Capabilities |= ((uint64_t)(HPET_CLK_PERIOD) << 32);
}
/**
* Initialization routine.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
static int hpetInit(PPDMDEVINS pDevIns)
{
unsigned i;
int rc;
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
memset(pThis, 0, sizeof(*pThis));
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
for (i = 0; i < HPET_NUM_TIMERS; i++)
{
HpetTimer *timer = &pThis->aTimers[i];
timer->pHpetR3 = pThis;
timer->pHpetR0 = PDMINS_2_DATA_R0PTR(pDevIns);
timer->pHpetRC = PDMINS_2_DATA_RCPTR(pDevIns);
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hpetTimer, timer,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "HPET Timer",
&pThis->aTimers[i].pTimerR3);
if (RT_FAILURE(rc))
return rc;
pThis->aTimers[i].pTimerRC = TMTimerRCPtr(pThis->aTimers[i].pTimerR3);
pThis->aTimers[i].pTimerR0 = TMTimerR0Ptr(pThis->aTimers[i].pTimerR3);
}
hpetReset(pDevIns);
return VINF_SUCCESS;
}
/**
* Info handler, device version.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) hpetInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
int i;
pHlp->pfnPrintf(pHlp,
"HPET status:\n"
" config = %016RX64\n"
" offset = %016RX64 counter = %016RX64 isr = %016RX64\n"
" legacy mode is %s\n",
pThis->u64Config,
pThis->u64HpetOffset, pThis->u64HpetCounter, pThis->u64Isr,
(pThis->u64Config & HPET_CFG_LEGACY) ? "on" : "off");
pHlp->pfnPrintf(pHlp,
"Timers:\n");
for (i = 0; i < HPET_NUM_TIMERS; i++)
{
pHlp->pfnPrintf(pHlp, " %d: comparator=%016RX64 period(hidden)=%016RX64 cfg=%016RX64\n",
pThis->aTimers[i].u8TimerNumber,
pThis->aTimers[i].u64Cmp,
pThis->aTimers[i].u64Period,
pThis->aTimers[i].u64Config);
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
static DECLCALLBACK(int) hpetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
HpetState *pThis = PDMINS_2_DATA(pDevIns, HpetState *);
int rc;
bool fRCEnabled = false;
bool fR0Enabled = false;
PDMHPETREG HpetReg;
/* Only one HPET device now */
Assert(iInstance == 0);
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfg, "GCEnabled\0" "R0Enabled\0"))
return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
/* Query configuration. */
#if 1
rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fRCEnabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"GCEnabled\" as a bool failed"));
rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: failed to read R0Enabled as boolean"));
#endif
/* Initialize the device state */
rc = hpetInit(pDevIns);
if (RT_FAILURE(rc))
return rc;
pThis->pDevInsR3 = pDevIns;
pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
/*
* Register the HPET and get helpers.
*/
HpetReg.u32Version = PDM_HPETREG_VERSION;
rc = PDMDevHlpHPETRegister(pDevIns, &HpetReg, &pThis->pHpetHlpR3);
if (RT_FAILURE(rc))
{
AssertMsgRC(rc, ("Cannot HPETRegister: %Rrc\n", rc));
return rc;
}
/*
* Initialize critical section.
*/
rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csLock, RT_SRC_POS, "HPET");
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc, N_("HPET cannot initialize critical section"));
/*
* Register the MMIO range, PDM API requests page aligned
* addresses and sizes.
*/
rc = PDMDevHlpMMIORegister(pDevIns, HPET_BASE, 0x1000, pThis,
hpetMMIOWrite, hpetMMIORead, NULL, "HPET Memory");
if (RT_FAILURE(rc))
{
AssertMsgRC(rc, ("Cannot register MMIO: %Rrc\n", rc));
return rc;
}
if (fRCEnabled)
{
rc = PDMDevHlpMMIORegisterRC(pDevIns, HPET_BASE, 0x1000, 0,
"hpetMMIOWrite", "hpetMMIORead", NULL);
if (RT_FAILURE(rc))
return rc;
pThis->pHpetHlpRC = pThis->pHpetHlpR3->pfnGetRCHelpers(pDevIns);
if (!pThis->pHpetHlpRC)
{
AssertReleaseMsgFailed(("cannot get RC helper\n"));
return VERR_INTERNAL_ERROR;
}
}
if (fR0Enabled)
{
rc = PDMDevHlpMMIORegisterR0(pDevIns, HPET_BASE, 0x1000, 0,
"hpetMMIOWrite", "hpetMMIORead", NULL);
if (RT_FAILURE(rc))
return rc;
pThis->pHpetHlpR0 = pThis->pHpetHlpR3->pfnGetR0Helpers(pDevIns);
if (!pThis->pHpetHlpR0)
{
AssertReleaseMsgFailed(("cannot get R0 helper\n"));
return VERR_INTERNAL_ERROR;
}
}
/* Register SSM callbacks */
rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetLiveExec, hpetSaveExec, hpetLoadExec);
if (RT_FAILURE(rc))
return rc;
/**
* @todo Register statistics.
*/
PDMDevHlpDBGFInfoRegister(pDevIns, "hpet", "Display HPET status. (no arguments)", hpetInfo);
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceHPET =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szName */
"hpet",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
" High Precision Event Timer (HPET) Device",
/* fFlags */
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,
/* fClass */
PDM_DEVREG_CLASS_PIT,
/* cMaxInstances */
1,
/* cbInstance */
sizeof(HpetState),
/* pfnConstruct */
hpetConstruct,
/* pfnDestruct */
NULL,
/* pfnRelocate */
hpetRelocate,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
hpetReset,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
PDM_DEVREG_VERSION
};
#endif /* IN_RING3 */
#endif /* VBOX_DEVICE_STRUCT_TESTCASE */