DevPit-i8254.cpp revision f20f327b65009074292a4b9ad44a02b6bfb2de8a
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * DevPIT-i8254 - Intel 8254 Programmable Interval Timer (PIT) And Dummy Speaker Device.
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
c58f1213e628a545081c70e26c6b67a841cff880vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * available from http://www.virtualbox.org. This file is free software;
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * you can redistribute it and/or modify it under the terms of the GNU
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * General Public License (GPL) as published by the Free Software
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * additional information or have any questions.
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * --------------------------------------------------------------------
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * This code is based on:
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * QEMU 8253/8254 interval timer emulation
4651430e55b9df9726347e3e3968618e540fe729vboxsync * Copyright (c) 2003-2004 Fabrice Bellard
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Permission is hereby granted, free of charge, to any person obtaining a copy
4651430e55b9df9726347e3e3968618e540fe729vboxsync * of this software and associated documentation files (the "Software"), to deal
4651430e55b9df9726347e3e3968618e540fe729vboxsync * in the Software without restriction, including without limitation the rights
4651430e55b9df9726347e3e3968618e540fe729vboxsync * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
4651430e55b9df9726347e3e3968618e540fe729vboxsync * copies of the Software, and to permit persons to whom the Software is
4651430e55b9df9726347e3e3968618e540fe729vboxsync * furnished to do so, subject to the following conditions:
4651430e55b9df9726347e3e3968618e540fe729vboxsync * The above copyright notice and this permission notice shall be included in
4651430e55b9df9726347e3e3968618e540fe729vboxsync * all copies or substantial portions of the Software.
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4651430e55b9df9726347e3e3968618e540fe729vboxsync * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5ff3fa0492332325f57e80636321619e2224027evboxsync * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5ff3fa0492332325f57e80636321619e2224027evboxsync * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
5ff3fa0492332325f57e80636321619e2224027evboxsync * THE SOFTWARE.
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync/*******************************************************************************
4651430e55b9df9726347e3e3968618e540fe729vboxsync* Header Files *
4651430e55b9df9726347e3e3968618e540fe729vboxsync*******************************************************************************/
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync/*******************************************************************************
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync* Defined Constants And Macros *
c7a378ed2fbad681c0b674351d698ef20a368935vboxsync*******************************************************************************/
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync/** The PIT frequency. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync/** The version of the saved state. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync/** @def FAKE_REFRESH_CLOCK
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * Define this to flip the 15usec refresh bit on every read.
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync * If not defined, it will be flipped correctly. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync//#define FAKE_REFRESH_CLOCK
30f7bd6bf198ae0489df375e5a17cb086acb30fbvboxsync/*******************************************************************************
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync* Structures and Typedefs *
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync*******************************************************************************/
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Pointer to the instance data - R3 Ptr. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** The timer - R3 Ptr. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Pointer to the instance data - R0 Ptr. */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /** The timer - R0 Ptr. */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /** Pointer to the instance data - RC Ptr. */
5ff3fa0492332325f57e80636321619e2224027evboxsync /** The timer - RC Ptr. */
5ff3fa0492332325f57e80636321619e2224027evboxsync /** The virtual time stamp at the last reload. (only used in mode 2 for now) */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /** The actual time of the next tick.
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync * As apposed to the next_transition_time which contains the correct time of the next tick. */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /** (count_load_time is only set by TMTimerGet() which returns uint64_t) */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /* irq handling */
5a6bbb9c0d896e804f267c6919f52158a420b998vboxsync /** Number of release log entries. Used to prevent floading. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsynctypedef struct PITState
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Speaker data. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Speaker dummy. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Pointer to the device instance. */
30f7bd6bf198ae0489df375e5a17cb086acb30fbvboxsync /** Number of IRQs that's been raised. */
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /** Profiling the timer callback handler. */
4fd4258d68911eacb0b3d1f096b778d19da4e55avboxsync/*******************************************************************************
4fd4258d68911eacb0b3d1f096b778d19da4e55avboxsync* Internal Functions *
f72cbd6a549c34992fa79cce84600fe2b92b3299vboxsync*******************************************************************************/
4fd4258d68911eacb0b3d1f096b778d19da4e55avboxsyncPDMBOTHCBDECL(int) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
4fd4258d68911eacb0b3d1f096b778d19da4e55avboxsyncPDMBOTHCBDECL(int) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
f72cbd6a549c34992fa79cce84600fe2b92b3299vboxsyncPDMBOTHCBDECL(int) pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
49207a1552ff3752904e36b9269f08fe643ba871vboxsyncPDMBOTHCBDECL(int) pitIOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
49207a1552ff3752904e36b9269f08fe643ba871vboxsyncstatic void pit_irq_timer_update(PITChannelState *s, uint64_t current_time);
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync PTMTIMER pTimer = s->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
9c3f21ab6ba51a44403b662e1691293ab266e5e3vboxsync d = ASMMultU64ByU32DivByU32(d - s->u64ReloadTS, s->count, s->u64NextTS - s->u64ReloadTS);
9c3f21ab6ba51a44403b662e1691293ab266e5e3vboxsync if (d >= s->count)
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync return s->count - d;
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync d = ASMMultU64ByU32DivByU32(TMTimerGet(pTimer) - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync switch(s->mode) {
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync /* XXX: may be incorrect for odd counts */
6c5e2fff0e3fdfc7c3f3fb2e7b7ec8ebb2223cecvboxsync /** @todo check that we don't return 0, in most modes (all?) the counter shouldn't be zero. */
6c5e2fff0e3fdfc7c3f3fb2e7b7ec8ebb2223cecvboxsync/* get pit output bit */
561574402775590253d11504354bfe21d80e4858vboxsyncstatic int pit_get_out1(PITChannelState *s, int64_t current_time)
615105a2b89b7dd89a76504e6a9b8e099704c0d9vboxsync PTMTIMER pTimer = s->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync d = ASMMultU64ByU32DivByU32(current_time - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
0b6e534f55fcb5870df42b58ae354ad5fdbda66avboxsync switch(s->mode) {
05c28d9d4557bed6e320dfee1acca69408bc3c15vboxsync Log2(("pit_get_out1: d=%llx c=%x %x \n", d, s->count, (unsigned)(d % s->count)));
f6adf1a86574758258baa232172c965aed0d848dvboxsync if ((d % s->count) == 0 && d != 0)
out = 0;
return out;
return s->gate;
if (!s->count_latched) {
s->latched_count, ASMMultU64ByU32DivByU32(s->count - s->latched_count, 1000000000, PIT_FREQ), s->count, s->mode));
#ifdef IN_RING3
switch(s->mode) {
if (val == 0)
switch(s->mode) {
if (d < s->count)
if ((d - base) == 0 && d != 0)
if (d < s->count)
else if (d == s->count)
next_time = s->count_load_time + ASMMultU64ByU32DivByU32(next_time, TMTimerGetFreq(pTimer), PIT_FREQ);
return next_time;
int irq_level;
/* We just flip-flop the irq level to save that extra timer call, which isn't generally required (we haven't served it for months). */
if (irq_level)
if (irq_level)
PDMBOTHCBDECL(int) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
return VERR_IOM_IOPORT_UNUSED;
int ret;
if (s->status_latched)
s->status_latched = 0;
else if (s->count_latched)
switch (s->count_latched)
case RW_STATE_LSB:
s->count_latched = 0;
case RW_STATE_MSB:
s->count_latched = 0;
case RW_STATE_WORD0:
int count;
switch (s->read_state)
case RW_STATE_LSB:
case RW_STATE_MSB:
case RW_STATE_WORD0:
case RW_STATE_WORD1:
return VINF_SUCCESS;
PDMBOTHCBDECL(int) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
return VINF_SUCCESS;
pit_latch_count(s);
| s->bcd;
if (access == 0)
pit_latch_count(s);
#ifndef IN_RING3
return VINF_IOM_HC_IOPORT_WRITE;
switch(s->write_state)
case RW_STATE_LSB:
case RW_STATE_MSB:
case RW_STATE_WORD0:
case RW_STATE_WORD1:
return VINF_SUCCESS;
PDMBOTHCBDECL(int) pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
#ifdef FAKE_REFRESH_CLOCK
return VINF_SUCCESS;
return VERR_IOM_IOPORT_UNUSED;
#ifdef IN_RING3
PDMBOTHCBDECL(int) pitIOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
return VINF_SUCCESS;
#ifdef FAKE_REFRESH_CLOCK
static DECLCALLBACK(int) pitLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
#ifdef FAKE_REFRESH_CLOCK
s->latched_count = 0;
s->count_latched = 0;
s->status_latched = 0;
s->status = 0;
s->read_state = 0;
s->write_state = 0;
s->write_latch = 0;
s->rw_mode = 0;
s->bcd = 0;
s->cRelLogEntries = 0;
pit_load_count(s, 0);
#ifdef FAKE_REFRESH_CLOCK
int rc;
bool fSpeaker;
bool fGCEnabled;
bool fR0Enabled;
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, pitTimer, "i8254 Programmable Interval Timer",
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 4, NULL, pitIOPortWrite, pitIOPortRead, NULL, NULL, "i8254 Programmable Interval Timer");
return rc;
if (fGCEnabled)
rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
return rc;
if (fR0Enabled)
rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
return rc;
if (fSpeaker)
rc = PDMDevHlpIOPortRegister(pDevIns, 0x61, 1, NULL, pitIOPortSpeakerWrite, pitIOPortSpeakerRead, NULL, NULL, "PC Speaker");
return rc;
if (fGCEnabled)
rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x61, 1, 0, NULL, "pitIOPortSpeakerRead", NULL, NULL, "PC Speaker");
return rc;
rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, PIT_SAVED_STATE_VERSION, sizeof(*pThis),
return rc;
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITIrq, STAMTYPE_COUNTER, "/TM/PIT/Irq", STAMUNIT_OCCURENCES, "The number of times a timer interrupt was triggered.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITHandler, STAMTYPE_PROFILE, "/TM/PIT/Handler", STAMUNIT_TICKS_PER_CALL, "Profiling timer callback handler.");
return VINF_SUCCESS;
PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
sizeof(PITState),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,